diff --git a/cmd/cachefs/main.go b/cmd/cachefs/main.go index f63b0eb..3ebe9a5 100644 --- a/cmd/cachefs/main.go +++ b/cmd/cachefs/main.go @@ -64,8 +64,8 @@ func main() { slog, ), } + log.Info("starting main server", "addr", c.Server.HTTP.Addr) go func() { - log.Info("starting main server", "addr", c.Server.HTTP.Addr) err := s.ListenAndServe() if err != nil && err != http.ErrServerClosed { klog.Fatalf("init failed: %s", err) @@ -78,7 +78,6 @@ func main() { ) davAddr := c.Server.WebDav.Addr if davAddr != "" { - log.Info("starting webdav server", "addr", davAddr) dav = &http.Server{ Addr: davAddr, Handler: srv.NewWebDavServer( @@ -86,6 +85,7 @@ func main() { slog.WithName("webdav"), ), } + log.Info("starting webdav server", "addr", davAddr) go func() { err := dav.ListenAndServe() if err != nil && err != http.ErrServerClosed { @@ -95,7 +95,6 @@ func main() { } cacheAddr := c.Server.Cache.Addr if cacheAddr != "" { - log.Info("starting cache server", "addr", cacheAddr) cache = &http.Server{ Addr: cacheAddr, Handler: srv.NewCacheServer( @@ -103,6 +102,7 @@ func main() { slog.WithName("cache"), ), } + log.Info("starting cache server", "addr", cacheAddr) go func() { err := cache.ListenAndServe() if err != nil && err != http.ErrServerClosed { @@ -112,7 +112,6 @@ func main() { } plainAddr := c.Server.Plain.Addr if plainAddr != "" { - log.Info("starting plain server", "addr", plainAddr) plain = &http.Server{ Addr: plainAddr, Handler: srv.NewPlainServer( @@ -120,6 +119,7 @@ func main() { slog.WithName("plain"), ), } + log.Info("starting plain server", "addr", plainAddr) go func() { err := plain.ListenAndServe() if err != nil && err != http.ErrServerClosed { diff --git a/pkg/fs/file.go b/pkg/fs/file.go index 98061dd..d8cf373 100644 --- a/pkg/fs/file.go +++ b/pkg/fs/file.go @@ -98,8 +98,12 @@ func (f *File) Read(p []byte) (n int, err error) { if err == io.EOF { return } + if err == crypto.ErrDecrypt { + log.V(2).Info("rereading to cache file", "err", err) + return f.readToCache(p) + } if err != nil { - if !IsIOErr(err) && err != crypto.ErrDecrypt { + if !IsIOErr(err) { log.Error(err, "error reading cache file") return } diff --git a/pkg/fs/fs.go b/pkg/fs/fs.go index b670167..abbf036 100644 --- a/pkg/fs/fs.go +++ b/pkg/fs/fs.go @@ -129,13 +129,13 @@ const ( func FileSize(s int64) string { switch { case s >= TiB: - return strconv.Itoa(int(s/TiB)) + " TiB" + return strconv.FormatFloat(float64(s)/TiB, 'f', 2, 64) + " TiB" case s >= GiB: - return strconv.Itoa(int(s/GiB)) + " GiB" + return strconv.FormatFloat(float64(s)/GiB, 'f', 2, 64) + " GiB" case s >= MiB: - return strconv.Itoa(int(s/MiB)) + " MiB" + return strconv.FormatFloat(float64(s)/MiB, 'f', 2, 64) + " MiB" case s >= KiB: - return strconv.Itoa(int(s/KiB)) + " KiB" + return strconv.FormatFloat(float64(s)/KiB, 'f', 2, 64) + " KiB" } return strconv.Itoa(int(s)) + " B" } diff --git a/pkg/fs/metadata.go b/pkg/fs/metadata.go index b894caf..efd931b 100644 --- a/pkg/fs/metadata.go +++ b/pkg/fs/metadata.go @@ -381,6 +381,7 @@ func (md *Metadata) close() (err error) { md.cancel() if md.wc != nil { close(md.wc) + <-md.done md.wc = nil } if md.f != nil { @@ -449,30 +450,27 @@ type writeAt struct { func (md *Metadata) resetChans() { md.wc = make(chan writeAt, 10) + md.done = make(chan struct{}) } func (md *Metadata) writer() { md.resetChans() md.err.Store(nil) go func() { - for { - select { - case <-md.ctx.Done(): - return - case wa := <-md.wc: - atomic.StoreInt64(&md.Atime, now()) - n, err := md.f.WriteAt(wa.data, wa.pos) - if err == nil { - md.addChunk(wa.pos, n) - md.fs.q.Add(n) - } else { - md.err.Store(&mdErr{err}) - } - if wa.ret != nil { - streamingPool.Put(wa.ret) - } + for wa := range md.wc { + atomic.StoreInt64(&md.Atime, now()) + n, err := md.f.WriteAt(wa.data, wa.pos) + if err == nil { + md.addChunk(wa.pos, n) + md.fs.q.Add(n) + } else { + md.err.Store(&mdErr{err}) + } + if wa.ret != nil { + streamingPool.Put(wa.ret) } } + close(md.done) }() } diff --git a/pkg/srv/cache.go b/pkg/srv/cache.go index 8a5d4a7..f40b707 100644 --- a/pkg/srv/cache.go +++ b/pkg/srv/cache.go @@ -82,7 +82,7 @@ func (cs *CacheServer) ServeHTTP(w http.ResponseWriter, r *http.Request) { h.Del("Last-Modified") - paths, err := i.GetPaths(p, cs.fs, true) + paths, pathAnchor, err := i.GetPaths(p, cs.fs, true) if err == io.EOF { w.WriteHeader(i.Status()) return @@ -94,7 +94,7 @@ func (cs *CacheServer) ServeHTTP(w http.ResponseWriter, r *http.Request) { h.Set(csp, indexCSP) w.WriteHeader(i.Status()) - err = cache.Execute(w, data{Paths: paths}) + err = cache.Execute(w, data{Paths: paths, PathAnchor: pathAnchor}) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return diff --git a/pkg/srv/interceptor.go b/pkg/srv/interceptor.go index 7ce7383..ac1c5f6 100644 --- a/pkg/srv/interceptor.go +++ b/pkg/srv/interceptor.go @@ -47,8 +47,9 @@ type dirContents struct { } type dir struct { - Name template.HTML - URI template.HTML + Name template.HTML + URI template.HTML + Anchor string } type dirs []dir @@ -96,7 +97,19 @@ func (r *responseInterceptor) Status() int { return r.status } -func (r *responseInterceptor) GetPaths(path string, filesystem *fs.FS, relative bool) (dc dirContents, err error) { +func xmldecode(s string) string { + s = strings.ReplaceAll(s, "&", "&") + s = strings.ReplaceAll(s, "'", "'") + return s +} + +func urlencode(s string) string { + s = strings.ReplaceAll(s, "[", "%5B") + s = strings.ReplaceAll(s, "]", "%5D") + return s +} + +func (r *responseInterceptor) GetPaths(path string, filesystem *fs.FS, relative bool) (dc dirContents, pa string, err error) { buf := r.buf.Bytes() buf = bytes.ReplaceAll(buf, []byte{'&'}, []byte("&")) err = xml.Unmarshal(buf, &dc) @@ -105,19 +118,20 @@ func (r *responseInterceptor) GetPaths(path string, filesystem *fs.FS, relative } dc.Base = path path = strings.TrimSuffix(path, "/") + pa = "#" + anchor(path) for _, p := range dc.AllPaths { - p = strings.ReplaceAll(p, "&", "&") - p = strings.ReplaceAll(p, "'", "'") + p = xmldecode(p) name := template.HTML(p) full := path + "/" + p - uri := template.HTML(full) + uri := template.HTML(urlencode(full)) if relative { - uri = template.HTML(p) + uri = template.HTML(urlencode(p)) } if p[len(p)-1] == '/' { dc.Dirs = append(dc.Dirs, dir{ - Name: name, - URI: uri, + Name: name, + URI: uri, + Anchor: pathAnchor(p), }) } else { dc.Files = append(dc.Files, file{ @@ -139,7 +153,7 @@ func getPreloads(path string, filesystem *fs.FS) (dc dirContents) { for _, p := range filesystem.Preloads() { dc.Files = append(dc.Files, file{ Name: template.HTML(p.Name), - URI: template.HTML(p.Name), + URI: template.HTML(urlencode(p.Name)), Size: fs.FileSize(p.Size), Rate: p.Rate(), Status: p.Status, @@ -151,6 +165,10 @@ func getPreloads(path string, filesystem *fs.FS) (dc dirContents) { return } +func pathAnchor(s string) string { + return anchor(strings.TrimSuffix(s, "/")) +} + func anchor(s string) string { if i := strings.LastIndex(s, "/"); i > 0 && i+1 < len(s) { s = s[i+1:] diff --git a/pkg/srv/srv.go b/pkg/srv/srv.go index 3091382..826a7bf 100644 --- a/pkg/srv/srv.go +++ b/pkg/srv/srv.go @@ -25,9 +25,10 @@ const ( ) type data struct { - QuotaCur float64 - QuotaMax float64 - Paths dirContents + QuotaCur float64 + QuotaMax float64 + Paths dirContents + PathAnchor string } type FileServer struct { @@ -136,7 +137,7 @@ func (fs *FileServer) ServeHTTP(w http.ResponseWriter, r *http.Request) { h.Del("Last-Modified") - paths, err := i.GetPaths(p, fs.fs, false) + paths, pathAnchor, err := i.GetPaths(p, fs.fs, false) if err == io.EOF { w.WriteHeader(i.Status()) return @@ -148,7 +149,7 @@ func (fs *FileServer) ServeHTTP(w http.ResponseWriter, r *http.Request) { h.Set(csp, indexCSP) w.WriteHeader(i.Status()) - err = index.Execute(w, data{Paths: paths}) + err = index.Execute(w, data{Paths: paths, PathAnchor: pathAnchor}) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return diff --git a/pkg/srv/templates/index.html b/pkg/srv/templates/index.html index 55e44f3..868ec3a 100644 --- a/pkg/srv/templates/index.html +++ b/pkg/srv/templates/index.html @@ -5,13 +5,13 @@ [Preloads] - + {{range $s := .Paths.Dirs -}} - +
../../ [dir]
{{$s.Name}}{{$s.Name}} [dir]