diff --git a/CHANGELOG.md b/CHANGELOG.md index be4d7ea2..b5da9fc3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,7 @@ # Changelog +* Windows: Fixed persistence of original path-name in watches within renamed directory (#259, #243) + ## v1.4.7 / 2018-01-09 * BSD/macOS: Fix possible deadlock on closing the watcher on kqueue (thanks @nhooyr and @glycerine) diff --git a/integration_test.go b/integration_test.go index 7096344d..aae0662b 100644 --- a/integration_test.go +++ b/integration_test.go @@ -13,6 +13,7 @@ import ( "path" "path/filepath" "runtime" + "strings" "sync/atomic" "testing" "time" @@ -1226,6 +1227,55 @@ func TestRemoveWithClose(t *testing.T) { } } +func TestMoveWatchedDirectory(t *testing.T) { + + testDir := tempMkdir(t) + defer os.RemoveAll(testDir) + + watcher := newWatcher(t) + + // event recording + var events []Event + go func() { + for { + event, ok := <-watcher.Events + if !ok { + return + } + events = append(events, event) + } + }() + + addWatch(t, watcher, testDir) + + if err := os.Mkdir(testDir+"/dir", 0o775); err != nil { + t.Fatal(err) + } + time.Sleep(10 * time.Millisecond) + addWatch(t, watcher, testDir+"/dir") + if err := os.Rename(testDir+"/dir", testDir+"/dir2"); err != nil { + t.Fatal(err) + } + time.Sleep(10 * time.Millisecond) + if err := ioutil.WriteFile(testDir+"/dir2/file.ext", []byte(""), 0o664); err != nil { + t.Fatal(err) + } + + if err := watcher.Close(); err != nil { + t.Fatal(err) + } + time.Sleep(10 * time.Millisecond) + + if len(events) != 4 { + t.Fatalf("Expected 4 events. Got: %d", len(events)) + } + + expectedSuffix := filepath.Join("dir2", "file.ext") + if !strings.HasSuffix(events[3].Name, expectedSuffix) { + t.Fatalf("Expected suffix %s, Got: %s", expectedSuffix, events[3].Name) + } +} + func testRename(file1, file2 string) error { switch runtime.GOOS { case "windows", "plan9": diff --git a/windows.go b/windows.go index 09436f31..58f34969 100644 --- a/windows.go +++ b/windows.go @@ -12,6 +12,7 @@ import ( "os" "path/filepath" "runtime" + "strings" "sync" "syscall" "unsafe" @@ -464,6 +465,17 @@ func (w *Watcher) readEvents() { case syscall.FILE_ACTION_RENAMED_OLD_NAME: watch.rename = name case syscall.FILE_ACTION_RENAMED_NEW_NAME: + + // update saved path of all sub-watches + oldFullName := filepath.Join(watch.path, watch.rename) + for _, watchMap := range w.watches { + for _, otherWatch := range watchMap { + if strings.HasPrefix(otherWatch.path, oldFullName) { + otherWatch.path = filepath.Join(fullname, strings.TrimPrefix(otherWatch.path, oldFullName)) + } + } + } + if watch.names[watch.rename] != 0 { watch.names[name] |= watch.names[watch.rename] delete(watch.names, watch.rename)