mirror of
https://github.com/containers/podman.git
synced 2025-06-14 20:44:53 +08:00

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>
67 lines
1.6 KiB
Go
67 lines
1.6 KiB
Go
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
|
|
}
|