diff options
author | Brent Baude <bbaude@redhat.com> | 2020-02-18 08:30:56 -0600 |
---|---|---|
committer | Brent Baude <bbaude@redhat.com> | 2020-02-20 10:22:45 -0600 |
commit | e3b31a3ab80c5f681fb157f0e10eb2d3819ef18b (patch) | |
tree | bc60e9b8f014d7de1d249aa23405320851329114 /libpod/logs/reversereader/reversereader.go | |
parent | 8c0df03debe064f0bf6595a54d48349c79385e8b (diff) | |
download | podman-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.go | 66 |
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 +} |