From 1a30f1673ba6075f92a670215f57c3bf533f6483 Mon Sep 17 00:00:00 2001 From: Martin Tournoij Date: Sat, 6 Aug 2022 19:35:29 +0200 Subject: [PATCH] Update --- fsnotify.go | 36 +++++++++++++++++++----------------- inotify.go | 29 ++++++----------------------- integration_test.go | 22 +++++++++++++--------- 3 files changed, 38 insertions(+), 49 deletions(-) diff --git a/fsnotify.go b/fsnotify.go index 07f70f48..864b487f 100644 --- a/fsnotify.go +++ b/fsnotify.go @@ -17,23 +17,6 @@ import ( "strings" ) -// These are the generalized file operations that can trigger a notification. -const ( - Create Op = 1 << iota - Write - Remove - Rename - Chmod -) - -// Common errors that can be reported by a watcher -var ( - ErrNonExistentWatch = errors.New("can't remove non-existent watcher") - ErrEventOverflow = errors.New("fsnotify queue overflow") - ErrNotDirectory = errors.New("not a directory") - ErrRecursionUnsupported = errors.New("recursion not supported") -) - // Event represents a single file system notification. type Event struct { // Path to the file or directory. @@ -53,6 +36,23 @@ type Event struct { // Op describes a set of file operations. type Op uint32 +// These are the generalized file operations that can trigger a notification. +const ( + Create Op = 1 << iota + Write + Remove + Rename + Chmod +) + +// Common errors that can be reported by a watcher +var ( + ErrNonExistentWatch = errors.New("can't remove non-existent watcher") + ErrEventOverflow = errors.New("fsnotify queue overflow") + ErrNotDirectory = errors.New("not a directory") + ErrRecursionUnsupported = errors.New("recursion not supported") +) + func (op Op) String() string { var b strings.Builder if op.Has(Create) { @@ -90,6 +90,8 @@ func (e Event) String() string { // findDirs finds all directories under path (return value *includes* path as // the first entry). +// +// A symlink for a directory is not considered a directory. func findDirs(path string) ([]string, error) { dirs := make([]string, 0, 8) err := filepath.WalkDir(path, func(root string, d fs.DirEntry, err error) error { diff --git a/inotify.go b/inotify.go index 6372966e..f2c475ce 100644 --- a/inotify.go +++ b/inotify.go @@ -121,8 +121,8 @@ func (w *Watcher) Close() error { // Add starts watching a file or directory. // // If the path is a directory then changes to that directory are watched -// non-recursively. If the path ends with "..." changes in the entire directory -// tree are watched. ErrNotDirectory is returned when using "..." on a file. +// non-recursively. If the path ends with "/..." changes in the entire directory +// tree are watched. ErrNotDirectory is returned when using "/..." on a file. // // Symlinks are not followed. func (w *Watcher) Add(path string) error { @@ -344,30 +344,13 @@ func (w *Watcher) readEvents() { } } -// Certain types of events can be "ignored" and not sent over the Events -// channel. Such as events marked ignore by the kernel, or MODIFY events -// against files that do not exist. -func (e *Event) ignoreLinux(mask uint32) bool { - // Ignore anything the inotify API says to ignore - if mask&unix.IN_IGNORED == unix.IN_IGNORED { - return true - } - - // If the event is Create or Write, the file must exist, or the - // event will be suppressed. - // *Note*: this was put in place because it was seen that a Write - // event was sent after the Remove. This ignores the Write and - // assumes a Remove will come or has come if the file doesn't exist. - if e.Op&Create == Create || e.Op&Write == Write { - _, statErr := os.Lstat(e.Name) - return os.IsNotExist(statErr) - } - return false -} - +// Check if path was added as a recursive watch ("dir/..."). +// +// Returns the watch for the path, or nil. func (w *Watcher) isRecursive(path string) *watch { ww, ok := w.watches[path] if !ok { + // path could be a file, so also check the Dir. path = filepath.Dir(path) ww, ok = w.watches[path] if !ok { diff --git a/integration_test.go b/integration_test.go index e8344e8a..b9a91881 100644 --- a/integration_test.go +++ b/integration_test.go @@ -577,7 +577,7 @@ func TestRemove(t *testing.T) { }) } -func TestRecursive(t *testing.T) { +func TestWatcherRecursive(t *testing.T) { switch runtime.GOOS { case "linux": // Run test. @@ -606,13 +606,13 @@ func TestRecursive(t *testing.T) { // recursively add watches for any subdirectories that it contains). tests := []testCase{ + // Make a nested directory tree, then write some files there. {"basic", func(t *testing.T, w *Watcher, tmp string) { mkdirAll(t, tmp, "/one/two/three/four") - addWatch(t, w, tmp, "...") + addWatch(t, w, tmp, "/...") - // func(t *testing.T, tmp string) { - cat(t, "asd", tmp, "file.txt") - cat(t, "asd", tmp, "one/two/three/file.txt") + cat(t, "asd", tmp, "/file.txt") + cat(t, "asd", tmp, "/one/two/three/file.txt") }, ` CREATE "/file.txt" WRITE "/file.txt" @@ -622,11 +622,11 @@ func TestRecursive(t *testing.T) { {"add directory", func(t *testing.T, w *Watcher, tmp string) { mkdirAll(t, tmp, "/one/two/three/four") - addWatch(t, w, tmp, "...") + addWatch(t, w, tmp, "/...") - mkdirAll(t, tmp, "one/two/new/dir") - touch(t, tmp, "one/two/new/file") - touch(t, tmp, "one/two/new/dir/file") + mkdirAll(t, tmp, "/one/two/new/dir") + touch(t, tmp, "/one/two/new/file") + touch(t, tmp, "/one/two/new/dir/file") }, ` # TODO: don't see the new/dir being created. CREATE "/one/two/new" @@ -634,6 +634,7 @@ func TestRecursive(t *testing.T) { CREATE "/one/two/new/dir/file" `}, + // Remove nested directory {"remove directory", func(t *testing.T, w *Watcher, tmp string) { mkdirAll(t, tmp, "/one/two/three/four") addWatch(t, w, tmp, "...") @@ -655,6 +656,7 @@ func TestRecursive(t *testing.T) { REMOVE "/one/two" `}, + // Rename nested directory {"rename directory", func(t *testing.T, w *Watcher, tmp string) { mkdirAll(t, tmp, "/one/two/three/four") addWatch(t, w, tmp, "...") @@ -670,6 +672,8 @@ func TestRecursive(t *testing.T) { CREATE "/one-rename/file" CREATE "/one-rename/two/three/file" `}, + + // TODO: rest that Remove doesn't keep watching stuff } for _, tt := range tests {