diff options
| author | Shulhan <ms@kilabit.info> | 2022-02-20 21:11:38 +0700 |
|---|---|---|
| committer | Shulhan <ms@kilabit.info> | 2022-02-24 00:00:25 +0700 |
| commit | fa03d152611f32c9b0e01546882cc871b4b930cf (patch) | |
| tree | dc2ed3c01e8a8a554a2b13040559434f1fc8e759 | |
| parent | d539f4db0c075b4f4087757ba5d96a678dc33962 (diff) | |
| download | pakakeh.go-fa03d152611f32c9b0e01546882cc871b4b930cf.tar.xz | |
lib/io: fix DirWatcher not removing old files on rename
Previously, if a sub-directory being watched by DirWatcher is renamed,
the old directory does not get removed from field dirs.
This commit fix this issue by deleting the sub directory on unmpSubdirs.
While at it, guard any read/write to dirs field with mutex to prevent
data race.
| -rw-r--r-- | lib/io/dirwatcher.go | 47 | ||||
| -rw-r--r-- | lib/io/dirwatcher_test.go | 85 |
2 files changed, 116 insertions, 16 deletions
diff --git a/lib/io/dirwatcher.go b/lib/io/dirwatcher.go index 0d7e7310..9ba59061 100644 --- a/lib/io/dirwatcher.go +++ b/lib/io/dirwatcher.go @@ -8,6 +8,7 @@ import ( "fmt" "log" "os" + "sort" "time" "github.com/shuLhan/share/lib/debug" @@ -93,6 +94,18 @@ func (dw *DirWatcher) Stop() { dw.ticker.Stop() } +// dirsKeys return all the key in field dirs sorted in ascending order. +func (dw *DirWatcher) dirsKeys() (keys []string) { + var ( + key string + ) + for key = range dw.dirs { + keys = append(keys, key) + } + sort.Strings(keys) + return keys +} + // // mapSubdirs iterate each child node and check if its a directory or regular // file. @@ -120,18 +133,21 @@ func (dw *DirWatcher) mapSubdirs(node *memfs.Node) { } // -// unmapSubdirs find any sub directories in node's childrens and remove it -// from map of node. +// unmapSubdirs find sub directories in node's childrens, recursively and +// remove it from map of node. // func (dw *DirWatcher) unmapSubdirs(node *memfs.Node) { for _, child := range node.Childs { - if !child.IsDir() { - continue + if child.IsDir() { + delete(dw.dirs, child.Path) + dw.unmapSubdirs(child) } - - delete(dw.dirs, child.Path) - dw.unmapSubdirs(child) + dw.fs.RemoveChild(node, child) } + if node.IsDir() { + delete(dw.dirs, node.Path) + } + dw.fs.RemoveChild(node.Parent, node) } // @@ -141,7 +157,9 @@ func (dw *DirWatcher) unmapSubdirs(node *memfs.Node) { // old content to detect deletion and addition of files. // func (dw *DirWatcher) onContentChange(node *memfs.Node) { - logp := "DirWatcher.onContentChange" + var ( + logp = "onContentChange" + ) if debug.Value >= 2 { fmt.Printf("%s: %+v\n", logp, node) @@ -179,10 +197,7 @@ func (dw *DirWatcher) onContentChange(node *memfs.Node) { if debug.Value >= 2 { fmt.Printf("%s: %q deleted\n", logp, child.Path) } - if child.IsDir() { - dw.unmapSubdirs(child) - } - dw.fs.RemoveChild(node, child) + dw.unmapSubdirs(child) } // Find new files in directory. @@ -352,7 +367,7 @@ func (dw *DirWatcher) start() { } func (dw *DirWatcher) processSubdirs() { - logp := "DirWatcher.processSubdirs" + logp := "processSubdirs" for _, node := range dw.dirs { if debug.Value >= 3 { @@ -361,11 +376,11 @@ func (dw *DirWatcher) processSubdirs() { newDirInfo, err := os.Stat(node.SysPath) if err != nil { - if !os.IsNotExist(err) { + if os.IsNotExist(err) { + dw.unmapSubdirs(node) + } else { log.Printf("%s: %q: %s", logp, node.SysPath, err) - continue } - dw.unmapSubdirs(node) continue } if node.Mode() != newDirInfo.Mode() { diff --git a/lib/io/dirwatcher_test.go b/lib/io/dirwatcher_test.go index a9c14018..f6c40fc6 100644 --- a/lib/io/dirwatcher_test.go +++ b/lib/io/dirwatcher_test.go @@ -16,6 +16,7 @@ import ( "time" "github.com/shuLhan/share/lib/memfs" + "github.com/shuLhan/share/lib/test" ) func TestDirWatcher(t *testing.T) { @@ -201,3 +202,87 @@ func TestDirWatcher(t *testing.T) { wg.Add(1) dw.Stop() } + +// +// Test renaming sub-directory being watched. +// +func TestDirWatcher_renameDirectory(t *testing.T) { + var ( + logp = "TestDirWatcher_renameDirectory" + nsq = make(chan *NodeState) + + dw DirWatcher + err error + + rootDir string + subDir string + subDirFile string + newSubDir string + ) + + // + // Create a directory with its content to be watched. + // + // rootDir + // |_ subDir + // |_ subDirFile + // + + rootDir, err = os.MkdirTemp("", "") + if err != nil { + t.Fatal(err) + } + + t.Cleanup(func() { + err = os.RemoveAll(rootDir) + if err != nil { + t.Logf("%s: on cleanup: %s", logp, err) + } + }) + + subDir = filepath.Join(rootDir, "subdir") + err = os.Mkdir(subDir, 0700) + if err != nil { + t.Fatal(err) + } + + subDirFile = filepath.Join(subDir, "testfile") + err = os.WriteFile(subDirFile, []byte(`content of testfile`), 0600) + if err != nil { + t.Fatal(err) + } + + dw = DirWatcher{ + Callback: func(ns *NodeState) { + nsq <- ns + }, + Options: memfs.Options{ + Root: rootDir, + }, + Delay: 200 * time.Millisecond, + } + + err = dw.Start() + if err != nil { + t.Fatal(err) + } + + // Wait for all watcher started. + time.Sleep(400 * time.Millisecond) + + newSubDir = filepath.Join(rootDir, "newsubdir") + err = os.Rename(subDir, newSubDir) + if err != nil { + t.Fatal(err) + } + + <-nsq + <-nsq + <-nsq + + var expDirs = []string{ + "/newsubdir", + } + + test.Assert(t, "dirs", expDirs, dw.dirsKeys()) +} |
