130 lines
2.4 KiB
Go
130 lines
2.4 KiB
Go
// Copyright (C) 2022 Marius Schellenberger
|
|
|
|
package fs
|
|
|
|
import (
|
|
"errors"
|
|
"io"
|
|
"os"
|
|
|
|
"github.com/go-logr/logr"
|
|
)
|
|
|
|
type File struct {
|
|
log logr.Logger
|
|
f *os.File
|
|
md *Metadata
|
|
offset int64
|
|
offline bool
|
|
}
|
|
|
|
type preload struct {
|
|
f *File
|
|
skipped int64
|
|
written int64
|
|
}
|
|
|
|
func (p *preload) Read(data []byte) (n int, err error) {
|
|
if p.f.offset >= p.f.size() {
|
|
return 0, io.EOF
|
|
}
|
|
n = len(data)
|
|
if p.f.hasChunk(n) {
|
|
p.skipped++
|
|
_, err = p.f.Seek(int64(n), io.SeekCurrent)
|
|
return
|
|
}
|
|
n, err = p.f.readWithoutCache(data)
|
|
p.written++
|
|
return
|
|
}
|
|
|
|
func (f *File) Preload(unlock func()) {
|
|
log := f.log
|
|
if f.offline {
|
|
log.V(2).Error(errors.New("no preload in offline mode"), "error preloading file")
|
|
unlock()
|
|
return
|
|
}
|
|
log.V(2).Info("preload started")
|
|
p := &preload{f: f}
|
|
_, err := io.Copy(io.Discard, p)
|
|
if err != nil && err != io.EOF {
|
|
log.Error(err, "error preloading file")
|
|
}
|
|
log.V(2).Info("preload finished", "skipped", p.skipped, "written", p.written)
|
|
unlock()
|
|
}
|
|
|
|
func (f *File) Read(p []byte) (n int, err error) {
|
|
log := f.log
|
|
if f.hasChunk(len(p)) {
|
|
n, err = f.md.ReadAt(p, f.offset)
|
|
if err != nil {
|
|
log.Error(err, "error reading cache file")
|
|
return
|
|
}
|
|
_, err = f.Seek(int64(n), io.SeekCurrent)
|
|
if err != nil {
|
|
log.Error(err, "error seeking source file")
|
|
}
|
|
return
|
|
}
|
|
return f.readWithoutCache(p)
|
|
}
|
|
|
|
func (f *File) size() int64 {
|
|
return f.md.Size
|
|
}
|
|
|
|
func (f *File) hasChunk(n int) bool {
|
|
return f.md.HasChunk(f.offset, n)
|
|
}
|
|
|
|
func (f *File) readWithoutCache(p []byte) (n int, err error) {
|
|
if f.offline {
|
|
return 0, io.EOF
|
|
}
|
|
log := f.log
|
|
n, err = f.f.Read(p)
|
|
if n > 0 {
|
|
if n, err := f.md.WriteAt(p[:n], f.offset); err != nil {
|
|
log.Error(err, "error writing cache file")
|
|
return n, err
|
|
}
|
|
}
|
|
if err != nil {
|
|
log.Error(err, "error reading source file")
|
|
return
|
|
}
|
|
f.md.AddChunk(f.offset, n)
|
|
f.offset += int64(n)
|
|
return
|
|
}
|
|
|
|
func (f *File) Seek(offset int64, whence int) (n int64, err error) {
|
|
n, err = f.f.Seek(offset, whence)
|
|
if err != nil {
|
|
f.log.Error(err, "error seeking source file")
|
|
return
|
|
}
|
|
switch whence {
|
|
case io.SeekStart:
|
|
f.offset = offset
|
|
case io.SeekCurrent:
|
|
f.offset += offset
|
|
}
|
|
return
|
|
}
|
|
|
|
func (f *File) Readdir(count int) ([]os.FileInfo, error) {
|
|
return f.f.Readdir(count)
|
|
}
|
|
|
|
func (f *File) Stat() (os.FileInfo, error) {
|
|
return f.f.Stat()
|
|
}
|
|
|
|
func (f *File) Close() error {
|
|
return f.f.Close()
|
|
}
|