From ea65ea2e47123ea2c3bbee79037ee37ea3f4dcbd Mon Sep 17 00:00:00 2001 From: Loren Segal Date: Thu, 21 Jun 2018 23:00:57 -0700 Subject: [PATCH] FSEvents: fix bug where rewatching path fails across channels The following code reproduces a bug where if you watch a parent and child directory across multiple channels and then call notify.Stop() on the either channel, any subsequent calls to notify.Watch() on a grandparent directory will fail: ```go package main import ( "fmt" "io/ioutil" "os" "github.com/rjeczalik/notify" ) func watch(path string) chan notify.EventInfo { c := make(chan notify.EventInfo, 1) if err := notify.Watch(path+"...", c, notify.All); err != nil { panic(err) } return c } func main() { os.MkdirAll("./a/b/c", 0775) defer os.RemoveAll("./a") // watch a child and parent path across multiple channels. // this can happen in any order. ch1 := watch("./a/b/c") ch2 := watch("./a/b") // unwatch ./a/b -- this is what causes the panic on the next line. // note that this also fails if we notify.Stop(ch1) instead. notify.Stop(ch2) // watching ./a will now return errNotWatched. ch3 := watch("./a") // just a test to make sure watching still works when we get here. go func() { ioutil.WriteFile("a/b/c/d", []byte("X"), 0664) }() fmt.Println(<-ch1, <-ch3) } ``` Fortunately we can fix this failure by simply not returning errNotWatched from unwatch(), which simply performs a no-op if there is no active stream for the path when Unwatch() is called. --- watcher_fsevents.go | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/watcher_fsevents.go b/watcher_fsevents.go index 7d9b97b..3f8a98d 100644 --- a/watcher_fsevents.go +++ b/watcher_fsevents.go @@ -207,10 +207,9 @@ func (fse *fsevents) watch(path string, event Event, isrec int32) (err error) { func (fse *fsevents) unwatch(path string) (err error) { w, ok := fse.watches[path] - if !ok { - return errNotWatched + if ok { + w.stream.Stop() } - w.stream.Stop() delete(fse.watches, path) return nil }