Bump up github.com/fsnotify/fsnotify to v1.7.0
Signed-off-by: Kohei Tokunaga <ktokunaga.mail@gmail.com>
This commit is contained in:
		
							
								
								
									
										13
									
								
								vendor/github.com/fsnotify/fsnotify/.cirrus.yml
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								vendor/github.com/fsnotify/fsnotify/.cirrus.yml
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,13 @@
 | 
			
		||||
freebsd_task:
 | 
			
		||||
  name: 'FreeBSD'
 | 
			
		||||
  freebsd_instance:
 | 
			
		||||
    image_family: freebsd-13-2
 | 
			
		||||
  install_script:
 | 
			
		||||
    - pkg update -f
 | 
			
		||||
    - pkg install -y go
 | 
			
		||||
  test_script:
 | 
			
		||||
      # run tests as user "cirrus" instead of root
 | 
			
		||||
    - pw useradd cirrus -m
 | 
			
		||||
    - chown -R cirrus:cirrus .
 | 
			
		||||
    - FSNOTIFY_BUFFER=4096 sudo --preserve-env=FSNOTIFY_BUFFER -u cirrus go test -parallel 1 -race ./...
 | 
			
		||||
    -                      sudo --preserve-env=FSNOTIFY_BUFFER -u cirrus go test -parallel 1 -race ./...
 | 
			
		||||
							
								
								
									
										1
									
								
								vendor/github.com/fsnotify/fsnotify/.gitignore
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								vendor/github.com/fsnotify/fsnotify/.gitignore
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -4,3 +4,4 @@
 | 
			
		||||
 | 
			
		||||
# Output of go build ./cmd/fsnotify
 | 
			
		||||
/fsnotify
 | 
			
		||||
/fsnotify.exe
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										87
									
								
								vendor/github.com/fsnotify/fsnotify/CHANGELOG.md
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										87
									
								
								vendor/github.com/fsnotify/fsnotify/CHANGELOG.md
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -1,16 +1,87 @@
 | 
			
		||||
# Changelog
 | 
			
		||||
 | 
			
		||||
All notable changes to this project will be documented in this file.
 | 
			
		||||
 | 
			
		||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
 | 
			
		||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
 | 
			
		||||
 | 
			
		||||
## [Unreleased]
 | 
			
		||||
 | 
			
		||||
Unreleased
 | 
			
		||||
----------
 | 
			
		||||
Nothing yet.
 | 
			
		||||
 | 
			
		||||
## [1.6.0] - 2022-10-13
 | 
			
		||||
1.7.0 - 2023-10-22
 | 
			
		||||
------------------
 | 
			
		||||
This version of fsnotify needs Go 1.17.
 | 
			
		||||
 | 
			
		||||
### Additions
 | 
			
		||||
 | 
			
		||||
