diff --git a/README.md b/README.md index f7e67db5..874b5617 100644 --- a/README.md +++ b/README.md @@ -43,6 +43,9 @@ See [example_test.go](https://github.com/fsnotify/fsnotify/blob/master/example_t ## FAQ +**Are symlinks followed?** +As of v2.0.0 symlinks are no longer followed. You will have to explicitly get the underlying path with [`filepath.EvalSymlinks(path)`](https://golang.org/pkg/path/filepath/#EvalSymlinks) before passing it to the watcher if you want this behaviour. + **When a file is moved to another directory is it still being watched?** No (it shouldn't be, unless you are watching where it was moved to). @@ -75,5 +78,4 @@ There are OS-specific limits as to how many watches can be created: ## Related Projects * [notify](https://github.com/rjeczalik/notify) -* [fsevents](https://github.com/fsnotify/fsevents) - +* [fsevents](https://github.com/fsnotify/fsevents) \ No newline at end of file diff --git a/inotify.go b/inotify.go index d9fd1b88..10ea4a28 100644 --- a/inotify.go +++ b/inotify.go @@ -96,7 +96,7 @@ func (w *Watcher) Add(name string) error { const agnosticEvents = unix.IN_MOVED_TO | unix.IN_MOVED_FROM | unix.IN_CREATE | unix.IN_ATTRIB | unix.IN_MODIFY | - unix.IN_MOVE_SELF | unix.IN_DELETE | unix.IN_DELETE_SELF + unix.IN_MOVE_SELF | unix.IN_DELETE | unix.IN_DELETE_SELF | unix.IN_DONT_FOLLOW var flags uint32 = agnosticEvents diff --git a/integration_test.go b/integration_test.go index 7096344d..6a79fd42 100644 --- a/integration_test.go +++ b/integration_test.go @@ -10,7 +10,6 @@ import ( "io/ioutil" "os" "os/exec" - "path" "path/filepath" "runtime" "sync/atomic" @@ -1012,6 +1011,71 @@ func TestFsnotifyClose(t *testing.T) { } } +func TestSymlinkNotFollowed(t *testing.T) { + testDir := tempMkdir(t) + file1 := filepath.Join(testDir, "file1") + file2 := filepath.Join(testDir, "file2") + link := filepath.Join(testDir, "link") + + f1, err := os.Create(file1) + if err != nil { + t.Fatalf("Failed to create file1: %s", err) + } + defer f1.Close() + if _, err := os.Create(file2); err != nil { + t.Fatalf("Failed to create file2: %s", err) + } + + // symlink works for Windows too + if err := os.Symlink(file1, link); err != nil { + t.Fatalf("Failed to create sylink: %s", err) + } + + w, err := NewWatcher() + if err != nil { + t.Fatalf("Failed to create watcher") + } + + err = w.Add(link) + if err != nil { + t.Fatalf("Failed to add link: %s", err) + } + + // change file 1 - no event + f1.Write([]byte("Hello")) + f1.Sync() + // XXX(obristow): doing a create here shows a CHMOD event on mac - is that an issue? + + select { + case event := <-w.Events: + t.Fatalf("Event from watcher: %v", event) + case err := <-w.Errors: + t.Fatalf("Error from watcher: %v", err) + case <-time.After(50 * time.Millisecond): + } + + // ~atomic link change - event + tmpLink := filepath.Join(testDir, "tml-link") + if err := os.Symlink(file2, tmpLink); err != nil { + t.Fatalf("Failed to create sylink: %s", err) + } + + if err := os.Rename(tmpLink, link); err != nil { + t.Fatalf("Failed to replace sylink: %s", err) + } + + select { + case _ = <-w.Events: + case err := <-w.Errors: + t.Fatalf("Error from watcher: %v", err) + case <-time.After(50 * time.Millisecond): + t.Fatalf("Took too long to wait for event") + } + +} + +/* XXX(obristow): these would return if implemeting the high level interfaced mentioned in https://github.com/fsnotify/fsnotify/issues/199#issuecomment-480277398 + func TestFsnotifyFakeSymlink(t *testing.T) { if runtime.GOOS == "windows" { t.Skip("symlinks don't work on Windows.") @@ -1078,6 +1142,7 @@ func TestFsnotifyFakeSymlink(t *testing.T) { func TestCyclicSymlink(t *testing.T) { if runtime.GOOS == "windows" { + // They do through extended attributes? t.Skip("symlinks don't work on Windows.") } @@ -1122,6 +1187,7 @@ func TestCyclicSymlink(t *testing.T) { watcher.Close() } +*/ // TestConcurrentRemovalOfWatch tests that concurrent calls to RemoveWatch do not race. // See https://codereview.appspot.com/103300045/ diff --git a/kqueue.go b/kqueue.go index 86e76a3d..fc868ce1 100644 --- a/kqueue.go +++ b/kqueue.go @@ -189,33 +189,7 @@ func (w *Watcher) addWatch(name string, flags uint32) (string, error) { return "", nil } - // Follow Symlinks - // Unfortunately, Linux can add bogus symlinks to watch list without - // issue, and Windows can't do symlinks period (AFAIK). To maintain - // consistency, we will act like everything is fine. There will simply - // be no file events for broken symlinks. - // Hence the returns of nil on errors. - if fi.Mode()&os.ModeSymlink == os.ModeSymlink { - name, err = filepath.EvalSymlinks(name) - if err != nil { - return "", nil - } - - w.mu.Lock() - _, alreadyWatching = w.watches[name] - w.mu.Unlock() - - if alreadyWatching { - return name, nil - } - - fi, err = os.Lstat(name) - if err != nil { - return "", nil - } - } - - watchfd, err = unix.Open(name, openMode, 0700) + watchfd, err = unix.Open(name, openMode|unix.O_SYMLINK, 0700) if watchfd == -1 { return "", err }