From 74ab10358a3dae3835a20a56d20026db96f21330 Mon Sep 17 00:00:00 2001 From: ston1th Date: Thu, 17 Mar 2022 21:10:23 +0100 Subject: [PATCH] added directory cleanup --- cmd/cachefs/main.go | 1 + pkg/fs/dir.go | 26 +++++++++++++++++++++--- pkg/fs/fs.go | 29 ++++++++++++++++++++++++--- pkg/srv/interceptor.go | 45 +++++++++++++++++++++++++++++++----------- pkg/srv/templates.go | 8 ++++---- 5 files changed, 87 insertions(+), 22 deletions(-) diff --git a/cmd/cachefs/main.go b/cmd/cachefs/main.go index c3b7893..8072c1b 100644 --- a/cmd/cachefs/main.go +++ b/cmd/cachefs/main.go @@ -88,4 +88,5 @@ func main() { } filesystem.Close() log.Info("cachefs shutdown completed") + os.Exit(0) } diff --git a/pkg/fs/dir.go b/pkg/fs/dir.go index 1f96628..113b7fa 100644 --- a/pkg/fs/dir.go +++ b/pkg/fs/dir.go @@ -8,6 +8,8 @@ import ( "os" "sync" "time" + + "github.com/go-logr/logr" ) var NoCacheEntry = errors.New("no cache entry") @@ -42,8 +44,9 @@ func (dc *DirCache) Set(name string, dce *DirCacheEntry) { } type Dir struct { - f *os.File - dc *DirCache + log logr.Logger + f *os.File + dc *DirCache } func (f *Dir) Read(_ []byte) (int, error) { @@ -54,6 +57,19 @@ func (f *Dir) Seek(_ int64, _ int) (int64, error) { return 0, io.EOF } +func readDir(f *os.File) (fi []os.FileInfo, err error) { + ent, err := f.ReadDir(0) + if err != nil { + return + } + fi = make([]os.FileInfo, len(ent)) + for j, e := range ent { + i, _ := e.Info() + fi[j] = i + } + return +} + func (f *Dir) Readdir(_ int) (fi []os.FileInfo, err error) { i, err := f.Stat() if err != nil { @@ -61,13 +77,17 @@ func (f *Dir) Readdir(_ int) (fi []os.FileInfo, err error) { } name := f.f.Name() dce, err := f.dc.Get(name) + log := f.log if err == NoCacheEntry || i.ModTime().After(dce.mtime) { - fi, err = f.f.Readdir(0) + log.V(3).Info("cache miss") + fi, err = readDir(f.f) if err != nil { return } dce = &DirCacheEntry{i.ModTime(), fi} f.dc.Set(name, dce) + } else { + log.V(3).Info("cache hit") } fi = dce.fi return diff --git a/pkg/fs/fs.go b/pkg/fs/fs.go index af3f575..102e92b 100644 --- a/pkg/fs/fs.go +++ b/pkg/fs/fs.go @@ -205,7 +205,7 @@ func (fs *FS) Open(name string) (f http.File, err error) { offline = true log.V(2).Info("dir offline mode", "path", dp) } - f = &Dir{f: sf, dc: fs.dc} + f = &Dir{log: log.WithName("dir"), f: sf, dc: fs.dc} return } md := fs.metadata(name, sfi.Size()) @@ -295,10 +295,32 @@ func (fs *FS) flusher(ctx context.Context) { return case <-time.After(time.Minute * 5): } + fs.cleanupEmptyDirs() fs.flushMetadata() } } +func (fs *FS) cleanupEmptyDirs() { + filepath.WalkDir(fs.dst, func(path string, d stdfs.DirEntry, err error) error { + if err == nil && d.IsDir() { + if path == fs.dst { + return nil + } + empty, err := dirEmpty(path) + if err != nil || !empty { + return nil + } + log := fs.log.WithValues("dir", path) + log.V(2).Info("removing empty dir") + err = os.Remove(path) + if err != nil { + log.Error(err, "failed to remove empty dir") + } + } + return nil + }) +} + func (fs *FS) flushMetadata() { log := fs.log fs.mu.Lock() @@ -341,10 +363,11 @@ func dirEmpty(name string) (bool, error) { if err != nil { return false, err } - defer f.Close() - _, err = f.Readdir(1) + _, err = f.ReadDir(1) if err == io.EOF { + f.Close() return true, nil } + f.Close() return false, err } diff --git a/pkg/srv/interceptor.go b/pkg/srv/interceptor.go index ec50cdf..5e22ccb 100644 --- a/pkg/srv/interceptor.go +++ b/pkg/srv/interceptor.go @@ -5,8 +5,8 @@ package srv import ( "bytes" "encoding/xml" + "html/template" "net/http" - "sort" "strings" "cachefs/pkg/fs" @@ -41,12 +41,22 @@ func (s *statusInterceptor) Status() int { type dirContents struct { AllPaths []string `xml:"a"` - Dirs []string `xml:"-"` + Dirs dirs `xml:"-"` Files files `xml:"-"` } +type dir struct { + Name template.HTML + URI template.HTML +} + +type dirs []dir + +func (dirs) Less(i, j dir) bool { return i.Name < j.Name } + type file struct { - Name string + Name template.HTML + URI template.HTML Anchor string Status int } @@ -80,25 +90,36 @@ func (r *responseInterceptor) Status() int { return r.status } -func (r *responseInterceptor) GetPaths(path string, fs *fs.FS) (dir dirContents, err error) { - err = xml.Unmarshal(r.buf.Bytes(), &dir) +func (r *responseInterceptor) GetPaths(path string, fs *fs.FS) (dc dirContents, err error) { + buf := r.buf.Bytes() + buf = bytes.ReplaceAll(buf, []byte{'&'}, []byte("&")) + err = xml.Unmarshal(buf, &dc) if err != nil { return } path = strings.TrimSuffix(path, "/") - for _, p := range dir.AllPaths { + for _, p := range dc.AllPaths { + p = strings.ReplaceAll(p, "&", "&") + p = strings.ReplaceAll(p, "'", "'") + name := template.HTML(p) + full := path + "/" + p + uri := template.HTML(full) if p[len(p)-1] == '/' { - dir.Dirs = append(dir.Dirs, p) + dc.Dirs = append(dc.Dirs, dir{ + Name: name, + URI: uri, + }) } else { - dir.Files = append(dir.Files, file{ - Name: p, + dc.Files = append(dc.Files, file{ + Name: name, + URI: uri, Anchor: anchor(p), - Status: fs.CacheStatus(path + "/" + p), + Status: fs.CacheStatus(full), }) } } - sort.Strings(dir.Dirs) - slices.SortFunc(dir.Files, dir.Files.Less) + slices.SortFunc(dc.Dirs, dc.Dirs.Less) + slices.SortFunc(dc.Files, dc.Files.Less) return } diff --git a/pkg/srv/templates.go b/pkg/srv/templates.go index f97a229..958d769 100644 --- a/pkg/srv/templates.go +++ b/pkg/srv/templates.go @@ -24,13 +24,13 @@ var ( - + {{range $s := .Dirs -}} - + {{end -}} {{range $s := .Files -}} - - + + {{end -}}
PathOptions
../
../[dir]
{{$s}}
{{$s.Name}}[dir]
{{$s.Name}}[v] [n] [p] [s] {{if ne $s.Status -1}}{{$s.Status}}%{{end}}
{{$s.Name}}[v] [n] [p] [s] {{if ne $s.Status -1}}{{$s.Status}}%{{end}}