From 0b3352e0c45fc2cc3cc011f8cbaedd7a703f1175 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9goire=20Delattre?= Date: Mon, 7 Oct 2024 23:32:57 +0200 Subject: [PATCH] Notify the kernel of node updates This invalidates kernel cache for the node path. This commit also: * Rename childs -> children * Remove persistent nodes * Fix async updates * Don't use time in inode computation for directories --- cmd/polochonfs/fs.go | 26 ++++++-- cmd/polochonfs/main.go | 11 +--- cmd/polochonfs/movies.go | 12 ++-- cmd/polochonfs/node.go | 136 +++++++++++++-------------------------- cmd/polochonfs/shows.go | 17 +++-- 5 files changed, 84 insertions(+), 118 deletions(-) diff --git a/cmd/polochonfs/fs.go b/cmd/polochonfs/fs.go index e6e86b7..9518469 100644 --- a/cmd/polochonfs/fs.go +++ b/cmd/polochonfs/fs.go @@ -6,6 +6,7 @@ import ( logger "log" "os" "os/signal" + "sync" "syscall" "time" @@ -24,6 +25,7 @@ var ( type polochonfs struct { fuseDebug bool ctx context.Context + wg sync.WaitGroup root *node @@ -47,10 +49,12 @@ func (pfs *polochonfs) init() error { {value: polochonToken, errMsg: "missing token"}, } { if field.value == "" { - return fmt.Errorf(field.errMsg) + return fmt.Errorf("%s", field.errMsg) } } + pfs.wg = sync.WaitGroup{} + var err error pfs.client, err = papi.New(polochonURL) if err != nil { @@ -62,19 +66,27 @@ func (pfs *polochonfs) init() error { return nil } +func (pfs *polochonfs) wait() { + pfs.wg.Wait() +} + func (pfs *polochonfs) buildFS(_ context.Context) { - log.Debug("Adding persistent nodes") - pfs.root.addChild(newPersistentNodeDir(movieDirName)) - pfs.root.addChild(newPersistentNodeDir(showDirName)) + pfs.root.addChild(newNodeDir(movieDirName, movieDirName, pfs.root.times)) + pfs.root.addChild(newNodeDir(showDirName, showDirName, pfs.root.times)) - pfs.updateMovies() - pfs.updateShows() + go pfs.handleUpdates() } func (pfs *polochonfs) handleUpdates() { + pfs.wg.Add(1) + defer pfs.wg.Done() + sigs := make(chan os.Signal, 1) signal.Notify(sigs, syscall.SIGUSR1, syscall.SIGUSR2) + pfs.updateMovies() + pfs.updateShows() + ticker := time.NewTicker(libraryRefresh) defer ticker.Stop() @@ -84,9 +96,11 @@ func (pfs *polochonfs) handleUpdates() { switch s { case syscall.SIGUSR1: log.Info("Updating movies from signal") + ticker.Reset(libraryRefresh) pfs.updateMovies() case syscall.SIGUSR2: log.Info("Updating shows from signal") + ticker.Reset(libraryRefresh) pfs.updateShows() } case <-ticker.C: diff --git a/cmd/polochonfs/main.go b/cmd/polochonfs/main.go index 1c03a58..28faf5b 100644 --- a/cmd/polochonfs/main.go +++ b/cmd/polochonfs/main.go @@ -7,7 +7,6 @@ import ( "os/signal" "os/user" "strconv" - "sync" "syscall" "time" @@ -114,20 +113,14 @@ func run() error { return err } - wg := sync.WaitGroup{} - go func() { - wg.Add(1) - defer wg.Done() - pfs.handleUpdates() - }() - log.Info("FUSE daemon started") sigs := make(chan os.Signal, 1) signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM) <-sigs cancel() - wg.Wait() + pfs.wait() + pfs.unmount(server) return nil } diff --git a/cmd/polochonfs/movies.go b/cmd/polochonfs/movies.go index 12a354a..47d01db 100644 --- a/cmd/polochonfs/movies.go +++ b/cmd/polochonfs/movies.go @@ -23,16 +23,21 @@ func movieDirTitle(m *papi.Movie) string { func (pfs *polochonfs) updateMovies() { dir := pfs.root.getChild(movieDirName) + defer func() { + pfs.root.addChild(dir) + _ = pfs.root.NotifyEntry(showDirName) + }() + log.Debug("Fecthing movies") movies, err := pfs.client.GetMovies() if err != nil { log.WithField("error", err).Error("Failed to get movies") - dir.rmAllChilds() + dir.rmAllChildren() return } clear(movieInodes) - dir.rmAllChilds() + dir.rmAllChildren() for _, m := range movies.List() { imdbID := m.ImdbID title := movieDirTitle(m) @@ -45,8 +50,7 @@ func (pfs *polochonfs) updateMovies() { continue } - movieDirNode := newNodeDir(imdbID, title) - movieDirNode.times = m.DateAdded + movieDirNode := newNodeDir(imdbID, title, m.DateAdded) dir.addChild(movieDirNode) movieNode := newNode(imdbID, m.Path, url, uint64(m.Size), m.DateAdded) diff --git a/cmd/polochonfs/node.go b/cmd/polochonfs/node.go index f9d2c37..0dbdcab 100644 --- a/cmd/polochonfs/node.go +++ b/cmd/polochonfs/node.go @@ -7,7 +7,6 @@ import ( "hash/crc32" "io" "net/http" - "sort" "strconv" "sync" "syscall" @@ -22,7 +21,6 @@ import ( var ( _ = (fs.NodeGetattrer)((*node)(nil)) _ = (fs.NodeOpener)((*node)(nil)) - _ = (fs.NodeLookuper)((*node)(nil)) _ = (fs.FileReader)((*fileHandle)(nil)) _ = (fs.FileFlusher)((*fileHandle)(nil)) @@ -30,48 +28,42 @@ var ( type node struct { fs.Inode - isDir, isPersistent bool - url string + isDir bool + url string times time.Time id, name string size uint64 - mu sync.Mutex - childs map[string]*node + mu sync.Mutex + children map[string]*node inode uint64 } -func newNodeDir(id, name string) *node { +func newNodeDir(id, name string, times time.Time) *node { return &node{ - id: id, - name: name, - isDir: true, - childs: map[string]*node{}, + id: id, + name: name, + isDir: true, + times: times, + children: map[string]*node{}, } } -func newPersistentNodeDir(name string) *node { - n := newNodeDir(name, name) - n.isPersistent = true - return n -} - func newRootNode() *node { - n := newNodeDir("root", "root") - n.times = time.Now() - return n + return newNodeDir("root", "root", time.Now()) } func newNode(id, name, url string, size uint64, times time.Time) *node { return &node{ - isDir: false, - name: name, - url: url, - size: size, - times: times, - childs: map[string]*node{}, + id: id, + isDir: false, + name: name, + url: url, + size: size, + times: times, + children: map[string]*node{}, } } @@ -84,7 +76,7 @@ func (n *node) getInode() uint64 { } var b bytes.Buffer - if !n.isPersistent { + if !n.isDir { b.WriteString(n.times.String()) } @@ -100,10 +92,10 @@ func (n *node) getInode() uint64 { func (n *node) addChildNode(c *node) { n.mu.Lock() defer n.mu.Unlock() - n.childs[c.name] = c + n.children[c.name] = c } -func checkInodeExist(i uint64) bool { +func inodeExists(i uint64) bool { if _, ok := movieInodes[i]; ok { return true } @@ -114,70 +106,43 @@ func checkInodeExist(i uint64) bool { } func (n *node) addChild(child *node) { - attr := fs.StableAttr{} + attr := fs.StableAttr{ + Ino: child.getInode(), + } if child.isDir { attr.Mode = syscall.S_IFDIR } - if child.isPersistent { - child.times = n.times - n.NewPersistentInode(context.Background(), child, attr) - } else { - attr.Ino = child.getInode() - if checkInodeExist(attr.Ino) { - log.WithFields(log.Fields{ - "file": n.name, - "inode": attr.Ino, - }).Error("Inode already exists") - } - n.NewInode(context.Background(), child, attr) + // Check for inode collision. + if inodeExists(attr.Ino) { + log.WithFields(log.Fields{ + "file": n.name, + "inode": attr.Ino, + }).Error("Inode already exists") } + inode := n.NewInode(context.Background(), child, attr) n.addChildNode(child) + n.AddChild(child.name, inode, true) } func (n *node) getChild(name string) *node { n.mu.Lock() defer n.mu.Unlock() - return n.childs[name] + return n.children[name] } -func (n *node) rmAllChilds() { +func (n *node) rmAllChildren() { n.mu.Lock() - n.childs = map[string]*node{} + n.children = map[string]*node{} n.mu.Unlock() n.RmAllChildren() } -func (n *node) Readdir(ctx context.Context) (fs.DirStream, syscall.Errno) { - if !n.isDir { - return nil, syscall.ENOTDIR - } - - n.mu.Lock() - entries := make([]fuse.DirEntry, 0, len(n.childs)) - for _, c := range n.childs { - entry := fuse.DirEntry{ - Mode: c.Mode(), - Name: c.name, - Ino: c.StableAttr().Ino, - } - - entries = append(entries, entry) - } - n.mu.Unlock() - - sort.Slice(entries, func(i, j int) bool { - return entries[i].Name < entries[j].Name - }) - - return fs.NewListDirStream(entries), 0 -} - func (n *node) childCount() uint32 { n.mu.Lock() defer n.mu.Unlock() - return uint32(len(n.childs)) + return uint32(len(n.children)) } func (n *node) updateAttr(out *fuse.Attr) { @@ -192,29 +157,14 @@ func (n *node) updateAttr(out *fuse.Attr) { } } -func (n *node) Getattr(ctx context.Context, _ fs.FileHandle, out *fuse.AttrOut) syscall.Errno { +func (n *node) Getattr(_ context.Context, _ fs.FileHandle, out *fuse.AttrOut) syscall.Errno { log.WithField("node", n.name).Debug("Getattr called") out.SetTimeout(libraryRefresh) n.updateAttr(&out.Attr) return 0 } -func (n *node) Lookup(ctx context.Context, name string, out *fuse.EntryOut) (*fs.Inode, syscall.Errno) { - log.WithFields(log.Fields{ - "node": n.name, - "lookup": name, - }).Debug("Looking up node") - - child := n.getChild(name) - if child == nil { - return nil, syscall.ENOENT - } - - child.updateAttr(&out.Attr) - return &child.Inode, 0 -} - -func (n *node) Open(ctx context.Context, flags uint32) (fh fs.FileHandle, fuseFlags uint32, errno syscall.Errno) { +func (n *node) Open(_ context.Context, flags uint32) (fh fs.FileHandle, fuseFlags uint32, errno syscall.Errno) { log.WithFields(log.Fields{ "node": n.name, "flags": flags, @@ -237,7 +187,7 @@ func newFileHandle(name, url string, size int64) *fileHandle { } } -func (fh *fileHandle) Flush(ctx context.Context) syscall.Errno { +func (fh *fileHandle) Flush(_ context.Context) syscall.Errno { log.WithField("name", fh.name).Debug("Flush called") fh.close() return 0 @@ -248,11 +198,11 @@ func (fh *fileHandle) close() { return } fh.cancel() - fh.buffer.Close() + _ = fh.buffer.Close() fh.buffer = nil } -func (fh *fileHandle) setup(ctx context.Context, offset int64) error { +func (fh *fileHandle) setup(_ context.Context, offset int64) error { log.WithFields(log.Fields{ "name": fh.name, "offset": offset, @@ -291,7 +241,7 @@ func (fh *fileHandle) setup(ctx context.Context, offset int64) error { if fh.buffer != nil { log.WithField("name", fh.name).Trace("Closing old buffer") - fh.buffer.Close() + _ = fh.buffer.Close() log.WithField("name", fh.name).Trace("Done closing old buffer") } @@ -352,7 +302,7 @@ func (fh *fileHandle) Read(ctx context.Context, dest []byte, offset int64) (fuse }) if timedOut { - err = fmt.Errorf("timeout after " + defaultTimeout.String()) + err = fmt.Errorf("timeout after %s", defaultTimeout.String()) } switch err { diff --git a/cmd/polochonfs/shows.go b/cmd/polochonfs/shows.go index 355aea0..483cd42 100644 --- a/cmd/polochonfs/shows.go +++ b/cmd/polochonfs/shows.go @@ -11,20 +11,24 @@ import ( func (pfs *polochonfs) updateShows() { dir := pfs.root.getChild(showDirName) + defer func() { + pfs.root.addChild(dir) + _ = pfs.root.NotifyEntry(showDirName) + }() + log.Debug("Fecthing shows") shows, err := pfs.client.GetShows() if err != nil { log.WithField("error", err).Error("Failed to get shows") - dir.rmAllChilds() + dir.rmAllChildren() return } clear(showInodes) - dir.rmAllChilds() + dir.rmAllChildren() for _, s := range shows.List() { imdbID := s.ImdbID - showDirNode := newNodeDir(imdbID, s.Title) - showDirNode.times = pfs.root.times + showDirNode := newNodeDir(imdbID, s.Title, pfs.root.times) dir.addChild(showDirNode) for _, file := range []*papi.File{s.Fanart, s.Banner, s.Poster, s.NFO} { @@ -47,8 +51,9 @@ func (pfs *polochonfs) updateShows() { } for _, season := range s.Seasons { - seasonDir := newNodeDir(season.ShowImdbID, fmt.Sprintf("Season %d", season.Season)) - seasonDir.times = pfs.root.times + seasonDir := newNodeDir(season.ShowImdbID, + fmt.Sprintf("Season %d", season.Season), + pfs.root.times) showDirNode.addChild(seasonDir) for _, episode := range season.Episodes {