diff --git a/go.mod b/go.mod
index 526713514f99e71f0ea700ad04d73da5cb59c06b..974a73ac8372070df0f31375d24090f1a76ca0eb 100644
--- a/go.mod
+++ b/go.mod
@@ -7,7 +7,7 @@ toolchain go1.23.5
 require (
 	github.com/caarlos0/env/v11 v11.3.1
 	github.com/cilium/ebpf v0.18.0
-	github.com/fsnotify/fsnotify v1.8.0
+	github.com/fsnotify/fsnotify v1.9.0
 	github.com/gavv/monotime v0.0.0-20190418164738-30dba4353424
 	github.com/gopacket/gopacket v1.3.1
 	github.com/mariomac/guara v0.0.0-20250306093316-0985019a30d4
diff --git a/go.sum b/go.sum
index fb092baabb2f811aaf51c62f21c55d68bef21ce9..664275a63c5a98cbe85b8aa2e30e97b1ef5ca972 100644
--- a/go.sum
+++ b/go.sum
@@ -210,8 +210,8 @@ github.com/franela/goblin v0.0.0-20200105215937-c9ffbefa60db/go.mod h1:7dvUGVsVB
 github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8/go.mod h1:ZhphrRTfi2rbfLwlschooIH4+wKKDR4Pdxhh+TRoA20=
 github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
 github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
-github.com/fsnotify/fsnotify v1.8.0 h1:dAwr6QBTBZIkG8roQaJjGof0pp0EeF+tNV7YBP3F/8M=
-github.com/fsnotify/fsnotify v1.8.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
+github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k=
+github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
 github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E=
 github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ=
 github.com/gaissmai/cidrtree v0.1.4 h1:/aYnv1LIwjtSDHNr1eNN99WJeh6vLrB+Sgr1tRMhHDc=
diff --git a/vendor/github.com/fsnotify/fsnotify/.cirrus.yml b/vendor/github.com/fsnotify/fsnotify/.cirrus.yml
index f4e7dbf37b363fcac673e697d469c3bbbf7fc5f9..7f257e99ac9ea891efe6e2935132d1ad66079018 100644
--- a/vendor/github.com/fsnotify/fsnotify/.cirrus.yml
+++ b/vendor/github.com/fsnotify/fsnotify/.cirrus.yml
@@ -1,7 +1,7 @@
 freebsd_task:
   name: 'FreeBSD'
   freebsd_instance:
-    image_family: freebsd-14-1
+    image_family: freebsd-14-2
   install_script:
     - pkg update -f
     - pkg install -y go
diff --git a/vendor/github.com/fsnotify/fsnotify/CHANGELOG.md b/vendor/github.com/fsnotify/fsnotify/CHANGELOG.md
index fa854785d0f548e339cb4b959f8290e68df1dd46..6468d2cf400fcfc3bb5c49c00b90f3699d5dea90 100644
--- a/vendor/github.com/fsnotify/fsnotify/CHANGELOG.md
+++ b/vendor/github.com/fsnotify/fsnotify/CHANGELOG.md
@@ -1,6 +1,39 @@
 # Changelog
 
-1.8.0 2023-10-31
+1.9.0 2024-04-04
+----------------
+
+### Changes and fixes
+
+- all: make BufferedWatcher buffered again ([#657])
+
+- inotify: fix race when adding/removing watches while a watched path is being
+  deleted ([#678], [#686])
+
+- inotify: don't send empty event if a watched path is unmounted ([#655])
+
+- inotify: don't register duplicate watches when watching both a symlink and its
+  target; previously that would get "half-added" and removing the second would
+  panic ([#679])
+
+- kqueue: fix watching relative symlinks ([#681])
+
+- kqueue: correctly mark pre-existing entries when watching a link to a dir on
+  kqueue ([#682])
+
+- illumos: don't send error if changed file is deleted while processing the
+  event ([#678])
+
+
+[#657]: https://github.com/fsnotify/fsnotify/pull/657
+[#678]: https://github.com/fsnotify/fsnotify/pull/678
+[#686]: https://github.com/fsnotify/fsnotify/pull/686
+[#655]: https://github.com/fsnotify/fsnotify/pull/655
+[#681]: https://github.com/fsnotify/fsnotify/pull/681
+[#679]: https://github.com/fsnotify/fsnotify/pull/679
+[#682]: https://github.com/fsnotify/fsnotify/pull/682
+
+1.8.0 2024-10-31
 ----------------
 
 ### Additions
diff --git a/vendor/github.com/fsnotify/fsnotify/CONTRIBUTING.md b/vendor/github.com/fsnotify/fsnotify/CONTRIBUTING.md
index e4ac2a2fffdc12c9385e5dfbeedf9061d8c27ec4..4cc40fa597d8ce9ff78c844242f067605776b2e4 100644
--- a/vendor/github.com/fsnotify/fsnotify/CONTRIBUTING.md
+++ b/vendor/github.com/fsnotify/fsnotify/CONTRIBUTING.md
@@ -77,6 +77,7 @@ End-of-line escapes with `\` are not supported.
     debug [yes/no]      # Enable/disable FSNOTIFY_DEBUG (tests are run in
                           parallel by default, so -parallel=1 is probably a good
                           idea).
+    print [any strings] # Print text to stdout; for debugging.
 
     touch path
     mkdir [-p] dir
diff --git a/vendor/github.com/fsnotify/fsnotify/README.md b/vendor/github.com/fsnotify/fsnotify/README.md
index e480733d16cb32fd3fbb2fae6cb6834a8edc0e46..1f4eb583d50b7aa9e8e3eea62faafe6e13697c60 100644
--- a/vendor/github.com/fsnotify/fsnotify/README.md
+++ b/vendor/github.com/fsnotify/fsnotify/README.md
@@ -15,7 +15,6 @@ Platform support:
 | ReadDirectoryChangesW | Windows    | Supported                                                                 |
 | FEN                   | illumos    | Supported                                                                 |
 | fanotify              | Linux 5.9+ | [Not yet](https://github.com/fsnotify/fsnotify/issues/114)                |
-| AHAFS                 | AIX        | [aix branch]; experimental due to lack of maintainer and test environment |
 | FSEvents              | macOS      | [Needs support in x/sys/unix][fsevents]                                   |
 | USN Journals          | Windows    | [Needs support in x/sys/windows][usn]                                     |
 | Polling               | *All*      | [Not yet](https://github.com/fsnotify/fsnotify/issues/9)                  |
@@ -25,7 +24,6 @@ untested.
 
 [fsevents]:   https://github.com/fsnotify/fsnotify/issues/11#issuecomment-1279133120
 [usn]:        https://github.com/fsnotify/fsnotify/issues/53#issuecomment-1279829847
-[aix branch]: https://github.com/fsnotify/fsnotify/issues/353#issuecomment-1284590129
 
 Usage
 -----
diff --git a/vendor/github.com/fsnotify/fsnotify/backend_fen.go b/vendor/github.com/fsnotify/fsnotify/backend_fen.go
index c349c326c718fc541fd89bd2322d3218cba1a0a9..57fc692848455dacbe06fc1614c8a8f38432cf7f 100644
--- a/vendor/github.com/fsnotify/fsnotify/backend_fen.go
+++ b/vendor/github.com/fsnotify/fsnotify/backend_fen.go
@@ -9,6 +9,7 @@ package fsnotify
 import (
 	"errors"
 	"fmt"
+	"io/fs"
 	"os"
 	"path/filepath"
 	"sync"
@@ -19,27 +20,25 @@ import (
 )
 
 type fen struct {
+	*shared
 	Events chan Event
 	Errors chan error
 
 	mu      sync.Mutex
 	port    *unix.EventPort
-	done    chan struct{} // Channel for sending a "quit message" to the reader goroutine
 	dirs    map[string]Op // Explicitly watched directories
 	watches map[string]Op // Explicitly watched non-directories
 }
 
-func newBackend(ev chan Event, errs chan error) (backend, error) {
-	return newBufferedBackend(0, ev, errs)
-}
+var defaultBufferSize = 0
 
-func newBufferedBackend(sz uint, ev chan Event, errs chan error) (backend, error) {
+func newBackend(ev chan Event, errs chan error) (backend, error) {
 	w := &fen{
+		shared:  newShared(ev, errs),
 		Events:  ev,
 		Errors:  errs,
 		dirs:    make(map[string]Op),
 		watches: make(map[string]Op),
-		done:    make(chan struct{}),
 	}
 
 	var err error
@@ -52,49 +51,10 @@ func newBufferedBackend(sz uint, ev chan Event, errs chan error) (backend, error
 	return w, nil
 }
 
-// sendEvent attempts to send an event to the user, returning true if the event
-// was put in the channel successfully and false if the watcher has been closed.
-func (w *fen) sendEvent(name string, op Op) (sent bool) {
-	select {
-	case <-w.done:
-		return false
-	case w.Events <- Event{Name: name, Op: op}:
-		return true
-	}
-}
-
-// sendError attempts to send an error to the user, returning true if the error
-// was put in the channel successfully and false if the watcher has been closed.
-func (w *fen) sendError(err error) (sent bool) {
-	if err == nil {
-		return true
-	}
-	select {
-	case <-w.done:
-		return false
-	case w.Errors <- err:
-		return true
-	}
-}
-
-func (w *fen) isClosed() bool {
-	select {
-	case <-w.done:
-		return true
-	default:
-		return false
-	}
-}
-
 func (w *fen) Close() error {
-	// Take the lock used by associateFile to prevent lingering events from
-	// being processed after the close
-	w.mu.Lock()
-	defer w.mu.Unlock()
-	if w.isClosed() {
+	if w.shared.close() {
 		return nil
 	}
-	close(w.done)
 	return w.port.Close()
 }
 
@@ -209,7 +169,7 @@ func (w *fen) readEvents() {
 				return
 			}
 			// There was an error not caused by calling w.Close()
-			if !w.sendError(err) {
+			if !w.sendError(fmt.Errorf("port.Get: %w", err)) {
 				return
 			}
 		}
@@ -277,13 +237,13 @@ func (w *fen) handleEvent(event *unix.PortEvent) error {
 	isWatched := watchedDir || watchedPath
 
 	if events&unix.FILE_DELETE != 0 {
-		if !w.sendEvent(path, Remove) {
+		if !w.sendEvent(Event{Name: path, Op: Remove}) {
 			return nil
 		}
 		reRegister = false
 	}
 	if events&unix.FILE_RENAME_FROM != 0 {
-		if !w.sendEvent(path, Rename) {
+		if !w.sendEvent(Event{Name: path, Op: Rename}) {
 			return nil
 		}
 		// Don't keep watching the new file name
@@ -297,7 +257,7 @@ func (w *fen) handleEvent(event *unix.PortEvent) error {
 
 		// inotify reports a Remove event in this case, so we simulate this
 		// here.
-		if !w.sendEvent(path, Remove) {
+		if !w.sendEvent(Event{Name: path, Op: Remove}) {
 			return nil
 		}
 		// Don't keep watching the file that was removed
@@ -331,7 +291,7 @@ func (w *fen) handleEvent(event *unix.PortEvent) error {
 		// get here, the sudirectory is already gone. Clearly we were watching
 		// this path but now it is gone. Let's tell the user that it was
 		// removed.
-		if !w.sendEvent(path, Remove) {
+		if !w.sendEvent(Event{Name: path, Op: Remove}) {
 			return nil
 		}
 		// Suppress extra write events on removed directories; they are not
@@ -346,7 +306,7 @@ func (w *fen) handleEvent(event *unix.PortEvent) error {
 		if err != nil {
 			// The symlink still exists, but the target is gone. Report the
 			// Remove similar to above.
-			if !w.sendEvent(path, Remove) {
+			if !w.sendEvent(Event{Name: path, Op: Remove}) {
 				return nil
 			}
 			// Don't return the error
@@ -359,7 +319,7 @@ func (w *fen) handleEvent(event *unix.PortEvent) error {
 				return err
 			}
 		} else {
-			if !w.sendEvent(path, Write) {
+			if !w.sendEvent(Event{Name: path, Op: Write}) {
 				return nil
 			}
 		}
@@ -367,7 +327,7 @@ func (w *fen) handleEvent(event *unix.PortEvent) error {
 	if events&unix.FILE_ATTRIB != 0 && stat != nil {
 		// Only send Chmod if perms changed
 		if stat.Mode().Perm() != fmode.Perm() {
-			if !w.sendEvent(path, Chmod) {
+			if !w.sendEvent(Event{Name: path, Op: Chmod}) {
 				return nil
 			}
 		}
@@ -376,17 +336,27 @@ func (w *fen) handleEvent(event *unix.PortEvent) error {
 	if stat != nil {
 		// If we get here, it means we've hit an event above that requires us to
 		// continue watching the file or directory
-		return w.associateFile(path, stat, isWatched)
+		err := w.associateFile(path, stat, isWatched)
+		if errors.Is(err, fs.ErrNotExist) {
+			// Path may have been removed since the stat.
+			err = nil
+		}
+		return err
 	}
 	return nil
 }
 
+// The directory was modified, so we must find unwatched entities and watch
+// them. If something was removed from the directory, nothing will happen, as
+// everything else should still be watched.
 func (w *fen) updateDirectory(path string) error {
-	// The directory was modified, so we must find unwatched entities and watch
-	// them. If something was removed from the directory, nothing will happen,
-	// as everything else should still be watched.
 	files, err := os.ReadDir(path)
 	if err != nil {
+		// Directory no longer exists: probably just deleted since we got the
+		// event.
+		if errors.Is(err, fs.ErrNotExist) {
+			return nil
+		}
 		return err
 	}
 
@@ -401,10 +371,15 @@ func (w *fen) updateDirectory(path string) error {
 			return err
 		}
 		err = w.associateFile(path, finfo, false)
+		if errors.Is(err, fs.ErrNotExist) {
+			// File may have disappeared between getting the dir listing and
+			// adding the port: that's okay to ignore.
+			continue
+		}
 		if !w.sendError(err) {
 			return nil
 		}
-		if !w.sendEvent(path, Create) {
+		if !w.sendEvent(Event{Name: path, Op: Create}) {
 			return nil
 		}
 	}
@@ -430,7 +405,7 @@ func (w *fen) associateFile(path string, stat os.FileInfo, follow bool) error {
 		// has fired but we haven't processed it yet.
 		err := w.port.DissociatePath(path)
 		if err != nil && !errors.Is(err, unix.ENOENT) {
-			return err
+			return fmt.Errorf("port.DissociatePath(%q): %w", path, err)
 		}
 	}
 
@@ -446,14 +421,22 @@ func (w *fen) associateFile(path string, stat os.FileInfo, follow bool) error {
 	if true {
 		events |= unix.FILE_ATTRIB
 	}
-	return w.port.AssociatePath(path, stat, events, stat.Mode())
+	err := w.port.AssociatePath(path, stat, events, stat.Mode())
+	if err != nil {
+		return fmt.Errorf("port.AssociatePath(%q): %w", path, err)
+	}
+	return nil
 }
 
 func (w *fen) dissociateFile(path string, stat os.FileInfo, unused bool) error {
 	if !w.port.PathIsWatched(path) {
 		return nil
 	}
-	return w.port.DissociatePath(path)
+	err := w.port.DissociatePath(path)
+	if err != nil {
+		return fmt.Errorf("port.DissociatePath(%q): %w", path, err)
+	}
+	return nil
 }
 
 func (w *fen) WatchList() []string {
diff --git a/vendor/github.com/fsnotify/fsnotify/backend_inotify.go b/vendor/github.com/fsnotify/fsnotify/backend_inotify.go
index 36c311694cd5ae27af198736d48c0ac9556e6520..a36cb89d736119643fc6e92aaf8a2ab9f138d1ac 100644
--- a/vendor/github.com/fsnotify/fsnotify/backend_inotify.go
+++ b/vendor/github.com/fsnotify/fsnotify/backend_inotify.go
@@ -19,6 +19,7 @@ import (
 )
 
 type inotify struct {
+	*shared
 	Events chan Event
 	Errors chan error
 
@@ -27,8 +28,6 @@ type inotify struct {
 	fd          int
 	inotifyFile *os.File
 	watches     *watches
-	done        chan struct{} // Channel for sending a "quit message" to the reader goroutine
-	doneMu      sync.Mutex
 	doneResp    chan struct{} // Channel to respond to Close
 
 	// Store rename cookies in an array, with the index wrapping to 0. Almost
@@ -52,7 +51,6 @@ type inotify struct {
 
 type (
 	watches struct {
-		mu   sync.RWMutex
 		wd   map[uint32]*watch // wd → watch
 		path map[string]uint32 // pathname → wd
 	}
@@ -75,34 +73,13 @@ func newWatches() *watches {
 	}
 }
 
-func (w *watches) len() int {
-	w.mu.RLock()
-	defer w.mu.RUnlock()
-	return len(w.wd)
-}
-
-func (w *watches) add(ww *watch) {
-	w.mu.Lock()
-	defer w.mu.Unlock()
-	w.wd[ww.wd] = ww
-	w.path[ww.path] = ww.wd
-}
-
-func (w *watches) remove(wd uint32) {
-	w.mu.Lock()
-	defer w.mu.Unlock()
-	watch := w.wd[wd] // Could have had Remove() called. See #616.
-	if watch == nil {
-		return
-	}
-	delete(w.path, watch.path)
-	delete(w.wd, wd)
-}
+func (w *watches) byPath(path string) *watch { return w.wd[w.path[path]] }
+func (w *watches) byWd(wd uint32) *watch     { return w.wd[wd] }
+func (w *watches) len() int                  { return len(w.wd) }
+func (w *watches) add(ww *watch)             { w.wd[ww.wd] = ww; w.path[ww.path] = ww.wd }
+func (w *watches) remove(watch *watch)       { delete(w.path, watch.path); delete(w.wd, watch.wd) }
 
 func (w *watches) removePath(path string) ([]uint32, error) {
-	w.mu.Lock()
-	defer w.mu.Unlock()
-
 	path, recurse := recursivePath(path)
 	wd, ok := w.path[path]
 	if !ok {
@@ -123,7 +100,7 @@ func (w *watches) removePath(path string) ([]uint32, error) {
 	wds := make([]uint32, 0, 8)
 	wds = append(wds, wd)
 	for p, rwd := range w.path {
-		if filepath.HasPrefix(p, path) {
+		if strings.HasPrefix(p, path) {
 			delete(w.path, p)
 			delete(w.wd, rwd)
 			wds = append(wds, rwd)
@@ -132,22 +109,7 @@ func (w *watches) removePath(path string) ([]uint32, error) {
 	return wds, nil
 }
 
-func (w *watches) byPath(path string) *watch {
-	w.mu.RLock()
-	defer w.mu.RUnlock()
-	return w.wd[w.path[path]]
-}
-
-func (w *watches) byWd(wd uint32) *watch {
-	w.mu.RLock()
-	defer w.mu.RUnlock()
-	return w.wd[wd]
-}
-
 func (w *watches) updatePath(path string, f func(*watch) (*watch, error)) error {
-	w.mu.Lock()
-	defer w.mu.Unlock()
-
 	var existing *watch
 	wd, ok := w.path[path]
 	if ok {
@@ -170,11 +132,9 @@ func (w *watches) updatePath(path string, f func(*watch) (*watch, error)) error
 	return nil
 }
 
-func newBackend(ev chan Event, errs chan error) (backend, error) {
-	return newBufferedBackend(0, ev, errs)
-}
+var defaultBufferSize = 0
 
-func newBufferedBackend(sz uint, ev chan Event, errs chan error) (backend, error) {
+func newBackend(ev chan Event, errs chan error) (backend, error) {
 	// Need to set nonblocking mode for SetDeadline to work, otherwise blocking
 	// I/O operations won't terminate on close.
 	fd, errno := unix.InotifyInit1(unix.IN_CLOEXEC | unix.IN_NONBLOCK)
@@ -183,12 +143,12 @@ func newBufferedBackend(sz uint, ev chan Event, errs chan error) (backend, error
 	}
 
 	w := &inotify{
+		shared:      newShared(ev, errs),
 		Events:      ev,
 		Errors:      errs,
 		fd:          fd,
 		inotifyFile: os.NewFile(uintptr(fd), ""),
 		watches:     newWatches(),
-		done:        make(chan struct{}),
 		doneResp:    make(chan struct{}),
 	}
 
@@ -196,46 +156,10 @@ func newBufferedBackend(sz uint, ev chan Event, errs chan error) (backend, error
 	return w, nil
 }
 
-// Returns true if the event was sent, or false if watcher is closed.
-func (w *inotify) sendEvent(e Event) bool {
-	select {
-	case <-w.done:
-		return false
-	case w.Events <- e:
-		return true
-	}
-}
-
-// Returns true if the error was sent, or false if watcher is closed.
-func (w *inotify) sendError(err error) bool {
-	if err == nil {
-		return true
-	}
-	select {
-	case <-w.done:
-		return false
-	case w.Errors <- err:
-		return true
-	}
-}
-
-func (w *inotify) isClosed() bool {
-	select {
-	case <-w.done:
-		return true
-	default:
-		return false
-	}
-}
-
 func (w *inotify) Close() error {
-	w.doneMu.Lock()
-	if w.isClosed() {
-		w.doneMu.Unlock()
+	if w.shared.close() {
 		return nil
 	}
-	close(w.done)
-	w.doneMu.Unlock()
 
 	// Causes any blocking reads to return with an error, provided the file
 	// still supports deadline operations.
@@ -244,9 +168,7 @@ func (w *inotify) Close() error {
 		return err
 	}
 
-	// Wait for goroutine to close
-	<-w.doneResp
-
+	<-w.doneResp // Wait for readEvents() to finish.
 	return nil
 }
 
@@ -266,6 +188,43 @@ func (w *inotify) AddWith(path string, opts ...addOpt) error {
 		return fmt.Errorf("%w: %s", xErrUnsupported, with.op)
 	}
 
+	add := func(path string, with withOpts, recurse bool) error {
+		var flags uint32
+		if with.noFollow {
+			flags |= unix.IN_DONT_FOLLOW
+		}
+		if with.op.Has(Create) {
+			flags |= unix.IN_CREATE
+		}
+		if with.op.Has(Write) {
+			flags |= unix.IN_MODIFY
+		}
+		if with.op.Has(Remove) {
+			flags |= unix.IN_DELETE | unix.IN_DELETE_SELF
+		}
+		if with.op.Has(Rename) {
+			flags |= unix.IN_MOVED_TO | unix.IN_MOVED_FROM | unix.IN_MOVE_SELF
+		}
+		if with.op.Has(Chmod) {
+			flags |= unix.IN_ATTRIB
+		}
+		if with.op.Has(xUnportableOpen) {
+			flags |= unix.IN_OPEN
+		}
+		if with.op.Has(xUnportableRead) {
+			flags |= unix.IN_ACCESS
+		}
+		if with.op.Has(xUnportableCloseWrite) {
+			flags |= unix.IN_CLOSE_WRITE
+		}
+		if with.op.Has(xUnportableCloseRead) {
+			flags |= unix.IN_CLOSE_NOWRITE
+		}
+		return w.register(path, flags, recurse)
+	}
+
+	w.mu.Lock()
+	defer w.mu.Unlock()
 	path, recurse := recursivePath(path)
 	if recurse {
 		return filepath.WalkDir(path, func(root string, d fs.DirEntry, err error) error {
@@ -289,46 +248,11 @@ func (w *inotify) AddWith(path string, opts ...addOpt) error {
 				w.sendEvent(Event{Name: root, Op: Create})
 			}
 
-			return w.add(root, with, true)
+			return add(root, with, true)
 		})
 	}
 
-	return w.add(path, with, false)
-}
-
-func (w *inotify) add(path string, with withOpts, recurse bool) error {
-	var flags uint32
-	if with.noFollow {
-		flags |= unix.IN_DONT_FOLLOW
-	}
-	if with.op.Has(Create) {
-		flags |= unix.IN_CREATE
-	}
-	if with.op.Has(Write) {
-		flags |= unix.IN_MODIFY
-	}
-	if with.op.Has(Remove) {
-		flags |= unix.IN_DELETE | unix.IN_DELETE_SELF
-	}
-	if with.op.Has(Rename) {
-		flags |= unix.IN_MOVED_TO | unix.IN_MOVED_FROM | unix.IN_MOVE_SELF
-	}
-	if with.op.Has(Chmod) {
-		flags |= unix.IN_ATTRIB
-	}
-	if with.op.Has(xUnportableOpen) {
-		flags |= unix.IN_OPEN
-	}
-	if with.op.Has(xUnportableRead) {
-		flags |= unix.IN_ACCESS
-	}
-	if with.op.Has(xUnportableCloseWrite) {
-		flags |= unix.IN_CLOSE_WRITE
-	}
-	if with.op.Has(xUnportableCloseRead) {
-		flags |= unix.IN_CLOSE_NOWRITE
-	}
-	return w.register(path, flags, recurse)
+	return add(path, with, false)
 }
 
 func (w *inotify) register(path string, flags uint32, recurse bool) error {
@@ -342,6 +266,10 @@ func (w *inotify) register(path string, flags uint32, recurse bool) error {
 			return nil, err
 		}
 
+		if e, ok := w.watches.wd[uint32(wd)]; ok {
+			return e, nil
+		}
+
 		if existing == nil {
 			return &watch{
 				wd:      uint32(wd),
@@ -365,6 +293,9 @@ func (w *inotify) Remove(name string) error {
 		fmt.Fprintf(os.Stderr, "FSNOTIFY_DEBUG: %s  Remove(%q)\n",
 			time.Now().Format("15:04:05.000000000"), name)
 	}
+
+	w.mu.Lock()
+	defer w.mu.Unlock()
 	return w.remove(filepath.Clean(name))
 }
 
@@ -399,13 +330,12 @@ func (w *inotify) WatchList() []string {
 		return nil
 	}
 
+	w.mu.Lock()
+	defer w.mu.Unlock()
 	entries := make([]string, 0, w.watches.len())
-	w.watches.mu.RLock()
 	for pathname := range w.watches.path {
 		entries = append(entries, pathname)
 	}
-	w.watches.mu.RUnlock()
-
 	return entries
 }
 
@@ -418,21 +348,17 @@ func (w *inotify) readEvents() {
 		close(w.Events)
 	}()
 
-	var (
-		buf   [unix.SizeofInotifyEvent * 4096]byte // Buffer for a maximum of 4096 raw events
-		errno error                                // Syscall errno
-	)
+	var buf [unix.SizeofInotifyEvent * 4096]byte // Buffer for a maximum of 4096 raw events
 	for {
-		// See if we have been closed.
 		if w.isClosed() {
 			return
 		}
 
 		n, err := w.inotifyFile.Read(buf[:])
-		switch {
-		case errors.Unwrap(err) == os.ErrClosed:
-			return
-		case err != nil:
+		if err != nil {
+			if errors.Is(err, os.ErrClosed) {
+				return
+			}
 			if !w.sendError(err) {
 				return
 			}
@@ -440,13 +366,9 @@ func (w *inotify) readEvents() {
 		}
 
 		if n < unix.SizeofInotifyEvent {
-			var err error
+			err := errors.New("notify: short read in readEvents()") // Read was too short.
 			if n == 0 {
 				err = io.EOF // If EOF is received. This should really never happen.
-			} else if n < 0 {
-				err = errno // If an error occurred while reading.
-			} else {
-				err = errors.New("notify: short read in readEvents()") // Read was too short.
 			}
 			if !w.sendError(err) {
 				return
@@ -454,132 +376,135 @@ func (w *inotify) readEvents() {
 			continue
 		}
 
-		// We don't know how many events we just read into the buffer
-		// While the offset points to at least one whole event...
+		// We don't know how many events we just read into the buffer While the
+		// offset points to at least one whole event.
 		var offset uint32
 		for offset <= uint32(n-unix.SizeofInotifyEvent) {
-			var (
-				// Point "raw" to the event in the buffer
-				raw     = (*unix.InotifyEvent)(unsafe.Pointer(&buf[offset]))
-				mask    = uint32(raw.Mask)
-				nameLen = uint32(raw.Len)
-				// Move to the next event in the buffer
-				next = func() { offset += unix.SizeofInotifyEvent + nameLen }
-			)
-
-			if mask&unix.IN_Q_OVERFLOW != 0 {
+			// Point to the event in the buffer.
+			inEvent := (*unix.InotifyEvent)(unsafe.Pointer(&buf[offset]))
+
+			if inEvent.Mask&unix.IN_Q_OVERFLOW != 0 {
 				if !w.sendError(ErrEventOverflow) {
 					return
 				}
 			}
 
-			/// If the event happened to the watched directory or the watched
-			/// file, the kernel doesn't append the filename to the event, but
-			/// we would like to always fill the the "Name" field with a valid
-			/// filename. We retrieve the path of the watch from the "paths"
-			/// map.
-			watch := w.watches.byWd(uint32(raw.Wd))
-			/// Can be nil if Remove() was called in another goroutine for this
-			/// path inbetween reading the events from the kernel and reading
-			/// the internal state. Not much we can do about it, so just skip.
-			/// See #616.
-			if watch == nil {
-				next()
-				continue
+			ev, ok := w.handleEvent(inEvent, &buf, offset)
+			if !ok {
+				return
 			}
-
-			name := watch.path
-			if nameLen > 0 {
-				/// Point "bytes" at the first byte of the filename
-				bytes := (*[unix.PathMax]byte)(unsafe.Pointer(&buf[offset+unix.SizeofInotifyEvent]))[:nameLen:nameLen]
-				/// The filename is padded with NULL bytes. TrimRight() gets rid of those.
-				name += "/" + strings.TrimRight(string(bytes[0:nameLen]), "\000")
+			if !w.sendEvent(ev) {
+				return
 			}
 
-			if debug {
-				internal.Debug(name, raw.Mask, raw.Cookie)
-			}
+			// Move to the next event in the buffer
+			offset += unix.SizeofInotifyEvent + inEvent.Len
+		}
+	}
+}
 
-			if mask&unix.IN_IGNORED != 0 { //&& event.Op != 0
-				next()
-				continue
-			}
+func (w *inotify) handleEvent(inEvent *unix.InotifyEvent, buf *[65536]byte, offset uint32) (Event, bool) {
+	w.mu.Lock()
+	defer w.mu.Unlock()
 
-			// inotify will automatically remove the watch on deletes; just need
-			// to clean our state here.
-			if mask&unix.IN_DELETE_SELF == unix.IN_DELETE_SELF {
-				w.watches.remove(watch.wd)
-			}
+	/// If the event happened to the watched directory or the watched file, the
+	/// kernel doesn't append the filename to the event, but we would like to
+	/// always fill the the "Name" field with a valid filename. We retrieve the
+	/// path of the watch from the "paths" map.
+	///
+	/// Can be nil if Remove() was called in another goroutine for this path
+	/// inbetween reading the events from the kernel and reading the internal
+	/// state. Not much we can do about it, so just skip. See #616.
+	watch := w.watches.byWd(uint32(inEvent.Wd))
+	if watch == nil {
+		return Event{}, true
+	}
 
-			// We can't really update the state when a watched path is moved;
-			// only IN_MOVE_SELF is sent and not IN_MOVED_{FROM,TO}. So remove
-			// the watch.
-			if mask&unix.IN_MOVE_SELF == unix.IN_MOVE_SELF {
-				if watch.recurse {
-					next() // Do nothing
-					continue
-				}
+	var (
+		name    = watch.path
+		nameLen = uint32(inEvent.Len)
+	)
+	if nameLen > 0 {
+		/// Point "bytes" at the first byte of the filename
+		bb := *buf
+		bytes := (*[unix.PathMax]byte)(unsafe.Pointer(&bb[offset+unix.SizeofInotifyEvent]))[:nameLen:nameLen]
+		/// The filename is padded with NULL bytes. TrimRight() gets rid of those.
+		name += "/" + strings.TrimRight(string(bytes[0:nameLen]), "\x00")
+	}
 
-				err := w.remove(watch.path)
-				if err != nil && !errors.Is(err, ErrNonExistentWatch) {
-					if !w.sendError(err) {
-						return
-					}
-				}
+	if debug {
+		internal.Debug(name, inEvent.Mask, inEvent.Cookie)
+	}
+
+	if inEvent.Mask&unix.IN_IGNORED != 0 || inEvent.Mask&unix.IN_UNMOUNT != 0 {
+		w.watches.remove(watch)
+		return Event{}, true
+	}
+
+	// inotify will automatically remove the watch on deletes; just need
+	// to clean our state here.
+	if inEvent.Mask&unix.IN_DELETE_SELF == unix.IN_DELETE_SELF {
+		w.watches.remove(watch)
+	}
+
+	// We can't really update the state when a watched path is moved; only
+	// IN_MOVE_SELF is sent and not IN_MOVED_{FROM,TO}. So remove the watch.
+	if inEvent.Mask&unix.IN_MOVE_SELF == unix.IN_MOVE_SELF {
+		if watch.recurse { // Do nothing
+			return Event{}, true
+		}
+
+		err := w.remove(watch.path)
+		if err != nil && !errors.Is(err, ErrNonExistentWatch) {
+			if !w.sendError(err) {
+				return Event{}, false
 			}
+		}
+	}
 
-			/// Skip if we're watching both this path and the parent; the parent
-			/// will already send a delete so no need to do it twice.
-			if mask&unix.IN_DELETE_SELF != 0 {
-				if _, ok := w.watches.path[filepath.Dir(watch.path)]; ok {
-					next()
-					continue
-				}
+	/// Skip if we're watching both this path and the parent; the parent will
+	/// already send a delete so no need to do it twice.
+	if inEvent.Mask&unix.IN_DELETE_SELF != 0 {
+		_, ok := w.watches.path[filepath.Dir(watch.path)]
+		if ok {
+			return Event{}, true
+		}
+	}
+
+	ev := w.newEvent(name, inEvent.Mask, inEvent.Cookie)
+	// Need to update watch path for recurse.
+	if watch.recurse {
+		isDir := inEvent.Mask&unix.IN_ISDIR == unix.IN_ISDIR
+		/// New directory created: set up watch on it.
+		if isDir && ev.Has(Create) {
+			err := w.register(ev.Name, watch.flags, true)
+			if !w.sendError(err) {
+				return Event{}, false
 			}
 
-			ev := w.newEvent(name, mask, raw.Cookie)
-			// Need to update watch path for recurse.
-			if watch.recurse {
-				isDir := mask&unix.IN_ISDIR == unix.IN_ISDIR
-				/// New directory created: set up watch on it.
-				if isDir && ev.Has(Create) {
-					err := w.register(ev.Name, watch.flags, true)
-					if !w.sendError(err) {
-						return
+			// This was a directory rename, so we need to update all the
+			// children.
+			//
+			// TODO: this is of course pretty slow; we should use a better data
+			// structure for storing all of this, e.g. store children in the
+			// watch. I have some code for this in my kqueue refactor we can use
+			// in the future. For now I'm okay with this as it's not publicly
+			// available. Correctness first, performance second.
+			if ev.renamedFrom != "" {
+				for k, ww := range w.watches.wd {
+					if k == watch.wd || ww.path == ev.Name {
+						continue
 					}
-
-					// This was a directory rename, so we need to update all
-					// the children.
-					//
-					// TODO: this is of course pretty slow; we should use a
-					// better data structure for storing all of this, e.g. store
-					// children in the watch. I have some code for this in my
-					// kqueue refactor we can use in the future. For now I'm
-					// okay with this as it's not publicly available.
-					// Correctness first, performance second.
-					if ev.renamedFrom != "" {
-						w.watches.mu.Lock()
-						for k, ww := range w.watches.wd {
-							if k == watch.wd || ww.path == ev.Name {
-								continue
-							}
-							if strings.HasPrefix(ww.path, ev.renamedFrom) {
-								ww.path = strings.Replace(ww.path, ev.renamedFrom, ev.Name, 1)
-								w.watches.wd[k] = ww
-							}
-						}
-						w.watches.mu.Unlock()
+					if strings.HasPrefix(ww.path, ev.renamedFrom) {
+						ww.path = strings.Replace(ww.path, ev.renamedFrom, ev.Name, 1)
+						w.watches.wd[k] = ww
 					}
 				}
 			}
-
-			/// Send the events that are not ignored on the events channel
-			if !w.sendEvent(ev) {
-				return
-			}
-			next()
 		}
 	}
+
+	return ev, true
 }
 
 func (w *inotify) isRecursive(path string) bool {
@@ -650,8 +575,8 @@ func (w *inotify) xSupports(op Op) bool {
 }
 
 func (w *inotify) state() {
-	w.watches.mu.Lock()
-	defer w.watches.mu.Unlock()
+	w.mu.Lock()
+	defer w.mu.Unlock()
 	for wd, ww := range w.watches.wd {
 		fmt.Fprintf(os.Stderr, "%4d: recurse=%t %q\n", wd, ww.recurse, ww.path)
 	}
diff --git a/vendor/github.com/fsnotify/fsnotify/backend_kqueue.go b/vendor/github.com/fsnotify/fsnotify/backend_kqueue.go
index d8de5ab76fdd0003efad540a2260e2ef0c4982ac..340aeec061c1e8a70b4e32ff739bf97f3f2dfcc1 100644
--- a/vendor/github.com/fsnotify/fsnotify/backend_kqueue.go
+++ b/vendor/github.com/fsnotify/fsnotify/backend_kqueue.go
@@ -16,14 +16,13 @@ import (
 )
 
 type kqueue struct {
+	*shared
 	Events chan Event
 	Errors chan error
 
 	kq        int    // File descriptor (as returned by the kqueue() syscall).
 	closepipe [2]int // Pipe used for closing kq.
 	watches   *watches
-	done      chan struct{}
-	doneMu    sync.Mutex
 }
 
 type (
@@ -132,14 +131,18 @@ func (w *watches) byPath(path string) (watch, bool) {
 	return info, ok
 }
 
-func (w *watches) updateDirFlags(path string, flags uint32) {
+func (w *watches) updateDirFlags(path string, flags uint32) bool {
 	w.mu.Lock()
 	defer w.mu.Unlock()
 
-	fd := w.path[path]
+	fd, ok := w.path[path]
+	if !ok { // Already deleted: don't re-set it here.
+		return false
+	}
 	info := w.wd[fd]
 	info.dirFlags = flags
 	w.wd[fd] = info
+	return true
 }
 
 func (w *watches) remove(fd int, path string) bool {
@@ -179,22 +182,20 @@ func (w *watches) seenBefore(path string) bool {
 	return ok
 }
 
-func newBackend(ev chan Event, errs chan error) (backend, error) {
-	return newBufferedBackend(0, ev, errs)
-}
+var defaultBufferSize = 0
 
-func newBufferedBackend(sz uint, ev chan Event, errs chan error) (backend, error) {
+func newBackend(ev chan Event, errs chan error) (backend, error) {
 	kq, closepipe, err := newKqueue()
 	if err != nil {
 		return nil, err
 	}
 
 	w := &kqueue{
+		shared:    newShared(ev, errs),
 		Events:    ev,
 		Errors:    errs,
 		kq:        kq,
 		closepipe: closepipe,
-		done:      make(chan struct{}),
 		watches:   newWatches(),
 	}
 
@@ -210,7 +211,7 @@ func newBufferedBackend(sz uint, ev chan Event, errs chan error) (backend, error
 // all.
 func newKqueue() (kq int, closepipe [2]int, err error) {
 	kq, err = unix.Kqueue()
-	if kq == -1 {
+	if err != nil {
 		return kq, closepipe, err
 	}
 
@@ -239,54 +240,17 @@ func newKqueue() (kq int, closepipe [2]int, err error) {
 	return kq, closepipe, nil
 }
 
-// Returns true if the event was sent, or false if watcher is closed.
-func (w *kqueue) sendEvent(e Event) bool {
-	select {
-	case <-w.done:
-		return false
-	case w.Events <- e:
-		return true
-	}
-}
-
-// Returns true if the error was sent, or false if watcher is closed.
-func (w *kqueue) sendError(err error) bool {
-	if err == nil {
-		return true
-	}
-	select {
-	case <-w.done:
-		return false
-	case w.Errors <- err:
-		return true
-	}
-}
-
-func (w *kqueue) isClosed() bool {
-	select {
-	case <-w.done:
-		return true
-	default:
-		return false
-	}
-}
-
 func (w *kqueue) Close() error {
-	w.doneMu.Lock()
-	if w.isClosed() {
-		w.doneMu.Unlock()
+	if w.shared.close() {
 		return nil
 	}
-	close(w.done)
-	w.doneMu.Unlock()
 
 	pathsToRemove := w.watches.listPaths(false)
 	for _, name := range pathsToRemove {
 		w.Remove(name)
 	}
 
-	// Send "quit" message to the reader goroutine.
-	unix.Close(w.closepipe[1])
+	unix.Close(w.closepipe[1]) // Send "quit" message to readEvents
 	return nil
 }
 
@@ -303,7 +267,7 @@ func (w *kqueue) AddWith(name string, opts ...addOpt) error {
 		return fmt.Errorf("%w: %s", xErrUnsupported, with.op)
 	}
 
-	_, err := w.addWatch(name, noteAllEvents)
+	_, err := w.addWatch(name, noteAllEvents, false)
 	if err != nil {
 		return err
 	}
@@ -366,7 +330,7 @@ const noteAllEvents = unix.NOTE_DELETE | unix.NOTE_WRITE | unix.NOTE_ATTRIB | un
 // described in kevent(2).
 //
 // Returns the real path to the file which was added, with symlinks resolved.
-func (w *kqueue) addWatch(name string, flags uint32) (string, error) {
+func (w *kqueue) addWatch(name string, flags uint32, listDir bool) (string, error) {
 	if w.isClosed() {
 		return "", ErrClosed
 	}
@@ -385,15 +349,15 @@ func (w *kqueue) addWatch(name string, flags uint32) (string, error) {
 			return "", nil
 		}
 
-		// Follow symlinks.
-		if fi.Mode()&os.ModeSymlink == os.ModeSymlink {
+		// Follow symlinks, but only for paths added with Add(), and not paths
+		// we're adding from internalWatch from a listdir.
+		if !listDir && fi.Mode()&os.ModeSymlink == os.ModeSymlink {
 			link, err := os.Readlink(name)
 			if err != nil {
-				// Return nil because Linux can add unresolvable symlinks to the
-				// watch list without problems, so maintain consistency with
-				// that. There will be no file events for broken symlinks.
-				// TODO: more specific check; returns os.PathError; ENOENT?
-				return "", nil
+				return "", err
+			}
+			if !filepath.IsAbs(link) {
+				link = filepath.Join(filepath.Dir(name), link)
 			}
 
 			_, alreadyWatching = w.watches.byPath(link)
@@ -408,7 +372,7 @@ func (w *kqueue) addWatch(name string, flags uint32) (string, error) {
 			name = link
 			fi, err = os.Lstat(name)
 			if err != nil {
-				return "", nil
+				return "", err
 			}
 		}
 
@@ -422,7 +386,6 @@ func (w *kqueue) addWatch(name string, flags uint32) (string, error) {
 			if errors.Is(err, unix.EINTR) {
 				continue
 			}
-
 			return "", err
 		}
 
@@ -444,10 +407,16 @@ func (w *kqueue) addWatch(name string, flags uint32) (string, error) {
 	if info.isDir {
 		watchDir := (flags&unix.NOTE_WRITE) == unix.NOTE_WRITE &&
 			(!alreadyWatching || (info.dirFlags&unix.NOTE_WRITE) != unix.NOTE_WRITE)
-		w.watches.updateDirFlags(name, flags)
+		if !w.watches.updateDirFlags(name, flags) {
+			return "", nil
+		}
 
 		if watchDir {
-			if err := w.watchDirectoryFiles(name); err != nil {
+			d := name
+			if info.linkName != "" {
+				d = info.linkName
+			}
+			if err := w.watchDirectoryFiles(d); err != nil {
 				return "", err
 			}
 		}
@@ -644,19 +613,22 @@ func (w *kqueue) dirChange(dir string) error {
 		if errors.Is(err, os.ErrNotExist) {
 			return nil
 		}
-		return fmt.Errorf("fsnotify.dirChange: %w", err)
+		return fmt.Errorf("fsnotify.dirChange %q: %w", dir, err)
 	}
 
 	for _, f := range files {
 		fi, err := f.Info()
 		if err != nil {
+			if errors.Is(err, os.ErrNotExist) {
+				return nil
+			}
 			return fmt.Errorf("fsnotify.dirChange: %w", err)
 		}
 
 		err = w.sendCreateIfNew(filepath.Join(dir, fi.Name()), fi)
 		if err != nil {
 			// Don't need to send an error if this file isn't readable.
-			if errors.Is(err, unix.EACCES) || errors.Is(err, unix.EPERM) {
+			if errors.Is(err, unix.EACCES) || errors.Is(err, unix.EPERM) || errors.Is(err, os.ErrNotExist) {
 				return nil
 			}
 			return fmt.Errorf("fsnotify.dirChange: %w", err)
@@ -688,11 +660,11 @@ func (w *kqueue) internalWatch(name string, fi os.FileInfo) (string, error) {
 		// mimic Linux providing delete events for subdirectories, but preserve
 		// the flags used if currently watching subdirectory
 		info, _ := w.watches.byPath(name)
-		return w.addWatch(name, info.dirFlags|unix.NOTE_DELETE|unix.NOTE_RENAME)
+		return w.addWatch(name, info.dirFlags|unix.NOTE_DELETE|unix.NOTE_RENAME, true)
 	}
 
-	// watch file to mimic Linux inotify
-	return w.addWatch(name, noteAllEvents)
+	// Watch file to mimic Linux inotify.
+	return w.addWatch(name, noteAllEvents, true)
 }
 
 // Register events with the queue.
@@ -722,9 +694,9 @@ func (w *kqueue) read(events []unix.Kevent_t) ([]unix.Kevent_t, error) {
 }
 
 func (w *kqueue) xSupports(op Op) bool {
-	if runtime.GOOS == "freebsd" {
-		//return true // Supports everything.
-	}
+	//if runtime.GOOS == "freebsd" {
+	//	return true // Supports everything.
+	//}
 	if op.Has(xUnportableOpen) || op.Has(xUnportableRead) ||
 		op.Has(xUnportableCloseWrite) || op.Has(xUnportableCloseRead) {
 		return false
diff --git a/vendor/github.com/fsnotify/fsnotify/backend_other.go b/vendor/github.com/fsnotify/fsnotify/backend_other.go
index 5eb5dbc66f264b5ec9b92d6e960ebedde1cbf79f..b8c0ad72267562f1581f75e74e6cc0cce8204b0b 100644
--- a/vendor/github.com/fsnotify/fsnotify/backend_other.go
+++ b/vendor/github.com/fsnotify/fsnotify/backend_other.go
@@ -9,12 +9,11 @@ type other struct {
 	Errors chan error
 }
 
+var defaultBufferSize = 0
+
 func newBackend(ev chan Event, errs chan error) (backend, error) {
 	return nil, errors.New("fsnotify not supported on the current platform")
 }
-func newBufferedBackend(sz uint, ev chan Event, errs chan error) (backend, error) {
-	return newBackend(ev, errs)
-}
 func (w *other) Close() error                              { return nil }
 func (w *other) WatchList() []string                       { return nil }
 func (w *other) Add(name string) error                     { return nil }
diff --git a/vendor/github.com/fsnotify/fsnotify/backend_windows.go b/vendor/github.com/fsnotify/fsnotify/backend_windows.go
index c54a630838358c5fd0c596f4e54335b15805ca83..3433642d64199a3e142e71e6a3c940f5f9d53359 100644
--- a/vendor/github.com/fsnotify/fsnotify/backend_windows.go
+++ b/vendor/github.com/fsnotify/fsnotify/backend_windows.go
@@ -28,18 +28,16 @@ type readDirChangesW struct {
 
 	port  windows.Handle // Handle to completion port
 	input chan *input    // Inputs to the reader are sent on this channel
-	quit  chan chan<- error
+	done  chan chan<- error
 
 	mu      sync.Mutex // Protects access to watches, closed
 	watches watchMap   // Map of watches (key: i-number)
 	closed  bool       // Set to true when Close() is first called
 }
 
-func newBackend(ev chan Event, errs chan error) (backend, error) {
-	return newBufferedBackend(50, ev, errs)
-}
+var defaultBufferSize = 50
 
-func newBufferedBackend(sz uint, ev chan Event, errs chan error) (backend, error) {
+func newBackend(ev chan Event, errs chan error) (backend, error) {
 	port, err := windows.CreateIoCompletionPort(windows.InvalidHandle, 0, 0, 0)
 	if err != nil {
 		return nil, os.NewSyscallError("CreateIoCompletionPort", err)
@@ -50,7 +48,7 @@ func newBufferedBackend(sz uint, ev chan Event, errs chan error) (backend, error
 		port:    port,
 		watches: make(watchMap),
 		input:   make(chan *input, 1),
-		quit:    make(chan chan<- error, 1),
+		done:    make(chan chan<- error, 1),
 	}
 	go w.readEvents()
 	return w, nil
@@ -70,8 +68,8 @@ func (w *readDirChangesW) sendEvent(name, renamedFrom string, mask uint64) bool
 	event := w.newEvent(name, uint32(mask))
 	event.renamedFrom = renamedFrom
 	select {
-	case ch := <-w.quit:
-		w.quit <- ch
+	case ch := <-w.done:
+		w.done <- ch
 	case w.Events <- event:
 	}
 	return true
@@ -83,10 +81,10 @@ func (w *readDirChangesW) sendError(err error) bool {
 		return true
 	}
 	select {
+	case <-w.done:
+		return false
 	case w.Errors <- err:
 		return true
-	case <-w.quit:
-		return false
 	}
 }
 
@@ -99,9 +97,9 @@ func (w *readDirChangesW) Close() error {
 	w.closed = true
 	w.mu.Unlock()
 
-	// Send "quit" message to the reader goroutine
+	// Send "done" message to the reader goroutine
 	ch := make(chan error)
-	w.quit <- ch
+	w.done <- ch
 	if err := w.wakeupReader(); err != nil {
 		return err
 	}
@@ -495,7 +493,7 @@ func (w *readDirChangesW) readEvents() {
 		watch := (*watch)(unsafe.Pointer(ov))
 		if watch == nil {
 			select {
-			case ch := <-w.quit:
+			case ch := <-w.done:
 				w.mu.Lock()
 				var indexes []indexMap
 				for _, index := range w.watches {
diff --git a/vendor/github.com/fsnotify/fsnotify/fsnotify.go b/vendor/github.com/fsnotify/fsnotify/fsnotify.go
index 0760efe91600fa4b7d428c66d5d5629a3df2ae77..f64be4bf98eef759282af2e5f33f9b0cdb5df824 100644
--- a/vendor/github.com/fsnotify/fsnotify/fsnotify.go
+++ b/vendor/github.com/fsnotify/fsnotify/fsnotify.go
@@ -244,12 +244,13 @@ var (
 
 	// ErrUnsupported is returned by AddWith() when WithOps() specified an
 	// Unportable event that's not supported on this platform.
+	//lint:ignore ST1012 not relevant
 	xErrUnsupported = errors.New("fsnotify: not supported with this backend")
 )
 
 // NewWatcher creates a new Watcher.
 func NewWatcher() (*Watcher, error) {
-	ev, errs := make(chan Event), make(chan error)
+	ev, errs := make(chan Event, defaultBufferSize), make(chan error)
 	b, err := newBackend(ev, errs)
 	if err != nil {
 		return nil, err
@@ -266,8 +267,8 @@ func NewWatcher() (*Watcher, error) {
 // cases, and whenever possible you will be better off increasing the kernel
 // buffers instead of adding a large userspace buffer.
 func NewBufferedWatcher(sz uint) (*Watcher, error) {
-	ev, errs := make(chan Event), make(chan error)
-	b, err := newBufferedBackend(sz, ev, errs)
+	ev, errs := make(chan Event, sz), make(chan error)
+	b, err := newBackend(ev, errs)
 	if err != nil {
 		return nil, err
 	}
@@ -337,7 +338,8 @@ func (w *Watcher) Close() error { return w.b.Close() }
 // WatchList returns all paths explicitly added with [Watcher.Add] (and are not
 // yet removed).
 //
-// Returns nil if [Watcher.Close] was called.
+// The order is undefined, and may differ per call. Returns nil if
+// [Watcher.Close] was called.
 func (w *Watcher) WatchList() []string { return w.b.WatchList() }
 
 // Supports reports if all the listed operations are supported by this platform.
diff --git a/vendor/github.com/fsnotify/fsnotify/internal/darwin.go b/vendor/github.com/fsnotify/fsnotify/internal/darwin.go
index b0eab10090d3bfec8042461630c991a0cc8057bf..0b01bc182a1e9e8d5d74f6e09d0cd70d4e92c501 100644
--- a/vendor/github.com/fsnotify/fsnotify/internal/darwin.go
+++ b/vendor/github.com/fsnotify/fsnotify/internal/darwin.go
@@ -9,14 +9,14 @@ import (
 )
 
 var (
-	SyscallEACCES = syscall.EACCES
-	UnixEACCES    = unix.EACCES
+	ErrSyscallEACCES = syscall.EACCES
+	ErrUnixEACCES    = unix.EACCES
 )
 
 var maxfiles uint64
 
-// Go 1.19 will do this automatically: https://go-review.googlesource.com/c/go/+/393354/
 func SetRlimit() {
+	// Go 1.19 will do this automatically: https://go-review.googlesource.com/c/go/+/393354/
 	var l syscall.Rlimit
 	err := syscall.Getrlimit(syscall.RLIMIT_NOFILE, &l)
 	if err == nil && l.Cur != l.Max {
diff --git a/vendor/github.com/fsnotify/fsnotify/internal/freebsd.go b/vendor/github.com/fsnotify/fsnotify/internal/freebsd.go
index 547df1df84b5b4825ca50f87557d645d8561933c..5ac8b507978f42899485e6dfbd7e940fe414895b 100644
--- a/vendor/github.com/fsnotify/fsnotify/internal/freebsd.go
+++ b/vendor/github.com/fsnotify/fsnotify/internal/freebsd.go
@@ -9,8 +9,8 @@ import (
 )
 
 var (
-	SyscallEACCES = syscall.EACCES
-	UnixEACCES    = unix.EACCES
+	ErrSyscallEACCES = syscall.EACCES
+	ErrUnixEACCES    = unix.EACCES
 )
 
 var maxfiles uint64
diff --git a/vendor/github.com/fsnotify/fsnotify/internal/unix.go b/vendor/github.com/fsnotify/fsnotify/internal/unix.go
index 30976ce9739535fc3bb6435b22295b9c600deb55..b251fb803869a0d0628fdfd3b171251e5610d944 100644
--- a/vendor/github.com/fsnotify/fsnotify/internal/unix.go
+++ b/vendor/github.com/fsnotify/fsnotify/internal/unix.go
@@ -1,4 +1,4 @@
-//go:build !windows && !darwin && !freebsd
+//go:build !windows && !darwin && !freebsd && !plan9
 
 package internal
 
@@ -9,8 +9,8 @@ import (
 )
 
 var (
-	SyscallEACCES = syscall.EACCES
-	UnixEACCES    = unix.EACCES
+	ErrSyscallEACCES = syscall.EACCES
+	ErrUnixEACCES    = unix.EACCES
 )
 
 var maxfiles uint64
diff --git a/vendor/github.com/fsnotify/fsnotify/internal/windows.go b/vendor/github.com/fsnotify/fsnotify/internal/windows.go
index a72c64954905cd351497f29c736b59d8e3af3b5a..896bc2e5a2f35ba6840f85ca53aa95d56b79147d 100644
--- a/vendor/github.com/fsnotify/fsnotify/internal/windows.go
+++ b/vendor/github.com/fsnotify/fsnotify/internal/windows.go
@@ -10,8 +10,8 @@ import (
 
 // Just a dummy.
 var (
-	SyscallEACCES = errors.New("dummy")
-	UnixEACCES    = errors.New("dummy")
+	ErrSyscallEACCES = errors.New("dummy")
+	ErrUnixEACCES    = errors.New("dummy")
 )
 
 func SetRlimit()                                    {}
diff --git a/vendor/github.com/fsnotify/fsnotify/shared.go b/vendor/github.com/fsnotify/fsnotify/shared.go
new file mode 100644
index 0000000000000000000000000000000000000000..3ee9b58f1d2b44dfc099c860c83b5c56c8d3b715
--- /dev/null
+++ b/vendor/github.com/fsnotify/fsnotify/shared.go
@@ -0,0 +1,64 @@
+package fsnotify
+
+import "sync"
+
+type shared struct {
+	Events chan Event
+	Errors chan error
+	done   chan struct{}
+	mu     sync.Mutex
+}
+
+func newShared(ev chan Event, errs chan error) *shared {
+	return &shared{
+		Events: ev,
+		Errors: errs,
+		done:   make(chan struct{}),
+	}
+}
+
+// Returns true if the event was sent, or false if watcher is closed.
+func (w *shared) sendEvent(e Event) bool {
+	if e.Op == 0 {
+		return true
+	}
+	select {
+	case <-w.done:
+		return false
+	case w.Events <- e:
+		return true
+	}
+}
+
+// Returns true if the error was sent, or false if watcher is closed.
+func (w *shared) sendError(err error) bool {
+	if err == nil {
+		return true
+	}
+	select {
+	case <-w.done:
+		return false
+	case w.Errors <- err:
+		return true
+	}
+}
+
+func (w *shared) isClosed() bool {
+	select {
+	case <-w.done:
+		return true
+	default:
+		return false
+	}
+}
+
+// Mark as closed; returns true if it was already closed.
+func (w *shared) close() bool {
+	w.mu.Lock()
+	defer w.mu.Unlock()
+	if w.isClosed() {
+		return true
+	}
+	close(w.done)
+	return false
+}
diff --git a/vendor/github.com/fsnotify/fsnotify/staticcheck.conf b/vendor/github.com/fsnotify/fsnotify/staticcheck.conf
new file mode 100644
index 0000000000000000000000000000000000000000..8fa7351f0c2418c8d897c56090a0301bfa0827d6
--- /dev/null
+++ b/vendor/github.com/fsnotify/fsnotify/staticcheck.conf
@@ -0,0 +1,3 @@
+checks = ['all',
+	'-U1000',  # Don't complain about unused functions.
+]
diff --git a/vendor/modules.txt b/vendor/modules.txt
index 8ccdd81e7c795fafc96988efe87147be6d5759cb..fd268e3eaa777a440f32c6ecf4b42175c59ffdc5 100644
--- a/vendor/modules.txt
+++ b/vendor/modules.txt
@@ -100,7 +100,7 @@ github.com/emicklei/go-restful/v3/log
 ## explicit; go 1.18
 github.com/evanphx/json-patch/v5
 github.com/evanphx/json-patch/v5/internal/json
-# github.com/fsnotify/fsnotify v1.8.0
+# github.com/fsnotify/fsnotify v1.9.0
 ## explicit; go 1.17
 github.com/fsnotify/fsnotify
 github.com/fsnotify/fsnotify/internal