summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorShulhan <ms@kilabit.info>2022-02-20 21:11:38 +0700
committerShulhan <ms@kilabit.info>2022-02-24 00:00:25 +0700
commitfa03d152611f32c9b0e01546882cc871b4b930cf (patch)
treedc2ed3c01e8a8a554a2b13040559434f1fc8e759
parentd539f4db0c075b4f4087757ba5d96a678dc33962 (diff)
downloadpakakeh.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.go47
-rw-r--r--lib/io/dirwatcher_test.go85
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())
+}