From 76351ddaa3f73589268022d23a20e9da85f8b38c Mon Sep 17 00:00:00 2001 From: Nathan Youngman <4566+nathany@users.noreply.github.com> Date: Tue, 24 Aug 2021 13:09:43 -0600 Subject: [PATCH] Revert "Add AddRaw to not follow symlinks + Fix link folloing on Windows (#289)" This reverts commit e2e95171bc7c7402864e613c8b00a768fbcc2380. --- README.md | 13 +------- fen.go | 4 +-- fsnotify.go | 17 ----------- fsnotify_test.go | 2 +- inotify.go | 6 ++-- inotify_test.go | 16 +++++----- integration_test.go | 73 ++++----------------------------------------- kqueue.go | 32 ++++++++++++++++++-- windows.go | 4 +-- 9 files changed, 51 insertions(+), 116 deletions(-) diff --git a/README.md b/README.md index 670f2cfc..df57b1b2 100644 --- a/README.md +++ b/README.md @@ -72,18 +72,10 @@ func main() { } }() - // if this is a link, it will follow all the links and watch the file pointed to err = watcher.Add("/tmp/foo") if err != nil { log.Fatal(err) } - - // this will watch the link, rather than the file it points to - err = watcher.AddRaw("/tmp/link") - if err != nil { - log.Fatal(err) - } - <-done } ``` @@ -98,10 +90,6 @@ See [example_test.go](https://github.com/fsnotify/fsnotify/blob/master/example_t ## FAQ -**Are symlinks resolved?** -Symlinks are implicitly resolved by [`filepath.EvalSymlinks(path)`](https://golang.org/pkg/path/filepath/#EvalSymlinks) when `watcher.Add(name)` is used. If that is not desired, you can use `watcher.AddRaw(name)` to not follow any symlinks before watching. See [example_test.go](https://github.com/fsnotify/fsnotify/blob/master/example_test.go). - - **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). @@ -139,3 +127,4 @@ fsnotify requires support from underlying OS to work. The current NFS protocol d * [notify](https://github.com/rjeczalik/notify) * [fsevents](https://github.com/fsnotify/fsevents) + diff --git a/fen.go b/fen.go index 325ece59..b3ac3d8f 100644 --- a/fen.go +++ b/fen.go @@ -27,8 +27,8 @@ func (w *Watcher) Close() error { return nil } -// AddRaw starts watching the named file or directory (non-recursively). Symlinks are not implicitly resolved. -func (w *Watcher) AddRaw(name string) error { +// Add starts watching the named file or directory (non-recursively). +func (w *Watcher) Add(name string) error { return nil } diff --git a/fsnotify.go b/fsnotify.go index 5e240dc2..0f4ee52e 100644 --- a/fsnotify.go +++ b/fsnotify.go @@ -12,7 +12,6 @@ import ( "bytes" "errors" "fmt" - "path/filepath" ) // Event represents a single file system notification. @@ -68,19 +67,3 @@ func (e Event) String() string { var ( ErrEventOverflow = errors.New("fsnotify queue overflow") ) - -// Add starts watching the named file or directory (non-recursively). Symlinks are implicitly resolved. -func (w *Watcher) Add(name string) error { - rawPath, err := filepath.EvalSymlinks(name) - if err != nil { - return fmt.Errorf("error resolving %#v: %s", name, err) - } - err = w.AddRaw(rawPath) - if err != nil { - if name != rawPath { - return fmt.Errorf("error adding %#v for %#v: %s", rawPath, name, err) - } - return err - } - return nil -} diff --git a/fsnotify_test.go b/fsnotify_test.go index 5751fbab..51aa49c5 100644 --- a/fsnotify_test.go +++ b/fsnotify_test.go @@ -52,7 +52,7 @@ func TestWatcherClose(t *testing.T) { name := tempMkFile(t, "") w := newWatcher(t) - err := w.AddRaw(name) + err := w.Add(name) if err != nil { t.Fatal(err) } diff --git a/inotify.go b/inotify.go index 994daf09..eb87699b 100644 --- a/inotify.go +++ b/inotify.go @@ -88,8 +88,8 @@ func (w *Watcher) Close() error { return nil } -// AddRaw starts watching the named file or directory (non-recursively). Symlinks are not implicitly resolved. -func (w *Watcher) AddRaw(name string) error { +// Add starts watching the named file or directory (non-recursively). +func (w *Watcher) Add(name string) error { name = filepath.Clean(name) if w.isClosed() { return errors.New("inotify instance already closed") @@ -97,7 +97,7 @@ func (w *Watcher) AddRaw(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_DONT_FOLLOW + unix.IN_MOVE_SELF | unix.IN_DELETE | unix.IN_DELETE_SELF var flags uint32 = agnosticEvents diff --git a/inotify_test.go b/inotify_test.go index 8cd1cfd5..90c82b8e 100644 --- a/inotify_test.go +++ b/inotify_test.go @@ -54,7 +54,7 @@ func TestInotifyCloseSlightlyLaterWithWatch(t *testing.T) { if err != nil { t.Fatalf("Failed to create watcher") } - w.AddRaw(testDir) + w.Add(testDir) // Wait until readEvents has reached unix.Read, and Close. <-time.After(50 * time.Millisecond) @@ -74,7 +74,7 @@ func TestInotifyCloseAfterRead(t *testing.T) { t.Fatalf("Failed to create watcher") } - err = w.AddRaw(testDir) + err = w.Add(testDir) if err != nil { t.Fatalf("Failed to add .") } @@ -121,7 +121,7 @@ func TestInotifyCloseCreate(t *testing.T) { } defer w.Close() - err = w.AddRaw(testDir) + err = w.Add(testDir) if err != nil { t.Fatalf("Failed to add testDir: %v", err) } @@ -149,7 +149,7 @@ func TestInotifyCloseCreate(t *testing.T) { } <-time.After(50 * time.Millisecond) - err = w.AddRaw(testDir) + err = w.Add(testDir) if err != nil { t.Fatalf("Error adding testDir again: %v", err) } @@ -170,7 +170,7 @@ func TestInotifyStress(t *testing.T) { } defer w.Close() - err = w.AddRaw(testDir) + err = w.Add(testDir) if err != nil { t.Fatalf("Failed to add testDir: %v", err) } @@ -290,7 +290,7 @@ func TestInotifyRemoveTwice(t *testing.T) { } defer w.Close() - err = w.AddRaw(testFile) + err = w.Add(testFile) if err != nil { t.Fatalf("Failed to add testFile: %v", err) } @@ -332,7 +332,7 @@ func TestInotifyInnerMapLength(t *testing.T) { } defer w.Close() - err = w.AddRaw(testFile) + err = w.Add(testFile) if err != nil { t.Fatalf("Failed to add testFile: %v", err) } @@ -384,7 +384,7 @@ func TestInotifyOverflow(t *testing.T) { t.Fatalf("Cannot create subdir: %v", err) } - err = w.AddRaw(testSubdir) + err = w.Add(testSubdir) if err != nil { t.Fatalf("Failed to add subdir: %v", err) } diff --git a/integration_test.go b/integration_test.go index cf1b8cff..fc09e03f 100644 --- a/integration_test.go +++ b/integration_test.go @@ -66,8 +66,8 @@ func newWatcher(t *testing.T) *Watcher { // addWatch adds a watch for a directory func addWatch(t *testing.T, watcher *Watcher, dir string) { - if err := watcher.AddRaw(dir); err != nil { - t.Fatalf("watcher.AddRaw(%q) failed: %s", dir, err) + if err := watcher.Add(dir); err != nil { + t.Fatalf("watcher.Add(%q) failed: %s", dir, err) } } @@ -1008,74 +1008,11 @@ func TestFsnotifyClose(t *testing.T) { testDir := tempMkdir(t) defer os.RemoveAll(testDir) - if err := watcher.AddRaw(testDir); err == nil { + if err := watcher.Add(testDir); err == nil { t.Fatal("expected error on Watch() after Close(), got nil") } } -func TestSymlinkNotResolved(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 symlink: %s", err) - } - - w, err := NewWatcher() - if err != nil { - t.Fatalf("Failed to create watcher") - } - - err = w.AddRaw(link) - if err != nil { - t.Fatalf("Failed to add link: %s", err) - } - - // change file 1 - no event - f1.Write([]byte("Hello")) - f1.Sync() - // XXX(Code0x58): 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, "tmp-link") - if err := os.Symlink(file2, tmpLink); err != nil { - t.Fatalf("Failed to create symlink: %s", err) - } - - if err := os.Rename(tmpLink, link); err != nil { - t.Fatalf("Failed to replace symlink: %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") - } - -} - func TestFsnotifyFakeSymlink(t *testing.T) { if runtime.GOOS == "windows" { t.Skip("symlinks don't work on Windows.") @@ -1237,7 +1174,7 @@ func TestClose(t *testing.T) { defer os.RemoveAll(testDir) watcher := newWatcher(t) - if err := watcher.AddRaw(testDir); err != nil { + if err := watcher.Add(testDir); err != nil { t.Fatalf("Expected no error on Add, got %v", err) } err := watcher.Close() @@ -1258,7 +1195,7 @@ func TestRemoveWithClose(t *testing.T) { tempFiles = append(tempFiles, tempMkFile(t, testDir)) } watcher := newWatcher(t) - if err := watcher.AddRaw(testDir); err != nil { + if err := watcher.Add(testDir); err != nil { t.Fatalf("Expected no error on Add, got %v", err) } startC, stopC := make(chan struct{}), make(chan struct{}) diff --git a/kqueue.go b/kqueue.go index caecacb5..368f5b79 100644 --- a/kqueue.go +++ b/kqueue.go @@ -91,8 +91,8 @@ func (w *Watcher) Close() error { return nil } -// AddRaw starts watching the named file or directory (non-recursively). Symlinks are not implicitly resolved. -func (w *Watcher) AddRaw(name string) error { +// Add starts watching the named file or directory (non-recursively). +func (w *Watcher) Add(name string) error { w.mu.Lock() w.externalWatches[name] = true w.mu.Unlock() @@ -190,7 +190,33 @@ func (w *Watcher) addWatch(name string, flags uint32) (string, error) { return "", nil } - watchfd, err = unix.Open(name, openMode|unix.O_SYMLINK, 0700) + // 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) if watchfd == -1 { return "", err } diff --git a/windows.go b/windows.go index 7dd51481..c02b75f7 100644 --- a/windows.go +++ b/windows.go @@ -64,8 +64,8 @@ func (w *Watcher) Close() error { return <-ch } -// AddRaw starts watching the named file or directory (non-recursively). Symlinks are not implicitly resolved. -func (w *Watcher) AddRaw(name string) error { +// Add starts watching the named file or directory (non-recursively). +func (w *Watcher) Add(name string) error { if w.isClosed { return errors.New("watcher already closed") }