diff options
Diffstat (limited to 'vendor/github.com')
25 files changed, 2689 insertions, 2015 deletions
diff --git a/vendor/github.com/fsnotify/fsnotify/.gitignore b/vendor/github.com/fsnotify/fsnotify/.gitignore index 4cd0cbaf4..1d89d85ce 100644 --- a/vendor/github.com/fsnotify/fsnotify/.gitignore +++ b/vendor/github.com/fsnotify/fsnotify/.gitignore @@ -1,6 +1,6 @@ -# Setup a Global .gitignore for OS and editor generated files: -# https://help.github.com/articles/ignoring-files -# git config --global core.excludesfile ~/.gitignore_global +# go test -c output +*.test +*.test.exe -.vagrant -*.sublime-project +# Output of go build ./cmd/fsnotify +/fsnotify diff --git a/vendor/github.com/fsnotify/fsnotify/AUTHORS b/vendor/github.com/fsnotify/fsnotify/AUTHORS deleted file mode 100644 index 6cbabe5ef..000000000 --- a/vendor/github.com/fsnotify/fsnotify/AUTHORS +++ /dev/null @@ -1,62 +0,0 @@ -# Names should be added to this file as -#	Name or Organization <email address> -# The email address is not required for organizations. - -# You can update this list using the following command: -# -#   $ (head -n10 AUTHORS && git shortlog -se | sed -E 's/^\s+[0-9]+\t//') | tee AUTHORS - -# Please keep the list sorted. - -Aaron L <aaron@bettercoder.net> -Adrien Bustany <adrien@bustany.org> -Alexey Kazakov <alkazako@redhat.com> -Amit Krishnan <amit.krishnan@oracle.com> -Anmol Sethi <me@anmol.io> -Bjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com> -Brian Goff <cpuguy83@gmail.com> -Bruno Bigras <bigras.bruno@gmail.com> -Caleb Spare <cespare@gmail.com> -Case Nelson <case@teammating.com> -Chris Howey <howeyc@gmail.com> -Christoffer Buchholz <christoffer.buchholz@gmail.com> -Daniel Wagner-Hall <dawagner@gmail.com> -Dave Cheney <dave@cheney.net> -Eric Lin <linxiulei@gmail.com> -Evan Phoenix <evan@fallingsnow.net> -Francisco Souza <f@souza.cc> -Gautam Dey <gautam.dey77@gmail.com> -Hari haran <hariharan.uno@gmail.com> -Ichinose Shogo <shogo82148@gmail.com> -Johannes Ebke <johannes@ebke.org> -John C Barstow <jbowtie@amathaine.com> -Kelvin Fo <vmirage@gmail.com> -Ken-ichirou MATSUZAWA <chamas@h4.dion.ne.jp> -Matt Layher <mdlayher@gmail.com> -Matthias Stone <matthias@bellstone.ca> -Nathan Youngman <git@nathany.com> -Nickolai Zeldovich <nickolai@csail.mit.edu> -Oliver Bristow <evilumbrella+github@gmail.com> -Patrick <patrick@dropbox.com> -Paul Hammond <paul@paulhammond.org> -Pawel Knap <pawelknap88@gmail.com> -Pieter Droogendijk <pieter@binky.org.uk> -Pratik Shinde <pratikshinde320@gmail.com> -Pursuit92 <JoshChase@techpursuit.net> -Riku Voipio <riku.voipio@linaro.org> -Rob Figueiredo <robfig@gmail.com> -Rodrigo Chiossi <rodrigochiossi@gmail.com> -Slawek Ligus <root@ooz.ie> -Soge Zhang <zhssoge@gmail.com> -Tiffany Jernigan <tiffany.jernigan@intel.com> -Tilak Sharma <tilaks@google.com> -Tobias Klauser <tobias.klauser@gmail.com> -Tom Payne <twpayne@gmail.com> -Travis Cline <travis.cline@gmail.com> -Tudor Golubenco <tudor.g@gmail.com> -Vahe Khachikyan <vahe@live.ca> -Yukang <moorekang@gmail.com> -bronze1man <bronze1man@gmail.com> -debrando <denis.brandolini@gmail.com> -henrikedwards <henrik.edwards@gmail.com> -铁哥 <guotie.9@gmail.com> diff --git a/vendor/github.com/fsnotify/fsnotify/CHANGELOG.md b/vendor/github.com/fsnotify/fsnotify/CHANGELOG.md index cc01c08f5..77f9593bd 100644 --- a/vendor/github.com/fsnotify/fsnotify/CHANGELOG.md +++ b/vendor/github.com/fsnotify/fsnotify/CHANGELOG.md @@ -7,6 +7,95 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0  ## [Unreleased] +Nothing yet. + +## [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. + +### Additions + +- all: add `Event.Has()` and `Op.Has()` ([#477]) + +  This makes checking events a lot easier; for example: + +	    if event.Op&Write == Write && !(event.Op&Remove == Remove) { +	    } + +	Becomes: + +	    if event.Has(Write) && !event.Has(Remove) { +	    } + +- all: add cmd/fsnotify ([#463]) + +  A command-line utility for testing and some examples. + +### Changes and fixes + +- inotify: don't ignore events for files that don't exist ([#260], [#470]) + +  Previously the inotify watcher would call `os.Lstat()` to check if a file +  still exists before emitting events. + +  This was inconsistent with other platforms and resulted in inconsistent event +  reporting (e.g. when a file is quickly removed and re-created), and generally +  a source of confusion. It was added in 2013 to fix a memory leak that no +  longer exists. + +- all: return `ErrNonExistentWatch` when `Remove()` is called on a path that's +  not watched ([#460]) + +- inotify: replace epoll() with non-blocking inotify ([#434]) + +  Non-blocking inotify was not generally available at the time this library was +  written in 2014, but now it is. As a result, the minimum Linux version is +  bumped from 2.6.27 to 2.6.32. This hugely simplifies the code and is faster. + +- kqueue: don't check for events every 100ms ([#480]) + +  The watcher would wake up every 100ms, even when there was nothing to do. Now +  it waits until there is something to do. + +- macos: retry opening files on EINTR ([#475]) + +- kqueue: skip unreadable files ([#479]) + +  kqueue requires a file descriptor for every file in a directory; this would +  fail if a file was unreadable by the current user. Now these files are simply +  skipped. + +- windows: fix renaming a watched directory if the parent is also watched ([#370]) + +- windows: increase buffer size from 4K to 64K ([#485]) + +- windows: close file handle on Remove() ([#288]) + +- kqueue: put pathname in the error if watching a file fails ([#471]) + +- inotify, windows: calling Close() more than once could race ([#465]) + +- kqueue: improve Close() performance ([#233]) + +- all: various documentation additions and clarifications. + +[#233]: https://github.com/fsnotify/fsnotify/pull/233 +[#260]: https://github.com/fsnotify/fsnotify/pull/260 +[#288]: https://github.com/fsnotify/fsnotify/pull/288 +[#370]: https://github.com/fsnotify/fsnotify/pull/370 +[#434]: https://github.com/fsnotify/fsnotify/pull/434 +[#460]: https://github.com/fsnotify/fsnotify/pull/460 +[#463]: https://github.com/fsnotify/fsnotify/pull/463 +[#465]: https://github.com/fsnotify/fsnotify/pull/465 +[#470]: https://github.com/fsnotify/fsnotify/pull/470 +[#471]: https://github.com/fsnotify/fsnotify/pull/471 +[#475]: https://github.com/fsnotify/fsnotify/pull/475 +[#477]: https://github.com/fsnotify/fsnotify/pull/477 +[#479]: https://github.com/fsnotify/fsnotify/pull/479 +[#480]: https://github.com/fsnotify/fsnotify/pull/480 +[#485]: https://github.com/fsnotify/fsnotify/pull/485 +  ## [1.5.4] - 2022-04-25  * Windows: add missing defer to `Watcher.WatchList` [#447](https://github.com/fsnotify/fsnotify/pull/447) @@ -40,6 +129,30 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0     [#385](https://github.com/fsnotify/fsnotify/pull/385)  * Go 1.14+: Fix unsafe pointer conversion [#325](https://github.com/fsnotify/fsnotify/pull/325) +## [1.4.9] - 2020-03-11 + +* Move example usage to the readme #329. This may resolve #328. + +## [1.4.8] - 2020-03-10 + +* CI: test more go versions (@nathany 1d13583d846ea9d66dcabbfefbfb9d8e6fb05216) +* Tests: Queued inotify events could have been read by the test before max_queued_events was hit (@matthias-stone #265) +* Tests:  t.Fatalf -> t.Errorf in go routines (@gdey #266) +* CI: Less verbosity (@nathany #267) +* Tests: Darwin: Exchangedata is deprecated on 10.13 (@nathany #267) +* Tests: Check if channels are closed in the example (@alexeykazakov #244) +* CI: Only run golint on latest version of go and fix issues (@cpuguy83 #284) +* CI: Add windows to travis matrix (@cpuguy83 #284) +* Docs: Remover appveyor badge (@nathany 11844c0959f6fff69ba325d097fce35bd85a8e93) +* Linux: create epoll and pipe fds with close-on-exec (@JohannesEbke #219) +* Linux: open files with close-on-exec (@linxiulei #273) +* Docs: Plan to support fanotify (@nathany ab058b44498e8b7566a799372a39d150d9ea0119 ) +* Project: Add go.mod (@nathany #309) +* Project: Revise editor config (@nathany #309) +* Project: Update copyright for 2019 (@nathany #309) +* CI: Drop go1.8 from CI matrix (@nathany #309) +* Docs: Updating the FAQ section for supportability with NFS & FUSE filesystems (@Pratik32 4bf2d1fec78374803a39307bfb8d340688f4f28e ) +  ## [1.4.7] - 2018-01-09  * BSD/macOS: Fix possible deadlock on closing the watcher on kqueue (thanks @nhooyr and @glycerine) diff --git a/vendor/github.com/fsnotify/fsnotify/CONTRIBUTING.md b/vendor/github.com/fsnotify/fsnotify/CONTRIBUTING.md index 8a642563d..ea379759d 100644 --- a/vendor/github.com/fsnotify/fsnotify/CONTRIBUTING.md +++ b/vendor/github.com/fsnotify/fsnotify/CONTRIBUTING.md @@ -1,60 +1,26 @@ -# Contributing +Thank you for your interest in contributing to fsnotify! We try to review and +merge PRs in a reasonable timeframe, but please be aware that: -## Issues +- To avoid "wasted" work, please discus changes on the issue tracker first. You +  can just send PRs, but they may end up being rejected for one reason or the +  other. -* Request features and report bugs using the [GitHub Issue Tracker](https://github.com/fsnotify/fsnotify/issues). -* Please indicate the platform you are using fsnotify on. -* A code example to reproduce the problem is appreciated. +- fsnotify is a cross-platform library, and changes must work reasonably well on +  all supported platforms. -## Pull Requests +- Changes will need to be compatible; old code should still compile, and the +  runtime behaviour can't change in ways that are likely to lead to problems for +  users. -### Contributor License Agreement +Testing +------- +Just `go test ./...` runs all the tests; the CI runs this on all supported +platforms. Testing different platforms locally can be done with something like +[goon] or [Vagrant], but this isn't super-easy to set up at the moment. -fsnotify is derived from code in the [golang.org/x/exp](https://godoc.org/golang.org/x/exp) package and it may be included [in the standard library](https://github.com/fsnotify/fsnotify/issues/1) in the future. Therefore fsnotify carries the same [LICENSE](https://github.com/fsnotify/fsnotify/blob/master/LICENSE) as Go. Contributors retain their copyright, so you need to fill out a short form before we can accept your contribution: [Google Individual Contributor License Agreement](https://developers.google.com/open-source/cla/individual). +Use the `-short` flag to make the "stress test" run faster. -Please indicate that you have signed the CLA in your pull request. -### How fsnotify is Developed - -* Development is done on feature branches. -* Tests are run on BSD, Linux, macOS and Windows. -* Pull requests are reviewed and [applied to master][am] using [hub][]. -  * Maintainers may modify or squash commits rather than asking contributors to. -* To issue a new release, the maintainers will: -  * Update the CHANGELOG -  * Tag a version, which will become available through gopkg.in. -  -### How to Fork - -For smooth sailing, always use the original import path. Installing with `go get` makes this easy.  - -1. Install from GitHub (`go get -u github.com/fsnotify/fsnotify`) -2. Create your feature branch (`git checkout -b my-new-feature`) -3. Ensure everything works and the tests pass (see below) -4. Commit your changes (`git commit -am 'Add some feature'`) - -Contribute upstream: - -1. Fork fsnotify on GitHub -2. Add your remote (`git remote add fork git@github.com:mycompany/repo.git`) -3. Push to the branch (`git push fork my-new-feature`) -4. Create a new Pull Request on GitHub - -This workflow is [thoroughly explained by Katrina Owen](https://splice.com/blog/contributing-open-source-git-repositories-go/). - -### Testing - -fsnotify uses build tags to compile different code on Linux, BSD, macOS, and Windows. - -Before doing a pull request, please do your best to test your changes on multiple platforms, and list which platforms you were able/unable to test on. - -### Maintainers - -Help maintaining fsnotify is welcome. To be a maintainer: - -* Submit a pull request and sign the CLA as above. -* You must be able to run the test suite on Mac, Windows, Linux and BSD. - -All code changes should be internal pull requests. - -Releases are tagged using [Semantic Versioning](http://semver.org/). +[goon]: https://github.com/arp242/goon +[Vagrant]: https://www.vagrantup.com/ +[integration_test.go]: /integration_test.go diff --git a/vendor/github.com/fsnotify/fsnotify/LICENSE b/vendor/github.com/fsnotify/fsnotify/LICENSE index e180c8fb0..fb03ade75 100644 --- a/vendor/github.com/fsnotify/fsnotify/LICENSE +++ b/vendor/github.com/fsnotify/fsnotify/LICENSE @@ -1,28 +1,25 @@ -Copyright (c) 2012 The Go Authors. All rights reserved. -Copyright (c) 2012-2019 fsnotify Authors. All rights reserved. +Copyright © 2012 The Go Authors. All rights reserved. +Copyright © fsnotify Authors. All rights reserved. -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: -   * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. -   * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. -   * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. +* Redistributions of source code must retain the above copyright notice, this +  list of conditions and the following disclaimer. +* Redistributions in binary form must reproduce the above copyright notice, this +  list of conditions and the following disclaimer in the documentation and/or +  other materials provided with the distribution. +* Neither the name of Google Inc. nor the names of its contributors may be used +  to endorse or promote products derived from this software without specific +  prior written permission. -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/vendor/github.com/fsnotify/fsnotify/README.md b/vendor/github.com/fsnotify/fsnotify/README.md index 0731c5ef8..d4e6080fe 100644 --- a/vendor/github.com/fsnotify/fsnotify/README.md +++ b/vendor/github.com/fsnotify/fsnotify/README.md @@ -1,120 +1,161 @@ -# File system notifications for Go +fsnotify is a Go library to provide cross-platform filesystem notifications on +Windows, Linux, macOS, and BSD systems. -[](https://pkg.go.dev/github.com/fsnotify/fsnotify) [](https://goreportcard.com/report/github.com/fsnotify/fsnotify) [](https://github.com/fsnotify/fsnotify/issues/413) +Go 1.16 or newer is required; the full documentation is at +https://pkg.go.dev/github.com/fsnotify/fsnotify -fsnotify utilizes [`golang.org/x/sys`](https://pkg.go.dev/golang.org/x/sys) rather than [`syscall`](https://pkg.go.dev/syscall) from the standard library. +**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.** -Cross platform: Windows, Linux, BSD and macOS. +--- -| Adapter               | OS                               | Status                                                                                                                          | -| --------------------- | -------------------------------- | ------------------------------------------------------------------------------------------------------------------------------- | -| inotify               | Linux 2.6.27 or later, Android\* | Supported | -| kqueue                | BSD, macOS, iOS\*                | 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 2.6.37+                    | [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)                                                                          | +Platform support: -\* Android and iOS are untested. +| 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)       | -Please see [the documentation](https://pkg.go.dev/github.com/fsnotify/fsnotify) and consult the [FAQ](#faq) for usage information. +Linux and macOS should include Android and iOS, but these are currently untested. -## API stability - -fsnotify is a fork of [howeyc/fsnotify](https://github.com/howeyc/fsnotify) with a new API as of v1.0. The API is based on [this design document](http://goo.gl/MrYxyA). - -All [releases](https://github.com/fsnotify/fsnotify/releases) are tagged based on [Semantic Versioning](http://semver.org/). - -## Usage +Usage +----- +A basic example:  ```go  package main  import ( -	"log" +    "log" -	"github.com/fsnotify/fsnotify" +    "github.com/fsnotify/fsnotify"  )  func main() { -	watcher, err := fsnotify.NewWatcher() -	if err != nil { -		log.Fatal(err) -	} -	defer watcher.Close() - -	done := make(chan bool) -	go func() { -		for { -			select { -			case event, ok := <-watcher.Events: -				if !ok { -					return -				} -				log.Println("event:", event) -				if event.Op&fsnotify.Write == fsnotify.Write { -					log.Println("modified file:", event.Name) -				} -			case err, ok := <-watcher.Errors: -				if !ok { -					return -				} -				log.Println("error:", err) -			} -		} -	}() - -	err = watcher.Add("/tmp/foo") -	if err != nil { -		log.Fatal(err) -	} -	<-done +    // Create new watcher. +    watcher, err := fsnotify.NewWatcher() +    if err != nil { +        log.Fatal(err) +    } +    defer watcher.Close() + +    // Start listening for events. +    go func() { +        for { +            select { +            case event, ok := <-watcher.Events: +                if !ok { +                    return +                } +                log.Println("event:", event) +                if event.Has(fsnotify.Write) { +                    log.Println("modified file:", event.Name) +                } +            case err, ok := <-watcher.Errors: +                if !ok { +                    return +                } +                log.Println("error:", err) +            } +        } +    }() + +    // Add a path. +    err = watcher.Add("/tmp") +    if err != nil { +        log.Fatal(err) +    } + +    // Block main goroutine forever. +    <-make(chan struct{})  }  ``` -## Contributing +Some more examples can be found in [cmd/fsnotify](cmd/fsnotify), which can be +run with: -Please refer to [CONTRIBUTING][] before opening an issue or pull request. +    % go run ./cmd/fsnotify -## FAQ +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. -**When a file is moved to another directory is it still being watched?** +### Are subdirectories watched too? +No, you must add watches for any directory you want to watch (a recursive +watcher is on the roadmap: [#18]). -No (it shouldn't be, unless you are watching where it was moved to). +[#18]: https://github.com/fsnotify/fsnotify/issues/18 -**When I watch a directory, are all subdirectories watched as well?** +### 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). -No, you must add watches for any directory you want to watch (a recursive watcher is on the roadmap [#18][]). +### 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 +protocols does not provide network level support for file notifications, and +neither do the /proc and /sys virtual filesystems. -**Do I have to watch the Error and Event channels in a separate goroutine?** +This could be fixed with a polling watcher ([#9]), but it's not yet implemented. -As of now, yes. Looking into making this single-thread friendly (see [howeyc #7][#7]) +[#9]: https://github.com/fsnotify/fsnotify/issues/9 -**Why am I receiving multiple events for the same file on OS X?** +Platform-specific notes +----------------------- +### Linux +When a file is removed a REMOVE event won't be emitted until all file +descriptors are closed; it will emit a CHMOD instead: -Spotlight indexing on OS X can result in multiple events (see [howeyc #62][#62]). A temporary workaround is to add your folder(s) to the *Spotlight Privacy settings* until we have a native FSEvents implementation (see [#11][]). +    fp := os.Open("file") +    os.Remove("file")        // CHMOD +    fp.Close()               // REMOVE -**How many files can be watched at once?** +This is the event that inotify sends, so not much can be changed about this. -There are OS-specific limits as to how many watches can be created: -* Linux: /proc/sys/fs/inotify/max_user_watches contains the limit, reaching this limit results in a "no space left on device" error. -* BSD / OSX: sysctl variables "kern.maxfiles" and "kern.maxfilesperproc", reaching these limits results in a "too many open files" error. +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". -**Why don't notifications work with NFS filesystems or filesystem in userspace (FUSE)?** +These are also exposed in `/proc` as `/proc/sys/fs/inotify/max_user_watches` and +`/proc/sys/fs/inotify/max_user_instances` -fsnotify requires support from underlying OS to work. The current NFS protocol does not provide network level support for file notifications. +To increase them you can use `sysctl` or write the value to proc file: -[#62]: https://github.com/howeyc/fsnotify/issues/62 -[#18]: https://github.com/fsnotify/fsnotify/issues/18 -[#11]: https://github.com/fsnotify/fsnotify/issues/11 -[#7]: https://github.com/howeyc/fsnotify/issues/7 +    # The 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 -[contributing]: https://github.com/fsnotify/fsnotify/blob/master/CONTRIBUTING.md +Reaching the limit will result in a "no space left on device" or "too many open +files" error. -## Related Projects +### kqueue (macOS, all BSD systems) +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. -* [notify](https://github.com/rjeczalik/notify) -* [fsevents](https://github.com/fsnotify/fsevents) +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 diff --git a/vendor/github.com/fsnotify/fsnotify/backend_fen.go b/vendor/github.com/fsnotify/fsnotify/backend_fen.go new file mode 100644 index 000000000..1a95ad8e7 --- /dev/null +++ b/vendor/github.com/fsnotify/fsnotify/backend_fen.go @@ -0,0 +1,162 @@ +//go:build solaris +// +build solaris + +package fsnotify + +import ( +	"errors" +) + +// 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. +// +// # macOS 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]). +// +// [#11]: https://github.com/fsnotify/fsnotify/issues/11 +// [#15]: https://github.com/fsnotify/fsnotify/issues/15 +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, so you +	//                      probably want to wait until you've stopped receiving +	//                      them (see the dedup example in cmd/fsnotify). +	// +	//   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. +	Events chan Event + +	// Errors sends any errors. +	Errors chan error +} + +// NewWatcher creates a new Watcher. +func NewWatcher() (*Watcher, error) { +	return nil, errors.New("FEN based watcher not yet supported for fsnotify\n") +} + +// Close removes all watches and closes the events channel. +func (w *Watcher) Close() error { +	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 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. +// +// Notifications on network filesystems (NFS, SMB, FUSE, etc.) or special +// filesystems (/proc, /sys, etc.) generally don't work. +// +// # Watching directories +// +// All files in a directory are monitored, including new files that are created +// after the watcher is started. Subdirectories are not watched (i.e. it's +// non-recursive). +// +// # 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. +// +// 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 +} + +// Remove stops monitoring the path for changes. +// +// Directories are always removed non-recursively. For example, if you added +// /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 +} diff --git a/vendor/github.com/fsnotify/fsnotify/backend_inotify.go b/vendor/github.com/fsnotify/fsnotify/backend_inotify.go new file mode 100644 index 000000000..54c77fbb0 --- /dev/null +++ b/vendor/github.com/fsnotify/fsnotify/backend_inotify.go @@ -0,0 +1,459 @@ +//go:build linux +// +build linux + +package fsnotify + +import ( +	"errors" +	"fmt" +	"io" +	"os" +	"path/filepath" +	"strings" +	"sync" +	"unsafe" + +	"golang.org/x/sys/unix" +) + +// 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. +// +// # macOS 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]). +// +// [#11]: https://github.com/fsnotify/fsnotify/issues/11 +// [#15]: https://github.com/fsnotify/fsnotify/issues/15 +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, so you +	//                      probably want to wait until you've stopped receiving +	//                      them (see the dedup example in cmd/fsnotify). +	// +	//   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. +	Events chan Event + +	// Errors sends any errors. +	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 +} + +// 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 +	fd, errno := unix.InotifyInit1(unix.IN_CLOEXEC | unix.IN_NONBLOCK) +	if fd == -1 { +		return nil, errno +	} + +	w := &Watcher{ +		fd:          fd, +		inotifyFile: os.NewFile(uintptr(fd), ""), +		watches:     make(map[string]*watch), +		paths:       make(map[int]string), +		Events:      make(chan Event), +		Errors:      make(chan error), +		done:        make(chan struct{}), +		doneResp:    make(chan struct{}), +	} + +	go w.readEvents() +	return w, nil +} + +// Returns true if the event was sent, or false if watcher is closed. +func (w *Watcher) sendEvent(e Event) bool { +	select { +	case w.Events <- e: +		return true +	case <-w.done: +	} +	return false +} + +// Returns true if the error was sent, or false if watcher is closed. +func (w *Watcher) sendError(err error) 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 { +	w.mu.Lock() +	if w.isClosed() { +		w.mu.Unlock() +		return nil +	} + +	// Send 'close' signal to goroutine, and set the Watcher to closed. +	close(w.done) +	w.mu.Unlock() + +	// Causes any blocking reads to return with an error, provided the file +	// still supports deadline operations. +	err := w.inotifyFile.Close() +	if err != nil { +		return err +	} + +	// Wait for goroutine to close +	<-w.doneResp + +	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 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. +// +// Notifications on network filesystems (NFS, SMB, FUSE, etc.) or special +// filesystems (/proc, /sys, etc.) generally don't work. +// +// # Watching directories +// +// All files in a directory are monitored, including new files that are created +// after the watcher is started. Subdirectories are not watched (i.e. it's +// non-recursive). +// +// # 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. +// +// 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) +	if w.isClosed() { +		return errors.New("inotify instance already closed") +	} + +	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 +	} + +	if watchEntry == nil { +		w.watches[name] = &watch{wd: uint32(wd), flags: flags} +		w.paths[wd] = name +	} else { +		watchEntry.wd = uint32(wd) +		watchEntry.flags = flags +	} + +	return nil +} + +// Remove stops monitoring the path for changes. +// +// Directories are always removed non-recursively. For example, if you added +// /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 { +	name = filepath.Clean(name) + +	// Fetch the watch. +	w.mu.Lock() +	defer w.mu.Unlock() +	watch, ok := w.watches[name] + +	// Remove it from inotify. +	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) +	if success == -1 { +		// TODO: Perhaps it's not helpful to return an error here in every case; +		//       The only two possible errors are: +		// +		//       - EBADF, which happens when w.fd is not a valid file descriptor +		//         of any kind. +		//       - EINVAL, which is when fd is not an inotify descriptor or wd +		//         is not a valid watch descriptor. Watch descriptors are +		//         invalidated when they are removed explicitly or implicitly; +		//         explicitly by inotify_rm_watch, implicitly when the file they +		//         are watching is deleted. +		return errno +	} + +	return nil +} + +// WatchList returns all paths added with [Add] (and are not yet removed). +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) +	} + +	return entries +} + +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) +} + +// readEvents reads from the inotify file descriptor, converts the +// received events into Event objects and sends them via the Events channel +func (w *Watcher) readEvents() { +	defer func() { +		close(w.doneResp) +		close(w.Errors) +		close(w.Events) +	}() + +	var ( +		buf   [unix.SizeofInotifyEvent * 4096]byte // Buffer for a maximum of 4096 raw events +		errno error                                // Syscall errno +	) +	for { +		// See if we have been closed. +		if w.isClosed() { +			return +		} + +		n, err := w.inotifyFile.Read(buf[:]) +		switch { +		case errors.Unwrap(err) == os.ErrClosed: +			return +		case err != nil: +			if !w.sendError(err) { +				return +			} +			continue +		} + +		if n < unix.SizeofInotifyEvent { +			var err error +			if n == 0 { +				// If EOF is received. This should really never happen. +				err = io.EOF +			} else if n < 0 { +				// If an error occurred while reading. +				err = errno +			} else { +				// Read was too short. +				err = errors.New("notify: short read in readEvents()") +			} +			if !w.sendError(err) { +				return +			} +			continue +		} + +		var offset uint32 +		// We don't know how many events we just read into the buffer +		// While the offset points to at least one whole event... +		for offset <= uint32(n-unix.SizeofInotifyEvent) { +			var ( +				// Point "raw" to the event in the buffer +				raw     = (*unix.InotifyEvent)(unsafe.Pointer(&buf[offset])) +				mask    = uint32(raw.Mask) +				nameLen = uint32(raw.Len) +			) + +			if mask&unix.IN_Q_OVERFLOW != 0 { +				if !w.sendError(ErrEventOverflow) { +					return +				} +			} + +			// If the event happened to the watched directory or the watched file, the kernel +			// doesn't append the filename to the event, but we would like to always fill the +			// the "Name" field with a valid filename. We retrieve the path of the watch from +			// the "paths" map. +			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() + +			if nameLen > 0 { +				// Point "bytes" at the first byte of the filename +				bytes := (*[unix.PathMax]byte)(unsafe.Pointer(&buf[offset+unix.SizeofInotifyEvent]))[:nameLen:nameLen] +				// The filename is padded with NULL bytes. TrimRight() gets rid of those. +				name += "/" + strings.TrimRight(string(bytes[0:nameLen]), "\000") +			} + +			event := w.newEvent(name, mask) + +			// Send the events that are not ignored on the events channel +			if mask&unix.IN_IGNORED == 0 { +				if !w.sendEvent(event) { +					return +				} +			} + +			// Move to the next event in the buffer +			offset += unix.SizeofInotifyEvent + nameLen +		} +	} +} + +// newEvent returns an platform-independent Event based on an inotify mask. +func (w *Watcher) newEvent(name string, mask uint32) Event { +	e := Event{Name: name} +	if mask&unix.IN_CREATE == unix.IN_CREATE || mask&unix.IN_MOVED_TO == unix.IN_MOVED_TO { +		e.Op |= Create +	} +	if mask&unix.IN_DELETE_SELF == unix.IN_DELETE_SELF || mask&unix.IN_DELETE == unix.IN_DELETE { +		e.Op |= Remove +	} +	if mask&unix.IN_MODIFY == unix.IN_MODIFY { +		e.Op |= Write +	} +	if mask&unix.IN_MOVE_SELF == unix.IN_MOVE_SELF || mask&unix.IN_MOVED_FROM == unix.IN_MOVED_FROM { +		e.Op |= Rename +	} +	if mask&unix.IN_ATTRIB == unix.IN_ATTRIB { +		e.Op |= Chmod +	} +	return e +} diff --git a/vendor/github.com/fsnotify/fsnotify/backend_kqueue.go b/vendor/github.com/fsnotify/fsnotify/backend_kqueue.go new file mode 100644 index 000000000..29087469b --- /dev/null +++ b/vendor/github.com/fsnotify/fsnotify/backend_kqueue.go @@ -0,0 +1,707 @@ +//go:build freebsd || openbsd || netbsd || dragonfly || darwin +// +build freebsd openbsd netbsd dragonfly darwin + +package fsnotify + +import ( +	"errors" +	"fmt" +	"io/ioutil" +	"os" +	"path/filepath" +	"sync" + +	"golang.org/x/sys/unix" +) + +// 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. +// +// # macOS 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]). +// +// [#11]: https://github.com/fsnotify/fsnotify/issues/11 +// [#15]: https://github.com/fsnotify/fsnotify/issues/15 +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, so you +	//                      probably want to wait until you've stopped receiving +	//                      them (see the dedup example in cmd/fsnotify). +	// +	//   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. +	Events chan Event + +	// Errors sends any errors. +	Errors chan error + +	done         chan struct{} +	kq           int                         // File descriptor (as returned by the kqueue() syscall). +	closepipe    [2]int                      // Pipe used for closing. +	mu           sync.Mutex                  // Protects access to watcher data +	watches      map[string]int              // Watched file descriptors (key: path). +	watchesByDir map[string]map[int]struct{} // Watched file descriptors indexed by the parent directory (key: dirname(path)). +	userWatches  map[string]struct{}         // Watches added with Watcher.Add() +	dirFlags     map[string]uint32           // Watched directories to fflags used in kqueue. +	paths        map[int]pathInfo            // File descriptors to path names for processing kqueue events. +	fileExists   map[string]struct{}         // Keep track of if we know this file exists (to stop duplicate create events). +	isClosed     bool                        // Set to true when Close() is first called +} + +type pathInfo struct { +	name  string +	isDir bool +} + +// NewWatcher creates a new Watcher. +func NewWatcher() (*Watcher, error) { +	kq, closepipe, err := newKqueue() +	if err != nil { +		return nil, err +	} + +	w := &Watcher{ +		kq:           kq, +		closepipe:    closepipe, +		watches:      make(map[string]int), +		watchesByDir: make(map[string]map[int]struct{}), +		dirFlags:     make(map[string]uint32), +		paths:        make(map[int]pathInfo), +		fileExists:   make(map[string]struct{}), +		userWatches:  make(map[string]struct{}), +		Events:       make(chan Event), +		Errors:       make(chan error), +		done:         make(chan struct{}), +	} + +	go w.readEvents() +	return w, nil +} + +// newKqueue creates a new kernel event queue and returns a descriptor. +// +// This registers a new event on closepipe, which will trigger an event when +// it's closed. This way we can use kevent() without timeout/polling; without +// the closepipe, it would block forever and we wouldn't be able to stop it at +// all. +func newKqueue() (kq int, closepipe [2]int, err error) { +	kq, err = unix.Kqueue() +	if kq == -1 { +		return kq, closepipe, err +	} + +	// Register the close pipe. +	err = unix.Pipe(closepipe[:]) +	if err != nil { +		unix.Close(kq) +		return kq, closepipe, err +	} + +	// Register changes to listen on the closepipe. +	changes := make([]unix.Kevent_t, 1) +	// SetKevent converts int to the platform-specific types. +	unix.SetKevent(&changes[0], closepipe[0], unix.EVFILT_READ, +		unix.EV_ADD|unix.EV_ENABLE|unix.EV_ONESHOT) + +	ok, err := unix.Kevent(kq, changes, nil, nil) +	if ok == -1 { +		unix.Close(kq) +		unix.Close(closepipe[0]) +		unix.Close(closepipe[1]) +		return kq, closepipe, err +	} +	return kq, closepipe, nil +} + +// Returns true if the event was sent, or false if watcher is closed. +func (w *Watcher) sendEvent(e Event) bool { +	select { +	case w.Events <- e: +		return true +	case <-w.done: +	} +	return false +} + +// Returns true if the error was sent, or false if watcher is closed. +func (w *Watcher) sendError(err error) bool { +	select { +	case w.Errors <- err: +		return true +	case <-w.done: +	} +	return false +} + +// Close removes all watches and closes the events channel. +func (w *Watcher) Close() error { +	w.mu.Lock() +	if w.isClosed { +		w.mu.Unlock() +		return nil +	} +	w.isClosed = true + +	// copy paths to remove while locked +	pathsToRemove := make([]string, 0, len(w.watches)) +	for name := range w.watches { +		pathsToRemove = append(pathsToRemove, name) +	} +	w.mu.Unlock() // Unlock before calling Remove, which also locks +	for _, name := range pathsToRemove { +		w.Remove(name) +	} + +	// Send "quit" message to the reader goroutine. +	unix.Close(w.closepipe[1]) +	close(w.done) + +	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 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. +// +// Notifications on network filesystems (NFS, SMB, FUSE, etc.) or special +// filesystems (/proc, /sys, etc.) generally don't work. +// +// # Watching directories +// +// All files in a directory are monitored, including new files that are created +// after the watcher is started. Subdirectories are not watched (i.e. it's +// non-recursive). +// +// # 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. +// +// 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() +	w.userWatches[name] = struct{}{} +	w.mu.Unlock() +	_, err := w.addWatch(name, noteAllEvents) +	return err +} + +// Remove stops monitoring the path for changes. +// +// Directories are always removed non-recursively. For example, if you added +// /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 { +	name = filepath.Clean(name) +	w.mu.Lock() +	watchfd, ok := w.watches[name] +	w.mu.Unlock() +	if !ok { +		return fmt.Errorf("%w: %s", ErrNonExistentWatch, name) +	} + +	err := w.register([]int{watchfd}, unix.EV_DELETE, 0) +	if err != nil { +		return err +	} + +	unix.Close(watchfd) + +	w.mu.Lock() +	isDir := w.paths[watchfd].isDir +	delete(w.watches, name) +	delete(w.userWatches, name) + +	parentName := filepath.Dir(name) +	delete(w.watchesByDir[parentName], watchfd) + +	if len(w.watchesByDir[parentName]) == 0 { +		delete(w.watchesByDir, parentName) +	} + +	delete(w.paths, watchfd) +	delete(w.dirFlags, name) +	delete(w.fileExists, name) +	w.mu.Unlock() + +	// Find all watched paths that are in this directory that are not external. +	if isDir { +		var pathsToRemove []string +		w.mu.Lock() +		for fd := range w.watchesByDir[name] { +			path := w.paths[fd] +			if _, ok := w.userWatches[path.name]; !ok { +				pathsToRemove = append(pathsToRemove, path.name) +			} +		} +		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. +			w.Remove(name) +		} +	} + +	return nil +} + +// WatchList returns all paths added with [Add] (and are not yet removed). +func (w *Watcher) WatchList() []string { +	w.mu.Lock() +	defer w.mu.Unlock() + +	entries := make([]string, 0, len(w.userWatches)) +	for pathname := range w.userWatches { +		entries = append(entries, pathname) +	} + +	return entries +} + +// 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. +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") +	} +	watchfd, alreadyWatching := w.watches[name] +	// We already have a watch, but we can still override flags. +	if alreadyWatching { +		isDir = w.paths[watchfd].isDir +	} +	w.mu.Unlock() + +	if !alreadyWatching { +		fi, err := os.Lstat(name) +		if err != nil { +			return "", err +		} + +		// Don't watch sockets or named pipes +		if (fi.Mode()&os.ModeSocket == os.ModeSocket) || (fi.Mode()&os.ModeNamedPipe == os.ModeNamedPipe) { +			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. +		if fi.Mode()&os.ModeSymlink == os.ModeSymlink { +			name, err = filepath.EvalSymlinks(name) +			if err != nil { +				return "", nil +			} + +			w.mu.Lock() +			_, alreadyWatching = w.watches[name] +			w.mu.Unlock() + +			if alreadyWatching { +				return name, nil +			} + +			fi, err = os.Lstat(name) +			if err != nil { +				return "", nil +			} +		} + +		// Retry on EINTR; open() can return EINTR in practice on macOS. +		// See #354, and go issues 11180 and 39237. +		for { +			watchfd, err = unix.Open(name, openMode, 0) +			if err == nil { +				break +			} +			if errors.Is(err, unix.EINTR) { +				continue +			} + +			return "", err +		} + +		isDir = fi.IsDir() +	} + +	err := w.register([]int{watchfd}, unix.EV_ADD|unix.EV_CLEAR|unix.EV_ENABLE, flags) +	if err != nil { +		unix.Close(watchfd) +		return "", err +	} + +	if !alreadyWatching { +		w.mu.Lock() +		parentName := filepath.Dir(name) +		w.watches[name] = watchfd + +		watchesByDir, ok := w.watchesByDir[parentName] +		if !ok { +			watchesByDir = make(map[int]struct{}, 1) +			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) +		w.mu.Lock() + +		watchDir := (flags&unix.NOTE_WRITE) == unix.NOTE_WRITE && +			(!alreadyWatching || (w.dirFlags[name]&unix.NOTE_WRITE) != unix.NOTE_WRITE) +		// Store flags so this watch can be updated later +		w.dirFlags[name] = flags +		w.mu.Unlock() + +		if watchDir { +			if err := w.watchDirectoryFiles(name); err != nil { +				return "", err +			} +		} +	} +	return name, nil +} + +// readEvents reads from kqueue and converts the received kevents into +// 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) +	}() + +	eventBuffer := make([]unix.Kevent_t, 10) +	for closed := false; !closed; { +		kevents, err := w.read(eventBuffer) +		// EINTR is okay, the syscall was interrupted before timeout expired. +		if err != nil && err != unix.EINTR { +			if !w.sendError(fmt.Errorf("fsnotify.readEvents: %w", err)) { +				closed = true +			} +			continue +		} + +		// Flush the events we received to the Events channel +		for _, kevent := range kevents { +			var ( +				watchfd = int(kevent.Ident) +				mask    = uint32(kevent.Fflags) +			) + +			// Shut down the loop when the pipe is closed, but only after all +			// other events have been processed. +			if watchfd == w.closepipe[0] { +				closed = true +				continue +			} + +			w.mu.Lock() +			path := w.paths[watchfd] +			w.mu.Unlock() + +			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.mu.Lock() +				delete(w.fileExists, event.Name) +				w.mu.Unlock() +			} + +			if path.isDir && event.Has(Write) && !event.Has(Remove) { +				w.sendDirectoryChangeEvents(event.Name) +			} else { +				if !w.sendEvent(event) { +					closed = true +					continue +				} +			} + +			if event.Has(Remove) { +				// 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) +						} +					} +				} else { +					filePath := filepath.Clean(event.Name) +					if fileInfo, err := os.Lstat(filePath); err == nil { +						w.sendFileCreatedEventIfNew(filePath, fileInfo) +					} +				} +			} +		} +	} +} + +// newEvent returns an platform-independent Event based on kqueue Fflags. +func (w *Watcher) newEvent(name string, mask uint32) Event { +	e := Event{Name: name} +	if mask&unix.NOTE_DELETE == unix.NOTE_DELETE { +		e.Op |= Remove +	} +	if mask&unix.NOTE_WRITE == unix.NOTE_WRITE { +		e.Op |= Write +	} +	if mask&unix.NOTE_RENAME == unix.NOTE_RENAME { +		e.Op |= Rename +	} +	if mask&unix.NOTE_ATTRIB == unix.NOTE_ATTRIB { +		e.Op |= Chmod +	} +	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) +	if err != nil { +		return err +	} + +	for _, fileInfo := range files { +		path := filepath.Join(dirPath, fileInfo.Name()) + +		cleanPath, err := w.internalWatch(path, fileInfo) +		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 +			// as a "new" file later (it still shows up in the directory +			// listing). +			switch { +			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) +			} +		} + +		w.mu.Lock() +		w.fileExists[cleanPath] = struct{}{} +		w.mu.Unlock() +	} + +	return nil +} + +// Search the directory for new files and send an event for them. +// +// 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) +	if err != nil { +		if !w.sendError(fmt.Errorf("fsnotify.sendDirectoryChangeEvents: %w", err)) { +			return +		} +	} + +	// Search for new files +	for _, fi := range files { +		err := w.sendFileCreatedEventIfNew(filepath.Join(dir, fi.Name()), fi) +		if err != nil { +			return +		} +	} +} + +// sendFileCreatedEvent sends a create event if the file isn't already being tracked. +func (w *Watcher) sendFileCreatedEventIfNew(filePath string, fileInfo os.FileInfo) (err error) { +	w.mu.Lock() +	_, doesExist := w.fileExists[filePath] +	w.mu.Unlock() +	if !doesExist { +		if !w.sendEvent(Event{Name: filePath, Op: Create}) { +			return +		} +	} + +	// like watchDirectoryFiles (but without doing another ReadDir) +	filePath, err = w.internalWatch(filePath, fileInfo) +	if err != nil { +		return err +	} + +	w.mu.Lock() +	w.fileExists[filePath] = struct{}{} +	w.mu.Unlock() + +	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 +		w.mu.Lock() +		flags := w.dirFlags[name] +		w.mu.Unlock() + +		flags |= unix.NOTE_DELETE | unix.NOTE_RENAME +		return w.addWatch(name, flags) +	} + +	// watch file to mimic Linux inotify +	return w.addWatch(name, noteAllEvents) +} + +// Register events with the queue. +func (w *Watcher) register(fds []int, flags int, fflags uint32) error { +	changes := make([]unix.Kevent_t, len(fds)) +	for i, fd := range fds { +		// SetKevent converts int to the platform-specific types. +		unix.SetKevent(&changes[i], fd, unix.EVFILT_VNODE, flags) +		changes[i].Fflags = fflags +	} + +	// Register the events. +	success, err := unix.Kevent(w.kq, changes, nil, nil) +	if success == -1 { +		return err +	} +	return nil +} + +// read retrieves pending events, or waits until an event occurs. +func (w *Watcher) read(events []unix.Kevent_t) ([]unix.Kevent_t, error) { +	n, err := unix.Kevent(w.kq, nil, events, nil) +	if err != nil { +		return nil, err +	} +	return events[0:n], nil +} diff --git a/vendor/github.com/fsnotify/fsnotify/backend_other.go b/vendor/github.com/fsnotify/fsnotify/backend_other.go new file mode 100644 index 000000000..a9bb1c3c4 --- /dev/null +++ b/vendor/github.com/fsnotify/fsnotify/backend_other.go @@ -0,0 +1,66 @@ +//go:build !darwin && !dragonfly && !freebsd && !openbsd && !linux && !netbsd && !solaris && !windows +// +build !darwin,!dragonfly,!freebsd,!openbsd,!linux,!netbsd,!solaris,!windows + +package fsnotify + +import ( +	"fmt" +	"runtime" +) + +// Watcher watches a set of files, delivering events to a channel. +type Watcher struct{} + +// NewWatcher creates a new Watcher. +func NewWatcher() (*Watcher, error) { +	return nil, fmt.Errorf("fsnotify not supported on %s", runtime.GOOS) +} + +// Close removes all watches and closes the events channel. +func (w *Watcher) Close() error { +	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 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. +// +// Notifications on network filesystems (NFS, SMB, FUSE, etc.) or special +// filesystems (/proc, /sys, etc.) generally don't work. +// +// # Watching directories +// +// All files in a directory are monitored, including new files that are created +// after the watcher is started. Subdirectories are not watched (i.e. it's +// non-recursive). +// +// # 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. +// +// 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 +} + +// Remove stops monitoring the path for changes. +// +// Directories are always removed non-recursively. For example, if you added +// /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 +} diff --git a/vendor/github.com/fsnotify/fsnotify/backend_windows.go b/vendor/github.com/fsnotify/fsnotify/backend_windows.go new file mode 100644 index 000000000..ae392867c --- /dev/null +++ b/vendor/github.com/fsnotify/fsnotify/backend_windows.go @@ -0,0 +1,746 @@ +//go:build windows +// +build windows + +package fsnotify + +import ( +	"errors" +	"fmt" +	"os" +	"path/filepath" +	"reflect" +	"runtime" +	"strings" +	"sync" +	"unsafe" + +	"golang.org/x/sys/windows" +) + +// 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. +// +// # macOS 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]). +// +// [#11]: https://github.com/fsnotify/fsnotify/issues/11 +// [#15]: https://github.com/fsnotify/fsnotify/issues/15 +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, so you +	//                      probably want to wait until you've stopped receiving +	//                      them (see the dedup example in cmd/fsnotify). +	// +	//   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. +	Events chan Event + +	// Errors sends any errors. +	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 +} + +// NewWatcher creates a new Watcher. +func NewWatcher() (*Watcher, error) { +	port, err := windows.CreateIoCompletionPort(windows.InvalidHandle, 0, 0, 0) +	if err != nil { +		return nil, os.NewSyscallError("CreateIoCompletionPort", err) +	} +	w := &Watcher{ +		port:    port, +		watches: make(watchMap), +		input:   make(chan *input, 1), +		Events:  make(chan Event, 50), +		Errors:  make(chan error), +		quit:    make(chan chan<- error, 1), +	} +	go w.readEvents() +	return w, nil +} + +func (w *Watcher) sendEvent(name string, mask uint64) bool { +	if mask == 0 { +		return false +	} + +	event := w.newEvent(name, uint32(mask)) +	select { +	case ch := <-w.quit: +		w.quit <- ch +	case w.Events <- event: +	} +	return true +} + +// Returns true if the error was sent, or false if watcher is closed. +func (w *Watcher) sendError(err error) bool { +	select { +	case w.Errors <- err: +		return true +	case <-w.quit: +	} +	return false +} + +// Close removes all watches and closes the events channel. +func (w *Watcher) Close() error { +	w.mu.Lock() +	if w.isClosed { +		w.mu.Unlock() +		return nil +	} +	w.isClosed = true +	w.mu.Unlock() + +	// Send "quit" message to the reader goroutine +	ch := make(chan error) +	w.quit <- ch +	if err := w.wakeupReader(); err != nil { +		return err +	} +	return <-ch +} + +// 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 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. +// +// Notifications on network filesystems (NFS, SMB, FUSE, etc.) or special +// filesystems (/proc, /sys, etc.) generally don't work. +// +// # Watching directories +// +// All files in a directory are monitored, including new files that are created +// after the watcher is started. Subdirectories are not watched (i.e. it's +// non-recursive). +// +// # 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. +// +// 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") +	} +	w.mu.Unlock() + +	in := &input{ +		op:    opAddWatch, +		path:  filepath.Clean(name), +		flags: sysFSALLEVENTS, +		reply: make(chan error), +	} +	w.input <- in +	if err := w.wakeupReader(); err != nil { +		return err +	} +	return <-in.reply +} + +// Remove stops monitoring the path for changes. +// +// Directories are always removed non-recursively. For example, if you added +// /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 { +	in := &input{ +		op:    opRemoveWatch, +		path:  filepath.Clean(name), +		reply: make(chan error), +	} +	w.input <- in +	if err := w.wakeupReader(); err != nil { +		return err +	} +	return <-in.reply +} + +// WatchList returns all paths added with [Add] (and are not yet removed). +func (w *Watcher) WatchList() []string { +	w.mu.Lock() +	defer w.mu.Unlock() + +	entries := make([]string, 0, len(w.watches)) +	for _, entry := range w.watches { +		for _, watchEntry := range entry { +			entries = append(entries, watchEntry.path) +		} +	} + +	return entries +} + +// These options are from the old golang.org/x/exp/winfsnotify, where you could +// add various options to the watch. This has long since been removed. +// +// The "sys" in the name is misleading as they're not part of any "system". +// +// 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 +	sysFSMODIFY     = 0x2 +	sysFSMOVE       = 0xc0 +	sysFSMOVEDFROM  = 0x40 +	sysFSMOVEDTO    = 0x80 +	sysFSMOVESELF   = 0x800 +	sysFSIGNORED    = 0x8000 +) + +func (w *Watcher) newEvent(name string, mask uint32) Event { +	e := Event{Name: name} +	if mask&sysFSCREATE == sysFSCREATE || mask&sysFSMOVEDTO == sysFSMOVEDTO { +		e.Op |= Create +	} +	if mask&sysFSDELETE == sysFSDELETE || mask&sysFSDELETESELF == sysFSDELETESELF { +		e.Op |= Remove +	} +	if mask&sysFSMODIFY == sysFSMODIFY { +		e.Op |= Write +	} +	if mask&sysFSMOVE == sysFSMOVE || mask&sysFSMOVESELF == sysFSMOVESELF || mask&sysFSMOVEDFROM == sysFSMOVEDFROM { +		e.Op |= Rename +	} +	if mask&sysFSATTRIB == sysFSATTRIB { +		e.Op |= Chmod +	} +	return e +} + +const ( +	opAddWatch = iota +	opRemoveWatch +) + +const ( +	provisional uint64 = 1 << (32 + iota) +) + +type input struct { +	op    int +	path  string +	flags uint32 +	reply chan error +} + +type inode struct { +	handle windows.Handle +	volume uint32 +	index  uint64 +} + +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 +} + +type ( +	indexMap map[uint64]*watch +	watchMap map[uint32]indexMap +) + +func (w *Watcher) wakeupReader() error { +	err := windows.PostQueuedCompletionStatus(w.port, 0, 0, nil) +	if err != nil { +		return os.NewSyscallError("PostQueuedCompletionStatus", err) +	} +	return nil +} + +func (w *Watcher) getDir(pathname string) (dir string, err error) { +	attr, err := windows.GetFileAttributes(windows.StringToUTF16Ptr(pathname)) +	if err != nil { +		return "", os.NewSyscallError("GetFileAttributes", err) +	} +	if attr&windows.FILE_ATTRIBUTE_DIRECTORY != 0 { +		dir = pathname +	} else { +		dir, _ = filepath.Split(pathname) +		dir = filepath.Clean(dir) +	} +	return +} + +func (w *Watcher) getIno(path string) (ino *inode, err error) { +	h, err := windows.CreateFile(windows.StringToUTF16Ptr(path), +		windows.FILE_LIST_DIRECTORY, +		windows.FILE_SHARE_READ|windows.FILE_SHARE_WRITE|windows.FILE_SHARE_DELETE, +		nil, windows.OPEN_EXISTING, +		windows.FILE_FLAG_BACKUP_SEMANTICS|windows.FILE_FLAG_OVERLAPPED, 0) +	if err != nil { +		return nil, os.NewSyscallError("CreateFile", err) +	} + +	var fi windows.ByHandleFileInformation +	err = windows.GetFileInformationByHandle(h, &fi) +	if err != nil { +		windows.CloseHandle(h) +		return nil, os.NewSyscallError("GetFileInformationByHandle", err) +	} +	ino = &inode{ +		handle: h, +		volume: fi.VolumeSerialNumber, +		index:  uint64(fi.FileIndexHigh)<<32 | uint64(fi.FileIndexLow), +	} +	return ino, nil +} + +// Must run within the I/O thread. +func (m watchMap) get(ino *inode) *watch { +	if i := m[ino.volume]; i != nil { +		return i[ino.index] +	} +	return nil +} + +// Must run within the I/O thread. +func (m watchMap) set(ino *inode, watch *watch) { +	i := m[ino.volume] +	if i == nil { +		i = make(indexMap) +		m[ino.volume] = i +	} +	i[ino.index] = watch +} + +// Must run within the I/O thread. +func (w *Watcher) addWatch(pathname string, flags uint64) error { +	dir, err := w.getDir(pathname) +	if err != nil { +		return err +	} + +	ino, err := w.getIno(dir) +	if err != nil { +		return err +	} +	w.mu.Lock() +	watchEntry := w.watches.get(ino) +	w.mu.Unlock() +	if watchEntry == nil { +		_, err := windows.CreateIoCompletionPort(ino.handle, w.port, 0, 0) +		if err != nil { +			windows.CloseHandle(ino.handle) +			return os.NewSyscallError("CreateIoCompletionPort", err) +		} +		watchEntry = &watch{ +			ino:   ino, +			path:  dir, +			names: make(map[string]uint64), +		} +		w.mu.Lock() +		w.watches.set(ino, watchEntry) +		w.mu.Unlock() +		flags |= provisional +	} else { +		windows.CloseHandle(ino.handle) +	} +	if pathname == dir { +		watchEntry.mask |= flags +	} else { +		watchEntry.names[filepath.Base(pathname)] |= flags +	} + +	err = w.startRead(watchEntry) +	if err != nil { +		return err +	} + +	if pathname == dir { +		watchEntry.mask &= ^provisional +	} else { +		watchEntry.names[filepath.Base(pathname)] &= ^provisional +	} +	return nil +} + +// Must run within the I/O thread. +func (w *Watcher) remWatch(pathname string) error { +	dir, err := w.getDir(pathname) +	if err != nil { +		return err +	} +	ino, err := w.getIno(dir) +	if err != nil { +		return err +	} + +	w.mu.Lock() +	watch := w.watches.get(ino) +	w.mu.Unlock() + +	err = windows.CloseHandle(ino.handle) +	if err != nil { +		w.sendError(os.NewSyscallError("CloseHandle", err)) +	} +	if watch == nil { +		return fmt.Errorf("%w: %s", ErrNonExistentWatch, pathname) +	} +	if pathname == dir { +		w.sendEvent(watch.path, watch.mask&sysFSIGNORED) +		watch.mask = 0 +	} else { +		name := filepath.Base(pathname) +		w.sendEvent(filepath.Join(watch.path, name), watch.names[name]&sysFSIGNORED) +		delete(watch.names, name) +	} + +	return w.startRead(watch) +} + +// Must run within the I/O thread. +func (w *Watcher) deleteWatch(watch *watch) { +	for name, mask := range watch.names { +		if mask&provisional == 0 { +			w.sendEvent(filepath.Join(watch.path, name), mask&sysFSIGNORED) +		} +		delete(watch.names, name) +	} +	if watch.mask != 0 { +		if watch.mask&provisional == 0 { +			w.sendEvent(watch.path, watch.mask&sysFSIGNORED) +		} +		watch.mask = 0 +	} +} + +// Must run within the I/O thread. +func (w *Watcher) startRead(watch *watch) error { +	err := windows.CancelIo(watch.ino.handle) +	if err != nil { +		w.sendError(os.NewSyscallError("CancelIo", err)) +		w.deleteWatch(watch) +	} +	mask := w.toWindowsFlags(watch.mask) +	for _, m := range watch.names { +		mask |= w.toWindowsFlags(m) +	} +	if mask == 0 { +		err := windows.CloseHandle(watch.ino.handle) +		if err != nil { +			w.sendError(os.NewSyscallError("CloseHandle", err)) +		} +		w.mu.Lock() +		delete(w.watches[watch.ino.volume], watch.ino.index) +		w.mu.Unlock() +		return nil +	} + +	rdErr := windows.ReadDirectoryChanges(watch.ino.handle, &watch.buf[0], +		uint32(unsafe.Sizeof(watch.buf)), false, mask, nil, &watch.ov, 0) +	if rdErr != nil { +		err := os.NewSyscallError("ReadDirectoryChanges", rdErr) +		if rdErr == windows.ERROR_ACCESS_DENIED && watch.mask&provisional == 0 { +			// Watched directory was probably removed +			w.sendEvent(watch.path, watch.mask&sysFSDELETESELF) +			err = nil +		} +		w.deleteWatch(watch) +		w.startRead(watch) +		return err +	} +	return nil +} + +// readEvents reads from the I/O completion port, converts the +// received events into Event objects and sends them via the Events channel. +// Entry point to the I/O thread. +func (w *Watcher) readEvents() { +	var ( +		n   uint32 +		key uintptr +		ov  *windows.Overlapped +	) +	runtime.LockOSThread() + +	for { +		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 { +			select { +			case ch := <-w.quit: +				w.mu.Lock() +				var indexes []indexMap +				for _, index := range w.watches { +					indexes = append(indexes, index) +				} +				w.mu.Unlock() +				for _, index := range indexes { +					for _, watch := range index { +						w.deleteWatch(watch) +						w.startRead(watch) +					} +				} + +				err := windows.CloseHandle(w.port) +				if err != nil { +					err = os.NewSyscallError("CloseHandle", err) +				} +				close(w.Events) +				close(w.Errors) +				ch <- err +				return +			case in := <-w.input: +				switch in.op { +				case opAddWatch: +					in.reply <- w.addWatch(in.path, uint64(in.flags)) +				case opRemoveWatch: +					in.reply <- w.remWatch(in.path) +				} +			default: +			} +			continue +		} + +		switch qErr { +		case windows.ERROR_MORE_DATA: +			if watch == nil { +				w.sendError(errors.New("ERROR_MORE_DATA has unexpectedly null lpOverlapped buffer")) +			} else { +				// The i/o succeeded but the buffer is full. +				// In theory we should be building up a full packet. +				// In practice we can get away with just carrying on. +				n = uint32(unsafe.Sizeof(watch.buf)) +			} +		case windows.ERROR_ACCESS_DENIED: +			// Watched directory was probably removed +			w.sendEvent(watch.path, watch.mask&sysFSDELETESELF) +			w.deleteWatch(watch) +			w.startRead(watch) +			continue +		case windows.ERROR_OPERATION_ABORTED: +			// CancelIo was called on this handle +			continue +		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()")) +				break +			} + +			// Point "raw" to the event in the buffer +			raw := (*windows.FileNotifyInformation)(unsafe.Pointer(&watch.buf[offset])) + +			// Create a buf that is the size of the path name +			size := int(raw.FileNameLength / 2) +			var buf []uint16 +			// TODO: Use unsafe.Slice in Go 1.17; https://stackoverflow.com/questions/51187973 +			sh := (*reflect.SliceHeader)(unsafe.Pointer(&buf)) +			sh.Data = uintptr(unsafe.Pointer(&raw.FileName)) +			sh.Len = size +			sh.Cap = size +			name := windows.UTF16ToString(buf) +			fullname := filepath.Join(watch.path, name) + +			var mask uint64 +			switch raw.Action { +			case windows.FILE_ACTION_REMOVED: +				mask = sysFSDELETESELF +			case windows.FILE_ACTION_MODIFIED: +				mask = sysFSMODIFY +			case windows.FILE_ACTION_RENAMED_OLD_NAME: +				watch.rename = name +			case windows.FILE_ACTION_RENAMED_NEW_NAME: +				// Update saved path of all sub-watches. +				old := filepath.Join(watch.path, watch.rename) +				w.mu.Lock() +				for _, watchMap := range w.watches { +					for _, ww := range watchMap { +						if strings.HasPrefix(ww.path, old) { +							ww.path = filepath.Join(fullname, strings.TrimPrefix(ww.path, old)) +						} +					} +				} +				w.mu.Unlock() + +				if watch.names[watch.rename] != 0 { +					watch.names[name] |= watch.names[watch.rename] +					delete(watch.names, watch.rename) +					mask = sysFSMOVESELF +				} +			} + +			sendNameEvent := func() { +				w.sendEvent(fullname, watch.names[name]&mask) +			} +			if raw.Action != windows.FILE_ACTION_RENAMED_NEW_NAME { +				sendNameEvent() +			} +			if raw.Action == windows.FILE_ACTION_REMOVED { +				w.sendEvent(fullname, watch.names[name]&sysFSIGNORED) +				delete(watch.names, name) +			} + +			w.sendEvent(fullname, watch.mask&w.toFSnotifyFlags(raw.Action)) +			if raw.Action == windows.FILE_ACTION_RENAMED_NEW_NAME { +				fullname = filepath.Join(watch.path, watch.rename) +				sendNameEvent() +			} + +			// Move to the next event in the buffer +			if raw.NextEntryOffset == 0 { +				break +			} +			offset += raw.NextEntryOffset + +			// Error! +			if offset >= n { +				w.sendError(errors.New( +					"Windows system assumed buffer larger than it is, events have likely been missed.")) +				break +			} +		} + +		if err := w.startRead(watch); err != nil { +			w.sendError(err) +		} +	} +} + +func (w *Watcher) toWindowsFlags(mask uint64) uint32 { +	var m 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 +	} +	return m +} + +func (w *Watcher) toFSnotifyFlags(action uint32) uint64 { +	switch action { +	case windows.FILE_ACTION_ADDED: +		return sysFSCREATE +	case windows.FILE_ACTION_REMOVED: +		return sysFSDELETE +	case windows.FILE_ACTION_MODIFIED: +		return sysFSMODIFY +	case windows.FILE_ACTION_RENAMED_OLD_NAME: +		return sysFSMOVEDFROM +	case windows.FILE_ACTION_RENAMED_NEW_NAME: +		return sysFSMOVEDTO +	} +	return 0 +} diff --git a/vendor/github.com/fsnotify/fsnotify/fen.go b/vendor/github.com/fsnotify/fsnotify/fen.go deleted file mode 100644 index b3ac3d8f5..000000000 --- a/vendor/github.com/fsnotify/fsnotify/fen.go +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright 2010 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -//go:build solaris -// +build solaris - -package fsnotify - -import ( -	"errors" -) - -// Watcher watches a set of files, delivering events to a channel. -type Watcher struct { -	Events chan Event -	Errors chan error -} - -// NewWatcher establishes a new watcher with the underlying OS and begins waiting for events. -func NewWatcher() (*Watcher, error) { -	return nil, errors.New("FEN based watcher not yet supported for fsnotify\n") -} - -// Close removes all watches and closes the events channel. -func (w *Watcher) Close() error { -	return nil -} - -// Add starts watching the named file or directory (non-recursively). -func (w *Watcher) Add(name string) error { -	return nil -} - -// Remove stops watching the the named file or directory (non-recursively). -func (w *Watcher) Remove(name string) error { -	return nil -} diff --git a/vendor/github.com/fsnotify/fsnotify/fsnotify.go b/vendor/github.com/fsnotify/fsnotify/fsnotify.go index 0f4ee52e8..30a5bf0f0 100644 --- a/vendor/github.com/fsnotify/fsnotify/fsnotify.go +++ b/vendor/github.com/fsnotify/fsnotify/fsnotify.go @@ -1,29 +1,37 @@ -// Copyright 2012 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. -  //go:build !plan9  // +build !plan9 -// Package fsnotify provides a platform-independent interface for file system notifications. +// Package fsnotify provides a cross-platform interface for file system +// notifications.  package fsnotify  import ( -	"bytes"  	"errors"  	"fmt" +	"strings"  ) -// Event represents a single file system notification. +// Event represents a file system notification.  type Event struct { -	Name string // Relative path to the file or directory. -	Op   Op     // File operation that triggered the event. +	// Path to the file or directory. +	// +	// Paths are relative to the input; for example with Add("dir") the Name +	// will be set to "dir/file" if you create that file, but if you use +	// Add("/path/to/dir") it will be "/path/to/dir/file". +	Name string + +	// File operation that triggered the event. +	// +	// This is a bitmask and some systems may send multiple operations at once. +	// Use the Event.Has() method instead of comparing with ==. +	Op Op  }  // Op describes a set of file operations.  type Op uint32 -// These are the generalized file operations that can trigger a notification. +// The operations fsnotify can trigger; see the documentation on [Watcher] for a +// full description, and check them with [Event.Has].  const (  	Create Op = 1 << iota  	Write @@ -32,38 +40,42 @@ const (  	Chmod  ) -func (op Op) String() string { -	// Use a buffer for efficient string concatenation -	var buffer bytes.Buffer +// Common errors that can be reported by a watcher +var ( +	ErrNonExistentWatch = errors.New("can't remove non-existent watcher") +	ErrEventOverflow    = errors.New("fsnotify queue overflow") +) -	if op&Create == Create { -		buffer.WriteString("|CREATE") +func (op Op) String() string { +	var b strings.Builder +	if op.Has(Create) { +		b.WriteString("|CREATE")  	} -	if op&Remove == Remove { -		buffer.WriteString("|REMOVE") +	if op.Has(Remove) { +		b.WriteString("|REMOVE")  	} -	if op&Write == Write { -		buffer.WriteString("|WRITE") +	if op.Has(Write) { +		b.WriteString("|WRITE")  	} -	if op&Rename == Rename { -		buffer.WriteString("|RENAME") +	if op.Has(Rename) { +		b.WriteString("|RENAME")  	} -	if op&Chmod == Chmod { -		buffer.WriteString("|CHMOD") +	if op.Has(Chmod) { +		b.WriteString("|CHMOD")  	} -	if buffer.Len() == 0 { -		return "" +	if b.Len() == 0 { +		return "[no events]"  	} -	return buffer.String()[1:] // Strip leading pipe +	return b.String()[1:]  } -// String returns a string representation of the event in the form -// "file: REMOVE|WRITE|..." +// Has reports if this operation has the given operation. +func (o Op) Has(h Op) bool { return o&h == h } + +// Has reports if this event has the given operation. +func (e Event) Has(op Op) bool { return e.Op.Has(op) } + +// String returns a string representation of the event with their path.  func (e Event) String() string { -	return fmt.Sprintf("%q: %s", e.Name, e.Op.String()) +	return fmt.Sprintf("%-13s %q", e.Op.String(), e.Name)  } - -// Common errors that can be reported by a watcher -var ( -	ErrEventOverflow = errors.New("fsnotify queue overflow") -) diff --git a/vendor/github.com/fsnotify/fsnotify/fsnotify_unsupported.go b/vendor/github.com/fsnotify/fsnotify/fsnotify_unsupported.go deleted file mode 100644 index 596885598..000000000 --- a/vendor/github.com/fsnotify/fsnotify/fsnotify_unsupported.go +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright 2022 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -//go:build !darwin && !dragonfly && !freebsd && !openbsd && !linux && !netbsd && !solaris && !windows -// +build !darwin,!dragonfly,!freebsd,!openbsd,!linux,!netbsd,!solaris,!windows - -package fsnotify - -import ( -	"fmt" -	"runtime" -) - -// Watcher watches a set of files, delivering events to a channel. -type Watcher struct{} - -// NewWatcher establishes a new watcher with the underlying OS and begins waiting for events. -func NewWatcher() (*Watcher, error) { -	return nil, fmt.Errorf("fsnotify not supported on %s", runtime.GOOS) -} - -// Close removes all watches and closes the events channel. -func (w *Watcher) Close() error { -	return nil -} - -// Add starts watching the named file or directory (non-recursively). -func (w *Watcher) Add(name string) error { -	return nil -} - -// Remove stops watching the the named file or directory (non-recursively). -func (w *Watcher) Remove(name string) error { -	return nil -} diff --git a/vendor/github.com/fsnotify/fsnotify/inotify.go b/vendor/github.com/fsnotify/fsnotify/inotify.go deleted file mode 100644 index a6d0e0ec8..000000000 --- a/vendor/github.com/fsnotify/fsnotify/inotify.go +++ /dev/null @@ -1,351 +0,0 @@ -// Copyright 2010 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -//go:build linux -// +build linux - -package fsnotify - -import ( -	"errors" -	"fmt" -	"io" -	"os" -	"path/filepath" -	"strings" -	"sync" -	"unsafe" - -	"golang.org/x/sys/unix" -) - -// Watcher watches a set of files, delivering events to a channel. -type Watcher struct { -	Events   chan Event -	Errors   chan error -	mu       sync.Mutex // Map access -	fd       int -	poller   *fdPoller -	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 -} - -// NewWatcher establishes a new watcher with the underlying OS and begins waiting for events. -func NewWatcher() (*Watcher, error) { -	// Create inotify fd -	fd, errno := unix.InotifyInit1(unix.IN_CLOEXEC) -	if fd == -1 { -		return nil, errno -	} -	// Create epoll -	poller, err := newFdPoller(fd) -	if err != nil { -		unix.Close(fd) -		return nil, err -	} -	w := &Watcher{ -		fd:       fd, -		poller:   poller, -		watches:  make(map[string]*watch), -		paths:    make(map[int]string), -		Events:   make(chan Event), -		Errors:   make(chan error), -		done:     make(chan struct{}), -		doneResp: make(chan struct{}), -	} - -	go w.readEvents() -	return w, nil -} - -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 { -	if w.isClosed() { -		return nil -	} - -	// Send 'close' signal to goroutine, and set the Watcher to closed. -	close(w.done) - -	// Wake up goroutine -	w.poller.wake() - -	// Wait for goroutine to close -	<-w.doneResp - -	return nil -} - -// Add starts watching the named file or directory (non-recursively). -func (w *Watcher) Add(name string) error { -	name = filepath.Clean(name) -	if w.isClosed() { -		return errors.New("inotify instance already closed") -	} - -	const agnosticEvents = unix.IN_MOVED_TO | unix.IN_MOVED_FROM | -		unix.IN_CREATE | unix.IN_ATTRIB | unix.IN_MODIFY | -		unix.IN_MOVE_SELF | unix.IN_DELETE | unix.IN_DELETE_SELF - -	var flags uint32 = agnosticEvents - -	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 -	} - -	if watchEntry == nil { -		w.watches[name] = &watch{wd: uint32(wd), flags: flags} -		w.paths[wd] = name -	} else { -		watchEntry.wd = uint32(wd) -		watchEntry.flags = flags -	} - -	return nil -} - -// Remove stops watching the named file or directory (non-recursively). -func (w *Watcher) Remove(name string) error { -	name = filepath.Clean(name) - -	// Fetch the watch. -	w.mu.Lock() -	defer w.mu.Unlock() -	watch, ok := w.watches[name] - -	// Remove it from inotify. -	if !ok { -		return fmt.Errorf("can't remove non-existent inotify watch for: %s", 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) -	if success == -1 { -		// TODO: Perhaps it's not helpful to return an error here in every case. -		// the only two possible errors are: -		// EBADF, which happens when w.fd is not a valid file descriptor of any kind. -		// EINVAL, which is when fd is not an inotify descriptor or wd is not a valid watch descriptor. -		// Watch descriptors are invalidated when they are removed explicitly or implicitly; -		// explicitly by inotify_rm_watch, implicitly when the file they are watching is deleted. -		return errno -	} - -	return nil -} - -// WatchList returns the directories and files that are being monitered. -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) -	} - -	return entries -} - -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) -} - -// readEvents reads from the inotify file descriptor, converts the -// received events into Event objects and sends them via the Events channel -func (w *Watcher) readEvents() { -	var ( -		buf   [unix.SizeofInotifyEvent * 4096]byte // Buffer for a maximum of 4096 raw events -		n     int                                  // Number of bytes read with read() -		errno error                                // Syscall errno -		ok    bool                                 // For poller.wait -	) - -	defer close(w.doneResp) -	defer close(w.Errors) -	defer close(w.Events) -	defer unix.Close(w.fd) -	defer w.poller.close() - -	for { -		// See if we have been closed. -		if w.isClosed() { -			return -		} - -		ok, errno = w.poller.wait() -		if errno != nil { -			select { -			case w.Errors <- errno: -			case <-w.done: -				return -			} -			continue -		} - -		if !ok { -			continue -		} - -		n, errno = unix.Read(w.fd, buf[:]) -		// If a signal interrupted execution, see if we've been asked to close, and try again. -		// http://man7.org/linux/man-pages/man7/signal.7.html : -		// "Before Linux 3.8, reads from an inotify(7) file descriptor were not restartable" -		if errno == unix.EINTR { -			continue -		} - -		// unix.Read might have been woken up by Close. If so, we're done. -		if w.isClosed() { -			return -		} - -		if n < unix.SizeofInotifyEvent { -			var err error -			if n == 0 { -				// If EOF is received. This should really never happen. -				err = io.EOF -			} else if n < 0 { -				// If an error occurred while reading. -				err = errno -			} else { -				// Read was too short. -				err = errors.New("notify: short read in readEvents()") -			} -			select { -			case w.Errors <- err: -			case <-w.done: -				return -			} -			continue -		} - -		var offset uint32 -		// We don't know how many events we just read into the buffer -		// While the offset points to at least one whole event... -		for offset <= uint32(n-unix.SizeofInotifyEvent) { -			// Point "raw" to the event in the buffer -			raw := (*unix.InotifyEvent)(unsafe.Pointer(&buf[offset])) - -			mask := uint32(raw.Mask) -			nameLen := uint32(raw.Len) - -			if mask&unix.IN_Q_OVERFLOW != 0 { -				select { -				case w.Errors <- ErrEventOverflow: -				case <-w.done: -					return -				} -			} - -			// If the event happened to the watched directory or the watched file, the kernel -			// doesn't append the filename to the event, but we would like to always fill the -			// the "Name" field with a valid filename. We retrieve the path of the watch from -			// the "paths" map. -			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() - -			if nameLen > 0 { -				// Point "bytes" at the first byte of the filename -				bytes := (*[unix.PathMax]byte)(unsafe.Pointer(&buf[offset+unix.SizeofInotifyEvent]))[:nameLen:nameLen] -				// The filename is padded with NULL bytes. TrimRight() gets rid of those. -				name += "/" + strings.TrimRight(string(bytes[0:nameLen]), "\000") -			} - -			event := newEvent(name, mask) - -			// Send the events that are not ignored on the events channel -			if !event.ignoreLinux(mask) { -				select { -				case w.Events <- event: -				case <-w.done: -					return -				} -			} - -			// Move to the next event in the buffer -			offset += unix.SizeofInotifyEvent + nameLen -		} -	} -} - -// Certain types of events can be "ignored" and not sent over the Events -// channel. Such as events marked ignore by the kernel, or MODIFY events -// against files that do not exist. -func (e *Event) ignoreLinux(mask uint32) bool { -	// Ignore anything the inotify API says to ignore -	if mask&unix.IN_IGNORED == unix.IN_IGNORED { -		return true -	} - -	// If the event is not a DELETE or RENAME, the file must exist. -	// Otherwise the event is ignored. -	// *Note*: this was put in place because it was seen that a MODIFY -	// event was sent after the DELETE. This ignores that MODIFY and -	// assumes a DELETE will come or has come if the file doesn't exist. -	if !(e.Op&Remove == Remove || e.Op&Rename == Rename) { -		_, statErr := os.Lstat(e.Name) -		return os.IsNotExist(statErr) -	} -	return false -} - -// newEvent returns an platform-independent Event based on an inotify mask. -func newEvent(name string, mask uint32) Event { -	e := Event{Name: name} -	if mask&unix.IN_CREATE == unix.IN_CREATE || mask&unix.IN_MOVED_TO == unix.IN_MOVED_TO { -		e.Op |= Create -	} -	if mask&unix.IN_DELETE_SELF == unix.IN_DELETE_SELF || mask&unix.IN_DELETE == unix.IN_DELETE { -		e.Op |= Remove -	} -	if mask&unix.IN_MODIFY == unix.IN_MODIFY { -		e.Op |= Write -	} -	if mask&unix.IN_MOVE_SELF == unix.IN_MOVE_SELF || mask&unix.IN_MOVED_FROM == unix.IN_MOVED_FROM { -		e.Op |= Rename -	} -	if mask&unix.IN_ATTRIB == unix.IN_ATTRIB { -		e.Op |= Chmod -	} -	return e -} diff --git a/vendor/github.com/fsnotify/fsnotify/inotify_poller.go b/vendor/github.com/fsnotify/fsnotify/inotify_poller.go deleted file mode 100644 index b572a37c3..000000000 --- a/vendor/github.com/fsnotify/fsnotify/inotify_poller.go +++ /dev/null @@ -1,187 +0,0 @@ -// Copyright 2015 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -//go:build linux -// +build linux - -package fsnotify - -import ( -	"errors" - -	"golang.org/x/sys/unix" -) - -type fdPoller struct { -	fd   int    // File descriptor (as returned by the inotify_init() syscall) -	epfd int    // Epoll file descriptor -	pipe [2]int // Pipe for waking up -} - -func emptyPoller(fd int) *fdPoller { -	poller := new(fdPoller) -	poller.fd = fd -	poller.epfd = -1 -	poller.pipe[0] = -1 -	poller.pipe[1] = -1 -	return poller -} - -// Create a new inotify poller. -// This creates an inotify handler, and an epoll handler. -func newFdPoller(fd int) (*fdPoller, error) { -	var errno error -	poller := emptyPoller(fd) -	defer func() { -		if errno != nil { -			poller.close() -		} -	}() - -	// Create epoll fd -	poller.epfd, errno = unix.EpollCreate1(unix.EPOLL_CLOEXEC) -	if poller.epfd == -1 { -		return nil, errno -	} -	// Create pipe; pipe[0] is the read end, pipe[1] the write end. -	errno = unix.Pipe2(poller.pipe[:], unix.O_NONBLOCK|unix.O_CLOEXEC) -	if errno != nil { -		return nil, errno -	} - -	// Register inotify fd with epoll -	event := unix.EpollEvent{ -		Fd:     int32(poller.fd), -		Events: unix.EPOLLIN, -	} -	errno = unix.EpollCtl(poller.epfd, unix.EPOLL_CTL_ADD, poller.fd, &event) -	if errno != nil { -		return nil, errno -	} - -	// Register pipe fd with epoll -	event = unix.EpollEvent{ -		Fd:     int32(poller.pipe[0]), -		Events: unix.EPOLLIN, -	} -	errno = unix.EpollCtl(poller.epfd, unix.EPOLL_CTL_ADD, poller.pipe[0], &event) -	if errno != nil { -		return nil, errno -	} - -	return poller, nil -} - -// Wait using epoll. -// Returns true if something is ready to be read, -// false if there is not. -func (poller *fdPoller) wait() (bool, error) { -	// 3 possible events per fd, and 2 fds, makes a maximum of 6 events. -	// I don't know whether epoll_wait returns the number of events returned, -	// or the total number of events ready. -	// I decided to catch both by making the buffer one larger than the maximum. -	events := make([]unix.EpollEvent, 7) -	for { -		n, errno := unix.EpollWait(poller.epfd, events, -1) -		if n == -1 { -			if errno == unix.EINTR { -				continue -			} -			return false, errno -		} -		if n == 0 { -			// If there are no events, try again. -			continue -		} -		if n > 6 { -			// This should never happen. More events were returned than should be possible. -			return false, errors.New("epoll_wait returned more events than I know what to do with") -		} -		ready := events[:n] -		epollhup := false -		epollerr := false -		epollin := false -		for _, event := range ready { -			if event.Fd == int32(poller.fd) { -				if event.Events&unix.EPOLLHUP != 0 { -					// This should not happen, but if it does, treat it as a wakeup. -					epollhup = true -				} -				if event.Events&unix.EPOLLERR != 0 { -					// If an error is waiting on the file descriptor, we should pretend -					// something is ready to read, and let unix.Read pick up the error. -					epollerr = true -				} -				if event.Events&unix.EPOLLIN != 0 { -					// There is data to read. -					epollin = true -				} -			} -			if event.Fd == int32(poller.pipe[0]) { -				if event.Events&unix.EPOLLHUP != 0 { -					// Write pipe descriptor was closed, by us. This means we're closing down the -					// watcher, and we should wake up. -				} -				if event.Events&unix.EPOLLERR != 0 { -					// If an error is waiting on the pipe file descriptor. -					// This is an absolute mystery, and should never ever happen. -					return false, errors.New("Error on the pipe descriptor.") -				} -				if event.Events&unix.EPOLLIN != 0 { -					// This is a regular wakeup, so we have to clear the buffer. -					err := poller.clearWake() -					if err != nil { -						return false, err -					} -				} -			} -		} - -		if epollhup || epollerr || epollin { -			return true, nil -		} -		return false, nil -	} -} - -// Close the write end of the poller. -func (poller *fdPoller) wake() error { -	buf := make([]byte, 1) -	n, errno := unix.Write(poller.pipe[1], buf) -	if n == -1 { -		if errno == unix.EAGAIN { -			// Buffer is full, poller will wake. -			return nil -		} -		return errno -	} -	return nil -} - -func (poller *fdPoller) clearWake() error { -	// You have to be woken up a LOT in order to get to 100! -	buf := make([]byte, 100) -	n, errno := unix.Read(poller.pipe[0], buf) -	if n == -1 { -		if errno == unix.EAGAIN { -			// Buffer is empty, someone else cleared our wake. -			return nil -		} -		return errno -	} -	return nil -} - -// Close all poller file descriptors, but not the one passed to it. -func (poller *fdPoller) close() { -	if poller.pipe[1] != -1 { -		unix.Close(poller.pipe[1]) -	} -	if poller.pipe[0] != -1 { -		unix.Close(poller.pipe[0]) -	} -	if poller.epfd != -1 { -		unix.Close(poller.epfd) -	} -} diff --git a/vendor/github.com/fsnotify/fsnotify/kqueue.go b/vendor/github.com/fsnotify/fsnotify/kqueue.go deleted file mode 100644 index 6fb8d8532..000000000 --- a/vendor/github.com/fsnotify/fsnotify/kqueue.go +++ /dev/null @@ -1,535 +0,0 @@ -// Copyright 2010 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -//go:build freebsd || openbsd || netbsd || dragonfly || darwin -// +build freebsd openbsd netbsd dragonfly darwin - -package fsnotify - -import ( -	"errors" -	"fmt" -	"io/ioutil" -	"os" -	"path/filepath" -	"sync" -	"time" - -	"golang.org/x/sys/unix" -) - -// Watcher watches a set of files, delivering events to a channel. -type Watcher struct { -	Events chan Event -	Errors chan error -	done   chan struct{} // Channel for sending a "quit message" to the reader goroutine - -	kq int // File descriptor (as returned by the kqueue() syscall). - -	mu              sync.Mutex        // Protects access to watcher data -	watches         map[string]int    // Map of watched file descriptors (key: path). -	externalWatches map[string]bool   // Map of watches added by user of the library. -	dirFlags        map[string]uint32 // Map of watched directories to fflags used in kqueue. -	paths           map[int]pathInfo  // Map file descriptors to path names for processing kqueue events. -	fileExists      map[string]bool   // Keep track of if we know this file exists (to stop duplicate create events). -	isClosed        bool              // Set to true when Close() is first called -} - -type pathInfo struct { -	name  string -	isDir bool -} - -// NewWatcher establishes a new watcher with the underlying OS and begins waiting for events. -func NewWatcher() (*Watcher, error) { -	kq, err := kqueue() -	if err != nil { -		return nil, err -	} - -	w := &Watcher{ -		kq:              kq, -		watches:         make(map[string]int), -		dirFlags:        make(map[string]uint32), -		paths:           make(map[int]pathInfo), -		fileExists:      make(map[string]bool), -		externalWatches: make(map[string]bool), -		Events:          make(chan Event), -		Errors:          make(chan error), -		done:            make(chan struct{}), -	} - -	go w.readEvents() -	return w, nil -} - -// Close removes all watches and closes the events channel. -func (w *Watcher) Close() error { -	w.mu.Lock() -	if w.isClosed { -		w.mu.Unlock() -		return nil -	} -	w.isClosed = true - -	// copy paths to remove while locked -	var pathsToRemove = make([]string, 0, len(w.watches)) -	for name := range w.watches { -		pathsToRemove = append(pathsToRemove, name) -	} -	w.mu.Unlock() -	// unlock before calling Remove, which also locks - -	for _, name := range pathsToRemove { -		w.Remove(name) -	} - -	// send a "quit" message to the reader goroutine -	close(w.done) - -	return nil -} - -// Add starts watching the named file or directory (non-recursively). -func (w *Watcher) Add(name string) error { -	w.mu.Lock() -	w.externalWatches[name] = true -	w.mu.Unlock() -	_, err := w.addWatch(name, noteAllEvents) -	return err -} - -// Remove stops watching the the named file or directory (non-recursively). -func (w *Watcher) Remove(name string) error { -	name = filepath.Clean(name) -	w.mu.Lock() -	watchfd, ok := w.watches[name] -	w.mu.Unlock() -	if !ok { -		return fmt.Errorf("can't remove non-existent kevent watch for: %s", name) -	} - -	const registerRemove = unix.EV_DELETE -	if err := register(w.kq, []int{watchfd}, registerRemove, 0); err != nil { -		return err -	} - -	unix.Close(watchfd) - -	w.mu.Lock() -	isDir := w.paths[watchfd].isDir -	delete(w.watches, name) -	delete(w.paths, watchfd) -	delete(w.dirFlags, name) -	w.mu.Unlock() - -	// Find all watched paths that are in this directory that are not external. -	if isDir { -		var pathsToRemove []string -		w.mu.Lock() -		for _, path := range w.paths { -			wdir, _ := filepath.Split(path.name) -			if filepath.Clean(wdir) == name { -				if !w.externalWatches[path.name] { -					pathsToRemove = append(pathsToRemove, path.name) -				} -			} -		} -		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. -			w.Remove(name) -		} -	} - -	return nil -} - -// WatchList returns the directories and files that are being monitered. -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) -	} - -	return entries -} - -// Watch all events (except NOTE_EXTEND, NOTE_LINK, NOTE_REVOKE) -const noteAllEvents = unix.NOTE_DELETE | unix.NOTE_WRITE | unix.NOTE_ATTRIB | unix.NOTE_RENAME - -// keventWaitTime to block on each read from kevent -var keventWaitTime = durationToTimespec(100 * time.Millisecond) - -// 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. -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") -	} -	watchfd, alreadyWatching := w.watches[name] -	// We already have a watch, but we can still override flags. -	if alreadyWatching { -		isDir = w.paths[watchfd].isDir -	} -	w.mu.Unlock() - -	if !alreadyWatching { -		fi, err := os.Lstat(name) -		if err != nil { -			return "", err -		} - -		// Don't watch sockets. -		if fi.Mode()&os.ModeSocket == os.ModeSocket { -			return "", nil -		} - -		// Don't watch named pipes. -		if fi.Mode()&os.ModeNamedPipe == os.ModeNamedPipe { -			return "", nil -		} - -		// Follow Symlinks -		// Unfortunately, Linux can add bogus symlinks to watch list without -		// issue, and Windows can't do symlinks period (AFAIK). To  maintain -		// consistency, we will act like everything is fine. There will simply -		// be no file events for broken symlinks. -		// Hence the returns of nil on errors. -		if fi.Mode()&os.ModeSymlink == os.ModeSymlink { -			name, err = filepath.EvalSymlinks(name) -			if err != nil { -				return "", nil -			} - -			w.mu.Lock() -			_, alreadyWatching = w.watches[name] -			w.mu.Unlock() - -			if alreadyWatching { -				return name, nil -			} - -			fi, err = os.Lstat(name) -			if err != nil { -				return "", nil -			} -		} - -		watchfd, err = unix.Open(name, openMode, 0700) -		if watchfd == -1 { -			return "", err -		} - -		isDir = fi.IsDir() -	} - -	const registerAdd = unix.EV_ADD | unix.EV_CLEAR | unix.EV_ENABLE -	if err := register(w.kq, []int{watchfd}, registerAdd, flags); err != nil { -		unix.Close(watchfd) -		return "", err -	} - -	if !alreadyWatching { -		w.mu.Lock() -		w.watches[name] = watchfd -		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) -		w.mu.Lock() - -		watchDir := (flags&unix.NOTE_WRITE) == unix.NOTE_WRITE && -			(!alreadyWatching || (w.dirFlags[name]&unix.NOTE_WRITE) != unix.NOTE_WRITE) -		// Store flags so this watch can be updated later -		w.dirFlags[name] = flags -		w.mu.Unlock() - -		if watchDir { -			if err := w.watchDirectoryFiles(name); err != nil { -				return "", err -			} -		} -	} -	return name, nil -} - -// readEvents reads from kqueue and converts the received kevents into -// Event values that it sends down the Events channel. -func (w *Watcher) readEvents() { -	eventBuffer := make([]unix.Kevent_t, 10) - -loop: -	for { -		// See if there is a message on the "done" channel -		select { -		case <-w.done: -			break loop -		default: -		} - -		// Get new events -		kevents, err := read(w.kq, eventBuffer, &keventWaitTime) -		// EINTR is okay, the syscall was interrupted before timeout expired. -		if err != nil && err != unix.EINTR { -			select { -			case w.Errors <- err: -			case <-w.done: -				break loop -			} -			continue -		} - -		// Flush the events we received to the Events channel -		for len(kevents) > 0 { -			kevent := &kevents[0] -			watchfd := int(kevent.Ident) -			mask := uint32(kevent.Fflags) -			w.mu.Lock() -			path := w.paths[watchfd] -			w.mu.Unlock() -			event := newEvent(path.name, mask) - -			if path.isDir && !(event.Op&Remove == 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) { -					// mark is as delete event -					event.Op |= Remove -				} -			} - -			if event.Op&Rename == Rename || event.Op&Remove == Remove { -				w.Remove(event.Name) -				w.mu.Lock() -				delete(w.fileExists, event.Name) -				w.mu.Unlock() -			} - -			if path.isDir && event.Op&Write == Write && !(event.Op&Remove == Remove) { -				w.sendDirectoryChangeEvents(event.Name) -			} else { -				// Send the event on the Events channel. -				select { -				case w.Events <- event: -				case <-w.done: -					break loop -				} -			} - -			if event.Op&Remove == Remove { -				// 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) -						} -					} -				} else { -					filePath := filepath.Clean(event.Name) -					if fileInfo, err := os.Lstat(filePath); err == nil { -						w.sendFileCreatedEventIfNew(filePath, fileInfo) -					} -				} -			} - -			// Move to next event -			kevents = kevents[1:] -		} -	} - -	// cleanup -	err := unix.Close(w.kq) -	if err != nil { -		// only way the previous loop breaks is if w.done was closed so we need to async send to w.Errors. -		select { -		case w.Errors <- err: -		default: -		} -	} -	close(w.Events) -	close(w.Errors) -} - -// newEvent returns an platform-independent Event based on kqueue Fflags. -func newEvent(name string, mask uint32) Event { -	e := Event{Name: name} -	if mask&unix.NOTE_DELETE == unix.NOTE_DELETE { -		e.Op |= Remove -	} -	if mask&unix.NOTE_WRITE == unix.NOTE_WRITE { -		e.Op |= Write -	} -	if mask&unix.NOTE_RENAME == unix.NOTE_RENAME { -		e.Op |= Rename -	} -	if mask&unix.NOTE_ATTRIB == unix.NOTE_ATTRIB { -		e.Op |= Chmod -	} -	return e -} - -func newCreateEvent(name string) Event { -	return Event{Name: name, Op: Create} -} - -// 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) -	if err != nil { -		return err -	} - -	for _, fileInfo := range files { -		filePath := filepath.Join(dirPath, fileInfo.Name()) -		filePath, err = w.internalWatch(filePath, fileInfo) -		if err != nil { -			return err -		} - -		w.mu.Lock() -		w.fileExists[filePath] = true -		w.mu.Unlock() -	} - -	return nil -} - -// sendDirectoryEvents searches the directory for newly created files -// and sends them over the event channel. This functionality is to have -// the BSD version of fsnotify match Linux inotify which provides a -// create event for files created in a watched directory. -func (w *Watcher) sendDirectoryChangeEvents(dirPath string) { -	// Get all files -	files, err := ioutil.ReadDir(dirPath) -	if err != nil { -		select { -		case w.Errors <- err: -		case <-w.done: -			return -		} -	} - -	// Search for new files -	for _, fileInfo := range files { -		filePath := filepath.Join(dirPath, fileInfo.Name()) -		err := w.sendFileCreatedEventIfNew(filePath, fileInfo) - -		if err != nil { -			return -		} -	} -} - -// sendFileCreatedEvent sends a create event if the file isn't already being tracked. -func (w *Watcher) sendFileCreatedEventIfNew(filePath string, fileInfo os.FileInfo) (err error) { -	w.mu.Lock() -	_, doesExist := w.fileExists[filePath] -	w.mu.Unlock() -	if !doesExist { -		// Send create event -		select { -		case w.Events <- newCreateEvent(filePath): -		case <-w.done: -			return -		} -	} - -	// like watchDirectoryFiles (but without doing another ReadDir) -	filePath, err = w.internalWatch(filePath, fileInfo) -	if err != nil { -		return err -	} - -	w.mu.Lock() -	w.fileExists[filePath] = true -	w.mu.Unlock() - -	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 -		w.mu.Lock() -		flags := w.dirFlags[name] -		w.mu.Unlock() - -		flags |= unix.NOTE_DELETE | unix.NOTE_RENAME -		return w.addWatch(name, flags) -	} - -	// watch file to mimic Linux inotify -	return w.addWatch(name, noteAllEvents) -} - -// kqueue creates a new kernel event queue and returns a descriptor. -func kqueue() (kq int, err error) { -	kq, err = unix.Kqueue() -	if kq == -1 { -		return kq, err -	} -	return kq, nil -} - -// register events with the queue -func register(kq int, fds []int, flags int, fflags uint32) error { -	changes := make([]unix.Kevent_t, len(fds)) - -	for i, fd := range fds { -		// SetKevent converts int to the platform-specific types: -		unix.SetKevent(&changes[i], fd, unix.EVFILT_VNODE, flags) -		changes[i].Fflags = fflags -	} - -	// register the events -	success, err := unix.Kevent(kq, changes, nil, nil) -	if success == -1 { -		return err -	} -	return nil -} - -// read retrieves pending events, or waits until an event occurs. -// A timeout of nil blocks indefinitely, while 0 polls the queue. -func read(kq int, events []unix.Kevent_t, timeout *unix.Timespec) ([]unix.Kevent_t, error) { -	n, err := unix.Kevent(kq, nil, events, timeout) -	if err != nil { -		return nil, err -	} -	return events[0:n], nil -} - -// durationToTimespec prepares a timeout value -func durationToTimespec(d time.Duration) unix.Timespec { -	return unix.NsecToTimespec(d.Nanoseconds()) -} diff --git a/vendor/github.com/fsnotify/fsnotify/mkdoc.zsh b/vendor/github.com/fsnotify/fsnotify/mkdoc.zsh new file mode 100644 index 000000000..b09ef7683 --- /dev/null +++ b/vendor/github.com/fsnotify/fsnotify/mkdoc.zsh @@ -0,0 +1,208 @@ +#!/usr/bin/env zsh +[ "${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 🙃 + +watcher=$(<<EOF +// 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. +// +// # macOS 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]). +// +// [#11]: https://github.com/fsnotify/fsnotify/issues/11 +// [#15]: https://github.com/fsnotify/fsnotify/issues/15 +EOF +) + +new=$(<<EOF +// NewWatcher creates a new Watcher. +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 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. +// +// Notifications on network filesystems (NFS, SMB, FUSE, etc.) or special +// filesystems (/proc, /sys, etc.) generally don't work. +// +// # Watching directories +// +// All files in a directory are monitored, including new files that are created +// after the watcher is started. Subdirectories are not watched (i.e. it's +// non-recursive). +// +// # 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. +// +// 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]. +EOF +) + +remove=$(<<EOF +// Remove stops monitoring the path for changes. +// +// Directories are always removed non-recursively. For example, if you added +// /tmp/dir and /tmp/dir/subdir then you will need to remove both. +// +// Removing a path that has not yet been added returns [ErrNonExistentWatch]. +EOF +) + +close=$(<<EOF +// Close removes all watches and closes the events channel. +EOF +) + +watchlist=$(<<EOF +// WatchList returns all paths added with [Add] (and are not yet removed). +EOF +) + +events=$(<<EOF +	// 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, so you +	//                      probably want to wait until you've stopped receiving +	//                      them (see the dedup example in cmd/fsnotify). +	// +	//   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. +EOF +) + +errors=$(<<EOF +	// Errors sends any errors. +EOF +) + +set-cmt() { +	local pat=$1 +	local cmt=$2 + +	IFS=$'\n' local files=($(grep -n $pat backend_*~*_test.go)) +	for f in $files; do +		IFS=':' local fields=($=f) +		local file=$fields[1] +		local end=$(( $fields[2] - 1 )) + +		# Find start of comment. +		local start=0 +		IFS=$'\n' local lines=($(head -n$end $file)) +		for (( i = 1; i <= $#lines; i++ )); do +			local line=$lines[-$i] +			if ! grep -q '^[[:space:]]*//' <<<$line; then +				start=$(( end - (i - 2) )) +				break +			fi +		done + +		head -n $(( start - 1 )) $file  >/tmp/x +		print -r -- $cmt                >>/tmp/x +		tail -n+$(( end + 1 ))   $file  >>/tmp/x +		mv /tmp/x $file +	done +} + +set-cmt '^type Watcher struct '             $watcher +set-cmt '^func NewWatcher('                 $new +set-cmt '^func (w \*Watcher) Add('          $add +set-cmt '^func (w \*Watcher) Remove('       $remove +set-cmt '^func (w \*Watcher) Close('        $close +set-cmt '^func (w \*Watcher) WatchList('    $watchlist +set-cmt '^[[:space:]]*Events *chan Event$'  $events +set-cmt '^[[:space:]]*Errors *chan error$'  $errors diff --git a/vendor/github.com/fsnotify/fsnotify/open_mode_bsd.go b/vendor/github.com/fsnotify/fsnotify/system_bsd.go index 36cc3845b..4322b0b88 100644 --- a/vendor/github.com/fsnotify/fsnotify/open_mode_bsd.go +++ b/vendor/github.com/fsnotify/fsnotify/system_bsd.go @@ -1,7 +1,3 @@ -// Copyright 2013 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. -  //go:build freebsd || openbsd || netbsd || dragonfly  // +build freebsd openbsd netbsd dragonfly diff --git a/vendor/github.com/fsnotify/fsnotify/open_mode_darwin.go b/vendor/github.com/fsnotify/fsnotify/system_darwin.go index 98cd8476f..5da5ffa78 100644 --- a/vendor/github.com/fsnotify/fsnotify/open_mode_darwin.go +++ b/vendor/github.com/fsnotify/fsnotify/system_darwin.go @@ -1,7 +1,3 @@ -// Copyright 2013 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. -  //go:build darwin  // +build darwin diff --git a/vendor/github.com/fsnotify/fsnotify/windows.go b/vendor/github.com/fsnotify/fsnotify/windows.go deleted file mode 100644 index 02ce7deb0..000000000 --- a/vendor/github.com/fsnotify/fsnotify/windows.go +++ /dev/null @@ -1,586 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -//go:build windows -// +build windows - -package fsnotify - -import ( -	"errors" -	"fmt" -	"os" -	"path/filepath" -	"reflect" -	"runtime" -	"sync" -	"syscall" -	"unsafe" -) - -// Watcher watches a set of files, delivering events to a channel. -type Watcher struct { -	Events   chan Event -	Errors   chan error -	isClosed bool           // Set to true when Close() is first called -	mu       sync.Mutex     // Map access -	port     syscall.Handle // Handle to completion port -	watches  watchMap       // Map of watches (key: i-number) -	input    chan *input    // Inputs to the reader are sent on this channel -	quit     chan chan<- error -} - -// NewWatcher establishes a new watcher with the underlying OS and begins waiting for events. -func NewWatcher() (*Watcher, error) { -	port, e := syscall.CreateIoCompletionPort(syscall.InvalidHandle, 0, 0, 0) -	if e != nil { -		return nil, os.NewSyscallError("CreateIoCompletionPort", e) -	} -	w := &Watcher{ -		port:    port, -		watches: make(watchMap), -		input:   make(chan *input, 1), -		Events:  make(chan Event, 50), -		Errors:  make(chan error), -		quit:    make(chan chan<- error, 1), -	} -	go w.readEvents() -	return w, nil -} - -// Close removes all watches and closes the events channel. -func (w *Watcher) Close() error { -	if w.isClosed { -		return nil -	} -	w.isClosed = true - -	// Send "quit" message to the reader goroutine -	ch := make(chan error) -	w.quit <- ch -	if err := w.wakeupReader(); err != nil { -		return err -	} -	return <-ch -} - -// Add starts watching the named file or directory (non-recursively). -func (w *Watcher) Add(name string) error { -	if w.isClosed { -		return errors.New("watcher already closed") -	} -	in := &input{ -		op:    opAddWatch, -		path:  filepath.Clean(name), -		flags: sysFSALLEVENTS, -		reply: make(chan error), -	} -	w.input <- in -	if err := w.wakeupReader(); err != nil { -		return err -	} -	return <-in.reply -} - -// Remove stops watching the the named file or directory (non-recursively). -func (w *Watcher) Remove(name string) error { -	in := &input{ -		op:    opRemoveWatch, -		path:  filepath.Clean(name), -		reply: make(chan error), -	} -	w.input <- in -	if err := w.wakeupReader(); err != nil { -		return err -	} -	return <-in.reply -} - -// WatchList returns the directories and files that are being monitered. -func (w *Watcher) WatchList() []string { -	w.mu.Lock() -	defer w.mu.Unlock() - -	entries := make([]string, 0, len(w.watches)) -	for _, entry := range w.watches { -		for _, watchEntry := range entry { -			entries = append(entries, watchEntry.path) -		} -	} - -	return entries -} - -const ( -	// Options for AddWatch -	sysFSONESHOT = 0x80000000 -	sysFSONLYDIR = 0x1000000 - -	// Events -	sysFSACCESS     = 0x1 -	sysFSALLEVENTS  = 0xfff -	sysFSATTRIB     = 0x4 -	sysFSCLOSE      = 0x18 -	sysFSCREATE     = 0x100 -	sysFSDELETE     = 0x200 -	sysFSDELETESELF = 0x400 -	sysFSMODIFY     = 0x2 -	sysFSMOVE       = 0xc0 -	sysFSMOVEDFROM  = 0x40 -	sysFSMOVEDTO    = 0x80 -	sysFSMOVESELF   = 0x800 - -	// Special events -	sysFSIGNORED   = 0x8000 -	sysFSQOVERFLOW = 0x4000 -) - -func newEvent(name string, mask uint32) Event { -	e := Event{Name: name} -	if mask&sysFSCREATE == sysFSCREATE || mask&sysFSMOVEDTO == sysFSMOVEDTO { -		e.Op |= Create -	} -	if mask&sysFSDELETE == sysFSDELETE || mask&sysFSDELETESELF == sysFSDELETESELF { -		e.Op |= Remove -	} -	if mask&sysFSMODIFY == sysFSMODIFY { -		e.Op |= Write -	} -	if mask&sysFSMOVE == sysFSMOVE || mask&sysFSMOVESELF == sysFSMOVESELF || mask&sysFSMOVEDFROM == sysFSMOVEDFROM { -		e.Op |= Rename -	} -	if mask&sysFSATTRIB == sysFSATTRIB { -		e.Op |= Chmod -	} -	return e -} - -const ( -	opAddWatch = iota -	opRemoveWatch -) - -const ( -	provisional uint64 = 1 << (32 + iota) -) - -type input struct { -	op    int -	path  string -	flags uint32 -	reply chan error -} - -type inode struct { -	handle syscall.Handle -	volume uint32 -	index  uint64 -} - -type watch struct { -	ov     syscall.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    [4096]byte -} - -type indexMap map[uint64]*watch -type watchMap map[uint32]indexMap - -func (w *Watcher) wakeupReader() error { -	e := syscall.PostQueuedCompletionStatus(w.port, 0, 0, nil) -	if e != nil { -		return os.NewSyscallError("PostQueuedCompletionStatus", e) -	} -	return nil -} - -func getDir(pathname string) (dir string, err error) { -	attr, e := syscall.GetFileAttributes(syscall.StringToUTF16Ptr(pathname)) -	if e != nil { -		return "", os.NewSyscallError("GetFileAttributes", e) -	} -	if attr&syscall.FILE_ATTRIBUTE_DIRECTORY != 0 { -		dir = pathname -	} else { -		dir, _ = filepath.Split(pathname) -		dir = filepath.Clean(dir) -	} -	return -} - -func getIno(path string) (ino *inode, err error) { -	h, e := syscall.CreateFile(syscall.StringToUTF16Ptr(path), -		syscall.FILE_LIST_DIRECTORY, -		syscall.FILE_SHARE_READ|syscall.FILE_SHARE_WRITE|syscall.FILE_SHARE_DELETE, -		nil, syscall.OPEN_EXISTING, -		syscall.FILE_FLAG_BACKUP_SEMANTICS|syscall.FILE_FLAG_OVERLAPPED, 0) -	if e != nil { -		return nil, os.NewSyscallError("CreateFile", e) -	} -	var fi syscall.ByHandleFileInformation -	if e = syscall.GetFileInformationByHandle(h, &fi); e != nil { -		syscall.CloseHandle(h) -		return nil, os.NewSyscallError("GetFileInformationByHandle", e) -	} -	ino = &inode{ -		handle: h, -		volume: fi.VolumeSerialNumber, -		index:  uint64(fi.FileIndexHigh)<<32 | uint64(fi.FileIndexLow), -	} -	return ino, nil -} - -// Must run within the I/O thread. -func (m watchMap) get(ino *inode) *watch { -	if i := m[ino.volume]; i != nil { -		return i[ino.index] -	} -	return nil -} - -// Must run within the I/O thread. -func (m watchMap) set(ino *inode, watch *watch) { -	i := m[ino.volume] -	if i == nil { -		i = make(indexMap) -		m[ino.volume] = i -	} -	i[ino.index] = watch -} - -// Must run within the I/O thread. -func (w *Watcher) addWatch(pathname string, flags uint64) error { -	dir, err := getDir(pathname) -	if err != nil { -		return err -	} -	if flags&sysFSONLYDIR != 0 && pathname != dir { -		return nil -	} -	ino, err := getIno(dir) -	if err != nil { -		return err -	} -	w.mu.Lock() -	watchEntry := w.watches.get(ino) -	w.mu.Unlock() -	if watchEntry == nil { -		if _, e := syscall.CreateIoCompletionPort(ino.handle, w.port, 0, 0); e != nil { -			syscall.CloseHandle(ino.handle) -			return os.NewSyscallError("CreateIoCompletionPort", e) -		} -		watchEntry = &watch{ -			ino:   ino, -			path:  dir, -			names: make(map[string]uint64), -		} -		w.mu.Lock() -		w.watches.set(ino, watchEntry) -		w.mu.Unlock() -		flags |= provisional -	} else { -		syscall.CloseHandle(ino.handle) -	} -	if pathname == dir { -		watchEntry.mask |= flags -	} else { -		watchEntry.names[filepath.Base(pathname)] |= flags -	} -	if err = w.startRead(watchEntry); err != nil { -		return err -	} -	if pathname == dir { -		watchEntry.mask &= ^provisional -	} else { -		watchEntry.names[filepath.Base(pathname)] &= ^provisional -	} -	return nil -} - -// Must run within the I/O thread. -func (w *Watcher) remWatch(pathname string) error { -	dir, err := getDir(pathname) -	if err != nil { -		return err -	} -	ino, err := getIno(dir) -	if err != nil { -		return err -	} -	w.mu.Lock() -	watch := w.watches.get(ino) -	w.mu.Unlock() -	if watch == nil { -		return fmt.Errorf("can't remove non-existent watch for: %s", pathname) -	} -	if pathname == dir { -		w.sendEvent(watch.path, watch.mask&sysFSIGNORED) -		watch.mask = 0 -	} else { -		name := filepath.Base(pathname) -		w.sendEvent(filepath.Join(watch.path, name), watch.names[name]&sysFSIGNORED) -		delete(watch.names, name) -	} -	return w.startRead(watch) -} - -// Must run within the I/O thread. -func (w *Watcher) deleteWatch(watch *watch) { -	for name, mask := range watch.names { -		if mask&provisional == 0 { -			w.sendEvent(filepath.Join(watch.path, name), mask&sysFSIGNORED) -		} -		delete(watch.names, name) -	} -	if watch.mask != 0 { -		if watch.mask&provisional == 0 { -			w.sendEvent(watch.path, watch.mask&sysFSIGNORED) -		} -		watch.mask = 0 -	} -} - -// Must run within the I/O thread. -func (w *Watcher) startRead(watch *watch) error { -	if e := syscall.CancelIo(watch.ino.handle); e != nil { -		w.Errors <- os.NewSyscallError("CancelIo", e) -		w.deleteWatch(watch) -	} -	mask := toWindowsFlags(watch.mask) -	for _, m := range watch.names { -		mask |= toWindowsFlags(m) -	} -	if mask == 0 { -		if e := syscall.CloseHandle(watch.ino.handle); e != nil { -			w.Errors <- os.NewSyscallError("CloseHandle", e) -		} -		w.mu.Lock() -		delete(w.watches[watch.ino.volume], watch.ino.index) -		w.mu.Unlock() -		return nil -	} -	e := syscall.ReadDirectoryChanges(watch.ino.handle, &watch.buf[0], -		uint32(unsafe.Sizeof(watch.buf)), false, mask, nil, &watch.ov, 0) -	if e != nil { -		err := os.NewSyscallError("ReadDirectoryChanges", e) -		if e == syscall.ERROR_ACCESS_DENIED && watch.mask&provisional == 0 { -			// Watched directory was probably removed -			if w.sendEvent(watch.path, watch.mask&sysFSDELETESELF) { -				if watch.mask&sysFSONESHOT != 0 { -					watch.mask = 0 -				} -			} -			err = nil -		} -		w.deleteWatch(watch) -		w.startRead(watch) -		return err -	} -	return nil -} - -// readEvents reads from the I/O completion port, converts the -// received events into Event objects and sends them via the Events channel. -// Entry point to the I/O thread. -func (w *Watcher) readEvents() { -	var ( -		n, key uint32 -		ov     *syscall.Overlapped -	) -	runtime.LockOSThread() - -	for { -		e := syscall.GetQueuedCompletionStatus(w.port, &n, &key, &ov, syscall.INFINITE) -		watch := (*watch)(unsafe.Pointer(ov)) - -		if watch == nil { -			select { -			case ch := <-w.quit: -				w.mu.Lock() -				var indexes []indexMap -				for _, index := range w.watches { -					indexes = append(indexes, index) -				} -				w.mu.Unlock() -				for _, index := range indexes { -					for _, watch := range index { -						w.deleteWatch(watch) -						w.startRead(watch) -					} -				} -				var err error -				if e := syscall.CloseHandle(w.port); e != nil { -					err = os.NewSyscallError("CloseHandle", e) -				} -				close(w.Events) -				close(w.Errors) -				ch <- err -				return -			case in := <-w.input: -				switch in.op { -				case opAddWatch: -					in.reply <- w.addWatch(in.path, uint64(in.flags)) -				case opRemoveWatch: -					in.reply <- w.remWatch(in.path) -				} -			default: -			} -			continue -		} - -		switch e { -		case syscall.ERROR_MORE_DATA: -			if watch == nil { -				w.Errors <- errors.New("ERROR_MORE_DATA has unexpectedly null lpOverlapped buffer") -			} else { -				// The i/o succeeded but the buffer is full. -				// In theory we should be building up a full packet. -				// In practice we can get away with just carrying on. -				n = uint32(unsafe.Sizeof(watch.buf)) -			} -		case syscall.ERROR_ACCESS_DENIED: -			// Watched directory was probably removed -			w.sendEvent(watch.path, watch.mask&sysFSDELETESELF) -			w.deleteWatch(watch) -			w.startRead(watch) -			continue -		case syscall.ERROR_OPERATION_ABORTED: -			// CancelIo was called on this handle -			continue -		default: -			w.Errors <- os.NewSyscallError("GetQueuedCompletionPort", e) -			continue -		case nil: -		} - -		var offset uint32 -		for { -			if n == 0 { -				w.Events <- newEvent("", sysFSQOVERFLOW) -				w.Errors <- errors.New("short read in readEvents()") -				break -			} - -			// Point "raw" to the event in the buffer -			raw := (*syscall.FileNotifyInformation)(unsafe.Pointer(&watch.buf[offset])) -			// TODO: Consider using unsafe.Slice that is available from go1.17 -			// https://stackoverflow.com/questions/51187973/how-to-create-an-array-or-a-slice-from-an-array-unsafe-pointer-in-golang -			// instead of using a fixed syscall.MAX_PATH buf, we create a buf that is the size of the path name -			size := int(raw.FileNameLength / 2) -			var buf []uint16 -			sh := (*reflect.SliceHeader)(unsafe.Pointer(&buf)) -			sh.Data = uintptr(unsafe.Pointer(&raw.FileName)) -			sh.Len = size -			sh.Cap = size -			name := syscall.UTF16ToString(buf) -			fullname := filepath.Join(watch.path, name) - -			var mask uint64 -			switch raw.Action { -			case syscall.FILE_ACTION_REMOVED: -				mask = sysFSDELETESELF -			case syscall.FILE_ACTION_MODIFIED: -				mask = sysFSMODIFY -			case syscall.FILE_ACTION_RENAMED_OLD_NAME: -				watch.rename = name -			case syscall.FILE_ACTION_RENAMED_NEW_NAME: -				if watch.names[watch.rename] != 0 { -					watch.names[name] |= watch.names[watch.rename] -					delete(watch.names, watch.rename) -					mask = sysFSMOVESELF -				} -			} - -			sendNameEvent := func() { -				if w.sendEvent(fullname, watch.names[name]&mask) { -					if watch.names[name]&sysFSONESHOT != 0 { -						delete(watch.names, name) -					} -				} -			} -			if raw.Action != syscall.FILE_ACTION_RENAMED_NEW_NAME { -				sendNameEvent() -			} -			if raw.Action == syscall.FILE_ACTION_REMOVED { -				w.sendEvent(fullname, watch.names[name]&sysFSIGNORED) -				delete(watch.names, name) -			} -			if w.sendEvent(fullname, watch.mask&toFSnotifyFlags(raw.Action)) { -				if watch.mask&sysFSONESHOT != 0 { -					watch.mask = 0 -				} -			} -			if raw.Action == syscall.FILE_ACTION_RENAMED_NEW_NAME { -				fullname = filepath.Join(watch.path, watch.rename) -				sendNameEvent() -			} - -			// Move to the next event in the buffer -			if raw.NextEntryOffset == 0 { -				break -			} -			offset += raw.NextEntryOffset - -			// Error! -			if offset >= n { -				w.Errors <- errors.New("Windows system assumed buffer larger than it is, events have likely been missed.") -				break -			} -		} - -		if err := w.startRead(watch); err != nil { -			w.Errors <- err -		} -	} -} - -func (w *Watcher) sendEvent(name string, mask uint64) bool { -	if mask == 0 { -		return false -	} -	event := newEvent(name, uint32(mask)) -	select { -	case ch := <-w.quit: -		w.quit <- ch -	case w.Events <- event: -	} -	return true -} - -func toWindowsFlags(mask uint64) uint32 { -	var m uint32 -	if mask&sysFSACCESS != 0 { -		m |= syscall.FILE_NOTIFY_CHANGE_LAST_ACCESS -	} -	if mask&sysFSMODIFY != 0 { -		m |= syscall.FILE_NOTIFY_CHANGE_LAST_WRITE -	} -	if mask&sysFSATTRIB != 0 { -		m |= syscall.FILE_NOTIFY_CHANGE_ATTRIBUTES -	} -	if mask&(sysFSMOVE|sysFSCREATE|sysFSDELETE) != 0 { -		m |= syscall.FILE_NOTIFY_CHANGE_FILE_NAME | syscall.FILE_NOTIFY_CHANGE_DIR_NAME -	} -	return m -} - -func toFSnotifyFlags(action uint32) uint64 { -	switch action { -	case syscall.FILE_ACTION_ADDED: -		return sysFSCREATE -	case syscall.FILE_ACTION_REMOVED: -		return sysFSDELETE -	case syscall.FILE_ACTION_MODIFIED: -		return sysFSMODIFY -	case syscall.FILE_ACTION_RENAMED_OLD_NAME: -		return sysFSMOVEDFROM -	case syscall.FILE_ACTION_RENAMED_NEW_NAME: -		return sysFSMOVEDTO -	} -	return 0 -} diff --git a/vendor/github.com/spf13/viper/README.md b/vendor/github.com/spf13/viper/README.md index 5701422c8..63413a7dc 100644 --- a/vendor/github.com/spf13/viper/README.md +++ b/vendor/github.com/spf13/viper/README.md @@ -11,7 +11,7 @@  [](https://github.com/spf13/viper/actions?query=workflow%3ACI)  [](https://gitter.im/spf13/viper?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)  [](https://goreportcard.com/report/github.com/spf13/viper) - +  [](https://pkg.go.dev/mod/github.com/spf13/viper)  **Go configuration with fangs!** diff --git a/vendor/github.com/spf13/viper/viper.go b/vendor/github.com/spf13/viper/viper.go index 5f76cc095..5c12529b4 100644 --- a/vendor/github.com/spf13/viper/viper.go +++ b/vendor/github.com/spf13/viper/viper.go @@ -463,9 +463,8 @@ func (v *Viper) WatchConfig() {  					// we only care about the config file with the following cases:  					// 1 - if the config file was modified or created  					// 2 - if the real path to the config file changed (eg: k8s ConfigMap replacement) -					const writeOrCreateMask = fsnotify.Write | fsnotify.Create  					if (filepath.Clean(event.Name) == configFile && -						event.Op&writeOrCreateMask != 0) || +						(event.Has(fsnotify.Write) || event.Has(fsnotify.Create))) ||  						(currentConfigFile != "" && currentConfigFile != realConfigFile) {  						realConfigFile = currentConfigFile  						err := v.ReadInConfig() @@ -475,8 +474,7 @@ func (v *Viper) WatchConfig() {  						if v.onConfigChange != nil {  							v.onConfigChange(event)  						} -					} else if filepath.Clean(event.Name) == configFile && -						event.Op&fsnotify.Remove != 0 { +					} else if filepath.Clean(event.Name) == configFile && event.Has(fsnotify.Remove) {  						eventsWG.Done()  						return  					} diff --git a/vendor/github.com/spf13/viper/watch.go b/vendor/github.com/spf13/viper/watch.go index b5523b8f9..1ce84eaf8 100644 --- a/vendor/github.com/spf13/viper/watch.go +++ b/vendor/github.com/spf13/viper/watch.go @@ -1,5 +1,5 @@ -//go:build !js -// +build !js +//go:build darwin || dragonfly || freebsd || openbsd || linux || netbsd || solaris || windows +// +build darwin dragonfly freebsd openbsd linux netbsd solaris windows  package viper diff --git a/vendor/github.com/spf13/viper/watch_wasm.go b/vendor/github.com/spf13/viper/watch_unsupported.go index 8e47e6a91..7e2715377 100644 --- a/vendor/github.com/spf13/viper/watch_wasm.go +++ b/vendor/github.com/spf13/viper/watch_unsupported.go @@ -1,13 +1,19 @@ -// +build js,wasm +//go:build appengine || (!darwin && !dragonfly && !freebsd && !openbsd && !linux && !netbsd && !solaris && !windows) +// +build appengine !darwin,!dragonfly,!freebsd,!openbsd,!linux,!netbsd,!solaris,!windows  package viper  import ( -	"errors" +	"fmt" +	"runtime"  	"github.com/fsnotify/fsnotify"  ) +func newWatcher() (*watcher, error) { +	return &watcher{}, fmt.Errorf("fsnotify not supported on %s", runtime.GOOS) +} +  type watcher struct {  	Events chan fsnotify.Event  	Errors chan error @@ -24,7 +30,3 @@ func (*watcher) Add(name string) error {  func (*watcher) Remove(name string) error {  	return nil  } - -func newWatcher() (*watcher, error) { -	return &watcher{}, errors.New("fsnotify is not supported on WASM") -}  | 
