Skip to content

Commit

Permalink
Do not follow symlinks by default
Browse files Browse the repository at this point in the history
  • Loading branch information
Code0x58 committed Apr 11, 2019
1 parent 1485a34 commit 3891ea9
Show file tree
Hide file tree
Showing 4 changed files with 73 additions and 31 deletions.
6 changes: 4 additions & 2 deletions README.md
Expand Up @@ -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).
Expand Down Expand Up @@ -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)
2 changes: 1 addition & 1 deletion inotify.go
Expand Up @@ -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

Expand Down
68 changes: 67 additions & 1 deletion integration_test.go
Expand Up @@ -10,7 +10,6 @@ import (
"io/ioutil"
"os"
"os/exec"
"path"
"path/filepath"
"runtime"
"sync/atomic"
Expand Down Expand Up @@ -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.")
Expand Down Expand Up @@ -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.")
}
Expand Down Expand Up @@ -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/
Expand Down
28 changes: 1 addition & 27 deletions kqueue.go
Expand Up @@ -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
}
Expand Down

0 comments on commit 3891ea9

Please sign in to comment.