Skip to content

Commit

Permalink
Merge pull request #201 from syncthing/fix-200
Browse files Browse the repository at this point in the history
Fixes for non-recursive tree when dirs are deleted (fixes #200)
  • Loading branch information
rjeczalik authored Jun 12, 2021
2 parents e2a77dc + 1a62061 commit 743ea07
Show file tree
Hide file tree
Showing 5 changed files with 98 additions and 15 deletions.
2 changes: 2 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
module github.com/rjeczalik/notify

go 1.11

require golang.org/x/sys v0.0.0-20180926160741-c2ed4eda69e7
16 changes: 8 additions & 8 deletions node.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ func (nd node) addchild(name, base string) node {
}

func (nd node) Add(name string) node {
i := indexbase(nd.Name, name)
i := indexrel(nd.Name, name)
if i == -1 {
return node{}
}
Expand Down Expand Up @@ -93,7 +93,7 @@ Traverse:
}

func (nd node) Get(name string) (node, error) {
i := indexbase(nd.Name, name)
i := indexrel(nd.Name, name)
if i == -1 {
return node{}, errnotexist(name)
}
Expand All @@ -111,7 +111,7 @@ func (nd node) Get(name string) (node, error) {
}

func (nd node) Del(name string) error {
i := indexbase(nd.Name, name)
i := indexrel(nd.Name, name)
if i == -1 {
return errnotexist(name)
}
Expand All @@ -122,13 +122,13 @@ func (nd node) Del(name string) error {
return errnotexist(name[:i+j])
}
stack = append(stack, nd)
i += j + 1
}
if nd, ok = nd.Child[name[i:]]; !ok {
if _, ok = nd.Child[name[i:]]; !ok {
return errnotexist(name)
}
nd.Child = nil
nd.Watch = nil
for name, i = base(nd.Name), len(stack); i != 0; name, i = base(nd.Name), i-1 {
delete(nd.Child, name[i:])
for name, i = name[i:], len(stack); i != 0; name, i = base(nd.Name), i-1 {
nd = stack[i-1]
if nd := nd.Child[name]; len(nd.Watch) > 1 || len(nd.Child) != 0 {
break
Expand Down Expand Up @@ -167,7 +167,7 @@ Traverse:
}

func (nd node) WalkPath(name string, fn walkPathFunc) error {
i := indexbase(nd.Name, name)
i := indexrel(nd.Name, name)
if i == -1 {
return errnotexist(name)
}
Expand Down
62 changes: 62 additions & 0 deletions notify_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,68 @@ func TestRenameInRoot(t *testing.T) {
}
}

func TestRecreated(t *testing.T) {
tmpDir, err := ioutil.TempDir("", "notify_test-")
if err != nil {
t.Fatal(err)
}
defer os.RemoveAll(tmpDir)

dir := filepath.Join(tmpDir, "folder")
file := filepath.Join(dir, "file")

// Start watching
eventChan := make(chan EventInfo, 1000)
mustT(t, Watch(tmpDir+"/...", eventChan, All))
defer Stop(eventChan)

recreateFolder := func() {
// Give the sync some time to process events
_ = os.RemoveAll(dir)
mustT(t, os.Mkdir(dir, 0777))
time.Sleep(100 * time.Millisecond)

// Create a file
mustT(t, ioutil.WriteFile(file, []byte("abc"), 0666))
}
timeout := time.After(5 * time.Second)
checkCreated := func() {
for {
select {
case ev := <-eventChan:
t.Log(ev.Path(), ev.Event())
if ev.Path() == file && ev.Event() == Create {
return
}
case <-timeout:
t.Fatal("timed out before receiving event")
}
}
}

// 1. Create a folder and a file within it
// This will create a node in the internal tree for the subfolder test/folder
// Will create a new inotify watch for the folder
t.Log("######## First ########")
recreateFolder()
checkCreated()

// 2. Create a folder and a file within it again
// This will set the events for the subfolder test/folder in the internal tree
// Will create a new inotify watch for the folder because events differ
t.Log("######## Second ########")
recreateFolder()
checkCreated()

// 3. Create a folder and a file within it yet again
// This time no new inotify watch will be created, because the events
// and node already exist in the internal tree and all subsequent events
// are lost, hence there is no event for the created file here anymore
t.Log("######## Third ########")
recreateFolder()
checkCreated()
}

func mustT(t testing.TB, err error) {
t.Helper()
if err != nil {
Expand Down
23 changes: 20 additions & 3 deletions tree_nonrecursive.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ func (t *nonrecursiveTree) dispatch(c <-chan EventInfo) {
}
t.rw.RUnlock()
// If the event describes newly leaf directory created within
if !isrec || ei.Event() != Create {
if !isrec || ei.Event()&(Create|Remove) == 0 {
return
}
if ok, err := ei.(isDirer).isDir(); !ok || err != nil {
Expand All @@ -79,9 +79,23 @@ func (t *nonrecursiveTree) dispatch(c <-chan EventInfo) {
// internal TODO(rjeczalik)
func (t *nonrecursiveTree) internal(rec <-chan EventInfo) {
for ei := range rec {
t.rw.Lock()
if ei.Event() == Remove {
nd, err := t.root.Get(ei.Path())
if err != nil {
t.rw.Unlock()
continue
}
t.walkWatchpoint(nd, func(_ Event, nd node) error {
t.w.Unwatch(nd.Name)
return nil
})
t.root.Del(ei.Path())
t.rw.Unlock()
continue
}
var nd node
var eset = internal
t.rw.Lock()
t.root.WalkPath(ei.Path(), func(it node, _ bool) error {
if e := it.Watch[t.rec]; e != 0 && e > eset {
eset = e
Expand All @@ -93,7 +107,10 @@ func (t *nonrecursiveTree) internal(rec <-chan EventInfo) {
t.rw.Unlock()
continue
}
err := nd.Add(ei.Path()).AddDir(t.recFunc(eset))
if ei.Path() != nd.Name {
nd = nd.Add(ei.Path())
}
err := nd.AddDir(t.recFunc(eset))
t.rw.Unlock()
if err != nil {
dbgprintf("internal(%p) error: %v", rec, err)
Expand Down
10 changes: 6 additions & 4 deletions util.go
Original file line number Diff line number Diff line change
Expand Up @@ -123,10 +123,12 @@ func base(s string) string {
return s
}

func indexbase(root, name string) int {
if n, m := len(root), len(name); m >= n && name[:n] == root &&
(n == m || name[n] == os.PathSeparator) {
return min(n+1, m)
// indexrel returns the index of the first char of name that is
// below/relative to root. It returns -1 if name is not a child of root.
func indexrel(root, name string) int {
if n, m := len(root), len(name); m > n && name[:n] == root &&
name[n] == os.PathSeparator {
return n + 1
}
return -1
}
Expand Down

0 comments on commit 743ea07

Please sign in to comment.