diff --git a/TODO.txt b/TODO.txt index 2f5634e..c60034a 100644 --- a/TODO.txt +++ b/TODO.txt @@ -1,5 +1,4 @@ * add disk quota * preload: dont load file if quota exceeded * add LRU cache -* implement directory listing cache * clear cache if file sizes differ and > 0 diff --git a/pkg/fs/dir.go b/pkg/fs/dir.go index 1bf1995..1f96628 100644 --- a/pkg/fs/dir.go +++ b/pkg/fs/dir.go @@ -3,12 +3,47 @@ package fs import ( + "errors" "io" "os" + "sync" + "time" ) +var NoCacheEntry = errors.New("no cache entry") + +type DirCacheEntry struct { + mtime time.Time + fi []os.FileInfo +} + +type DirCache struct { + mu sync.RWMutex + m map[string]*DirCacheEntry +} + +func NewDirCache() *DirCache { + return &DirCache{m: make(map[string]*DirCacheEntry)} +} + +func (dc *DirCache) Get(name string) (*DirCacheEntry, error) { + dc.mu.RLock() + defer dc.mu.RUnlock() + if dce, ok := dc.m[name]; ok { + return dce, nil + } + return nil, NoCacheEntry +} + +func (dc *DirCache) Set(name string, dce *DirCacheEntry) { + dc.mu.Lock() + defer dc.mu.Unlock() + dc.m[name] = dce +} + type Dir struct { - f *os.File + f *os.File + dc *DirCache } func (f *Dir) Read(_ []byte) (int, error) { @@ -19,8 +54,23 @@ func (f *Dir) Seek(_ int64, _ int) (int64, error) { return 0, io.EOF } -func (f *Dir) Readdir(count int) ([]os.FileInfo, error) { - return f.f.Readdir(count) +func (f *Dir) Readdir(_ int) (fi []os.FileInfo, err error) { + i, err := f.Stat() + if err != nil { + return + } + name := f.f.Name() + dce, err := f.dc.Get(name) + if err == NoCacheEntry || i.ModTime().After(dce.mtime) { + fi, err = f.f.Readdir(0) + if err != nil { + return + } + dce = &DirCacheEntry{i.ModTime(), fi} + f.dc.Set(name, dce) + } + fi = dce.fi + return } func (f *Dir) Stat() (os.FileInfo, error) { diff --git a/pkg/fs/fs.go b/pkg/fs/fs.go index a21f482..0f72c3d 100644 --- a/pkg/fs/fs.go +++ b/pkg/fs/fs.go @@ -28,6 +28,7 @@ type FS struct { dst string mdf *os.File jenc *json.Encoder + dc *DirCache mm map[string]*Metadata prem map[string]func() cancel func() @@ -56,6 +57,7 @@ func NewFS(src, dst, metadata string, log logr.Logger) (fs *FS, err error) { src: src, dst: dst, mdf: mdf, + dc: NewDirCache(), mm: make(map[string]*Metadata), prem: make(map[string]func()), jenc: json.NewEncoder(mdf), @@ -197,7 +199,7 @@ func (fs *FS) Open(name string) (f http.File, err error) { offline = true log.V(2).Info("dir offline mode", "path", dp) } - return &Dir{f: sf}, err + return &Dir{f: sf, dc: fs.dc}, err } md := fs.metadata(name, sfi.Size()) return &File{ diff --git a/pkg/srv/srv.go b/pkg/srv/srv.go index 8510e05..235818d 100644 --- a/pkg/srv/srv.go +++ b/pkg/srv/srv.go @@ -67,10 +67,10 @@ func (fs *FileServer) ServeHTTP(w http.ResponseWriter, r *http.Request) { } if !d.IsDir() { - if option == "p" || option == "sp" { + if option == "p" || option == "s" { if option == "p" { fs.fs.Preload(p) - } else { + } else if option == "s" { fs.fs.CancelPreload(p) } rdir := "/" diff --git a/pkg/srv/templates.go b/pkg/srv/templates.go index 3eac8ee..382788e 100644 --- a/pkg/srv/templates.go +++ b/pkg/srv/templates.go @@ -30,7 +30,7 @@ var ( {{end -}} {{range $s := .Files -}}