- illumos: add FEN backend to support illumos and Solaris. ([#371])
 | 
			
		||||
 | 
			
		||||
- all: add `NewBufferedWatcher()` to use a buffered channel, which can be useful
 | 
			
		||||
  in cases where you can't control the kernel buffer and receive a large number
 | 
			
		||||
  of events in bursts. ([#550], [#572])
 | 
			
		||||
 | 
			
		||||
- all: add `AddWith()`, which is identical to `Add()` but allows passing
 | 
			
		||||
  options. ([#521])
 | 
			
		||||
 | 
			
		||||
- windows: allow setting the ReadDirectoryChangesW() buffer size with
 | 
			
		||||
  `fsnotify.WithBufferSize()`; the default of 64K is the highest value that
 | 
			
		||||
  works on all platforms and is enough for most purposes, but in some cases a
 | 
			
		||||
  highest buffer is needed. ([#521])
 | 
			
		||||
 | 
			
		||||
### Changes and fixes
 | 
			
		||||
 | 
			
		||||
- inotify: remove watcher if a watched path is renamed ([#518])
 | 
			
		||||
 | 
			
		||||
  After a rename the reported name wasn't updated, or even an empty string.
 | 
			
		||||
  Inotify doesn't provide any good facilities to update it, so just remove the
 | 
			
		||||
  watcher. This is already how it worked on kqueue and FEN.
 | 
			
		||||
 | 
			
		||||
  On Windows this does work, and remains working.
 | 
			
		||||
 | 
			
		||||
- windows: don't listen for file attribute changes ([#520])
 | 
			
		||||
 | 
			
		||||
  File attribute changes are sent as `FILE_ACTION_MODIFIED` by the Windows API,
 | 
			
		||||
  with no way to see if they're a file write or attribute change, so would show
 | 
			
		||||
  up as a fsnotify.Write event. This is never useful, and could result in many
 | 
			
		||||
  spurious Write events.
 | 
			
		||||
 | 
			
		||||
- windows: return `ErrEventOverflow` if the buffer is full ([#525])
 | 
			
		||||
 | 
			
		||||
  Before it would merely return "short read", making it hard to detect this
 | 
			
		||||
  error.
 | 
			
		||||
 | 
			
		||||
- kqueue: make sure events for all files are delivered properly when removing a
 | 
			
		||||
  watched directory ([#526])
 | 
			
		||||
 | 
			
		||||
  Previously they would get sent with `""` (empty string) or `"."` as the path
 | 
			
		||||
  name.
 | 
			
		||||
 | 
			
		||||
- kqueue: don't emit spurious Create events for symbolic links ([#524])
 | 
			
		||||
 | 
			
		||||
  The link would get resolved but kqueue would "forget" it already saw the link
 | 
			
		||||
  itself, resulting on a Create for every Write event for the directory.
 | 
			
		||||
 | 
			
		||||
- all: return `ErrClosed` on `Add()` when the watcher is closed ([#516])
 | 
			
		||||
 | 
			
		||||
- other: add `Watcher.Errors` and `Watcher.Events` to the no-op `Watcher` in
 | 
			
		||||
  `backend_other.go`, making it easier to use on unsupported platforms such as
 | 
			
		||||
  WASM, AIX, etc. ([#528])
 | 
			
		||||
 | 
			
		||||
- other: use the `backend_other.go` no-op if the `appengine` build tag is set;
 | 
			
		||||
  Google AppEngine forbids usage of the unsafe package so the inotify backend
 | 
			
		||||
  won't compile there.
 | 
			
		||||
 | 
			
		||||
[#371]: https://github.com/fsnotify/fsnotify/pull/371
 | 
			
		||||
[#516]: https://github.com/fsnotify/fsnotify/pull/516
 | 
			
		||||
[#518]: https://github.com/fsnotify/fsnotify/pull/518
 | 
			
		||||
[#520]: https://github.com/fsnotify/fsnotify/pull/520
 | 
			
		||||
[#521]: https://github.com/fsnotify/fsnotify/pull/521
 | 
			
		||||
[#524]: https://github.com/fsnotify/fsnotify/pull/524
 | 
			
		||||
[#525]: https://github.com/fsnotify/fsnotify/pull/525
 | 
			
		||||
[#526]: https://github.com/fsnotify/fsnotify/pull/526
 | 
			
		||||
[#528]: https://github.com/fsnotify/fsnotify/pull/528
 | 
			
		||||
[#537]: https://github.com/fsnotify/fsnotify/pull/537
 | 
			
		||||
[#550]: https://github.com/fsnotify/fsnotify/pull/550
 | 
			
		||||
[#572]: https://github.com/fsnotify/fsnotify/pull/572
 | 
			
		||||
 | 
			
		||||
1.6.0 - 2022-10-13
 | 
			
		||||
------------------
 | 
			
		||||
This version of fsnotify needs Go 1.16 (this was already the case since 1.5.1,
 | 
			
		||||
but not documented). It also increases the minimum Linux version to 2.6.32.
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										79
									
								
								vendor/github.com/fsnotify/fsnotify/README.md
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										79
									
								
								vendor/github.com/fsnotify/fsnotify/README.md
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -1,29 +1,31 @@
 | 
			
		||||
fsnotify is a Go library to provide cross-platform filesystem notifications on
 | 
			
		||||
Windows, Linux, macOS, and BSD systems.
 | 
			
		||||
Windows, Linux, macOS, BSD, and illumos.
 | 
			
		||||
 | 
			
		||||
Go 1.16 or newer is required; the full documentation is at
 | 
			
		||||
Go 1.17 or newer is required; the full documentation is at
 | 
			
		||||
https://pkg.go.dev/github.com/fsnotify/fsnotify
 | 
			
		||||
 | 
			
		||||
**It's best to read the documentation at pkg.go.dev, as it's pinned to the last
 | 
			
		||||
released version, whereas this README is for the last development version which
 | 
			
		||||
may include additions/changes.**
 | 
			
		||||
 | 
			
		||||
---
 | 
			
		||||
 | 
			
		||||
Platform support:
 | 
			
		||||
 | 
			
		||||
| Adapter               | OS             | Status                                                       |
 | 
			
		||||
| --------------------- | ---------------| -------------------------------------------------------------|
 | 
			
		||||
| inotify               | Linux 2.6.32+  | Supported                                                    |
 | 
			
		||||
| kqueue                | BSD, macOS     | Supported                                                    |
 | 
			
		||||
| ReadDirectoryChangesW | Windows        | Supported                                                    |
 | 
			
		||||
| FSEvents              | macOS          | [Planned](https://github.com/fsnotify/fsnotify/issues/11)    |
 | 
			
		||||
| FEN                   | Solaris 11     | [In Progress](https://github.com/fsnotify/fsnotify/pull/371) |
 | 
			
		||||
| fanotify              | Linux 5.9+     | [Maybe](https://github.com/fsnotify/fsnotify/issues/114)     |
 | 
			
		||||
| USN Journals          | Windows        | [Maybe](https://github.com/fsnotify/fsnotify/issues/53)      |
 | 
			
		||||
| Polling               | *All*          | [Maybe](https://github.com/fsnotify/fsnotify/issues/9)       |
 | 
			
		||||
| Backend               | OS         | Status                                                                    |
 | 
			
		||||
| :-------------------- | :--------- | :------------------------------------------------------------------------ |
 | 
			
		||||
| inotify               | Linux      | Supported                                                                 |
 | 
			
		||||
| kqueue                | BSD, macOS | Supported                                                                 |
 | 
			
		||||
| 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)                  |
 | 
			
		||||
 | 
			
		||||
Linux and macOS should include Android and iOS, but these are currently untested.
 | 
			
		||||
Linux and illumos should include Android and Solaris, but these are currently
 | 
			
		||||
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
 | 
			
		||||
-----
 | 
			
		||||
@@ -83,20 +85,23 @@ run with:
 | 
			
		||||
 | 
			
		||||
    % go run ./cmd/fsnotify
 | 
			
		||||
 | 
			
		||||
Further detailed documentation can be found in godoc:
 | 
			
		||||
https://pkg.go.dev/github.com/fsnotify/fsnotify
 | 
			
		||||
 | 
			
		||||
FAQ
 | 
			
		||||
---
 | 
			
		||||
### Will a file still be watched when it's moved to another directory?
 | 
			
		||||
No, not unless you are watching the location it was moved to.
 | 
			
		||||
 | 
			
		||||
### Are subdirectories watched too?
 | 
			
		||||
### Are subdirectories watched?
 | 
			
		||||
No, you must add watches for any directory you want to watch (a recursive
 | 
			
		||||
watcher is on the roadmap: [#18]).
 | 
			
		||||
 | 
			
		||||
[#18]: https://github.com/fsnotify/fsnotify/issues/18
 | 
			
		||||
 | 
			
		||||
### Do I have to watch the Error and Event channels in a goroutine?
 | 
			
		||||
As of now, yes (you can read both channels in the same goroutine using `select`,
 | 
			
		||||
you don't need a separate goroutine for both channels; see the example).
 | 
			
		||||
Yes. You can read both channels in the same goroutine using `select` (you don't
 | 
			
		||||
need a separate goroutine for both channels; see the example).
 | 
			
		||||
 | 
			
		||||
### Why don't notifications work with NFS, SMB, FUSE, /proc, or /sys?
 | 
			
		||||
fsnotify requires support from underlying OS to work. The current NFS and SMB
 | 
			
		||||
@@ -107,6 +112,32 @@ This could be fixed with a polling watcher ([#9]), but it's not yet implemented.
 | 
			
		||||
 | 
			
		||||
[#9]: https://github.com/fsnotify/fsnotify/issues/9
 | 
			
		||||
 | 
			
		||||
### Why do I get many Chmod events?
 | 
			
		||||
Some programs may generate a lot of attribute changes; for example Spotlight on
 | 
			
		||||
macOS, anti-virus programs, backup applications, and some others are known to do
 | 
			
		||||
this. As a rule, it's typically best to ignore Chmod events. They're often not
 | 
			
		||||
useful, and tend to cause problems.
 | 
			
		||||
 | 
			
		||||
Spotlight indexing on macOS can result in multiple events (see [#15]). A
 | 
			
		||||
temporary workaround is to add your folder(s) to the *Spotlight Privacy
 | 
			
		||||
settings* until we have a native FSEvents implementation (see [#11]).
 | 
			
		||||
 | 
			
		||||
[#11]: https://github.com/fsnotify/fsnotify/issues/11
 | 
			
		||||
[#15]: https://github.com/fsnotify/fsnotify/issues/15
 | 
			
		||||
 | 
			
		||||
### Watching a file doesn't work well
 | 
			
		||||
Watching individual files (rather than directories) is generally not recommended
 | 
			
		||||
as many programs (especially editors) update files atomically: it will write to
 | 
			
		||||
a temporary file which is then moved to to destination, overwriting the original
 | 
			
		||||
(or some variant thereof). The watcher on the original file is now lost, as that
 | 
			
		||||
no longer exists.
 | 
			
		||||
 | 
			
		||||
The upshot of this is that a power failure or crash won't leave a half-written
 | 
			
		||||
file.
 | 
			
		||||
 | 
			
		||||
Watch the parent directory and use `Event.Name` to filter out files you're not
 | 
			
		||||
interested in. There is an example of this in `cmd/fsnotify/file.go`.
 | 
			
		||||
 | 
			
		||||
Platform-specific notes
 | 
			
		||||
-----------------------
 | 
			
		||||
### Linux
 | 
			
		||||
@@ -151,11 +182,3 @@ these platforms.
 | 
			
		||||
 | 
			
		||||
The sysctl variables `kern.maxfiles` and `kern.maxfilesperproc` can be used to
 | 
			
		||||
control the maximum number of open files.
 | 
			
		||||
 | 
			
		||||
### macOS
 | 
			
		||||
Spotlight indexing on macOS can result in multiple events (see [#15]). A temporary
 | 
			
		||||
workaround is to add your folder(s) to the *Spotlight Privacy settings* until we
 | 
			
		||||
have a native FSEvents implementation (see [#11]).
 | 
			
		||||
 | 
			
		||||
[#11]: https://github.com/fsnotify/fsnotify/issues/11
 | 
			
		||||
[#15]: https://github.com/fsnotify/fsnotify/issues/15
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										550
									
								
								vendor/github.com/fsnotify/fsnotify/backend_fen.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										550
									
								
								vendor/github.com/fsnotify/fsnotify/backend_fen.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -1,10 +1,19 @@
 | 
			
		||||
//go:build solaris
 | 
			
		||||
// +build solaris
 | 
			
		||||
 | 
			
		||||
// Note: the documentation on the Watcher type and methods is generated from
 | 
			
		||||
// mkdoc.zsh
 | 
			
		||||
 | 
			
		||||
package fsnotify
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"errors"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"os"
 | 
			
		||||
	"path/filepath"
 | 
			
		||||
	"sync"
 | 
			
		||||
 | 
			
		||||
	"golang.org/x/sys/unix"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Watcher watches a set of paths, delivering events on a channel.
 | 
			
		||||
@@ -17,9 +26,9 @@ import (
 | 
			
		||||
// When a file is removed a Remove event won't be emitted until all file
 | 
			
		||||
// descriptors are closed, and deletes will always emit a Chmod. For example:
 | 
			
		||||
//
 | 
			
		||||
//     fp := os.Open("file")
 | 
			
		||||
//     os.Remove("file")        // Triggers Chmod
 | 
			
		||||
//     fp.Close()               // Triggers Remove
 | 
			
		||||
//	fp := os.Open("file")
 | 
			
		||||
//	os.Remove("file")        // Triggers Chmod
 | 
			
		||||
//	fp.Close()               // Triggers Remove
 | 
			
		||||
//
 | 
			
		||||
// This is the event that inotify sends, so not much can be changed about this.
 | 
			
		||||
//
 | 
			
		||||
@@ -33,16 +42,16 @@ import (
 | 
			
		||||
//
 | 
			
		||||
// To increase them you can use sysctl or write the value to the /proc file:
 | 
			
		||||
//
 | 
			
		||||
//     # Default values on Linux 5.18
 | 
			
		||||
//     sysctl fs.inotify.max_user_watches=124983
 | 
			
		||||
//     sysctl fs.inotify.max_user_instances=128
 | 
			
		||||
//	# Default values on Linux 5.18
 | 
			
		||||
//	sysctl fs.inotify.max_user_watches=124983
 | 
			
		||||
//	sysctl fs.inotify.max_user_instances=128
 | 
			
		||||
//
 | 
			
		||||
// To make the changes persist on reboot edit /etc/sysctl.conf or
 | 
			
		||||
// /usr/lib/sysctl.d/50-default.conf (details differ per Linux distro; check
 | 
			
		||||
// your distro's documentation):
 | 
			
		||||
//
 | 
			
		||||
//     fs.inotify.max_user_watches=124983
 | 
			
		||||
//     fs.inotify.max_user_instances=128
 | 
			
		||||
//	fs.inotify.max_user_watches=124983
 | 
			
		||||
//	fs.inotify.max_user_instances=128
 | 
			
		||||
//
 | 
			
		||||
// Reaching the limit will result in a "no space left on device" or "too many open
 | 
			
		||||
// files" error.
 | 
			
		||||
@@ -58,14 +67,20 @@ import (
 | 
			
		||||
// control the maximum number of open files, as well as /etc/login.conf on BSD
 | 
			
		||||
// systems.
 | 
			
		||||
//
 | 
			
		||||
// # macOS notes
 | 
			
		||||
// # Windows notes
 | 
			
		||||
//
 | 
			
		||||
// Spotlight indexing on macOS can result in multiple events (see [#15]). A
 | 
			
		||||
// temporary workaround is to add your folder(s) to the "Spotlight Privacy
 | 
			
		||||
// Settings" until we have a native FSEvents implementation (see [#11]).
 | 
			
		||||
// Paths can be added as "C:\path\to\dir", but forward slashes
 | 
			
		||||
// ("C:/path/to/dir") will also work.
 | 
			
		||||
//
 | 
			
		||||
// [#11]: https://github.com/fsnotify/fsnotify/issues/11
 | 
			
		||||
// [#15]: https://github.com/fsnotify/fsnotify/issues/15
 | 
			
		||||
// When a watched directory is removed it will always send an event for the
 | 
			
		||||
// directory itself, but may not send events for all files in that directory.
 | 
			
		||||
// Sometimes it will send events for all times, sometimes it will send no
 | 
			
		||||
// events, and often only for some files.
 | 
			
		||||
//
 | 
			
		||||
// The default ReadDirectoryChangesW() buffer size is 64K, which is the largest
 | 
			
		||||
// value that is guaranteed to work with SMB filesystems. If you have many
 | 
			
		||||
// events in quick succession this may not be enough, and you will have to use
 | 
			
		||||
// [WithBufferSize] to increase the value.
 | 
			
		||||
type Watcher struct {
 | 
			
		||||
	// Events sends the filesystem change events.
 | 
			
		||||
	//
 | 
			
		||||
@@ -92,44 +107,129 @@ type Watcher struct {
 | 
			
		||||
	//                      initiated by the user may show up as one or multiple
 | 
			
		||||
	//                      writes, depending on when the system syncs things to
 | 
			
		||||
	//                      disk. For example when compiling a large Go program
 | 
			
		||||
	//                      you may get hundreds of Write events, so you
 | 
			
		||||
	//                      probably want to wait until you've stopped receiving
 | 
			
		||||
	//                      them (see the dedup example in cmd/fsnotify).
 | 
			
		||||
	//                      you may get hundreds of Write events, and you may
 | 
			
		||||
	//                      want to wait until you've stopped receiving them
 | 
			
		||||
	//                      (see the dedup example in cmd/fsnotify).
 | 
			
		||||
	//
 | 
			
		||||
	//                      Some systems may send Write event for directories
 | 
			
		||||
	//                      when the directory content changes.
 | 
			
		||||
	//
 | 
			
		||||
	//   fsnotify.Chmod     Attributes were changed. On Linux this is also sent
 | 
			
		||||
	//                      when a file is removed (or more accurately, when a
 | 
			
		||||
	//                      link to an inode is removed). On kqueue it's sent
 | 
			
		||||
	//                      and on kqueue when a file is truncated. On Windows
 | 
			
		||||
	//                      it's never sent.
 | 
			
		||||
	//                      when a file is truncated. On Windows it's never
 | 
			
		||||
	//                      sent.
 | 
			
		||||
	Events chan Event
 | 
			
		||||
 | 
			
		||||
	// Errors sends any errors.
 | 
			
		||||
	//
 | 
			
		||||
	// ErrEventOverflow is used to indicate there are too many events:
 | 
			
		||||
	//
 | 
			
		||||
	//  - inotify:      There are too many queued events (fs.inotify.max_queued_events sysctl)
 | 
			
		||||
	//  - windows:      The buffer size is too small; WithBufferSize() can be used to increase it.
 | 
			
		||||
	//  - kqueue, fen:  Not used.
 | 
			
		||||
	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]struct{} // Explicitly watched directories
 | 
			
		||||
	watches map[string]struct{} // Explicitly watched non-directories
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewWatcher creates a new Watcher.
 | 
			
		||||
func NewWatcher() (*Watcher, error) {
 | 
			
		||||
	return nil, errors.New("FEN based watcher not yet supported for fsnotify\n")
 | 
			
		||||
	return NewBufferedWatcher(0)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Close removes all watches and closes the events channel.
 | 
			
		||||
// NewBufferedWatcher creates a new Watcher with a buffered Watcher.Events
 | 
			
		||||
// channel.
 | 
			
		||||
//
 | 
			
		||||
// The main use case for this is situations with a very large number of events
 | 
			
		||||
// where the kernel buffer size can't be increased (e.g. due to lack of
 | 
			
		||||
// permissions). An unbuffered Watcher will perform better for almost all use
 | 
			
		||||
// 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) {
 | 
			
		||||
	w := &Watcher{
 | 
			
		||||
		Events:  make(chan Event, sz),
 | 
			
		||||
		Errors:  make(chan error),
 | 
			
		||||
		dirs:    make(map[string]struct{}),
 | 
			
		||||
		watches: make(map[string]struct{}),
 | 
			
		||||
		done:    make(chan struct{}),
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var err error
 | 
			
		||||
	w.port, err = unix.NewEventPort()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, fmt.Errorf("fsnotify.NewWatcher: %w", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	go w.readEvents()
 | 
			
		||||
	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 *Watcher) sendEvent(name string, op Op) (sent bool) {
 | 
			
		||||
	select {
 | 
			
		||||
	case w.Events <- Event{Name: name, Op: op}:
 | 
			
		||||
		return true
 | 
			
		||||
	case <-w.done:
 | 
			
		||||
		return false
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 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 *Watcher) sendError(err error) (sent bool) {
 | 
			
		||||
	select {
 | 
			
		||||
	case w.Errors <- err:
 | 
			
		||||
		return true
 | 
			
		||||
	case <-w.done:
 | 
			
		||||
		return false
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (w *Watcher) isClosed() bool {
 | 
			
		||||
	select {
 | 
			
		||||
	case <-w.done:
 | 
			
		||||
		return true
 | 
			
		||||
	default:
 | 
			
		||||
		return false
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Close removes all watches and closes the Events channel.
 | 
			
		||||
func (w *Watcher) Close() error {
 | 
			
		||||
	return nil
 | 
			
		||||
	// 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() {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
	close(w.done)
 | 
			
		||||
	return w.port.Close()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Add starts monitoring the path for changes.
 | 
			
		||||
//
 | 
			
		||||
// A path can only be watched once; attempting to watch it more than once will
 | 
			
		||||
// return an error. Paths that do not yet exist on the filesystem cannot be
 | 
			
		||||
// added. A watch will be automatically removed if the path is deleted.
 | 
			
		||||
// A path can only be watched once; watching it more than once is a no-op and will
 | 
			
		||||
// not return an error. Paths that do not yet exist on the filesystem cannot be
 | 
			
		||||
// watched.
 | 
			
		||||
//
 | 
			
		||||
// A path will remain watched if it gets renamed to somewhere else on the same
 | 
			
		||||
// filesystem, but the monitor will get removed if the path gets deleted and
 | 
			
		||||
// re-created, or if it's moved to a different filesystem.
 | 
			
		||||
// A watch will be automatically removed if the watched path is deleted or
 | 
			
		||||
// renamed. The exception is the Windows backend, which doesn't remove the
 | 
			
		||||
// watcher on renames.
 | 
			
		||||
//
 | 
			
		||||
// Notifications on network filesystems (NFS, SMB, FUSE, etc.) or special
 | 
			
		||||
// filesystems (/proc, /sys, etc.) generally don't work.
 | 
			
		||||
//
 | 
			
		||||
// Returns [ErrClosed] if [Watcher.Close] was called.
 | 
			
		||||
//
 | 
			
		||||
// See [Watcher.AddWith] for a version that allows adding options.
 | 
			
		||||
//
 | 
			
		||||
// # Watching directories
 | 
			
		||||
//
 | 
			
		||||
// All files in a directory are monitored, including new files that are created
 | 
			
		||||
@@ -139,15 +239,63 @@ func (w *Watcher) Close() error {
 | 
			
		||||
// # Watching files
 | 
			
		||||
//
 | 
			
		||||
// Watching individual files (rather than directories) is generally not
 | 
			
		||||
// recommended as many tools update files atomically. Instead of "just" writing
 | 
			
		||||
// to the file a temporary file will be written to first, and if successful the
 | 
			
		||||
// temporary file is moved to to destination removing the original, or some
 | 
			
		||||
// variant thereof. The watcher on the original file is now lost, as it no
 | 
			
		||||
// longer exists.
 | 
			
		||||
// recommended as many programs (especially editors) update files atomically: it
 | 
			
		||||
// will write to a temporary file which is then moved to to destination,
 | 
			
		||||
// overwriting the original (or some variant thereof). The watcher on the
 | 
			
		||||
// original file is now lost, as that no longer exists.
 | 
			
		||||
//
 | 
			
		||||
// Instead, watch the parent directory and use Event.Name to filter out files
 | 
			
		||||
// you're not interested in. There is an example of this in [cmd/fsnotify/file.go].
 | 
			
		||||
func (w *Watcher) Add(name string) error {
 | 
			
		||||
// The upshot of this is that a power failure or crash won't leave a
 | 
			
		||||
// half-written file.
 | 
			
		||||
//
 | 
			
		||||
// Watch the parent directory and use Event.Name to filter out files you're not
 | 
			
		||||
// interested in. There is an example of this in cmd/fsnotify/file.go.
 | 
			
		||||
func (w *Watcher) Add(name string) error { return w.AddWith(name) }
 | 
			
		||||
 | 
			
		||||
// AddWith is like [Watcher.Add], but allows adding options. When using Add()
 | 
			
		||||
// the defaults described below are used.
 | 
			
		||||
//
 | 
			
		||||
// Possible options are:
 | 
			
		||||
//
 | 
			
		||||
//   - [WithBufferSize] sets the buffer size for the Windows backend; no-op on
 | 
			
		||||
//     other platforms. The default is 64K (65536 bytes).
 | 
			
		||||
func (w *Watcher) AddWith(name string, opts ...addOpt) error {
 | 
			
		||||
	if w.isClosed() {
 | 
			
		||||
		return ErrClosed
 | 
			
		||||
	}
 | 
			
		||||
	if w.port.PathIsWatched(name) {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	_ = getOptions(opts...)
 | 
			
		||||
 | 
			
		||||
	// Currently we resolve symlinks that were explicitly requested to be
 | 
			
		||||
	// watched. Otherwise we would use LStat here.
 | 
			
		||||
	stat, err := os.Stat(name)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Associate all files in the directory.
 | 
			
		||||
	if stat.IsDir() {
 | 
			
		||||
		err := w.handleDirectory(name, stat, true, w.associateFile)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		w.mu.Lock()
 | 
			
		||||
		w.dirs[name] = struct{}{}
 | 
			
		||||
		w.mu.Unlock()
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	err = w.associateFile(name, stat, true)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	w.mu.Lock()
 | 
			
		||||
	w.watches[name] = struct{}{}
 | 
			
		||||
	w.mu.Unlock()
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -157,6 +305,336 @@ func (w *Watcher) Add(name string) error {
 | 
			
		||||
// /tmp/dir and /tmp/dir/subdir then you will need to remove both.
 | 
			
		||||
//
 | 
			
		||||
// Removing a path that has not yet been added returns [ErrNonExistentWatch].
 | 
			
		||||
//
 | 
			
		||||
// Returns nil if [Watcher.Close] was called.
 | 
			
		||||
func (w *Watcher) Remove(name string) error {
 | 
			
		||||
	if w.isClosed() {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
	if !w.port.PathIsWatched(name) {
 | 
			
		||||
		return fmt.Errorf("%w: %s", ErrNonExistentWatch, name)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// The user has expressed an intent. Immediately remove this name from
 | 
			
		||||
	// whichever watch list it might be in. If it's not in there the delete
 | 
			
		||||
	// doesn't cause harm.
 | 
			
		||||
	w.mu.Lock()
 | 
			
		||||
	delete(w.watches, name)
 | 
			
		||||
	delete(w.dirs, name)
 | 
			
		||||
	w.mu.Unlock()
 | 
			
		||||
 | 
			
		||||
	stat, err := os.Stat(name)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Remove associations for every file in the directory.
 | 
			
		||||
	if stat.IsDir() {
 | 
			
		||||
		err := w.handleDirectory(name, stat, false, w.dissociateFile)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	err = w.port.DissociatePath(name)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// readEvents contains the main loop that runs in a goroutine watching for events.
 | 
			
		||||
func (w *Watcher) readEvents() {
 | 
			
		||||
	// If this function returns, the watcher has been closed and we can close
 | 
			
		||||
	// these channels
 | 
			
		||||
	defer func() {
 | 
			
		||||
		close(w.Errors)
 | 
			
		||||
		close(w.Events)
 | 
			
		||||
	}()
 | 
			
		||||
 | 
			
		||||
	pevents := make([]unix.PortEvent, 8)
 | 
			
		||||
	for {
 | 
			
		||||
		count, err := w.port.Get(pevents, 1, nil)
 | 
			
		||||
		if err != nil && err != unix.ETIME {
 | 
			
		||||
			// Interrupted system call (count should be 0) ignore and continue
 | 
			
		||||
			if errors.Is(err, unix.EINTR) && count == 0 {
 | 
			
		||||
				continue
 | 
			
		||||
			}
 | 
			
		||||
			// Get failed because we called w.Close()
 | 
			
		||||
			if errors.Is(err, unix.EBADF) && w.isClosed() {
 | 
			
		||||
				return
 | 
			
		||||
			}
 | 
			
		||||
			// There was an error not caused by calling w.Close()
 | 
			
		||||
			if !w.sendError(err) {
 | 
			
		||||
				return
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		p := pevents[:count]
 | 
			
		||||
		for _, pevent := range p {
 | 
			
		||||
			if pevent.Source != unix.PORT_SOURCE_FILE {
 | 
			
		||||
				// Event from unexpected source received; should never happen.
 | 
			
		||||
				if !w.sendError(errors.New("Event from unexpected source received")) {
 | 
			
		||||
					return
 | 
			
		||||
				}
 | 
			
		||||
				continue
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			err = w.handleEvent(&pevent)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				if !w.sendError(err) {
 | 
			
		||||
					return
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (w *Watcher) handleDirectory(path string, stat os.FileInfo, follow bool, handler func(string, os.FileInfo, bool) error) error {
 | 
			
		||||
	files, err := os.ReadDir(path)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Handle all children of the directory.
 | 
			
		||||
	for _, entry := range files {
 | 
			
		||||
		finfo, err := entry.Info()
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
		err = handler(filepath.Join(path, finfo.Name()), finfo, false)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// And finally handle the directory itself.
 | 
			
		||||
	return handler(path, stat, follow)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// handleEvent might need to emit more than one fsnotify event if the events
 | 
			
		||||
// bitmap matches more than one event type (e.g. the file was both modified and
 | 
			
		||||
// had the attributes changed between when the association was created and the
 | 
			
		||||
// when event was returned)
 | 
			
		||||
func (w *Watcher) handleEvent(event *unix.PortEvent) error {
 | 
			
		||||
	var (
 | 
			
		||||
		events     = event.Events
 | 
			
		||||
		path       = event.Path
 | 
			
		||||
		fmode      = event.Cookie.(os.FileMode)
 | 
			
		||||
		reRegister = true
 | 
			
		||||
	)
 | 
			
		||||
 | 
			
		||||
	w.mu.Lock()
 | 
			
		||||
	_, watchedDir := w.dirs[path]
 | 
			
		||||
	_, watchedPath := w.watches[path]
 | 
			
		||||
	w.mu.Unlock()
 | 
			
		||||
	isWatched := watchedDir || watchedPath
 | 
			
		||||
 | 
			
		||||
	if events&unix.FILE_DELETE != 0 {
 | 
			
		||||
		if !w.sendEvent(path, Remove) {
 | 
			
		||||
			return nil
 | 
			
		||||
		}
 | 
			
		||||
		reRegister = false
 | 
			
		||||
	}
 | 
			
		||||
	if events&unix.FILE_RENAME_FROM != 0 {
 | 
			
		||||
		if !w.sendEvent(path, Rename) {
 | 
			
		||||
			return nil
 | 
			
		||||
		}
 | 
			
		||||
		// Don't keep watching the new file name
 | 
			
		||||
		reRegister = false
 | 
			
		||||
	}
 | 
			
		||||
	if events&unix.FILE_RENAME_TO != 0 {
 | 
			
		||||
		// We don't report a Rename event for this case, because Rename events
 | 
			
		||||
		// are interpreted as referring to the _old_ name of the file, and in
 | 
			
		||||
		// this case the event would refer to the new name of the file. This
 | 
			
		||||
		// type of rename event is not supported by fsnotify.
 | 
			
		||||
 | 
			
		||||
		// inotify reports a Remove event in this case, so we simulate this
 | 
			
		||||
		// here.
 | 
			
		||||
		if !w.sendEvent(path, Remove) {
 | 
			
		||||
			return nil
 | 
			
		||||
		}
 | 
			
		||||
		// Don't keep watching the file that was removed
 | 
			
		||||
		reRegister = false
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// The file is gone, nothing left to do.
 | 
			
		||||
	if !reRegister {
 | 
			
		||||
		if watchedDir {
 | 
			
		||||
			w.mu.Lock()
 | 
			
		||||
			delete(w.dirs, path)
 | 
			
		||||
			w.mu.Unlock()
 | 
			
		||||
		}
 | 
			
		||||
		if watchedPath {
 | 
			
		||||
			w.mu.Lock()
 | 
			
		||||
			delete(w.watches, path)
 | 
			
		||||
			w.mu.Unlock()
 | 
			
		||||
		}
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// If we didn't get a deletion the file still exists and we're going to have
 | 
			
		||||
	// to watch it again. Let's Stat it now so that we can compare permissions
 | 
			
		||||
	// and have what we need to continue watching the file
 | 
			
		||||
 | 
			
		||||
	stat, err := os.Lstat(path)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		// This is unexpected, but we should still emit an event. This happens
 | 
			
		||||
		// most often on "rm -r" of a subdirectory inside a watched directory We
 | 
			
		||||
		// get a modify event of something happening inside, but by the time we
 | 
			
		||||
		// 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) {
 | 
			
		||||
			return nil
 | 
			
		||||
		}
 | 
			
		||||
		// Suppress extra write events on removed directories; they are not
 | 
			
		||||
		// informative and can be confusing.
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// resolve symlinks that were explicitly watched as we would have at Add()
 | 
			
		||||
	// time. this helps suppress spurious Chmod events on watched symlinks
 | 
			
		||||
	if isWatched {
 | 
			
		||||
		stat, err = os.Stat(path)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			// The symlink still exists, but the target is gone. Report the
 | 
			
		||||
			// Remove similar to above.
 | 
			
		||||
			if !w.sendEvent(path, Remove) {
 | 
			
		||||
				return nil
 | 
			
		||||
			}
 | 
			
		||||
			// Don't return the error
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if events&unix.FILE_MODIFIED != 0 {
 | 
			
		||||
		if fmode.IsDir() {
 | 
			
		||||
			if watchedDir {
 | 
			
		||||
				if err := w.updateDirectory(path); err != nil {
 | 
			
		||||
					return err
 | 
			
		||||
				}
 | 
			
		||||
			} else {
 | 
			
		||||
				if !w.sendEvent(path, Write) {
 | 
			
		||||
					return nil
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		} else {
 | 
			
		||||
			if !w.sendEvent(path, Write) {
 | 
			
		||||
				return nil
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	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) {
 | 
			
		||||
				return nil
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	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)
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (w *Watcher) 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 {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for _, entry := range files {
 | 
			
		||||
		path := filepath.Join(path, entry.Name())
 | 
			
		||||
		if w.port.PathIsWatched(path) {
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		finfo, err := entry.Info()
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
		err = w.associateFile(path, finfo, false)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			if !w.sendError(err) {
 | 
			
		||||
				return nil
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		if !w.sendEvent(path, Create) {
 | 
			
		||||
			return nil
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (w *Watcher) associateFile(path string, stat os.FileInfo, follow bool) error {
 | 
			
		||||
	if w.isClosed() {
 | 
			
		||||
		return ErrClosed
 | 
			
		||||
	}
 | 
			
		||||
	// This is primarily protecting the call to AssociatePath but it is
 | 
			
		||||
	// important and intentional that the call to PathIsWatched is also
 | 
			
		||||
	// protected by this mutex. Without this mutex, AssociatePath has been seen
 | 
			
		||||
	// to error out that the path is already associated.
 | 
			
		||||
	w.mu.Lock()
 | 
			
		||||
	defer w.mu.Unlock()
 | 
			
		||||
 | 
			
		||||
	if w.port.PathIsWatched(path) {
 | 
			
		||||
		// Remove the old association in favor of this one If we get ENOENT,
 | 
			
		||||
		// then while the x/sys/unix wrapper still thought that this path was
 | 
			
		||||
		// associated, the underlying event port did not. This call will have
 | 
			
		||||
		// cleared up that discrepancy. The most likely cause is that the event
 | 
			
		||||
		// has fired but we haven't processed it yet.
 | 
			
		||||
		err := w.port.DissociatePath(path)
 | 
			
		||||
		if err != nil && err != unix.ENOENT {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	// FILE_NOFOLLOW means we watch symlinks themselves rather than their
 | 
			
		||||
	// targets.
 | 
			
		||||
	events := unix.FILE_MODIFIED | unix.FILE_ATTRIB | unix.FILE_NOFOLLOW
 | 
			
		||||
	if follow {
 | 
			
		||||
		// We *DO* follow symlinks for explicitly watched entries.
 | 
			
		||||
		events = unix.FILE_MODIFIED | unix.FILE_ATTRIB
 | 
			
		||||
	}
 | 
			
		||||
	return w.port.AssociatePath(path, stat,
 | 
			
		||||
		events,
 | 
			
		||||
		stat.Mode())
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (w *Watcher) dissociateFile(path string, stat os.FileInfo, unused bool) error {
 | 
			
		||||
	if !w.port.PathIsWatched(path) {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
	return w.port.DissociatePath(path)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// WatchList returns all paths explicitly added with [Watcher.Add] (and are not
 | 
			
		||||
// yet removed).
 | 
			
		||||
//
 | 
			
		||||
// Returns nil if [Watcher.Close] was called.
 | 
			
		||||
func (w *Watcher) WatchList() []string {
 | 
			
		||||
	if w.isClosed() {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	w.mu.Lock()
 | 
			
		||||
	defer w.mu.Unlock()
 | 
			
		||||
 | 
			
		||||
	entries := make([]string, 0, len(w.watches)+len(w.dirs))
 | 
			
		||||
	for pathname := range w.dirs {
 | 
			
		||||
		entries = append(entries, pathname)
 | 
			
		||||
	}
 | 
			
		||||
	for pathname := range w.watches {
 | 
			
		||||
		entries = append(entries, pathname)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return entries
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										381
									
								
								vendor/github.com/fsnotify/fsnotify/backend_inotify.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										381
									
								
								vendor/github.com/fsnotify/fsnotify/backend_inotify.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -1,5 +1,8 @@
 | 
			
		||||
//go:build linux
 | 
			
		||||
// +build linux
 | 
			
		||||
//go:build linux && !appengine
 | 
			
		||||
// +build linux,!appengine
 | 
			
		||||
 | 
			
		||||
// Note: the documentation on the Watcher type and methods is generated from
 | 
			
		||||
// mkdoc.zsh
 | 
			
		||||
 | 
			
		||||
package fsnotify
 | 
			
		||||
 | 
			
		||||
@@ -26,9 +29,9 @@ import (
 | 
			
		||||
// When a file is removed a Remove event won't be emitted until all file
 | 
			
		||||
// descriptors are closed, and deletes will always emit a Chmod. For example:
 | 
			
		||||
//
 | 
			
		||||
//     fp := os.Open("file")
 | 
			
		||||
//     os.Remove("file")        // Triggers Chmod
 | 
			
		||||
//     fp.Close()               // Triggers Remove
 | 
			
		||||
//	fp := os.Open("file")
 | 
			
		||||
//	os.Remove("file")        // Triggers Chmod
 | 
			
		||||
//	fp.Close()               // Triggers Remove
 | 
			
		||||
//
 | 
			
		||||
// This is the event that inotify sends, so not much can be changed about this.
 | 
			
		||||
//
 | 
			
		||||
@@ -42,16 +45,16 @@ import (
 | 
			
		||||
//
 | 
			
		||||
// To increase them you can use sysctl or write the value to the /proc file:
 | 
			
		||||
//
 | 
			
		||||
//     # Default values on Linux 5.18
 | 
			
		||||
//     sysctl fs.inotify.max_user_watches=124983
 | 
			
		||||
//     sysctl fs.inotify.max_user_instances=128
 | 
			
		||||
//	# Default values on Linux 5.18
 | 
			
		||||
//	sysctl fs.inotify.max_user_watches=124983
 | 
			
		||||
//	sysctl fs.inotify.max_user_instances=128
 | 
			
		||||
//
 | 
			
		||||
// To make the changes persist on reboot edit /etc/sysctl.conf or
 | 
			
		||||
// /usr/lib/sysctl.d/50-default.conf (details differ per Linux distro; check
 | 
			
		||||
// your distro's documentation):
 | 
			
		||||
//
 | 
			
		||||
//     fs.inotify.max_user_watches=124983
 | 
			
		||||
//     fs.inotify.max_user_instances=128
 | 
			
		||||
//	fs.inotify.max_user_watches=124983
 | 
			
		||||
//	fs.inotify.max_user_instances=128
 | 
			
		||||
//
 | 
			
		||||
// Reaching the limit will result in a "no space left on device" or "too many open
 | 
			
		||||
// files" error.
 | 
			
		||||
@@ -67,14 +70,20 @@ import (
 | 
			
		||||
// control the maximum number of open files, as well as /etc/login.conf on BSD
 | 
			
		||||
// systems.
 | 
			
		||||
//
 | 
			
		||||
// # macOS notes
 | 
			
		||||
// # Windows notes
 | 
			
		||||
//
 | 
			
		||||
// Spotlight indexing on macOS can result in multiple events (see [#15]). A
 | 
			
		||||
// temporary workaround is to add your folder(s) to the "Spotlight Privacy
 | 
			
		||||
// Settings" until we have a native FSEvents implementation (see [#11]).
 | 
			
		||||
// Paths can be added as "C:\path\to\dir", but forward slashes
 | 
			
		||||
// ("C:/path/to/dir") will also work.
 | 
			
		||||
//
 | 
			
		||||
// [#11]: https://github.com/fsnotify/fsnotify/issues/11
 | 
			
		||||
// [#15]: https://github.com/fsnotify/fsnotify/issues/15
 | 
			
		||||
// When a watched directory is removed it will always send an event for the
 | 
			
		||||
// directory itself, but may not send events for all files in that directory.
 | 
			
		||||
// Sometimes it will send events for all times, sometimes it will send no
 | 
			
		||||
// events, and often only for some files.
 | 
			
		||||
//
 | 
			
		||||
// The default ReadDirectoryChangesW() buffer size is 64K, which is the largest
 | 
			
		||||
// value that is guaranteed to work with SMB filesystems. If you have many
 | 
			
		||||
// events in quick succession this may not be enough, and you will have to use
 | 
			
		||||
// [WithBufferSize] to increase the value.
 | 
			
		||||
type Watcher struct {
 | 
			
		||||
	// Events sends the filesystem change events.
 | 
			
		||||
	//
 | 
			
		||||
@@ -101,36 +110,148 @@ type Watcher struct {
 | 
			
		||||
	//                      initiated by the user may show up as one or multiple
 | 
			
		||||
	//                      writes, depending on when the system syncs things to
 | 
			
		||||
	//                      disk. For example when compiling a large Go program
 | 
			
		||||
	//                      you may get hundreds of Write events, so you
 | 
			
		||||
	//                      probably want to wait until you've stopped receiving
 | 
			
		||||
	//                      them (see the dedup example in cmd/fsnotify).
 | 
			
		||||
	//                      you may get hundreds of Write events, and you may
 | 
			
		||||
	//                      want to wait until you've stopped receiving them
 | 
			
		||||
	//                      (see the dedup example in cmd/fsnotify).
 | 
			
		||||
	//
 | 
			
		||||
	//                      Some systems may send Write event for directories
 | 
			
		||||
	//                      when the directory content changes.
 | 
			
		||||
	//
 | 
			
		||||
	//   fsnotify.Chmod     Attributes were changed. On Linux this is also sent
 | 
			
		||||
	//                      when a file is removed (or more accurately, when a
 | 
			
		||||
	//                      link to an inode is removed). On kqueue it's sent
 | 
			
		||||
	//                      and on kqueue when a file is truncated. On Windows
 | 
			
		||||
	//                      it's never sent.
 | 
			
		||||
	//                      when a file is truncated. On Windows it's never
 | 
			
		||||
	//                      sent.
 | 
			
		||||
	Events chan Event
 | 
			
		||||
 | 
			
		||||
	// Errors sends any errors.
 | 
			
		||||
	//
 | 
			
		||||
	// ErrEventOverflow is used to indicate there are too many events:
 | 
			
		||||
	//
 | 
			
		||||
	//  - inotify:      There are too many queued events (fs.inotify.max_queued_events sysctl)
 | 
			
		||||
	//  - windows:      The buffer size is too small; WithBufferSize() can be used to increase it.
 | 
			
		||||
	//  - kqueue, fen:  Not used.
 | 
			
		||||
	Errors chan error
 | 
			
		||||
 | 
			
		||||
	// Store fd here as os.File.Read() will no longer return on close after
 | 
			
		||||
	// calling Fd(). See: https://github.com/golang/go/issues/26439
 | 
			
		||||
	fd          int
 | 
			
		||||
	mu          sync.Mutex // Map access
 | 
			
		||||
	inotifyFile *os.File
 | 
			
		||||
	watches     map[string]*watch // Map of inotify watches (key: path)
 | 
			
		||||
	paths       map[int]string    // Map of watched paths (key: watch descriptor)
 | 
			
		||||
	done        chan struct{}     // Channel for sending a "quit message" to the reader goroutine
 | 
			
		||||
	doneResp    chan struct{}     // Channel to respond to Close
 | 
			
		||||
	watches     *watches
 | 
			
		||||
	done        chan struct{} // Channel for sending a "quit message" to the reader goroutine
 | 
			
		||||
	closeMu     sync.Mutex
 | 
			
		||||
	doneResp    chan struct{} // Channel to respond to Close
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type (
 | 
			
		||||
	watches struct {
 | 
			
		||||
		mu   sync.RWMutex
 | 
			
		||||
		wd   map[uint32]*watch // wd → watch
 | 
			
		||||
		path map[string]uint32 // pathname → wd
 | 
			
		||||
	}
 | 
			
		||||
	watch struct {
 | 
			
		||||
		wd    uint32 // Watch descriptor (as returned by the inotify_add_watch() syscall)
 | 
			
		||||
		flags uint32 // inotify flags of this watch (see inotify(7) for the list of valid flags)
 | 
			
		||||
		path  string // Watch path.
 | 
			
		||||
	}
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func newWatches() *watches {
 | 
			
		||||
	return &watches{
 | 
			
		||||
		wd:   make(map[uint32]*watch),
 | 
			
		||||
		path: make(map[string]uint32),
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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()
 | 
			
		||||
	delete(w.path, w.wd[wd].path)
 | 
			
		||||
	delete(w.wd, wd)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (w *watches) removePath(path string) (uint32, bool) {
 | 
			
		||||
	w.mu.Lock()
 | 
			
		||||
	defer w.mu.Unlock()
 | 
			
		||||
 | 
			
		||||
	wd, ok := w.path[path]
 | 
			
		||||
	if !ok {
 | 
			
		||||
		return 0, false
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	delete(w.path, path)
 | 
			
		||||
	delete(w.wd, wd)
 | 
			
		||||
 | 
			
		||||
	return wd, true
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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 {
 | 
			
		||||
		existing = w.wd[wd]
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	upd, err := f(existing)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	if upd != nil {
 | 
			
		||||
		w.wd[upd.wd] = upd
 | 
			
		||||
		w.path[upd.path] = upd.wd
 | 
			
		||||
 | 
			
		||||
		if upd.wd != wd {
 | 
			
		||||
			delete(w.wd, wd)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewWatcher creates a new Watcher.
 | 
			
		||||
func NewWatcher() (*Watcher, error) {
 | 
			
		||||
	// Create inotify fd
 | 
			
		||||
	// Need to set the FD to nonblocking mode in order for SetDeadline methods to work
 | 
			
		||||
	// Otherwise, blocking i/o operations won't terminate on close
 | 
			
		||||
	return NewBufferedWatcher(0)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewBufferedWatcher creates a new Watcher with a buffered Watcher.Events
 | 
			
		||||
// channel.
 | 
			
		||||
//
 | 
			
		||||
// The main use case for this is situations with a very large number of events
 | 
			
		||||
// where the kernel buffer size can't be increased (e.g. due to lack of
 | 
			
		||||
// permissions). An unbuffered Watcher will perform better for almost all use
 | 
			
		||||
// 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) {
 | 
			
		||||
	// 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)
 | 
			
		||||
	if fd == -1 {
 | 
			
		||||
		return nil, errno
 | 
			
		||||
@@ -139,9 +260,8 @@ func NewWatcher() (*Watcher, error) {
 | 
			
		||||
	w := &Watcher{
 | 
			
		||||
		fd:          fd,
 | 
			
		||||
		inotifyFile: os.NewFile(uintptr(fd), ""),
 | 
			
		||||
		watches:     make(map[string]*watch),
 | 
			
		||||
		paths:       make(map[int]string),
 | 
			
		||||
		Events:      make(chan Event),
 | 
			
		||||
		watches:     newWatches(),
 | 
			
		||||
		Events:      make(chan Event, sz),
 | 
			
		||||
		Errors:      make(chan error),
 | 
			
		||||
		done:        make(chan struct{}),
 | 
			
		||||
		doneResp:    make(chan struct{}),
 | 
			
		||||
@@ -157,8 +277,8 @@ func (w *Watcher) sendEvent(e Event) bool {
 | 
			
		||||
	case w.Events <- e:
 | 
			
		||||
		return true
 | 
			
		||||
	case <-w.done:
 | 
			
		||||
		return false
 | 
			
		||||
	}
 | 
			
		||||
	return false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Returns true if the error was sent, or false if watcher is closed.
 | 
			
		||||
@@ -180,17 +300,15 @@ func (w *Watcher) isClosed() bool {
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Close removes all watches and closes the events channel.
 | 
			
		||||
// Close removes all watches and closes the Events channel.
 | 
			
		||||
func (w *Watcher) Close() error {
 | 
			
		||||
	w.mu.Lock()
 | 
			
		||||
	w.closeMu.Lock()
 | 
			
		||||
	if w.isClosed() {
 | 
			
		||||
		w.mu.Unlock()
 | 
			
		||||
		w.closeMu.Unlock()
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Send 'close' signal to goroutine, and set the Watcher to closed.
 | 
			
		||||
	close(w.done)
 | 
			
		||||
	w.mu.Unlock()
 | 
			
		||||
	w.closeMu.Unlock()
 | 
			
		||||
 | 
			
		||||
	// Causes any blocking reads to return with an error, provided the file
 | 
			
		||||
	// still supports deadline operations.
 | 
			
		||||
@@ -207,17 +325,21 @@ func (w *Watcher) Close() error {
 | 
			
		||||
 | 
			
		||||
// Add starts monitoring the path for changes.
 | 
			
		||||
//
 | 
			
		||||
// A path can only be watched once; attempting to watch it more than once will
 | 
			
		||||
// return an error. Paths that do not yet exist on the filesystem cannot be
 | 
			
		||||
// added. A watch will be automatically removed if the path is deleted.
 | 
			
		||||
// A path can only be watched once; watching it more than once is a no-op and will
 | 
			
		||||
// not return an error. Paths that do not yet exist on the filesystem cannot be
 | 
			
		||||
// watched.
 | 
			
		||||
//
 | 
			
		||||
// A path will remain watched if it gets renamed to somewhere else on the same
 | 
			
		||||
// filesystem, but the monitor will get removed if the path gets deleted and
 | 
			
		||||
// re-created, or if it's moved to a different filesystem.
 | 
			
		||||
// A watch will be automatically removed if the watched path is deleted or
 | 
			
		||||
// renamed. The exception is the Windows backend, which doesn't remove the
 | 
			
		||||
// watcher on renames.
 | 
			
		||||
//
 | 
			
		||||
// Notifications on network filesystems (NFS, SMB, FUSE, etc.) or special
 | 
			
		||||
// filesystems (/proc, /sys, etc.) generally don't work.
 | 
			
		||||
//
 | 
			
		||||
// Returns [ErrClosed] if [Watcher.Close] was called.
 | 
			
		||||
//
 | 
			
		||||
// See [Watcher.AddWith] for a version that allows adding options.
 | 
			
		||||
//
 | 
			
		||||
// # Watching directories
 | 
			
		||||
//
 | 
			
		||||
// All files in a directory are monitored, including new files that are created
 | 
			
		||||
@@ -227,44 +349,59 @@ func (w *Watcher) Close() error {
 | 
			
		||||
// # Watching files
 | 
			
		||||
//
 | 
			
		||||
// Watching individual files (rather than directories) is generally not
 | 
			
		||||
// recommended as many tools update files atomically. Instead of "just" writing
 | 
			
		||||
// to the file a temporary file will be written to first, and if successful the
 | 
			
		||||
// temporary file is moved to to destination removing the original, or some
 | 
			
		||||
// variant thereof. The watcher on the original file is now lost, as it no
 | 
			
		||||
// longer exists.
 | 
			
		||||
// recommended as many programs (especially editors) update files atomically: it
 | 
			
		||||
// will write to a temporary file which is then moved to to destination,
 | 
			
		||||
// overwriting the original (or some variant thereof). The watcher on the
 | 
			
		||||
// original file is now lost, as that no longer exists.
 | 
			
		||||
//
 | 
			
		||||
// Instead, watch the parent directory and use Event.Name to filter out files
 | 
			
		||||
// you're not interested in. There is an example of this in [cmd/fsnotify/file.go].
 | 
			
		||||
func (w *Watcher) Add(name string) error {
 | 
			
		||||
	name = filepath.Clean(name)
 | 
			
		||||
// The upshot of this is that a power failure or crash won't leave a
 | 
			
		||||
// half-written file.
 | 
			
		||||
//
 | 
			
		||||
// Watch the parent directory and use Event.Name to filter out files you're not
 | 
			
		||||
// interested in. There is an example of this in cmd/fsnotify/file.go.
 | 
			
		||||
func (w *Watcher) Add(name string) error { return w.AddWith(name) }
 | 
			
		||||
 | 
			
		||||
// AddWith is like [Watcher.Add], but allows adding options. When using Add()
 | 
			
		||||
// the defaults described below are used.
 | 
			
		||||
//
 | 
			
		||||
// Possible options are:
 | 
			
		||||
//
 | 
			
		||||
//   - [WithBufferSize] sets the buffer size for the Windows backend; no-op on
 | 
			
		||||
//     other platforms. The default is 64K (65536 bytes).
 | 
			
		||||
func (w *Watcher) AddWith(name string, opts ...addOpt) error {
 | 
			
		||||
	if w.isClosed() {
 | 
			
		||||
		return errors.New("inotify instance already closed")
 | 
			
		||||
		return ErrClosed
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	name = filepath.Clean(name)
 | 
			
		||||
	_ = getOptions(opts...)
 | 
			
		||||
 | 
			
		||||
	var flags uint32 = 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
 | 
			
		||||
 | 
			
		||||
	w.mu.Lock()
 | 
			
		||||
	defer w.mu.Unlock()
 | 
			
		||||
	watchEntry := w.watches[name]
 | 
			
		||||
	if watchEntry != nil {
 | 
			
		||||
		flags |= watchEntry.flags | unix.IN_MASK_ADD
 | 
			
		||||
	}
 | 
			
		||||
	wd, errno := unix.InotifyAddWatch(w.fd, name, flags)
 | 
			
		||||
	if wd == -1 {
 | 
			
		||||
		return errno
 | 
			
		||||
	}
 | 
			
		||||
	return w.watches.updatePath(name, func(existing *watch) (*watch, error) {
 | 
			
		||||
		if existing != nil {
 | 
			
		||||
			flags |= existing.flags | unix.IN_MASK_ADD
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
	if watchEntry == nil {
 | 
			
		||||
		w.watches[name] = &watch{wd: uint32(wd), flags: flags}
 | 
			
		||||
		w.paths[wd] = name
 | 
			
		||||
	} else {
 | 
			
		||||
		watchEntry.wd = uint32(wd)
 | 
			
		||||
		watchEntry.flags = flags
 | 
			
		||||
	}
 | 
			
		||||
		wd, err := unix.InotifyAddWatch(w.fd, name, flags)
 | 
			
		||||
		if wd == -1 {
 | 
			
		||||
			return nil, err
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
		if existing == nil {
 | 
			
		||||
			return &watch{
 | 
			
		||||
				wd:    uint32(wd),
 | 
			
		||||
				path:  name,
 | 
			
		||||
				flags: flags,
 | 
			
		||||
			}, nil
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		existing.wd = uint32(wd)
 | 
			
		||||
		existing.flags = flags
 | 
			
		||||
		return existing, nil
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Remove stops monitoring the path for changes.
 | 
			
		||||
@@ -273,32 +410,22 @@ func (w *Watcher) Add(name string) error {
 | 
			
		||||
// /tmp/dir and /tmp/dir/subdir then you will need to remove both.
 | 
			
		||||
//
 | 
			
		||||
// Removing a path that has not yet been added returns [ErrNonExistentWatch].
 | 
			
		||||
//
 | 
			
		||||
// Returns nil if [Watcher.Close] was called.
 | 
			
		||||
func (w *Watcher) Remove(name string) error {
 | 
			
		||||
	name = filepath.Clean(name)
 | 
			
		||||
	if w.isClosed() {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
	return w.remove(filepath.Clean(name))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
	// Fetch the watch.
 | 
			
		||||
	w.mu.Lock()
 | 
			
		||||
	defer w.mu.Unlock()
 | 
			
		||||
	watch, ok := w.watches[name]
 | 
			
		||||
 | 
			
		||||
	// Remove it from inotify.
 | 
			
		||||
func (w *Watcher) remove(name string) error {
 | 
			
		||||
	wd, ok := w.watches.removePath(name)
 | 
			
		||||
	if !ok {
 | 
			
		||||
		return fmt.Errorf("%w: %s", ErrNonExistentWatch, name)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// We successfully removed the watch if InotifyRmWatch doesn't return an
 | 
			
		||||
	// error, we need to clean up our internal state to ensure it matches
 | 
			
		||||
	// inotify's kernel state.
 | 
			
		||||
	delete(w.paths, int(watch.wd))
 | 
			
		||||
	delete(w.watches, name)
 | 
			
		||||
 | 
			
		||||
	// inotify_rm_watch will return EINVAL if the file has been deleted;
 | 
			
		||||
	// the inotify will already have been removed.
 | 
			
		||||
	// watches and pathes are deleted in ignoreLinux() implicitly and asynchronously
 | 
			
		||||
	// by calling inotify_rm_watch() below. e.g. readEvents() goroutine receives IN_IGNORE
 | 
			
		||||
	// so that EINVAL means that the wd is being rm_watch()ed or its file removed
 | 
			
		||||
	// by another thread and we have not received IN_IGNORE event.
 | 
			
		||||
	success, errno := unix.InotifyRmWatch(w.fd, watch.wd)
 | 
			
		||||
	success, errno := unix.InotifyRmWatch(w.fd, wd)
 | 
			
		||||
	if success == -1 {
 | 
			
		||||
		// TODO: Perhaps it's not helpful to return an error here in every case;
 | 
			
		||||
		//       The only two possible errors are:
 | 
			
		||||
@@ -312,26 +439,26 @@ func (w *Watcher) Remove(name string) error {
 | 
			
		||||
		//         are watching is deleted.
 | 
			
		||||
		return errno
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// WatchList returns all paths added with [Add] (and are not yet removed).
 | 
			
		||||
// WatchList returns all paths explicitly added with [Watcher.Add] (and are not
 | 
			
		||||
// yet removed).
 | 
			
		||||
//
 | 
			
		||||
// Returns nil if [Watcher.Close] was called.
 | 
			
		||||
func (w *Watcher) WatchList() []string {
 | 
			
		||||
	w.mu.Lock()
 | 
			
		||||
	defer w.mu.Unlock()
 | 
			
		||||
 | 
			
		||||
	entries := make([]string, 0, len(w.watches))
 | 
			
		||||
	for pathname := range w.watches {
 | 
			
		||||
		entries = append(entries, pathname)
 | 
			
		||||
	if w.isClosed() {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return entries
 | 
			
		||||
}
 | 
			
		||||
	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()
 | 
			
		||||
 | 
			
		||||
type watch struct {
 | 
			
		||||
	wd    uint32 // Watch descriptor (as returned by the inotify_add_watch() syscall)
 | 
			
		||||
	flags uint32 // inotify flags of this watch (see inotify(7) for the list of valid flags)
 | 
			
		||||
	return entries
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// readEvents reads from the inotify file descriptor, converts the
 | 
			
		||||
@@ -367,14 +494,11 @@ func (w *Watcher) readEvents() {
 | 
			
		||||
		if n < unix.SizeofInotifyEvent {
 | 
			
		||||
			var err error
 | 
			
		||||
			if n == 0 {
 | 
			
		||||
				// If EOF is received. This should really never happen.
 | 
			
		||||
				err = io.EOF
 | 
			
		||||
				err = io.EOF // If EOF is received. This should really never happen.
 | 
			
		||||
			} else if n < 0 {
 | 
			
		||||
				// If an error occurred while reading.
 | 
			
		||||
				err = errno
 | 
			
		||||
				err = errno // If an error occurred while reading.
 | 
			
		||||
			} else {
 | 
			
		||||
				// Read was too short.
 | 
			
		||||
				err = errors.New("notify: short read in readEvents()")
 | 
			
		||||
				err = errors.New("notify: short read in readEvents()") // Read was too short.
 | 
			
		||||
			}
 | 
			
		||||
			if !w.sendError(err) {
 | 
			
		||||
				return
 | 
			
		||||
@@ -403,18 +527,29 @@ func (w *Watcher) readEvents() {
 | 
			
		||||
			// 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.
 | 
			
		||||
			w.mu.Lock()
 | 
			
		||||
			name, ok := w.paths[int(raw.Wd)]
 | 
			
		||||
			// IN_DELETE_SELF occurs when the file/directory being watched is removed.
 | 
			
		||||
			// This is a sign to clean up the maps, otherwise we are no longer in sync
 | 
			
		||||
			// with the inotify kernel state which has already deleted the watch
 | 
			
		||||
			// automatically.
 | 
			
		||||
			if ok && mask&unix.IN_DELETE_SELF == unix.IN_DELETE_SELF {
 | 
			
		||||
				delete(w.paths, int(raw.Wd))
 | 
			
		||||
				delete(w.watches, name)
 | 
			
		||||
			}
 | 
			
		||||
			w.mu.Unlock()
 | 
			
		||||
			watch := w.watches.byWd(uint32(raw.Wd))
 | 
			
		||||
 | 
			
		||||
			// inotify will automatically remove the watch on deletes; just need
 | 
			
		||||
			// to clean our state here.
 | 
			
		||||
			if watch != nil && mask&unix.IN_DELETE_SELF == unix.IN_DELETE_SELF {
 | 
			
		||||
				w.watches.remove(watch.wd)
 | 
			
		||||
			}
 | 
			
		||||
			// 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 watch != nil && mask&unix.IN_MOVE_SELF == unix.IN_MOVE_SELF {
 | 
			
		||||
				err := w.remove(watch.path)
 | 
			
		||||
				if err != nil && !errors.Is(err, ErrNonExistentWatch) {
 | 
			
		||||
					if !w.sendError(err) {
 | 
			
		||||
						return
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			var name string
 | 
			
		||||
			if watch != nil {
 | 
			
		||||
				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]
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										293
									
								
								vendor/github.com/fsnotify/fsnotify/backend_kqueue.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										293
									
								
								vendor/github.com/fsnotify/fsnotify/backend_kqueue.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -1,12 +1,14 @@
 | 
			
		||||
//go:build freebsd || openbsd || netbsd || dragonfly || darwin
 | 
			
		||||
// +build freebsd openbsd netbsd dragonfly darwin
 | 
			
		||||
 | 
			
		||||
// Note: the documentation on the Watcher type and methods is generated from
 | 
			
		||||
// mkdoc.zsh
 | 
			
		||||
 | 
			
		||||
package fsnotify
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"errors"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"io/ioutil"
 | 
			
		||||
	"os"
 | 
			
		||||
	"path/filepath"
 | 
			
		||||
	"sync"
 | 
			
		||||
@@ -24,9 +26,9 @@ import (
 | 
			
		||||
// When a file is removed a Remove event won't be emitted until all file
 | 
			
		||||
// descriptors are closed, and deletes will always emit a Chmod. For example:
 | 
			
		||||
//
 | 
			
		||||
//     fp := os.Open("file")
 | 
			
		||||
//     os.Remove("file")        // Triggers Chmod
 | 
			
		||||
//     fp.Close()               // Triggers Remove
 | 
			
		||||
//	fp := os.Open("file")
 | 
			
		||||
//	os.Remove("file")        // Triggers Chmod
 | 
			
		||||
//	fp.Close()               // Triggers Remove
 | 
			
		||||
//
 | 
			
		||||
// This is the event that inotify sends, so not much can be changed about this.
 | 
			
		||||
//
 | 
			
		||||
@@ -40,16 +42,16 @@ import (
 | 
			
		||||
//
 | 
			
		||||
// To increase them you can use sysctl or write the value to the /proc file:
 | 
			
		||||
//
 | 
			
		||||
//     # Default values on Linux 5.18
 | 
			
		||||
//     sysctl fs.inotify.max_user_watches=124983
 | 
			
		||||
//     sysctl fs.inotify.max_user_instances=128
 | 
			
		||||
//	# Default values on Linux 5.18
 | 
			
		||||
//	sysctl fs.inotify.max_user_watches=124983
 | 
			
		||||
//	sysctl fs.inotify.max_user_instances=128
 | 
			
		||||
//
 | 
			
		||||
// To make the changes persist on reboot edit /etc/sysctl.conf or
 | 
			
		||||
// /usr/lib/sysctl.d/50-default.conf (details differ per Linux distro; check
 | 
			
		||||
// your distro's documentation):
 | 
			
		||||
//
 | 
			
		||||
//     fs.inotify.max_user_watches=124983
 | 
			
		||||
//     fs.inotify.max_user_instances=128
 | 
			
		||||
//	fs.inotify.max_user_watches=124983
 | 
			
		||||
//	fs.inotify.max_user_instances=128
 | 
			
		||||
//
 | 
			
		||||
// Reaching the limit will result in a "no space left on device" or "too many open
 | 
			
		||||
// files" error.
 | 
			
		||||
@@ -65,14 +67,20 @@ import (
 | 
			
		||||
// control the maximum number of open files, as well as /etc/login.conf on BSD
 | 
			
		||||
// systems.
 | 
			
		||||
//
 | 
			
		||||
// # macOS notes
 | 
			
		||||
// # Windows notes
 | 
			
		||||
//
 | 
			
		||||
// Spotlight indexing on macOS can result in multiple events (see [#15]). A
 | 
			
		||||
// temporary workaround is to add your folder(s) to the "Spotlight Privacy
 | 
			
		||||
// Settings" until we have a native FSEvents implementation (see [#11]).
 | 
			
		||||
// Paths can be added as "C:\path\to\dir", but forward slashes
 | 
			
		||||
// ("C:/path/to/dir") will also work.
 | 
			
		||||
//
 | 
			
		||||
// [#11]: https://github.com/fsnotify/fsnotify/issues/11
 | 
			
		||||
// [#15]: https://github.com/fsnotify/fsnotify/issues/15
 | 
			
		||||
// When a watched directory is removed it will always send an event for the
 | 
			
		||||
// directory itself, but may not send events for all files in that directory.
 | 
			
		||||
// Sometimes it will send events for all times, sometimes it will send no
 | 
			
		||||
// events, and often only for some files.
 | 
			
		||||
//
 | 
			
		||||
// The default ReadDirectoryChangesW() buffer size is 64K, which is the largest
 | 
			
		||||
// value that is guaranteed to work with SMB filesystems. If you have many
 | 
			
		||||
// events in quick succession this may not be enough, and you will have to use
 | 
			
		||||
// [WithBufferSize] to increase the value.
 | 
			
		||||
type Watcher struct {
 | 
			
		||||
	// Events sends the filesystem change events.
 | 
			
		||||
	//
 | 
			
		||||
@@ -99,18 +107,27 @@ type Watcher struct {
 | 
			
		||||
	//                      initiated by the user may show up as one or multiple
 | 
			
		||||
	//                      writes, depending on when the system syncs things to
 | 
			
		||||
	//                      disk. For example when compiling a large Go program
 | 
			
		||||
	//                      you may get hundreds of Write events, so you
 | 
			
		||||
	//                      probably want to wait until you've stopped receiving
 | 
			
		||||
	//                      them (see the dedup example in cmd/fsnotify).
 | 
			
		||||
	//                      you may get hundreds of Write events, and you may
 | 
			
		||||
	//                      want to wait until you've stopped receiving them
 | 
			
		||||
	//                      (see the dedup example in cmd/fsnotify).
 | 
			
		||||
	//
 | 
			
		||||
	//                      Some systems may send Write event for directories
 | 
			
		||||
	//                      when the directory content changes.
 | 
			
		||||
	//
 | 
			
		||||
	//   fsnotify.Chmod     Attributes were changed. On Linux this is also sent
 | 
			
		||||
	//                      when a file is removed (or more accurately, when a
 | 
			
		||||
	//                      link to an inode is removed). On kqueue it's sent
 | 
			
		||||
	//                      and on kqueue when a file is truncated. On Windows
 | 
			
		||||
	//                      it's never sent.
 | 
			
		||||
	//                      when a file is truncated. On Windows it's never
 | 
			
		||||
	//                      sent.
 | 
			
		||||
	Events chan Event
 | 
			
		||||
 | 
			
		||||
	// Errors sends any errors.
 | 
			
		||||
	//
 | 
			
		||||
	// ErrEventOverflow is used to indicate there are too many events:
 | 
			
		||||
	//
 | 
			
		||||
	//  - inotify:      There are too many queued events (fs.inotify.max_queued_events sysctl)
 | 
			
		||||
	//  - windows:      The buffer size is too small; WithBufferSize() can be used to increase it.
 | 
			
		||||
	//  - kqueue, fen:  Not used.
 | 
			
		||||
	Errors chan error
 | 
			
		||||
 | 
			
		||||
	done         chan struct{}
 | 
			
		||||
@@ -133,6 +150,18 @@ type pathInfo struct {
 | 
			
		||||
 | 
			
		||||
// NewWatcher creates a new Watcher.
 | 
			
		||||
func NewWatcher() (*Watcher, error) {
 | 
			
		||||
	return NewBufferedWatcher(0)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewBufferedWatcher creates a new Watcher with a buffered Watcher.Events
 | 
			
		||||
// channel.
 | 
			
		||||
//
 | 
			
		||||
// The main use case for this is situations with a very large number of events
 | 
			
		||||
// where the kernel buffer size can't be increased (e.g. due to lack of
 | 
			
		||||
// permissions). An unbuffered Watcher will perform better for almost all use
 | 
			
		||||
// 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) {
 | 
			
		||||
	kq, closepipe, err := newKqueue()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
@@ -147,7 +176,7 @@ func NewWatcher() (*Watcher, error) {
 | 
			
		||||
		paths:        make(map[int]pathInfo),
 | 
			
		||||
		fileExists:   make(map[string]struct{}),
 | 
			
		||||
		userWatches:  make(map[string]struct{}),
 | 
			
		||||
		Events:       make(chan Event),
 | 
			
		||||
		Events:       make(chan Event, sz),
 | 
			
		||||
		Errors:       make(chan error),
 | 
			
		||||
		done:         make(chan struct{}),
 | 
			
		||||
	}
 | 
			
		||||
@@ -197,8 +226,8 @@ func (w *Watcher) sendEvent(e Event) bool {
 | 
			
		||||
	case w.Events <- e:
 | 
			
		||||
		return true
 | 
			
		||||
	case <-w.done:
 | 
			
		||||
		return false
 | 
			
		||||
	}
 | 
			
		||||
	return false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Returns true if the error was sent, or false if watcher is closed.
 | 
			
		||||
@@ -207,11 +236,11 @@ func (w *Watcher) sendError(err error) bool {
 | 
			
		||||
	case w.Errors <- err:
 | 
			
		||||
		return true
 | 
			
		||||
	case <-w.done:
 | 
			
		||||
		return false
 | 
			
		||||
	}
 | 
			
		||||
	return false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Close removes all watches and closes the events channel.
 | 
			
		||||
// Close removes all watches and closes the Events channel.
 | 
			
		||||
func (w *Watcher) Close() error {
 | 
			
		||||
	w.mu.Lock()
 | 
			
		||||
	if w.isClosed {
 | 
			
		||||
@@ -239,17 +268,21 @@ func (w *Watcher) Close() error {
 | 
			
		||||
 | 
			
		||||
// Add starts monitoring the path for changes.
 | 
			
		||||
//
 | 
			
		||||
// A path can only be watched once; attempting to watch it more than once will
 | 
			
		||||
// return an error. Paths that do not yet exist on the filesystem cannot be
 | 
			
		||||
// added. A watch will be automatically removed if the path is deleted.
 | 
			
		||||
// A path can only be watched once; watching it more than once is a no-op and will
 | 
			
		||||
// not return an error. Paths that do not yet exist on the filesystem cannot be
 | 
			
		||||
// watched.
 | 
			
		||||
//
 | 
			
		||||
// A path will remain watched if it gets renamed to somewhere else on the same
 | 
			
		||||
// filesystem, but the monitor will get removed if the path gets deleted and
 | 
			
		||||
// re-created, or if it's moved to a different filesystem.
 | 
			
		||||
// A watch will be automatically removed if the watched path is deleted or
 | 
			
		||||
// renamed. The exception is the Windows backend, which doesn't remove the
 | 
			
		||||
// watcher on renames.
 | 
			
		||||
//
 | 
			
		||||
// Notifications on network filesystems (NFS, SMB, FUSE, etc.) or special
 | 
			
		||||
// filesystems (/proc, /sys, etc.) generally don't work.
 | 
			
		||||
//
 | 
			
		||||
// Returns [ErrClosed] if [Watcher.Close] was called.
 | 
			
		||||
//
 | 
			
		||||
// See [Watcher.AddWith] for a version that allows adding options.
 | 
			
		||||
//
 | 
			
		||||
// # Watching directories
 | 
			
		||||
//
 | 
			
		||||
// All files in a directory are monitored, including new files that are created
 | 
			
		||||
@@ -259,15 +292,28 @@ func (w *Watcher) Close() error {
 | 
			
		||||
// # Watching files
 | 
			
		||||
//
 | 
			
		||||
// Watching individual files (rather than directories) is generally not
 | 
			
		||||
// recommended as many tools update files atomically. Instead of "just" writing
 | 
			
		||||
// to the file a temporary file will be written to first, and if successful the
 | 
			
		||||
// temporary file is moved to to destination removing the original, or some
 | 
			
		||||
// variant thereof. The watcher on the original file is now lost, as it no
 | 
			
		||||
// longer exists.
 | 
			
		||||
// recommended as many programs (especially editors) update files atomically: it
 | 
			
		||||
// will write to a temporary file which is then moved to to destination,
 | 
			
		||||
// overwriting the original (or some variant thereof). The watcher on the
 | 
			
		||||
// original file is now lost, as that no longer exists.
 | 
			
		||||
//
 | 
			
		||||
// Instead, watch the parent directory and use Event.Name to filter out files
 | 
			
		||||
// you're not interested in. There is an example of this in [cmd/fsnotify/file.go].
 | 
			
		||||
func (w *Watcher) Add(name string) error {
 | 
			
		||||
// The upshot of this is that a power failure or crash won't leave a
 | 
			
		||||
// half-written file.
 | 
			
		||||
//
 | 
			
		||||
// Watch the parent directory and use Event.Name to filter out files you're not
 | 
			
		||||
// interested in. There is an example of this in cmd/fsnotify/file.go.
 | 
			
		||||
func (w *Watcher) Add(name string) error { return w.AddWith(name) }
 | 
			
		||||
 | 
			
		||||
// AddWith is like [Watcher.Add], but allows adding options. When using Add()
 | 
			
		||||
// the defaults described below are used.
 | 
			
		||||
//
 | 
			
		||||
// Possible options are:
 | 
			
		||||
//
 | 
			
		||||
//   - [WithBufferSize] sets the buffer size for the Windows backend; no-op on
 | 
			
		||||
//     other platforms. The default is 64K (65536 bytes).
 | 
			
		||||
func (w *Watcher) AddWith(name string, opts ...addOpt) error {
 | 
			
		||||
	_ = getOptions(opts...)
 | 
			
		||||
 | 
			
		||||
	w.mu.Lock()
 | 
			
		||||
	w.userWatches[name] = struct{}{}
 | 
			
		||||
	w.mu.Unlock()
 | 
			
		||||
@@ -281,9 +327,19 @@ func (w *Watcher) Add(name string) error {
 | 
			
		||||
// /tmp/dir and /tmp/dir/subdir then you will need to remove both.
 | 
			
		||||
//
 | 
			
		||||
// Removing a path that has not yet been added returns [ErrNonExistentWatch].
 | 
			
		||||
//
 | 
			
		||||
// Returns nil if [Watcher.Close] was called.
 | 
			
		||||
func (w *Watcher) Remove(name string) error {
 | 
			
		||||
	return w.remove(name, true)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (w *Watcher) remove(name string, unwatchFiles bool) error {
 | 
			
		||||
	name = filepath.Clean(name)
 | 
			
		||||
	w.mu.Lock()
 | 
			
		||||
	if w.isClosed {
 | 
			
		||||
		w.mu.Unlock()
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
	watchfd, ok := w.watches[name]
 | 
			
		||||
	w.mu.Unlock()
 | 
			
		||||
	if !ok {
 | 
			
		||||
@@ -315,7 +371,7 @@ func (w *Watcher) Remove(name string) error {
 | 
			
		||||
	w.mu.Unlock()
 | 
			
		||||
 | 
			
		||||
	// Find all watched paths that are in this directory that are not external.
 | 
			
		||||
	if isDir {
 | 
			
		||||
	if unwatchFiles && isDir {
 | 
			
		||||
		var pathsToRemove []string
 | 
			
		||||
		w.mu.Lock()
 | 
			
		||||
		for fd := range w.watchesByDir[name] {
 | 
			
		||||
@@ -326,20 +382,25 @@ func (w *Watcher) Remove(name string) error {
 | 
			
		||||
		}
 | 
			
		||||
		w.mu.Unlock()
 | 
			
		||||
		for _, name := range pathsToRemove {
 | 
			
		||||
			// Since these are internal, not much sense in propagating error
 | 
			
		||||
			// to the user, as that will just confuse them with an error about
 | 
			
		||||
			// a path they did not explicitly watch themselves.
 | 
			
		||||
			// Since these are internal, not much sense in propagating error to
 | 
			
		||||
			// the user, as that will just confuse them with an error about a
 | 
			
		||||
			// path they did not explicitly watch themselves.
 | 
			
		||||
			w.Remove(name)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// WatchList returns all paths added with [Add] (and are not yet removed).
 | 
			
		||||
// WatchList returns all paths explicitly added with [Watcher.Add] (and are not
 | 
			
		||||
// yet removed).
 | 
			
		||||
//
 | 
			
		||||
// Returns nil if [Watcher.Close] was called.
 | 
			
		||||
func (w *Watcher) WatchList() []string {
 | 
			
		||||
	w.mu.Lock()
 | 
			
		||||
	defer w.mu.Unlock()
 | 
			
		||||
	if w.isClosed {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	entries := make([]string, 0, len(w.userWatches))
 | 
			
		||||
	for pathname := range w.userWatches {
 | 
			
		||||
@@ -352,18 +413,18 @@ func (w *Watcher) WatchList() []string {
 | 
			
		||||
// Watch all events (except NOTE_EXTEND, NOTE_LINK, NOTE_REVOKE)
 | 
			
		||||
const noteAllEvents = unix.NOTE_DELETE | unix.NOTE_WRITE | unix.NOTE_ATTRIB | unix.NOTE_RENAME
 | 
			
		||||
 | 
			
		||||
// addWatch adds name to the watched file set.
 | 
			
		||||
// The flags are interpreted as described in kevent(2).
 | 
			
		||||
// Returns the real path to the file which was added, if any, which may be different from the one passed in the case of symlinks.
 | 
			
		||||
// addWatch adds name to the watched file set; the flags are interpreted as
 | 
			
		||||
// described in kevent(2).
 | 
			
		||||
//
 | 
			
		||||
// Returns the real path to the file which was added, with symlinks resolved.
 | 
			
		||||
func (w *Watcher) addWatch(name string, flags uint32) (string, error) {
 | 
			
		||||
	var isDir bool
 | 
			
		||||
	// Make ./name and name equivalent
 | 
			
		||||
	name = filepath.Clean(name)
 | 
			
		||||
 | 
			
		||||
	w.mu.Lock()
 | 
			
		||||
	if w.isClosed {
 | 
			
		||||
		w.mu.Unlock()
 | 
			
		||||
		return "", errors.New("kevent instance already closed")
 | 
			
		||||
		return "", ErrClosed
 | 
			
		||||
	}
 | 
			
		||||
	watchfd, alreadyWatching := w.watches[name]
 | 
			
		||||
	// We already have a watch, but we can still override flags.
 | 
			
		||||
@@ -383,27 +444,30 @@ func (w *Watcher) addWatch(name string, flags uint32) (string, error) {
 | 
			
		||||
			return "", nil
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// Follow Symlinks
 | 
			
		||||
		//
 | 
			
		||||
		// Linux can add unresolvable symlinks to the watch list without issue,
 | 
			
		||||
		// and Windows can't do symlinks period. To maintain consistency, we
 | 
			
		||||
		// will act like everything is fine if the link can't be resolved.
 | 
			
		||||
		// There will simply be no file events for broken symlinks. Hence the
 | 
			
		||||
		// returns of nil on errors.
 | 
			
		||||
		// Follow Symlinks.
 | 
			
		||||
		if fi.Mode()&os.ModeSymlink == os.ModeSymlink {
 | 
			
		||||
			name, err = filepath.EvalSymlinks(name)
 | 
			
		||||
			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
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			w.mu.Lock()
 | 
			
		||||
			_, alreadyWatching = w.watches[name]
 | 
			
		||||
			_, alreadyWatching = w.watches[link]
 | 
			
		||||
			w.mu.Unlock()
 | 
			
		||||
 | 
			
		||||
			if alreadyWatching {
 | 
			
		||||
				return name, nil
 | 
			
		||||
				// Add to watches so we don't get spurious Create events later
 | 
			
		||||
				// on when we diff the directories.
 | 
			
		||||
				w.watches[name] = 0
 | 
			
		||||
				w.fileExists[name] = struct{}{}
 | 
			
		||||
				return link, nil
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			name = link
 | 
			
		||||
			fi, err = os.Lstat(name)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return "", nil
 | 
			
		||||
@@ -411,7 +475,7 @@ func (w *Watcher) addWatch(name string, flags uint32) (string, error) {
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// Retry on EINTR; open() can return EINTR in practice on macOS.
 | 
			
		||||
		// See #354, and go issues 11180 and 39237.
 | 
			
		||||
		// See #354, and Go issues 11180 and 39237.
 | 
			
		||||
		for {
 | 
			
		||||
			watchfd, err = unix.Open(name, openMode, 0)
 | 
			
		||||
			if err == nil {
 | 
			
		||||
@@ -444,14 +508,13 @@ func (w *Watcher) addWatch(name string, flags uint32) (string, error) {
 | 
			
		||||
			w.watchesByDir[parentName] = watchesByDir
 | 
			
		||||
		}
 | 
			
		||||
		watchesByDir[watchfd] = struct{}{}
 | 
			
		||||
 | 
			
		||||
		w.paths[watchfd] = pathInfo{name: name, isDir: isDir}
 | 
			
		||||
		w.mu.Unlock()
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if isDir {
 | 
			
		||||
		// Watch the directory if it has not been watched before,
 | 
			
		||||
		// or if it was watched before, but perhaps only a NOTE_DELETE (watchDirectoryFiles)
 | 
			
		||||
		// Watch the directory if it has not been watched before, or if it was
 | 
			
		||||
		// watched before, but perhaps only a NOTE_DELETE (watchDirectoryFiles)
 | 
			
		||||
		w.mu.Lock()
 | 
			
		||||
 | 
			
		||||
		watchDir := (flags&unix.NOTE_WRITE) == unix.NOTE_WRITE &&
 | 
			
		||||
@@ -473,13 +536,10 @@ func (w *Watcher) addWatch(name string, flags uint32) (string, error) {
 | 
			
		||||
// Event values that it sends down the Events channel.
 | 
			
		||||
func (w *Watcher) readEvents() {
 | 
			
		||||
	defer func() {
 | 
			
		||||
		err := unix.Close(w.kq)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			w.Errors <- err
 | 
			
		||||
		}
 | 
			
		||||
		unix.Close(w.closepipe[0])
 | 
			
		||||
		close(w.Events)
 | 
			
		||||
		close(w.Errors)
 | 
			
		||||
		_ = unix.Close(w.kq)
 | 
			
		||||
		unix.Close(w.closepipe[0])
 | 
			
		||||
	}()
 | 
			
		||||
 | 
			
		||||
	eventBuffer := make([]unix.Kevent_t, 10)
 | 
			
		||||
@@ -513,18 +573,8 @@ func (w *Watcher) readEvents() {
 | 
			
		||||
 | 
			
		||||
			event := w.newEvent(path.name, mask)
 | 
			
		||||
 | 
			
		||||
			if path.isDir && !event.Has(Remove) {
 | 
			
		||||
				// Double check to make sure the directory exists. This can
 | 
			
		||||
				// happen when we do a rm -fr on a recursively watched folders
 | 
			
		||||
				// and we receive a modification event first but the folder has
 | 
			
		||||
				// been deleted and later receive the delete event.
 | 
			
		||||
				if _, err := os.Lstat(event.Name); os.IsNotExist(err) {
 | 
			
		||||
					event.Op |= Remove
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			if event.Has(Rename) || event.Has(Remove) {
 | 
			
		||||
				w.Remove(event.Name)
 | 
			
		||||
				w.remove(event.Name, false)
 | 
			
		||||
				w.mu.Lock()
 | 
			
		||||
				delete(w.fileExists, event.Name)
 | 
			
		||||
				w.mu.Unlock()
 | 
			
		||||
@@ -540,26 +590,30 @@ func (w *Watcher) readEvents() {
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			if event.Has(Remove) {
 | 
			
		||||
				// Look for a file that may have overwritten this.
 | 
			
		||||
				// For example, mv f1 f2 will delete f2, then create f2.
 | 
			
		||||
				// Look for a file that may have overwritten this; for example,
 | 
			
		||||
				// mv f1 f2 will delete f2, then create f2.
 | 
			
		||||
				if path.isDir {
 | 
			
		||||
					fileDir := filepath.Clean(event.Name)
 | 
			
		||||
					w.mu.Lock()
 | 
			
		||||
					_, found := w.watches[fileDir]
 | 
			
		||||
					w.mu.Unlock()
 | 
			
		||||
					if found {
 | 
			
		||||
						// make sure the directory exists before we watch for changes. When we
 | 
			
		||||
						// do a recursive watch and perform rm -fr, the parent directory might
 | 
			
		||||
						// have gone missing, ignore the missing directory and let the
 | 
			
		||||
						// upcoming delete event remove the watch from the parent directory.
 | 
			
		||||
						if _, err := os.Lstat(fileDir); err == nil {
 | 
			
		||||
							w.sendDirectoryChangeEvents(fileDir)
 | 
			
		||||
						err := w.sendDirectoryChangeEvents(fileDir)
 | 
			
		||||
						if err != nil {
 | 
			
		||||
							if !w.sendError(err) {
 | 
			
		||||
								closed = true
 | 
			
		||||
							}
 | 
			
		||||
						}
 | 
			
		||||
					}
 | 
			
		||||
				} else {
 | 
			
		||||
					filePath := filepath.Clean(event.Name)
 | 
			
		||||
					if fileInfo, err := os.Lstat(filePath); err == nil {
 | 
			
		||||
						w.sendFileCreatedEventIfNew(filePath, fileInfo)
 | 
			
		||||
					if fi, err := os.Lstat(filePath); err == nil {
 | 
			
		||||
						err := w.sendFileCreatedEventIfNew(filePath, fi)
 | 
			
		||||
						if err != nil {
 | 
			
		||||
							if !w.sendError(err) {
 | 
			
		||||
								closed = true
 | 
			
		||||
							}
 | 
			
		||||
						}
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
@@ -582,21 +636,31 @@ func (w *Watcher) newEvent(name string, mask uint32) Event {
 | 
			
		||||
	if mask&unix.NOTE_ATTRIB == unix.NOTE_ATTRIB {
 | 
			
		||||
		e.Op |= Chmod
 | 
			
		||||
	}
 | 
			
		||||
	// No point sending a write and delete event at the same time: if it's gone,
 | 
			
		||||
	// then it's gone.
 | 
			
		||||
	if e.Op.Has(Write) && e.Op.Has(Remove) {
 | 
			
		||||
		e.Op &^= Write
 | 
			
		||||
	}
 | 
			
		||||
	return e
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// watchDirectoryFiles to mimic inotify when adding a watch on a directory
 | 
			
		||||
func (w *Watcher) watchDirectoryFiles(dirPath string) error {
 | 
			
		||||
	// Get all files
 | 
			
		||||
	files, err := ioutil.ReadDir(dirPath)
 | 
			
		||||
	files, err := os.ReadDir(dirPath)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for _, fileInfo := range files {
 | 
			
		||||
		path := filepath.Join(dirPath, fileInfo.Name())
 | 
			
		||||
	for _, f := range files {
 | 
			
		||||
		path := filepath.Join(dirPath, f.Name())
 | 
			
		||||
 | 
			
		||||
		cleanPath, err := w.internalWatch(path, fileInfo)
 | 
			
		||||
		fi, err := f.Info()
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return fmt.Errorf("%q: %w", path, err)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		cleanPath, err := w.internalWatch(path, fi)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			// No permission to read the file; that's not a problem: just skip.
 | 
			
		||||
			// But do add it to w.fileExists to prevent it from being picked up
 | 
			
		||||
@@ -606,7 +670,7 @@ func (w *Watcher) watchDirectoryFiles(dirPath string) error {
 | 
			
		||||
			case errors.Is(err, unix.EACCES) || errors.Is(err, unix.EPERM):
 | 
			
		||||
				cleanPath = filepath.Clean(path)
 | 
			
		||||
			default:
 | 
			
		||||
				return fmt.Errorf("%q: %w", filepath.Join(dirPath, fileInfo.Name()), err)
 | 
			
		||||
				return fmt.Errorf("%q: %w", path, err)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
@@ -622,26 +686,37 @@ func (w *Watcher) watchDirectoryFiles(dirPath string) error {
 | 
			
		||||
//
 | 
			
		||||
// This functionality is to have the BSD watcher match the inotify, which sends
 | 
			
		||||
// a create event for files created in a watched directory.
 | 
			
		||||
func (w *Watcher) sendDirectoryChangeEvents(dir string) {
 | 
			
		||||
	// Get all files
 | 
			
		||||
	files, err := ioutil.ReadDir(dir)
 | 
			
		||||
func (w *Watcher) sendDirectoryChangeEvents(dir string) error {
 | 
			
		||||
	files, err := os.ReadDir(dir)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		if !w.sendError(fmt.Errorf("fsnotify.sendDirectoryChangeEvents: %w", err)) {
 | 
			
		||||
			return
 | 
			
		||||
		// Directory no longer exists: we can ignore this safely. kqueue will
 | 
			
		||||
		// still give us the correct events.
 | 
			
		||||
		if errors.Is(err, os.ErrNotExist) {
 | 
			
		||||
			return nil
 | 
			
		||||
		}
 | 
			
		||||
		return fmt.Errorf("fsnotify.sendDirectoryChangeEvents: %w", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Search for new files
 | 
			
		||||
	for _, fi := range files {
 | 
			
		||||
		err := w.sendFileCreatedEventIfNew(filepath.Join(dir, fi.Name()), fi)
 | 
			
		||||
	for _, f := range files {
 | 
			
		||||
		fi, err := f.Info()
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return
 | 
			
		||||
			return fmt.Errorf("fsnotify.sendDirectoryChangeEvents: %w", err)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		err = w.sendFileCreatedEventIfNew(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) {
 | 
			
		||||
				return nil
 | 
			
		||||
			}
 | 
			
		||||
			return fmt.Errorf("fsnotify.sendDirectoryChangeEvents: %w", err)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// sendFileCreatedEvent sends a create event if the file isn't already being tracked.
 | 
			
		||||
func (w *Watcher) sendFileCreatedEventIfNew(filePath string, fileInfo os.FileInfo) (err error) {
 | 
			
		||||
func (w *Watcher) sendFileCreatedEventIfNew(filePath string, fi os.FileInfo) (err error) {
 | 
			
		||||
	w.mu.Lock()
 | 
			
		||||
	_, doesExist := w.fileExists[filePath]
 | 
			
		||||
	w.mu.Unlock()
 | 
			
		||||
@@ -652,7 +727,7 @@ func (w *Watcher) sendFileCreatedEventIfNew(filePath string, fileInfo os.FileInf
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// like watchDirectoryFiles (but without doing another ReadDir)
 | 
			
		||||
	filePath, err = w.internalWatch(filePath, fileInfo)
 | 
			
		||||
	filePath, err = w.internalWatch(filePath, fi)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
@@ -664,10 +739,10 @@ func (w *Watcher) sendFileCreatedEventIfNew(filePath string, fileInfo os.FileInf
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (w *Watcher) internalWatch(name string, fileInfo os.FileInfo) (string, error) {
 | 
			
		||||
	if fileInfo.IsDir() {
 | 
			
		||||
		// mimic Linux providing delete events for subdirectories
 | 
			
		||||
		// but preserve the flags used if currently watching subdirectory
 | 
			
		||||
func (w *Watcher) internalWatch(name string, fi os.FileInfo) (string, error) {
 | 
			
		||||
	if fi.IsDir() {
 | 
			
		||||
		// mimic Linux providing delete events for subdirectories, but preserve
 | 
			
		||||
		// the flags used if currently watching subdirectory
 | 
			
		||||
		w.mu.Lock()
 | 
			
		||||
		flags := w.dirFlags[name]
 | 
			
		||||
		w.mu.Unlock()
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										203
									
								
								vendor/github.com/fsnotify/fsnotify/backend_other.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										203
									
								
								vendor/github.com/fsnotify/fsnotify/backend_other.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -1,39 +1,169 @@
 | 
			
		||||
//go:build !darwin && !dragonfly && !freebsd && !openbsd && !linux && !netbsd && !solaris && !windows
 | 
			
		||||
// +build !darwin,!dragonfly,!freebsd,!openbsd,!linux,!netbsd,!solaris,!windows
 | 
			
		||||
//go:build appengine || (!darwin && !dragonfly && !freebsd && !openbsd && !linux && !netbsd && !solaris && !windows)
 | 
			
		||||
// +build appengine !darwin,!dragonfly,!freebsd,!openbsd,!linux,!netbsd,!solaris,!windows
 | 
			
		||||
 | 
			
		||||
// Note: the documentation on the Watcher type and methods is generated from
 | 
			
		||||
// mkdoc.zsh
 | 
			
		||||
 | 
			
		||||
package fsnotify
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"runtime"
 | 
			
		||||
)
 | 
			
		||||
import "errors"
 | 
			
		||||
 | 
			
		||||
// Watcher watches a set of files, delivering events to a channel.
 | 
			
		||||
type Watcher struct{}
 | 
			
		||||
// Watcher watches a set of paths, delivering events on a channel.
 | 
			
		||||
//
 | 
			
		||||
// A watcher should not be copied (e.g. pass it by pointer, rather than by
 | 
			
		||||
// value).
 | 
			
		||||
//
 | 
			
		||||
// # Linux notes
 | 
			
		||||
//
 | 
			
		||||
// When a file is removed a Remove event won't be emitted until all file
 | 
			
		||||
// descriptors are closed, and deletes will always emit a Chmod. For example:
 | 
			
		||||
//
 | 
			
		||||
//	fp := os.Open("file")
 | 
			
		||||
//	os.Remove("file")        // Triggers Chmod
 | 
			
		||||
//	fp.Close()               // Triggers Remove
 | 
			
		||||
//
 | 
			
		||||
// This is the event that inotify sends, so not much can be changed about this.
 | 
			
		||||
//
 | 
			
		||||
// The fs.inotify.max_user_watches sysctl variable specifies the upper limit
 | 
			
		||||
// for the number of watches per user, and fs.inotify.max_user_instances
 | 
			
		||||
// specifies the maximum number of inotify instances per user. Every Watcher you
 | 
			
		||||
// create is an "instance", and every path you add is a "watch".
 | 
			
		||||
//
 | 
			
		||||
// These are also exposed in /proc as /proc/sys/fs/inotify/max_user_watches and
 | 
			
		||||
// /proc/sys/fs/inotify/max_user_instances
 | 
			
		||||
//
 | 
			
		||||
// To increase them you can use sysctl or write the value to the /proc file:
 | 
			
		||||
//
 | 
			
		||||
//	# Default values on Linux 5.18
 | 
			
		||||
//	sysctl fs.inotify.max_user_watches=124983
 | 
			
		||||
//	sysctl fs.inotify.max_user_instances=128
 | 
			
		||||
//
 | 
			
		||||
// To make the changes persist on reboot edit /etc/sysctl.conf or
 | 
			
		||||
// /usr/lib/sysctl.d/50-default.conf (details differ per Linux distro; check
 | 
			
		||||
// your distro's documentation):
 | 
			
		||||
//
 | 
			
		||||
//	fs.inotify.max_user_watches=124983
 | 
			
		||||
//	fs.inotify.max_user_instances=128
 | 
			
		||||
//
 | 
			
		||||
// Reaching the limit will result in a "no space left on device" or "too many open
 | 
			
		||||
// files" error.
 | 
			
		||||
//
 | 
			
		||||
// # kqueue notes (macOS, BSD)
 | 
			
		||||
//
 | 
			
		||||
// kqueue requires opening a file descriptor for every file that's being watched;
 | 
			
		||||
// so if you're watching a directory with five files then that's six file
 | 
			
		||||
// descriptors. You will run in to your system's "max open files" limit faster on
 | 
			
		||||
// these platforms.
 | 
			
		||||
//
 | 
			
		||||
// The sysctl variables kern.maxfiles and kern.maxfilesperproc can be used to
 | 
			
		||||
// control the maximum number of open files, as well as /etc/login.conf on BSD
 | 
			
		||||
// systems.
 | 
			
		||||
//
 | 
			
		||||
// # Windows notes
 | 
			
		||||
//
 | 
			
		||||
// Paths can be added as "C:\path\to\dir", but forward slashes
 | 
			
		||||
// ("C:/path/to/dir") will also work.
 | 
			
		||||
//
 | 
			
		||||
// When a watched directory is removed it will always send an event for the
 | 
			
		||||
// directory itself, but may not send events for all files in that directory.
 | 
			
		||||
// Sometimes it will send events for all times, sometimes it will send no
 | 
			
		||||
// events, and often only for some files.
 | 
			
		||||
//
 | 
			
		||||
// The default ReadDirectoryChangesW() buffer size is 64K, which is the largest
 | 
			
		||||
// value that is guaranteed to work with SMB filesystems. If you have many
 | 
			
		||||
// events in quick succession this may not be enough, and you will have to use
 | 
			
		||||
// [WithBufferSize] to increase the value.
 | 
			
		||||
type Watcher struct {
 | 
			
		||||
	// Events sends the filesystem change events.
 | 
			
		||||
	//
 | 
			
		||||
	// fsnotify can send the following events; a "path" here can refer to a
 | 
			
		||||
	// file, directory, symbolic link, or special file like a FIFO.
 | 
			
		||||
	//
 | 
			
		||||
	//   fsnotify.Create    A new path was created; this may be followed by one
 | 
			
		||||
	//                      or more Write events if data also gets written to a
 | 
			
		||||
	//                      file.
 | 
			
		||||
	//
 | 
			
		||||
	//   fsnotify.Remove    A path was removed.
 | 
			
		||||
	//
 | 
			
		||||
	//   fsnotify.Rename    A path was renamed. A rename is always sent with the
 | 
			
		||||
	//                      old path as Event.Name, and a Create event will be
 | 
			
		||||
	//                      sent with the new name. Renames are only sent for
 | 
			
		||||
	//                      paths that are currently watched; e.g. moving an
 | 
			
		||||
	//                      unmonitored file into a monitored directory will
 | 
			
		||||
	//                      show up as just a Create. Similarly, renaming a file
 | 
			
		||||
	//                      to outside a monitored directory will show up as
 | 
			
		||||
	//                      only a Rename.
 | 
			
		||||
	//
 | 
			
		||||
	//   fsnotify.Write     A file or named pipe was written to. A Truncate will
 | 
			
		||||
	//                      also trigger a Write. A single "write action"
 | 
			
		||||
	//                      initiated by the user may show up as one or multiple
 | 
			
		||||
	//                      writes, depending on when the system syncs things to
 | 
			
		||||
	//                      disk. For example when compiling a large Go program
 | 
			
		||||
	//                      you may get hundreds of Write events, and you may
 | 
			
		||||
	//                      want to wait until you've stopped receiving them
 | 
			
		||||
	//                      (see the dedup example in cmd/fsnotify).
 | 
			
		||||
	//
 | 
			
		||||
	//                      Some systems may send Write event for directories
 | 
			
		||||
	//                      when the directory content changes.
 | 
			
		||||
	//
 | 
			
		||||
	//   fsnotify.Chmod     Attributes were changed. On Linux this is also sent
 | 
			
		||||
	//                      when a file is removed (or more accurately, when a
 | 
			
		||||
	//                      link to an inode is removed). On kqueue it's sent
 | 
			
		||||
	//                      when a file is truncated. On Windows it's never
 | 
			
		||||
	//                      sent.
 | 
			
		||||
	Events chan Event
 | 
			
		||||
 | 
			
		||||
	// Errors sends any errors.
 | 
			
		||||
	//
 | 
			
		||||
	// ErrEventOverflow is used to indicate there are too many events:
 | 
			
		||||
	//
 | 
			
		||||
	//  - inotify:      There are too many queued events (fs.inotify.max_queued_events sysctl)
 | 
			
		||||
	//  - windows:      The buffer size is too small; WithBufferSize() can be used to increase it.
 | 
			
		||||
	//  - kqueue, fen:  Not used.
 | 
			
		||||
	Errors chan error
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewWatcher creates a new Watcher.
 | 
			
		||||
func NewWatcher() (*Watcher, error) {
 | 
			
		||||
	return nil, fmt.Errorf("fsnotify not supported on %s", runtime.GOOS)
 | 
			
		||||
	return nil, errors.New("fsnotify not supported on the current platform")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Close removes all watches and closes the events channel.
 | 
			
		||||
func (w *Watcher) Close() error {
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
// NewBufferedWatcher creates a new Watcher with a buffered Watcher.Events
 | 
			
		||||
// channel.
 | 
			
		||||
//
 | 
			
		||||
// The main use case for this is situations with a very large number of events
 | 
			
		||||
// where the kernel buffer size can't be increased (e.g. due to lack of
 | 
			
		||||
// permissions). An unbuffered Watcher will perform better for almost all use
 | 
			
		||||
// 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) { return NewWatcher() }
 | 
			
		||||
 | 
			
		||||
// Close removes all watches and closes the Events channel.
 | 
			
		||||
func (w *Watcher) Close() error { return nil }
 | 
			
		||||
 | 
			
		||||
// WatchList returns all paths explicitly added with [Watcher.Add] (and are not
 | 
			
		||||
// yet removed).
 | 
			
		||||
//
 | 
			
		||||
// Returns nil if [Watcher.Close] was called.
 | 
			
		||||
func (w *Watcher) WatchList() []string { return nil }
 | 
			
		||||
 | 
			
		||||
// Add starts monitoring the path for changes.
 | 
			
		||||
//
 | 
			
		||||
// A path can only be watched once; attempting to watch it more than once will
 | 
			
		||||
// return an error. Paths that do not yet exist on the filesystem cannot be
 | 
			
		||||
// added. A watch will be automatically removed if the path is deleted.
 | 
			
		||||
// A path can only be watched once; watching it more than once is a no-op and will
 | 
			
		||||
// not return an error. Paths that do not yet exist on the filesystem cannot be
 | 
			
		||||
// watched.
 | 
			
		||||
//
 | 
			
		||||
// A path will remain watched if it gets renamed to somewhere else on the same
 | 
			
		||||
// filesystem, but the monitor will get removed if the path gets deleted and
 | 
			
		||||
// re-created, or if it's moved to a different filesystem.
 | 
			
		||||
// A watch will be automatically removed if the watched path is deleted or
 | 
			
		||||
// renamed. The exception is the Windows backend, which doesn't remove the
 | 
			
		||||
// watcher on renames.
 | 
			
		||||
//
 | 
			
		||||
// Notifications on network filesystems (NFS, SMB, FUSE, etc.) or special
 | 
			
		||||
// filesystems (/proc, /sys, etc.) generally don't work.
 | 
			
		||||
//
 | 
			
		||||
// Returns [ErrClosed] if [Watcher.Close] was called.
 | 
			
		||||
//
 | 
			
		||||
// See [Watcher.AddWith] for a version that allows adding options.
 | 
			
		||||
//
 | 
			
		||||
// # Watching directories
 | 
			
		||||
//
 | 
			
		||||
// All files in a directory are monitored, including new files that are created
 | 
			
		||||
@@ -43,17 +173,26 @@ func (w *Watcher) Close() error {
 | 
			
		||||
// # Watching files
 | 
			
		||||
//
 | 
			
		||||
// Watching individual files (rather than directories) is generally not
 | 
			
		||||
// recommended as many tools update files atomically. Instead of "just" writing
 | 
			
		||||
// to the file a temporary file will be written to first, and if successful the
 | 
			
		||||
// temporary file is moved to to destination removing the original, or some
 | 
			
		||||
// variant thereof. The watcher on the original file is now lost, as it no
 | 
			
		||||
// longer exists.
 | 
			
		||||
// recommended as many programs (especially editors) update files atomically: it
 | 
			
		||||
// will write to a temporary file which is then moved to to destination,
 | 
			
		||||
// overwriting the original (or some variant thereof). The watcher on the
 | 
			
		||||
// original file is now lost, as that no longer exists.
 | 
			
		||||
//
 | 
			
		||||
// Instead, watch the parent directory and use Event.Name to filter out files
 | 
			
		||||
// you're not interested in. There is an example of this in [cmd/fsnotify/file.go].
 | 
			
		||||
func (w *Watcher) Add(name string) error {
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
// The upshot of this is that a power failure or crash won't leave a
 | 
			
		||||
// half-written file.
 | 
			
		||||
//
 | 
			
		||||
// Watch the parent directory and use Event.Name to filter out files you're not
 | 
			
		||||
// interested in. There is an example of this in cmd/fsnotify/file.go.
 | 
			
		||||
func (w *Watcher) Add(name string) error { return nil }
 | 
			
		||||
 | 
			
		||||
// AddWith is like [Watcher.Add], but allows adding options. When using Add()
 | 
			
		||||
// the defaults described below are used.
 | 
			
		||||
//
 | 
			
		||||
// Possible options are:
 | 
			
		||||
//
 | 
			
		||||
//   - [WithBufferSize] sets the buffer size for the Windows backend; no-op on
 | 
			
		||||
//     other platforms. The default is 64K (65536 bytes).
 | 
			
		||||
func (w *Watcher) AddWith(name string, opts ...addOpt) error { return nil }
 | 
			
		||||
 | 
			
		||||
// Remove stops monitoring the path for changes.
 | 
			
		||||
//
 | 
			
		||||
@@ -61,6 +200,6 @@ func (w *Watcher) Add(name string) error {
 | 
			
		||||
// /tmp/dir and /tmp/dir/subdir then you will need to remove both.
 | 
			
		||||
//
 | 
			
		||||
// Removing a path that has not yet been added returns [ErrNonExistentWatch].
 | 
			
		||||
func (w *Watcher) Remove(name string) error {
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
//
 | 
			
		||||
// Returns nil if [Watcher.Close] was called.
 | 
			
		||||
func (w *Watcher) Remove(name string) error { return nil }
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										245
									
								
								vendor/github.com/fsnotify/fsnotify/backend_windows.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										245
									
								
								vendor/github.com/fsnotify/fsnotify/backend_windows.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -1,6 +1,13 @@
 | 
			
		||||
//go:build windows
 | 
			
		||||
// +build windows
 | 
			
		||||
 | 
			
		||||
// Windows backend based on ReadDirectoryChangesW()
 | 
			
		||||
//
 | 
			
		||||
// https://learn.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-readdirectorychangesw
 | 
			
		||||
//
 | 
			
		||||
// Note: the documentation on the Watcher type and methods is generated from
 | 
			
		||||
// mkdoc.zsh
 | 
			
		||||
 | 
			
		||||
package fsnotify
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
@@ -27,9 +34,9 @@ import (
 | 
			
		||||
// When a file is removed a Remove event won't be emitted until all file
 | 
			
		||||
// descriptors are closed, and deletes will always emit a Chmod. For example:
 | 
			
		||||
//
 | 
			
		||||
//     fp := os.Open("file")
 | 
			
		||||
//     os.Remove("file")        // Triggers Chmod
 | 
			
		||||
//     fp.Close()               // Triggers Remove
 | 
			
		||||
//	fp := os.Open("file")
 | 
			
		||||
//	os.Remove("file")        // Triggers Chmod
 | 
			
		||||
//	fp.Close()               // Triggers Remove
 | 
			
		||||
//
 | 
			
		||||
// This is the event that inotify sends, so not much can be changed about this.
 | 
			
		||||
//
 | 
			
		||||
@@ -43,16 +50,16 @@ import (
 | 
			
		||||
//
 | 
			
		||||
// To increase them you can use sysctl or write the value to the /proc file:
 | 
			
		||||
//
 | 
			
		||||
//     # Default values on Linux 5.18
 | 
			
		||||
//     sysctl fs.inotify.max_user_watches=124983
 | 
			
		||||
//     sysctl fs.inotify.max_user_instances=128
 | 
			
		||||
//	# Default values on Linux 5.18
 | 
			
		||||
//	sysctl fs.inotify.max_user_watches=124983
 | 
			
		||||
//	sysctl fs.inotify.max_user_instances=128
 | 
			
		||||
//
 | 
			
		||||
// To make the changes persist on reboot edit /etc/sysctl.conf or
 | 
			
		||||
// /usr/lib/sysctl.d/50-default.conf (details differ per Linux distro; check
 | 
			
		||||
// your distro's documentation):
 | 
			
		||||
//
 | 
			
		||||
//     fs.inotify.max_user_watches=124983
 | 
			
		||||
//     fs.inotify.max_user_instances=128
 | 
			
		||||
//	fs.inotify.max_user_watches=124983
 | 
			
		||||
//	fs.inotify.max_user_instances=128
 | 
			
		||||
//
 | 
			
		||||
// Reaching the limit will result in a "no space left on device" or "too many open
 | 
			
		||||
// files" error.
 | 
			
		||||
@@ -68,14 +75,20 @@ import (
 | 
			
		||||
// control the maximum number of open files, as well as /etc/login.conf on BSD
 | 
			
		||||
// systems.
 | 
			
		||||
//
 | 
			
		||||
// # macOS notes
 | 
			
		||||
// # Windows notes
 | 
			
		||||
//
 | 
			
		||||
// Spotlight indexing on macOS can result in multiple events (see [#15]). A
 | 
			
		||||
// temporary workaround is to add your folder(s) to the "Spotlight Privacy
 | 
			
		||||
// Settings" until we have a native FSEvents implementation (see [#11]).
 | 
			
		||||
// Paths can be added as "C:\path\to\dir", but forward slashes
 | 
			
		||||
// ("C:/path/to/dir") will also work.
 | 
			
		||||
//
 | 
			
		||||
// [#11]: https://github.com/fsnotify/fsnotify/issues/11
 | 
			
		||||
// [#15]: https://github.com/fsnotify/fsnotify/issues/15
 | 
			
		||||
// When a watched directory is removed it will always send an event for the
 | 
			
		||||
// directory itself, but may not send events for all files in that directory.
 | 
			
		||||
// Sometimes it will send events for all times, sometimes it will send no
 | 
			
		||||
// events, and often only for some files.
 | 
			
		||||
//
 | 
			
		||||
// The default ReadDirectoryChangesW() buffer size is 64K, which is the largest
 | 
			
		||||
// value that is guaranteed to work with SMB filesystems. If you have many
 | 
			
		||||
// events in quick succession this may not be enough, and you will have to use
 | 
			
		||||
// [WithBufferSize] to increase the value.
 | 
			
		||||
type Watcher struct {
 | 
			
		||||
	// Events sends the filesystem change events.
 | 
			
		||||
	//
 | 
			
		||||
@@ -102,31 +115,52 @@ type Watcher struct {
 | 
			
		||||
	//                      initiated by the user may show up as one or multiple
 | 
			
		||||
	//                      writes, depending on when the system syncs things to
 | 
			
		||||
	//                      disk. For example when compiling a large Go program
 | 
			
		||||
	//                      you may get hundreds of Write events, so you
 | 
			
		||||
	//                      probably want to wait until you've stopped receiving
 | 
			
		||||
	//                      them (see the dedup example in cmd/fsnotify).
 | 
			
		||||
	//                      you may get hundreds of Write events, and you may
 | 
			
		||||
	//                      want to wait until you've stopped receiving them
 | 
			
		||||
	//                      (see the dedup example in cmd/fsnotify).
 | 
			
		||||
	//
 | 
			
		||||
	//                      Some systems may send Write event for directories
 | 
			
		||||
	//                      when the directory content changes.
 | 
			
		||||
	//
 | 
			
		||||
	//   fsnotify.Chmod     Attributes were changed. On Linux this is also sent
 | 
			
		||||
	//                      when a file is removed (or more accurately, when a
 | 
			
		||||
	//                      link to an inode is removed). On kqueue it's sent
 | 
			
		||||
	//                      and on kqueue when a file is truncated. On Windows
 | 
			
		||||
	//                      it's never sent.
 | 
			
		||||
	//                      when a file is truncated. On Windows it's never
 | 
			
		||||
	//                      sent.
 | 
			
		||||
	Events chan Event
 | 
			
		||||
 | 
			
		||||
	// Errors sends any errors.
 | 
			
		||||
	//
 | 
			
		||||
	// ErrEventOverflow is used to indicate there are too many events:
 | 
			
		||||
	//
 | 
			
		||||
	//  - inotify:      There are too many queued events (fs.inotify.max_queued_events sysctl)
 | 
			
		||||
	//  - windows:      The buffer size is too small; WithBufferSize() can be used to increase it.
 | 
			
		||||
	//  - kqueue, fen:  Not used.
 | 
			
		||||
	Errors chan error
 | 
			
		||||
 | 
			
		||||
	port  windows.Handle // Handle to completion port
 | 
			
		||||
	input chan *input    // Inputs to the reader are sent on this channel
 | 
			
		||||
	quit  chan chan<- error
 | 
			
		||||
 | 
			
		||||
	mu       sync.Mutex // Protects access to watches, isClosed
 | 
			
		||||
	watches  watchMap   // Map of watches (key: i-number)
 | 
			
		||||
	isClosed bool       // Set to true when Close() is first called
 | 
			
		||||
	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
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewWatcher creates a new Watcher.
 | 
			
		||||
func NewWatcher() (*Watcher, error) {
 | 
			
		||||
	return NewBufferedWatcher(50)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewBufferedWatcher creates a new Watcher with a buffered Watcher.Events
 | 
			
		||||
// channel.
 | 
			
		||||
//
 | 
			
		||||
// The main use case for this is situations with a very large number of events
 | 
			
		||||
// where the kernel buffer size can't be increased (e.g. due to lack of
 | 
			
		||||
// permissions). An unbuffered Watcher will perform better for almost all use
 | 
			
		||||
// 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) {
 | 
			
		||||
	port, err := windows.CreateIoCompletionPort(windows.InvalidHandle, 0, 0, 0)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, os.NewSyscallError("CreateIoCompletionPort", err)
 | 
			
		||||
@@ -135,7 +169,7 @@ func NewWatcher() (*Watcher, error) {
 | 
			
		||||
		port:    port,
 | 
			
		||||
		watches: make(watchMap),
 | 
			
		||||
		input:   make(chan *input, 1),
 | 
			
		||||
		Events:  make(chan Event, 50),
 | 
			
		||||
		Events:  make(chan Event, sz),
 | 
			
		||||
		Errors:  make(chan error),
 | 
			
		||||
		quit:    make(chan chan<- error, 1),
 | 
			
		||||
	}
 | 
			
		||||
@@ -143,6 +177,12 @@ func NewWatcher() (*Watcher, error) {
 | 
			
		||||
	return w, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (w *Watcher) isClosed() bool {
 | 
			
		||||
	w.mu.Lock()
 | 
			
		||||
	defer w.mu.Unlock()
 | 
			
		||||
	return w.closed
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (w *Watcher) sendEvent(name string, mask uint64) bool {
 | 
			
		||||
	if mask == 0 {
 | 
			
		||||
		return false
 | 
			
		||||
@@ -167,14 +207,14 @@ func (w *Watcher) sendError(err error) bool {
 | 
			
		||||
	return false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Close removes all watches and closes the events channel.
 | 
			
		||||
// Close removes all watches and closes the Events channel.
 | 
			
		||||
func (w *Watcher) Close() error {
 | 
			
		||||
	w.mu.Lock()
 | 
			
		||||
	if w.isClosed {
 | 
			
		||||
		w.mu.Unlock()
 | 
			
		||||
	if w.isClosed() {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
	w.isClosed = true
 | 
			
		||||
 | 
			
		||||
	w.mu.Lock()
 | 
			
		||||
	w.closed = true
 | 
			
		||||
	w.mu.Unlock()
 | 
			
		||||
 | 
			
		||||
	// Send "quit" message to the reader goroutine
 | 
			
		||||
@@ -188,17 +228,21 @@ func (w *Watcher) Close() error {
 | 
			
		||||
 | 
			
		||||
// Add starts monitoring the path for changes.
 | 
			
		||||
//
 | 
			
		||||
// A path can only be watched once; attempting to watch it more than once will
 | 
			
		||||
// return an error. Paths that do not yet exist on the filesystem cannot be
 | 
			
		||||
// added. A watch will be automatically removed if the path is deleted.
 | 
			
		||||
// A path can only be watched once; watching it more than once is a no-op and will
 | 
			
		||||
// not return an error. Paths that do not yet exist on the filesystem cannot be
 | 
			
		||||
// watched.
 | 
			
		||||
//
 | 
			
		||||
// A path will remain watched if it gets renamed to somewhere else on the same
 | 
			
		||||
// filesystem, but the monitor will get removed if the path gets deleted and
 | 
			
		||||
// re-created, or if it's moved to a different filesystem.
 | 
			
		||||
// A watch will be automatically removed if the watched path is deleted or
 | 
			
		||||
// renamed. The exception is the Windows backend, which doesn't remove the
 | 
			
		||||
// watcher on renames.
 | 
			
		||||
//
 | 
			
		||||
// Notifications on network filesystems (NFS, SMB, FUSE, etc.) or special
 | 
			
		||||
// filesystems (/proc, /sys, etc.) generally don't work.
 | 
			
		||||
//
 | 
			
		||||
// Returns [ErrClosed] if [Watcher.Close] was called.
 | 
			
		||||
//
 | 
			
		||||
// See [Watcher.AddWith] for a version that allows adding options.
 | 
			
		||||
//
 | 
			
		||||
// # Watching directories
 | 
			
		||||
//
 | 
			
		||||
// All files in a directory are monitored, including new files that are created
 | 
			
		||||
@@ -208,27 +252,41 @@ func (w *Watcher) Close() error {
 | 
			
		||||
// # Watching files
 | 
			
		||||
//
 | 
			
		||||
// Watching individual files (rather than directories) is generally not
 | 
			
		||||
// recommended as many tools update files atomically. Instead of "just" writing
 | 
			
		||||
// to the file a temporary file will be written to first, and if successful the
 | 
			
		||||
// temporary file is moved to to destination removing the original, or some
 | 
			
		||||
// variant thereof. The watcher on the original file is now lost, as it no
 | 
			
		||||
// longer exists.
 | 
			
		||||
// recommended as many programs (especially editors) update files atomically: it
 | 
			
		||||
// will write to a temporary file which is then moved to to destination,
 | 
			
		||||
// overwriting the original (or some variant thereof). The watcher on the
 | 
			
		||||
// original file is now lost, as that no longer exists.
 | 
			
		||||
//
 | 
			
		||||
// Instead, watch the parent directory and use Event.Name to filter out files
 | 
			
		||||
// you're not interested in. There is an example of this in [cmd/fsnotify/file.go].
 | 
			
		||||
func (w *Watcher) Add(name string) error {
 | 
			
		||||
	w.mu.Lock()
 | 
			
		||||
	if w.isClosed {
 | 
			
		||||
		w.mu.Unlock()
 | 
			
		||||
		return errors.New("watcher already closed")
 | 
			
		||||
// The upshot of this is that a power failure or crash won't leave a
 | 
			
		||||
// half-written file.
 | 
			
		||||
//
 | 
			
		||||
// Watch the parent directory and use Event.Name to filter out files you're not
 | 
			
		||||
// interested in. There is an example of this in cmd/fsnotify/file.go.
 | 
			
		||||
func (w *Watcher) Add(name string) error { return w.AddWith(name) }
 | 
			
		||||
 | 
			
		||||
// AddWith is like [Watcher.Add], but allows adding options. When using Add()
 | 
			
		||||
// the defaults described below are used.
 | 
			
		||||
//
 | 
			
		||||
// Possible options are:
 | 
			
		||||
//
 | 
			
		||||
//   - [WithBufferSize] sets the buffer size for the Windows backend; no-op on
 | 
			
		||||
//     other platforms. The default is 64K (65536 bytes).
 | 
			
		||||
func (w *Watcher) AddWith(name string, opts ...addOpt) error {
 | 
			
		||||
	if w.isClosed() {
 | 
			
		||||
		return ErrClosed
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	with := getOptions(opts...)
 | 
			
		||||
	if with.bufsize < 4096 {
 | 
			
		||||
		return fmt.Errorf("fsnotify.WithBufferSize: buffer size cannot be smaller than 4096 bytes")
 | 
			
		||||
	}
 | 
			
		||||
	w.mu.Unlock()
 | 
			
		||||
 | 
			
		||||
	in := &input{
 | 
			
		||||
		op:    opAddWatch,
 | 
			
		||||
		path:  filepath.Clean(name),
 | 
			
		||||
		flags: sysFSALLEVENTS,
 | 
			
		||||
		reply: make(chan error),
 | 
			
		||||
		op:      opAddWatch,
 | 
			
		||||
		path:    filepath.Clean(name),
 | 
			
		||||
		flags:   sysFSALLEVENTS,
 | 
			
		||||
		reply:   make(chan error),
 | 
			
		||||
		bufsize: with.bufsize,
 | 
			
		||||
	}
 | 
			
		||||
	w.input <- in
 | 
			
		||||
	if err := w.wakeupReader(); err != nil {
 | 
			
		||||
@@ -243,7 +301,13 @@ func (w *Watcher) Add(name string) error {
 | 
			
		||||
// /tmp/dir and /tmp/dir/subdir then you will need to remove both.
 | 
			
		||||
//
 | 
			
		||||
// Removing a path that has not yet been added returns [ErrNonExistentWatch].
 | 
			
		||||
//
 | 
			
		||||
// Returns nil if [Watcher.Close] was called.
 | 
			
		||||
func (w *Watcher) Remove(name string) error {
 | 
			
		||||
	if w.isClosed() {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	in := &input{
 | 
			
		||||
		op:    opRemoveWatch,
 | 
			
		||||
		path:  filepath.Clean(name),
 | 
			
		||||
@@ -256,8 +320,15 @@ func (w *Watcher) Remove(name string) error {
 | 
			
		||||
	return <-in.reply
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// WatchList returns all paths added with [Add] (and are not yet removed).
 | 
			
		||||
// WatchList returns all paths explicitly added with [Watcher.Add] (and are not
 | 
			
		||||
// yet removed).
 | 
			
		||||
//
 | 
			
		||||
// Returns nil if [Watcher.Close] was called.
 | 
			
		||||
func (w *Watcher) WatchList() []string {
 | 
			
		||||
	if w.isClosed() {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	w.mu.Lock()
 | 
			
		||||
	defer w.mu.Unlock()
 | 
			
		||||
 | 
			
		||||
@@ -279,7 +350,6 @@ func (w *Watcher) WatchList() []string {
 | 
			
		||||
// This should all be removed at some point, and just use windows.FILE_NOTIFY_*
 | 
			
		||||
const (
 | 
			
		||||
	sysFSALLEVENTS  = 0xfff
 | 
			
		||||
	sysFSATTRIB     = 0x4
 | 
			
		||||
	sysFSCREATE     = 0x100
 | 
			
		||||
	sysFSDELETE     = 0x200
 | 
			
		||||
	sysFSDELETESELF = 0x400
 | 
			
		||||
@@ -305,9 +375,6 @@ func (w *Watcher) newEvent(name string, mask uint32) Event {
 | 
			
		||||
	if mask&sysFSMOVE == sysFSMOVE || mask&sysFSMOVESELF == sysFSMOVESELF || mask&sysFSMOVEDFROM == sysFSMOVEDFROM {
 | 
			
		||||
		e.Op |= Rename
 | 
			
		||||
	}
 | 
			
		||||
	if mask&sysFSATTRIB == sysFSATTRIB {
 | 
			
		||||
		e.Op |= Chmod
 | 
			
		||||
	}
 | 
			
		||||
	return e
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -321,10 +388,11 @@ const (
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type input struct {
 | 
			
		||||
	op    int
 | 
			
		||||
	path  string
 | 
			
		||||
	flags uint32
 | 
			
		||||
	reply chan error
 | 
			
		||||
	op      int
 | 
			
		||||
	path    string
 | 
			
		||||
	flags   uint32
 | 
			
		||||
	bufsize int
 | 
			
		||||
	reply   chan error
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type inode struct {
 | 
			
		||||
@@ -334,13 +402,14 @@ type inode struct {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type watch struct {
 | 
			
		||||
	ov     windows.Overlapped
 | 
			
		||||
	ino    *inode            // i-number
 | 
			
		||||
	path   string            // Directory path
 | 
			
		||||
	mask   uint64            // Directory itself is being watched with these notify flags
 | 
			
		||||
	names  map[string]uint64 // Map of names being watched and their notify flags
 | 
			
		||||
	rename string            // Remembers the old name while renaming a file
 | 
			
		||||
	buf    [65536]byte       // 64K buffer
 | 
			
		||||
	ov      windows.Overlapped
 | 
			
		||||
	ino     *inode            // i-number
 | 
			
		||||
	recurse bool              // Recursive watch?
 | 
			
		||||
	path    string            // Directory path
 | 
			
		||||
	mask    uint64            // Directory itself is being watched with these notify flags
 | 
			
		||||
	names   map[string]uint64 // Map of names being watched and their notify flags
 | 
			
		||||
	rename  string            // Remembers the old name while renaming a file
 | 
			
		||||
	buf     []byte            // buffer, allocated later
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type (
 | 
			
		||||
@@ -413,7 +482,10 @@ func (m watchMap) set(ino *inode, watch *watch) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Must run within the I/O thread.
 | 
			
		||||
func (w *Watcher) addWatch(pathname string, flags uint64) error {
 | 
			
		||||
func (w *Watcher) addWatch(pathname string, flags uint64, bufsize int) error {
 | 
			
		||||
	//pathname, recurse := recursivePath(pathname)
 | 
			
		||||
	recurse := false
 | 
			
		||||
 | 
			
		||||
	dir, err := w.getDir(pathname)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
@@ -433,9 +505,11 @@ func (w *Watcher) addWatch(pathname string, flags uint64) error {
 | 
			
		||||
			return os.NewSyscallError("CreateIoCompletionPort", err)
 | 
			
		||||
		}
 | 
			
		||||
		watchEntry = &watch{
 | 
			
		||||
			ino:   ino,
 | 
			
		||||
			path:  dir,
 | 
			
		||||
			names: make(map[string]uint64),
 | 
			
		||||
			ino:     ino,
 | 
			
		||||
			path:    dir,
 | 
			
		||||
			names:   make(map[string]uint64),
 | 
			
		||||
			recurse: recurse,
 | 
			
		||||
			buf:     make([]byte, bufsize),
 | 
			
		||||
		}
 | 
			
		||||
		w.mu.Lock()
 | 
			
		||||
		w.watches.set(ino, watchEntry)
 | 
			
		||||
@@ -465,6 +539,8 @@ func (w *Watcher) addWatch(pathname string, flags uint64) error {
 | 
			
		||||
 | 
			
		||||
// Must run within the I/O thread.
 | 
			
		||||
func (w *Watcher) remWatch(pathname string) error {
 | 
			
		||||
	pathname, recurse := recursivePath(pathname)
 | 
			
		||||
 | 
			
		||||
	dir, err := w.getDir(pathname)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
@@ -478,6 +554,10 @@ func (w *Watcher) remWatch(pathname string) error {
 | 
			
		||||
	watch := w.watches.get(ino)
 | 
			
		||||
	w.mu.Unlock()
 | 
			
		||||
 | 
			
		||||
	if recurse && !watch.recurse {
 | 
			
		||||
		return fmt.Errorf("can't use \\... with non-recursive watch %q", pathname)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	err = windows.CloseHandle(ino.handle)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		w.sendError(os.NewSyscallError("CloseHandle", err))
 | 
			
		||||
@@ -535,8 +615,11 @@ func (w *Watcher) startRead(watch *watch) error {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	rdErr := windows.ReadDirectoryChanges(watch.ino.handle, &watch.buf[0],
 | 
			
		||||
		uint32(unsafe.Sizeof(watch.buf)), false, mask, nil, &watch.ov, 0)
 | 
			
		||||
	// We need to pass the array, rather than the slice.
 | 
			
		||||
	hdr := (*reflect.SliceHeader)(unsafe.Pointer(&watch.buf))
 | 
			
		||||
	rdErr := windows.ReadDirectoryChanges(watch.ino.handle,
 | 
			
		||||
		(*byte)(unsafe.Pointer(hdr.Data)), uint32(hdr.Len),
 | 
			
		||||
		watch.recurse, mask, nil, &watch.ov, 0)
 | 
			
		||||
	if rdErr != nil {
 | 
			
		||||
		err := os.NewSyscallError("ReadDirectoryChanges", rdErr)
 | 
			
		||||
		if rdErr == windows.ERROR_ACCESS_DENIED && watch.mask&provisional == 0 {
 | 
			
		||||
@@ -563,9 +646,8 @@ func (w *Watcher) readEvents() {
 | 
			
		||||
	runtime.LockOSThread()
 | 
			
		||||
 | 
			
		||||
	for {
 | 
			
		||||
		// This error is handled after the watch == nil check below.
 | 
			
		||||
		qErr := windows.GetQueuedCompletionStatus(w.port, &n, &key, &ov, windows.INFINITE)
 | 
			
		||||
		// This error is handled after the watch == nil check below. NOTE: this
 | 
			
		||||
		// seems odd, note sure if it's correct.
 | 
			
		||||
 | 
			
		||||
		watch := (*watch)(unsafe.Pointer(ov))
 | 
			
		||||
		if watch == nil {
 | 
			
		||||
@@ -595,7 +677,7 @@ func (w *Watcher) readEvents() {
 | 
			
		||||
			case in := <-w.input:
 | 
			
		||||
				switch in.op {
 | 
			
		||||
				case opAddWatch:
 | 
			
		||||
					in.reply <- w.addWatch(in.path, uint64(in.flags))
 | 
			
		||||
					in.reply <- w.addWatch(in.path, uint64(in.flags), in.bufsize)
 | 
			
		||||
				case opRemoveWatch:
 | 
			
		||||
					in.reply <- w.remWatch(in.path)
 | 
			
		||||
				}
 | 
			
		||||
@@ -605,6 +687,8 @@ func (w *Watcher) readEvents() {
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		switch qErr {
 | 
			
		||||
		case nil:
 | 
			
		||||
			// No error
 | 
			
		||||
		case windows.ERROR_MORE_DATA:
 | 
			
		||||
			if watch == nil {
 | 
			
		||||
				w.sendError(errors.New("ERROR_MORE_DATA has unexpectedly null lpOverlapped buffer"))
 | 
			
		||||
@@ -626,13 +710,12 @@ func (w *Watcher) readEvents() {
 | 
			
		||||
		default:
 | 
			
		||||
			w.sendError(os.NewSyscallError("GetQueuedCompletionPort", qErr))
 | 
			
		||||
			continue
 | 
			
		||||
		case nil:
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		var offset uint32
 | 
			
		||||
		for {
 | 
			
		||||
			if n == 0 {
 | 
			
		||||
				w.sendError(errors.New("short read in readEvents()"))
 | 
			
		||||
				w.sendError(ErrEventOverflow)
 | 
			
		||||
				break
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
@@ -703,8 +786,9 @@ func (w *Watcher) readEvents() {
 | 
			
		||||
 | 
			
		||||
			// Error!
 | 
			
		||||
			if offset >= n {
 | 
			
		||||
				//lint:ignore ST1005 Windows should be capitalized
 | 
			
		||||
				w.sendError(errors.New(
 | 
			
		||||
					"Windows system assumed buffer larger than it is, events have likely been missed."))
 | 
			
		||||
					"Windows system assumed buffer larger than it is, events have likely been missed"))
 | 
			
		||||
				break
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
@@ -720,9 +804,6 @@ func (w *Watcher) toWindowsFlags(mask uint64) uint32 {
 | 
			
		||||
	if mask&sysFSMODIFY != 0 {
 | 
			
		||||
		m |= windows.FILE_NOTIFY_CHANGE_LAST_WRITE
 | 
			
		||||
	}
 | 
			
		||||
	if mask&sysFSATTRIB != 0 {
 | 
			
		||||
		m |= windows.FILE_NOTIFY_CHANGE_ATTRIBUTES
 | 
			
		||||
	}
 | 
			
		||||
	if mask&(sysFSMOVE|sysFSCREATE|sysFSDELETE) != 0 {
 | 
			
		||||
		m |= windows.FILE_NOTIFY_CHANGE_FILE_NAME | windows.FILE_NOTIFY_CHANGE_DIR_NAME
 | 
			
		||||
	}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										91
									
								
								vendor/github.com/fsnotify/fsnotify/fsnotify.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										91
									
								
								vendor/github.com/fsnotify/fsnotify/fsnotify.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -1,13 +1,18 @@
 | 
			
		||||
//go:build !plan9
 | 
			
		||||
// +build !plan9
 | 
			
		||||
 | 
			
		||||
// Package fsnotify provides a cross-platform interface for file system
 | 
			
		||||
// notifications.
 | 
			
		||||
//
 | 
			
		||||
// Currently supported systems:
 | 
			
		||||
//
 | 
			
		||||
//	Linux 2.6.32+    via inotify
 | 
			
		||||
//	BSD, macOS       via kqueue
 | 
			
		||||
//	Windows          via ReadDirectoryChangesW
 | 
			
		||||
//	illumos          via FEN
 | 
			
		||||
package fsnotify
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"errors"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"path/filepath"
 | 
			
		||||
	"strings"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
@@ -33,34 +38,52 @@ type Op uint32
 | 
			
		||||
// The operations fsnotify can trigger; see the documentation on [Watcher] for a
 | 
			
		||||
// full description, and check them with [Event.Has].
 | 
			
		||||
const (
 | 
			
		||||
	// A new pathname was created.
 | 
			
		||||
	Create Op = 1 << iota
 | 
			
		||||
 | 
			
		||||
	// The pathname was written to; this does *not* mean the write has finished,
 | 
			
		||||
	// and a write can be followed by more writes.
 | 
			
		||||
	Write
 | 
			
		||||
 | 
			
		||||
	// The path was removed; any watches on it will be removed. Some "remove"
 | 
			
		||||
	// operations may trigger a Rename if the file is actually moved (for
 | 
			
		||||
	// example "remove to trash" is often a rename).
 | 
			
		||||
	Remove
 | 
			
		||||
 | 
			
		||||
	// The path was renamed to something else; any watched on it will be
 | 
			
		||||
	// removed.
 | 
			
		||||
	Rename
 | 
			
		||||
 | 
			
		||||
	// File attributes were changed.
 | 
			
		||||
	//
 | 
			
		||||
	// It's generally not recommended to take action on this event, as it may
 | 
			
		||||
	// get triggered very frequently by some software. For example, Spotlight
 | 
			
		||||
	// indexing on macOS, anti-virus software, backup software, etc.
 | 
			
		||||
	Chmod
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Common errors that can be reported by a watcher
 | 
			
		||||
// Common errors that can be reported.
 | 
			
		||||
var (
 | 
			
		||||
	ErrNonExistentWatch = errors.New("can't remove non-existent watcher")
 | 
			
		||||
	ErrEventOverflow    = errors.New("fsnotify queue overflow")
 | 
			
		||||
	ErrNonExistentWatch = errors.New("fsnotify: can't remove non-existent watch")
 | 
			
		||||
	ErrEventOverflow    = errors.New("fsnotify: queue or buffer overflow")
 | 
			
		||||
	ErrClosed           = errors.New("fsnotify: watcher already closed")
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func (op Op) String() string {
 | 
			
		||||
func (o Op) String() string {
 | 
			
		||||
	var b strings.Builder
 | 
			
		||||
	if op.Has(Create) {
 | 
			
		||||
	if o.Has(Create) {
 | 
			
		||||
		b.WriteString("|CREATE")
 | 
			
		||||
	}
 | 
			
		||||
	if op.Has(Remove) {
 | 
			
		||||
	if o.Has(Remove) {
 | 
			
		||||
		b.WriteString("|REMOVE")
 | 
			
		||||
	}
 | 
			
		||||
	if op.Has(Write) {
 | 
			
		||||
	if o.Has(Write) {
 | 
			
		||||
		b.WriteString("|WRITE")
 | 
			
		||||
	}
 | 
			
		||||
	if op.Has(Rename) {
 | 
			
		||||
	if o.Has(Rename) {
 | 
			
		||||
		b.WriteString("|RENAME")
 | 
			
		||||
	}
 | 
			
		||||
	if op.Has(Chmod) {
 | 
			
		||||
	if o.Has(Chmod) {
 | 
			
		||||
		b.WriteString("|CHMOD")
 | 
			
		||||
	}
 | 
			
		||||
	if b.Len() == 0 {
 | 
			
		||||
@@ -70,7 +93,7 @@ func (op Op) String() string {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Has reports if this operation has the given operation.
 | 
			
		||||
func (o Op) Has(h Op) bool { return o&h == h }
 | 
			
		||||
func (o Op) Has(h Op) bool { return o&h != 0 }
 | 
			
		||||
 | 
			
		||||
// Has reports if this event has the given operation.
 | 
			
		||||
func (e Event) Has(op Op) bool { return e.Op.Has(op) }
 | 
			
		||||
@@ -79,3 +102,45 @@ func (e Event) Has(op Op) bool { return e.Op.Has(op) }
 | 
			
		||||
func (e Event) String() string {
 | 
			
		||||
	return fmt.Sprintf("%-13s %q", e.Op.String(), e.Name)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type (
 | 
			
		||||
	addOpt   func(opt *withOpts)
 | 
			
		||||
	withOpts struct {
 | 
			
		||||
		bufsize int
 | 
			
		||||
	}
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var defaultOpts = withOpts{
 | 
			
		||||
	bufsize: 65536, // 64K
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func getOptions(opts ...addOpt) withOpts {
 | 
			
		||||
	with := defaultOpts
 | 
			
		||||
	for _, o := range opts {
 | 
			
		||||
		o(&with)
 | 
			
		||||
	}
 | 
			
		||||
	return with
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// WithBufferSize sets the [ReadDirectoryChangesW] buffer size.
 | 
			
		||||
//
 | 
			
		||||
// This only has effect on Windows systems, and is a no-op for other backends.
 | 
			
		||||
//
 | 
			
		||||
// The default value is 64K (65536 bytes) which is the highest value that works
 | 
			
		||||
// on all filesystems and should be enough for most applications, but if you
 | 
			
		||||
// have a large burst of events it may not be enough. You can increase it if
 | 
			
		||||
// you're hitting "queue or buffer overflow" errors ([ErrEventOverflow]).
 | 
			
		||||
//
 | 
			
		||||
// [ReadDirectoryChangesW]: https://learn.microsoft.com/en-gb/windows/win32/api/winbase/nf-winbase-readdirectorychangesw
 | 
			
		||||
func WithBufferSize(bytes int) addOpt {
 | 
			
		||||
	return func(opt *withOpts) { opt.bufsize = bytes }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Check if this path is recursive (ends with "/..." or "\..."), and return the
 | 
			
		||||
// path with the /... stripped.
 | 
			
		||||
func recursivePath(path string) (string, bool) {
 | 
			
		||||
	if filepath.Base(path) == "..." {
 | 
			
		||||
		return filepath.Dir(path), true
 | 
			
		||||
	}
 | 
			
		||||
	return path, false
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										123
									
								
								vendor/github.com/fsnotify/fsnotify/mkdoc.zsh
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										123
									
								
								vendor/github.com/fsnotify/fsnotify/mkdoc.zsh
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -2,8 +2,8 @@
 | 
			
		||||
[ "${ZSH_VERSION:-}" = "" ] && echo >&2 "Only works with zsh" && exit 1
 | 
			
		||||
setopt err_exit no_unset pipefail extended_glob
 | 
			
		||||
 | 
			
		||||
# Simple script to update the godoc comments on all watchers. Probably took me
 | 
			
		||||
# more time to write this than doing it manually, but ah well 🙃
 | 
			
		||||
# Simple script to update the godoc comments on all watchers so you don't need
 | 
			
		||||
# to update the same comment 5 times.
 | 
			
		||||
 | 
			
		||||
watcher=$(<<EOF
 | 
			
		||||
// Watcher watches a set of paths, delivering events on a channel.
 | 
			
		||||
@@ -16,9 +16,9 @@ watcher=$(<<EOF
 | 
			
		||||
// When a file is removed a Remove event won't be emitted until all file
 | 
			
		||||
// descriptors are closed, and deletes will always emit a Chmod. For example:
 | 
			
		||||
//
 | 
			
		||||
//     fp := os.Open("file")
 | 
			
		||||
//     os.Remove("file")        // Triggers Chmod
 | 
			
		||||
//     fp.Close()               // Triggers Remove
 | 
			
		||||
//	fp := os.Open("file")
 | 
			
		||||
//	os.Remove("file")        // Triggers Chmod
 | 
			
		||||
//	fp.Close()               // Triggers Remove
 | 
			
		||||
//
 | 
			
		||||
// This is the event that inotify sends, so not much can be changed about this.
 | 
			
		||||
//
 | 
			
		||||
@@ -32,16 +32,16 @@ watcher=$(<<EOF
 | 
			
		||||
//
 | 
			
		||||
// To increase them you can use sysctl or write the value to the /proc file:
 | 
			
		||||
//
 | 
			
		||||
//     # Default values on Linux 5.18
 | 
			
		||||
//     sysctl fs.inotify.max_user_watches=124983
 | 
			
		||||
//     sysctl fs.inotify.max_user_instances=128
 | 
			
		||||
//	# Default values on Linux 5.18
 | 
			
		||||
//	sysctl fs.inotify.max_user_watches=124983
 | 
			
		||||
//	sysctl fs.inotify.max_user_instances=128
 | 
			
		||||
//
 | 
			
		||||
// To make the changes persist on reboot edit /etc/sysctl.conf or
 | 
			
		||||
// /usr/lib/sysctl.d/50-default.conf (details differ per Linux distro; check
 | 
			
		||||
// your distro's documentation):
 | 
			
		||||
//
 | 
			
		||||
//     fs.inotify.max_user_watches=124983
 | 
			
		||||
//     fs.inotify.max_user_instances=128
 | 
			
		||||
//	fs.inotify.max_user_watches=124983
 | 
			
		||||
//	fs.inotify.max_user_instances=128
 | 
			
		||||
//
 | 
			
		||||
// Reaching the limit will result in a "no space left on device" or "too many open
 | 
			
		||||
// files" error.
 | 
			
		||||
@@ -57,14 +57,20 @@ watcher=$(<<EOF
 | 
			
		||||
// control the maximum number of open files, as well as /etc/login.conf on BSD
 | 
			
		||||
// systems.
 | 
			
		||||
//
 | 
			
		||||
// # macOS notes
 | 
			
		||||
// # Windows notes
 | 
			
		||||
//
 | 
			
		||||
// Spotlight indexing on macOS can result in multiple events (see [#15]). A
 | 
			
		||||
// temporary workaround is to add your folder(s) to the "Spotlight Privacy
 | 
			
		||||
// Settings" until we have a native FSEvents implementation (see [#11]).
 | 
			
		||||
// Paths can be added as "C:\\path\\to\\dir", but forward slashes
 | 
			
		||||
// ("C:/path/to/dir") will also work.
 | 
			
		||||
//
 | 
			
		||||
// [#11]: https://github.com/fsnotify/fsnotify/issues/11
 | 
			
		||||
// [#15]: https://github.com/fsnotify/fsnotify/issues/15
 | 
			
		||||
// When a watched directory is removed it will always send an event for the
 | 
			
		||||
// directory itself, but may not send events for all files in that directory.
 | 
			
		||||
// Sometimes it will send events for all times, sometimes it will send no
 | 
			
		||||
// events, and often only for some files.
 | 
			
		||||
//
 | 
			
		||||
// The default ReadDirectoryChangesW() buffer size is 64K, which is the largest
 | 
			
		||||
// value that is guaranteed to work with SMB filesystems. If you have many
 | 
			
		||||
// events in quick succession this may not be enough, and you will have to use
 | 
			
		||||
// [WithBufferSize] to increase the value.
 | 
			
		||||
EOF
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
@@ -73,20 +79,36 @@ new=$(<<EOF
 | 
			
		||||
EOF
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
newbuffered=$(<<EOF
 | 
			
		||||
// NewBufferedWatcher creates a new Watcher with a buffered Watcher.Events
 | 
			
		||||
// channel.
 | 
			
		||||
//
 | 
			
		||||
// The main use case for this is situations with a very large number of events
 | 
			
		||||
// where the kernel buffer size can't be increased (e.g. due to lack of
 | 
			
		||||
// permissions). An unbuffered Watcher will perform better for almost all use
 | 
			
		||||
// cases, and whenever possible you will be better off increasing the kernel
 | 
			
		||||
// buffers instead of adding a large userspace buffer.
 | 
			
		||||
EOF
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
add=$(<<EOF
 | 
			
		||||
// Add starts monitoring the path for changes.
 | 
			
		||||
//
 | 
			
		||||
// A path can only be watched once; attempting to watch it more than once will
 | 
			
		||||
// return an error. Paths that do not yet exist on the filesystem cannot be
 | 
			
		||||
// added. A watch will be automatically removed if the path is deleted.
 | 
			
		||||
// A path can only be watched once; watching it more than once is a no-op and will
 | 
			
		||||
// not return an error. Paths that do not yet exist on the filesystem cannot be
 | 
			
		||||
// watched.
 | 
			
		||||
//
 | 
			
		||||
// A path will remain watched if it gets renamed to somewhere else on the same
 | 
			
		||||
// filesystem, but the monitor will get removed if the path gets deleted and
 | 
			
		||||
// re-created, or if it's moved to a different filesystem.
 | 
			
		||||
// A watch will be automatically removed if the watched path is deleted or
 | 
			
		||||
// renamed. The exception is the Windows backend, which doesn't remove the
 | 
			
		||||
// watcher on renames.
 | 
			
		||||
//
 | 
			
		||||
// Notifications on network filesystems (NFS, SMB, FUSE, etc.) or special
 | 
			
		||||
// filesystems (/proc, /sys, etc.) generally don't work.
 | 
			
		||||
//
 | 
			
		||||
// Returns [ErrClosed] if [Watcher.Close] was called.
 | 
			
		||||
//
 | 
			
		||||
// See [Watcher.AddWith] for a version that allows adding options.
 | 
			
		||||
//
 | 
			
		||||
// # Watching directories
 | 
			
		||||
//
 | 
			
		||||
// All files in a directory are monitored, including new files that are created
 | 
			
		||||
@@ -96,14 +118,27 @@ add=$(<<EOF
 | 
			
		||||
// # Watching files
 | 
			
		||||
//
 | 
			
		||||
// Watching individual files (rather than directories) is generally not
 | 
			
		||||
// recommended as many tools update files atomically. Instead of "just" writing
 | 
			
		||||
// to the file a temporary file will be written to first, and if successful the
 | 
			
		||||
// temporary file is moved to to destination removing the original, or some
 | 
			
		||||
// variant thereof. The watcher on the original file is now lost, as it no
 | 
			
		||||
// longer exists.
 | 
			
		||||
// recommended as many programs (especially editors) update files atomically: it
 | 
			
		||||
// will write to a temporary file which is then moved to to destination,
 | 
			
		||||
// overwriting the original (or some variant thereof). The watcher on the
 | 
			
		||||
// original file is now lost, as that no longer exists.
 | 
			
		||||
//
 | 
			
		||||
// Instead, watch the parent directory and use Event.Name to filter out files
 | 
			
		||||
// you're not interested in. There is an example of this in [cmd/fsnotify/file.go].
 | 
			
		||||
// The upshot of this is that a power failure or crash won't leave a
 | 
			
		||||
// half-written file.
 | 
			
		||||
//
 | 
			
		||||
// Watch the parent directory and use Event.Name to filter out files you're not
 | 
			
		||||
// interested in. There is an example of this in cmd/fsnotify/file.go.
 | 
			
		||||
EOF
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
addwith=$(<<EOF
 | 
			
		||||
// AddWith is like [Watcher.Add], but allows adding options. When using Add()
 | 
			
		||||
// the defaults described below are used.
 | 
			
		||||
//
 | 
			
		||||
// Possible options are:
 | 
			
		||||
//
 | 
			
		||||
//   - [WithBufferSize] sets the buffer size for the Windows backend; no-op on
 | 
			
		||||
//     other platforms. The default is 64K (65536 bytes).
 | 
			
		||||
EOF
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
@@ -114,16 +149,21 @@ remove=$(<<EOF
 | 
			
		||||
// /tmp/dir and /tmp/dir/subdir then you will need to remove both.
 | 
			
		||||
//
 | 
			
		||||
// Removing a path that has not yet been added returns [ErrNonExistentWatch].
 | 
			
		||||
//
 | 
			
		||||
// Returns nil if [Watcher.Close] was called.
 | 
			
		||||
EOF
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
close=$(<<EOF
 | 
			
		||||
// Close removes all watches and closes the events channel.
 | 
			
		||||
// Close removes all watches and closes the Events channel.
 | 
			
		||||
EOF
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
watchlist=$(<<EOF
 | 
			
		||||
// WatchList returns all paths added with [Add] (and are not yet removed).
 | 
			
		||||
// WatchList returns all paths explicitly added with [Watcher.Add] (and are not
 | 
			
		||||
// yet removed).
 | 
			
		||||
//
 | 
			
		||||
// Returns nil if [Watcher.Close] was called.
 | 
			
		||||
EOF
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
@@ -153,20 +193,29 @@ events=$(<<EOF
 | 
			
		||||
	//                      initiated by the user may show up as one or multiple
 | 
			
		||||
	//                      writes, depending on when the system syncs things to
 | 
			
		||||
	//                      disk. For example when compiling a large Go program
 | 
			
		||||
	//                      you may get hundreds of Write events, so you
 | 
			
		||||
	//                      probably want to wait until you've stopped receiving
 | 
			
		||||
	//                      them (see the dedup example in cmd/fsnotify).
 | 
			
		||||
	//                      you may get hundreds of Write events, and you may
 | 
			
		||||
	//                      want to wait until you've stopped receiving them
 | 
			
		||||
	//                      (see the dedup example in cmd/fsnotify).
 | 
			
		||||
	//
 | 
			
		||||
	//                      Some systems may send Write event for directories
 | 
			
		||||
	//                      when the directory content changes.
 | 
			
		||||
	//
 | 
			
		||||
	//   fsnotify.Chmod     Attributes were changed. On Linux this is also sent
 | 
			
		||||
	//                      when a file is removed (or more accurately, when a
 | 
			
		||||
	//                      link to an inode is removed). On kqueue it's sent
 | 
			
		||||
	//                      and on kqueue when a file is truncated. On Windows
 | 
			
		||||
	//                      it's never sent.
 | 
			
		||||
	//                      when a file is truncated. On Windows it's never
 | 
			
		||||
	//                      sent.
 | 
			
		||||
EOF
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
errors=$(<<EOF
 | 
			
		||||
	// Errors sends any errors.
 | 
			
		||||
	//
 | 
			
		||||
	// ErrEventOverflow is used to indicate there are too many events:
 | 
			
		||||
	//
 | 
			
		||||
	//  - inotify:      There are too many queued events (fs.inotify.max_queued_events sysctl)
 | 
			
		||||
	//  - windows:      The buffer size is too small; WithBufferSize() can be used to increase it.
 | 
			
		||||
	//  - kqueue, fen:  Not used.
 | 
			
		||||
EOF
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
@@ -200,7 +249,9 @@ set-cmt() {
 | 
			
		||||
 | 
			
		||||
set-cmt '^type Watcher struct '             $watcher
 | 
			
		||||
set-cmt '^func NewWatcher('                 $new
 | 
			
		||||
set-cmt '^func NewBufferedWatcher('         $newbuffered
 | 
			
		||||
set-cmt '^func (w \*Watcher) Add('          $add
 | 
			
		||||
set-cmt '^func (w \*Watcher) AddWith('      $addwith
 | 
			
		||||
set-cmt '^func (w \*Watcher) Remove('       $remove
 | 
			
		||||
set-cmt '^func (w \*Watcher) Close('        $close
 | 
			
		||||
set-cmt '^func (w \*Watcher) WatchList('    $watchlist
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user