summaryrefslogtreecommitdiff
path: root/libpod/logs/reversereader/reversereader.go
diff options
context:
space:
mode:
authorBrent Baude <bbaude@redhat.com>2020-02-18 08:30:56 -0600
committerBrent Baude <bbaude@redhat.com>2020-02-20 10:22:45 -0600
commite3b31a3ab80c5f681fb157f0e10eb2d3819ef18b (patch)
treebc60e9b8f014d7de1d249aa23405320851329114 /libpod/logs/reversereader/reversereader.go
parent8c0df03debe064f0bf6595a54d48349c79385e8b (diff)
downloadpodman-e3b31a3ab80c5f681fb157f0e10eb2d3819ef18b.tar.gz
podman-e3b31a3ab80c5f681fb157f0e10eb2d3819ef18b.tar.bz2
podman-e3b31a3ab80c5f681fb157f0e10eb2d3819ef18b.zip
implement reverse reader for log reads
in cases where the log file exceeds the available memory of a system, we had a bug that triggered an oom because the entire logfile was being read when the tail parameter was given. this reads in chunks and is more or less memory safe. fixes: #5131 Signed-off-by: Brent Baude <bbaude@redhat.com>
Diffstat (limited to 'libpod/logs/reversereader/reversereader.go')
-rw-r--r--libpod/logs/reversereader/reversereader.go66
1 files changed, 66 insertions, 0 deletions
diff --git a/libpod/logs/reversereader/reversereader.go b/libpod/logs/reversereader/reversereader.go
new file mode 100644
index 000000000..72d9ad975
--- /dev/null
+++ b/libpod/logs/reversereader/reversereader.go
@@ -0,0 +1,66 @@
+package reversereader
+
+import (
+ "io"
+ "os"
+
+ "github.com/pkg/errors"
+)
+
+// ReverseReader structure for reading a file backwards
+type ReverseReader struct {
+ reader *os.File
+ offset int64
+ readSize int64
+}
+
+// NewReverseReader returns a reader that reads from the end of a file
+// rather than the beginning. It sets the readsize to pagesize and determines
+// the first offset using using modulus.
+func NewReverseReader(reader *os.File) (*ReverseReader, error) {
+ // pagesize should be safe for memory use and file reads should be on page
+ // boundaries as well
+ pageSize := int64(os.Getpagesize())
+ stat, err := reader.Stat()
+ if err != nil {
+ return nil, err
+ }
+ // figure out the last page boundary
+ remainder := stat.Size() % pageSize
+ end, err := reader.Seek(0, 2)
+ if err != nil {
+ return nil, err
+ }
+ // set offset (starting position) to the last page boundary or
+ // zero if fits in one page
+ startOffset := end - remainder
+ if startOffset < 0 {
+ startOffset = 0
+ }
+ rr := ReverseReader{
+ reader: reader,
+ offset: startOffset,
+ readSize: pageSize,
+ }
+ return &rr, nil
+}
+
+// ReverseReader reads from a given offset to the previous offset and
+// then sets the newoff set one pagesize less than the previous read.
+func (r *ReverseReader) Read() (string, error) {
+ if r.offset < 0 {
+ return "", errors.Wrap(io.EOF, "at beginning of file")
+ }
+ // Read from given offset
+ b := make([]byte, r.readSize)
+ n, err := r.reader.ReadAt(b, r.offset)
+ if err != nil && errors.Cause(err) != io.EOF {
+ return "", err
+ }
+ if int64(n) < r.readSize {
+ b = b[0:n]
+ }
+ // Set to the next page boundary
+ r.offset = -r.readSize
+ return string(b), nil
+}