backend_windows.go 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827
  1. //go:build windows
  2. // +build windows
  3. // Windows backend based on ReadDirectoryChangesW()
  4. //
  5. // https://learn.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-readdirectorychangesw
  6. //
  7. // Note: the documentation on the Watcher type and methods is generated from
  8. // mkdoc.zsh
  9. package fsnotify
  10. import (
  11. "errors"
  12. "fmt"
  13. "os"
  14. "path/filepath"
  15. "reflect"
  16. "runtime"
  17. "strings"
  18. "sync"
  19. "unsafe"
  20. "golang.org/x/sys/windows"
  21. )
  22. // Watcher watches a set of paths, delivering events on a channel.
  23. //
  24. // A watcher should not be copied (e.g. pass it by pointer, rather than by
  25. // value).
  26. //
  27. // # Linux notes
  28. //
  29. // When a file is removed a Remove event won't be emitted until all file
  30. // descriptors are closed, and deletes will always emit a Chmod. For example:
  31. //
  32. // fp := os.Open("file")
  33. // os.Remove("file") // Triggers Chmod
  34. // fp.Close() // Triggers Remove
  35. //
  36. // This is the event that inotify sends, so not much can be changed about this.
  37. //
  38. // The fs.inotify.max_user_watches sysctl variable specifies the upper limit
  39. // for the number of watches per user, and fs.inotify.max_user_instances
  40. // specifies the maximum number of inotify instances per user. Every Watcher you
  41. // create is an "instance", and every path you add is a "watch".
  42. //
  43. // These are also exposed in /proc as /proc/sys/fs/inotify/max_user_watches and
  44. // /proc/sys/fs/inotify/max_user_instances
  45. //
  46. // To increase them you can use sysctl or write the value to the /proc file:
  47. //
  48. // # Default values on Linux 5.18
  49. // sysctl fs.inotify.max_user_watches=124983
  50. // sysctl fs.inotify.max_user_instances=128
  51. //
  52. // To make the changes persist on reboot edit /etc/sysctl.conf or
  53. // /usr/lib/sysctl.d/50-default.conf (details differ per Linux distro; check
  54. // your distro's documentation):
  55. //
  56. // fs.inotify.max_user_watches=124983
  57. // fs.inotify.max_user_instances=128
  58. //
  59. // Reaching the limit will result in a "no space left on device" or "too many open
  60. // files" error.
  61. //
  62. // # kqueue notes (macOS, BSD)
  63. //
  64. // kqueue requires opening a file descriptor for every file that's being watched;
  65. // so if you're watching a directory with five files then that's six file
  66. // descriptors. You will run in to your system's "max open files" limit faster on
  67. // these platforms.
  68. //
  69. // The sysctl variables kern.maxfiles and kern.maxfilesperproc can be used to
  70. // control the maximum number of open files, as well as /etc/login.conf on BSD
  71. // systems.
  72. //
  73. // # Windows notes
  74. //
  75. // Paths can be added as "C:\path\to\dir", but forward slashes
  76. // ("C:/path/to/dir") will also work.
  77. //
  78. // When a watched directory is removed it will always send an event for the
  79. // directory itself, but may not send events for all files in that directory.
  80. // Sometimes it will send events for all times, sometimes it will send no
  81. // events, and often only for some files.
  82. //
  83. // The default ReadDirectoryChangesW() buffer size is 64K, which is the largest
  84. // value that is guaranteed to work with SMB filesystems. If you have many
  85. // events in quick succession this may not be enough, and you will have to use
  86. // [WithBufferSize] to increase the value.
  87. type Watcher struct {
  88. // Events sends the filesystem change events.
  89. //
  90. // fsnotify can send the following events; a "path" here can refer to a
  91. // file, directory, symbolic link, or special file like a FIFO.
  92. //
  93. // fsnotify.Create A new path was created; this may be followed by one
  94. // or more Write events if data also gets written to a
  95. // file.
  96. //
  97. // fsnotify.Remove A path was removed.
  98. //
  99. // fsnotify.Rename A path was renamed. A rename is always sent with the
  100. // old path as Event.Name, and a Create event will be
  101. // sent with the new name. Renames are only sent for
  102. // paths that are currently watched; e.g. moving an
  103. // unmonitored file into a monitored directory will
  104. // show up as just a Create. Similarly, renaming a file
  105. // to outside a monitored directory will show up as
  106. // only a Rename.
  107. //
  108. // fsnotify.Write A file or named pipe was written to. A Truncate will
  109. // also trigger a Write. A single "write action"
  110. // initiated by the user may show up as one or multiple
  111. // writes, depending on when the system syncs things to
  112. // disk. For example when compiling a large Go program
  113. // you may get hundreds of Write events, and you may
  114. // want to wait until you've stopped receiving them
  115. // (see the dedup example in cmd/fsnotify).
  116. //
  117. // Some systems may send Write event for directories
  118. // when the directory content changes.
  119. //
  120. // fsnotify.Chmod Attributes were changed. On Linux this is also sent
  121. // when a file is removed (or more accurately, when a
  122. // link to an inode is removed). On kqueue it's sent
  123. // when a file is truncated. On Windows it's never
  124. // sent.
  125. Events chan Event
  126. // Errors sends any errors.
  127. //
  128. // ErrEventOverflow is used to indicate there are too many events:
  129. //
  130. // - inotify: There are too many queued events (fs.inotify.max_queued_events sysctl)
  131. // - windows: The buffer size is too small; WithBufferSize() can be used to increase it.
  132. // - kqueue, fen: Not used.
  133. Errors chan error
  134. port windows.Handle // Handle to completion port
  135. input chan *input // Inputs to the reader are sent on this channel
  136. quit chan chan<- error
  137. mu sync.Mutex // Protects access to watches, closed
  138. watches watchMap // Map of watches (key: i-number)
  139. closed bool // Set to true when Close() is first called
  140. }
  141. // NewWatcher creates a new Watcher.
  142. func NewWatcher() (*Watcher, error) {
  143. return NewBufferedWatcher(50)
  144. }
  145. // NewBufferedWatcher creates a new Watcher with a buffered Watcher.Events
  146. // channel.
  147. //
  148. // The main use case for this is situations with a very large number of events
  149. // where the kernel buffer size can't be increased (e.g. due to lack of
  150. // permissions). An unbuffered Watcher will perform better for almost all use
  151. // cases, and whenever possible you will be better off increasing the kernel
  152. // buffers instead of adding a large userspace buffer.
  153. func NewBufferedWatcher(sz uint) (*Watcher, error) {
  154. port, err := windows.CreateIoCompletionPort(windows.InvalidHandle, 0, 0, 0)
  155. if err != nil {
  156. return nil, os.NewSyscallError("CreateIoCompletionPort", err)
  157. }
  158. w := &Watcher{
  159. port: port,
  160. watches: make(watchMap),
  161. input: make(chan *input, 1),
  162. Events: make(chan Event, sz),
  163. Errors: make(chan error),
  164. quit: make(chan chan<- error, 1),
  165. }
  166. go w.readEvents()
  167. return w, nil
  168. }
  169. func (w *Watcher) isClosed() bool {
  170. w.mu.Lock()
  171. defer w.mu.Unlock()
  172. return w.closed
  173. }
  174. func (w *Watcher) sendEvent(name string, mask uint64) bool {
  175. if mask == 0 {
  176. return false
  177. }
  178. event := w.newEvent(name, uint32(mask))
  179. select {
  180. case ch := <-w.quit:
  181. w.quit <- ch
  182. case w.Events <- event:
  183. }
  184. return true
  185. }
  186. // Returns true if the error was sent, or false if watcher is closed.
  187. func (w *Watcher) sendError(err error) bool {
  188. select {
  189. case w.Errors <- err:
  190. return true
  191. case <-w.quit:
  192. }
  193. return false
  194. }
  195. // Close removes all watches and closes the Events channel.
  196. func (w *Watcher) Close() error {
  197. if w.isClosed() {
  198. return nil
  199. }
  200. w.mu.Lock()
  201. w.closed = true
  202. w.mu.Unlock()
  203. // Send "quit" message to the reader goroutine
  204. ch := make(chan error)
  205. w.quit <- ch
  206. if err := w.wakeupReader(); err != nil {
  207. return err
  208. }
  209. return <-ch
  210. }
  211. // Add starts monitoring the path for changes.
  212. //
  213. // A path can only be watched once; watching it more than once is a no-op and will
  214. // not return an error. Paths that do not yet exist on the filesystem cannot be
  215. // watched.
  216. //
  217. // A watch will be automatically removed if the watched path is deleted or
  218. // renamed. The exception is the Windows backend, which doesn't remove the
  219. // watcher on renames.
  220. //
  221. // Notifications on network filesystems (NFS, SMB, FUSE, etc.) or special
  222. // filesystems (/proc, /sys, etc.) generally don't work.
  223. //
  224. // Returns [ErrClosed] if [Watcher.Close] was called.
  225. //
  226. // See [Watcher.AddWith] for a version that allows adding options.
  227. //
  228. // # Watching directories
  229. //
  230. // All files in a directory are monitored, including new files that are created
  231. // after the watcher is started. Subdirectories are not watched (i.e. it's
  232. // non-recursive).
  233. //
  234. // # Watching files
  235. //
  236. // Watching individual files (rather than directories) is generally not
  237. // recommended as many programs (especially editors) update files atomically: it
  238. // will write to a temporary file which is then moved to to destination,
  239. // overwriting the original (or some variant thereof). The watcher on the
  240. // original file is now lost, as that no longer exists.
  241. //
  242. // The upshot of this is that a power failure or crash won't leave a
  243. // half-written file.
  244. //
  245. // Watch the parent directory and use Event.Name to filter out files you're not
  246. // interested in. There is an example of this in cmd/fsnotify/file.go.
  247. func (w *Watcher) Add(name string) error { return w.AddWith(name) }
  248. // AddWith is like [Watcher.Add], but allows adding options. When using Add()
  249. // the defaults described below are used.
  250. //
  251. // Possible options are:
  252. //
  253. // - [WithBufferSize] sets the buffer size for the Windows backend; no-op on
  254. // other platforms. The default is 64K (65536 bytes).
  255. func (w *Watcher) AddWith(name string, opts ...addOpt) error {
  256. if w.isClosed() {
  257. return ErrClosed
  258. }
  259. with := getOptions(opts...)
  260. if with.bufsize < 4096 {
  261. return fmt.Errorf("fsnotify.WithBufferSize: buffer size cannot be smaller than 4096 bytes")
  262. }
  263. in := &input{
  264. op: opAddWatch,
  265. path: filepath.Clean(name),
  266. flags: sysFSALLEVENTS,
  267. reply: make(chan error),
  268. bufsize: with.bufsize,
  269. }
  270. w.input <- in
  271. if err := w.wakeupReader(); err != nil {
  272. return err
  273. }
  274. return <-in.reply
  275. }
  276. // Remove stops monitoring the path for changes.
  277. //
  278. // Directories are always removed non-recursively. For example, if you added
  279. // /tmp/dir and /tmp/dir/subdir then you will need to remove both.
  280. //
  281. // Removing a path that has not yet been added returns [ErrNonExistentWatch].
  282. //
  283. // Returns nil if [Watcher.Close] was called.
  284. func (w *Watcher) Remove(name string) error {
  285. if w.isClosed() {
  286. return nil
  287. }
  288. in := &input{
  289. op: opRemoveWatch,
  290. path: filepath.Clean(name),
  291. reply: make(chan error),
  292. }
  293. w.input <- in
  294. if err := w.wakeupReader(); err != nil {
  295. return err
  296. }
  297. return <-in.reply
  298. }
  299. // WatchList returns all paths explicitly added with [Watcher.Add] (and are not
  300. // yet removed).
  301. //
  302. // Returns nil if [Watcher.Close] was called.
  303. func (w *Watcher) WatchList() []string {
  304. if w.isClosed() {
  305. return nil
  306. }
  307. w.mu.Lock()
  308. defer w.mu.Unlock()
  309. entries := make([]string, 0, len(w.watches))
  310. for _, entry := range w.watches {
  311. for _, watchEntry := range entry {
  312. entries = append(entries, watchEntry.path)
  313. }
  314. }
  315. return entries
  316. }
  317. // These options are from the old golang.org/x/exp/winfsnotify, where you could
  318. // add various options to the watch. This has long since been removed.
  319. //
  320. // The "sys" in the name is misleading as they're not part of any "system".
  321. //
  322. // This should all be removed at some point, and just use windows.FILE_NOTIFY_*
  323. const (
  324. sysFSALLEVENTS = 0xfff
  325. sysFSCREATE = 0x100
  326. sysFSDELETE = 0x200
  327. sysFSDELETESELF = 0x400
  328. sysFSMODIFY = 0x2
  329. sysFSMOVE = 0xc0
  330. sysFSMOVEDFROM = 0x40
  331. sysFSMOVEDTO = 0x80
  332. sysFSMOVESELF = 0x800
  333. sysFSIGNORED = 0x8000
  334. )
  335. func (w *Watcher) newEvent(name string, mask uint32) Event {
  336. e := Event{Name: name}
  337. if mask&sysFSCREATE == sysFSCREATE || mask&sysFSMOVEDTO == sysFSMOVEDTO {
  338. e.Op |= Create
  339. }
  340. if mask&sysFSDELETE == sysFSDELETE || mask&sysFSDELETESELF == sysFSDELETESELF {
  341. e.Op |= Remove
  342. }
  343. if mask&sysFSMODIFY == sysFSMODIFY {
  344. e.Op |= Write
  345. }
  346. if mask&sysFSMOVE == sysFSMOVE || mask&sysFSMOVESELF == sysFSMOVESELF || mask&sysFSMOVEDFROM == sysFSMOVEDFROM {
  347. e.Op |= Rename
  348. }
  349. return e
  350. }
  351. const (
  352. opAddWatch = iota
  353. opRemoveWatch
  354. )
  355. const (
  356. provisional uint64 = 1 << (32 + iota)
  357. )
  358. type input struct {
  359. op int
  360. path string
  361. flags uint32
  362. bufsize int
  363. reply chan error
  364. }
  365. type inode struct {
  366. handle windows.Handle
  367. volume uint32
  368. index uint64
  369. }
  370. type watch struct {
  371. ov windows.Overlapped
  372. ino *inode // i-number
  373. recurse bool // Recursive watch?
  374. path string // Directory path
  375. mask uint64 // Directory itself is being watched with these notify flags
  376. names map[string]uint64 // Map of names being watched and their notify flags
  377. rename string // Remembers the old name while renaming a file
  378. buf []byte // buffer, allocated later
  379. }
  380. type (
  381. indexMap map[uint64]*watch
  382. watchMap map[uint32]indexMap
  383. )
  384. func (w *Watcher) wakeupReader() error {
  385. err := windows.PostQueuedCompletionStatus(w.port, 0, 0, nil)
  386. if err != nil {
  387. return os.NewSyscallError("PostQueuedCompletionStatus", err)
  388. }
  389. return nil
  390. }
  391. func (w *Watcher) getDir(pathname string) (dir string, err error) {
  392. attr, err := windows.GetFileAttributes(windows.StringToUTF16Ptr(pathname))
  393. if err != nil {
  394. return "", os.NewSyscallError("GetFileAttributes", err)
  395. }
  396. if attr&windows.FILE_ATTRIBUTE_DIRECTORY != 0 {
  397. dir = pathname
  398. } else {
  399. dir, _ = filepath.Split(pathname)
  400. dir = filepath.Clean(dir)
  401. }
  402. return
  403. }
  404. func (w *Watcher) getIno(path string) (ino *inode, err error) {
  405. h, err := windows.CreateFile(windows.StringToUTF16Ptr(path),
  406. windows.FILE_LIST_DIRECTORY,
  407. windows.FILE_SHARE_READ|windows.FILE_SHARE_WRITE|windows.FILE_SHARE_DELETE,
  408. nil, windows.OPEN_EXISTING,
  409. windows.FILE_FLAG_BACKUP_SEMANTICS|windows.FILE_FLAG_OVERLAPPED, 0)
  410. if err != nil {
  411. return nil, os.NewSyscallError("CreateFile", err)
  412. }
  413. var fi windows.ByHandleFileInformation
  414. err = windows.GetFileInformationByHandle(h, &fi)
  415. if err != nil {
  416. windows.CloseHandle(h)
  417. return nil, os.NewSyscallError("GetFileInformationByHandle", err)
  418. }
  419. ino = &inode{
  420. handle: h,
  421. volume: fi.VolumeSerialNumber,
  422. index: uint64(fi.FileIndexHigh)<<32 | uint64(fi.FileIndexLow),
  423. }
  424. return ino, nil
  425. }
  426. // Must run within the I/O thread.
  427. func (m watchMap) get(ino *inode) *watch {
  428. if i := m[ino.volume]; i != nil {
  429. return i[ino.index]
  430. }
  431. return nil
  432. }
  433. // Must run within the I/O thread.
  434. func (m watchMap) set(ino *inode, watch *watch) {
  435. i := m[ino.volume]
  436. if i == nil {
  437. i = make(indexMap)
  438. m[ino.volume] = i
  439. }
  440. i[ino.index] = watch
  441. }
  442. // Must run within the I/O thread.
  443. func (w *Watcher) addWatch(pathname string, flags uint64, bufsize int) error {
  444. //pathname, recurse := recursivePath(pathname)
  445. recurse := false
  446. dir, err := w.getDir(pathname)
  447. if err != nil {
  448. return err
  449. }
  450. ino, err := w.getIno(dir)
  451. if err != nil {
  452. return err
  453. }
  454. w.mu.Lock()
  455. watchEntry := w.watches.get(ino)
  456. w.mu.Unlock()
  457. if watchEntry == nil {
  458. _, err := windows.CreateIoCompletionPort(ino.handle, w.port, 0, 0)
  459. if err != nil {
  460. windows.CloseHandle(ino.handle)
  461. return os.NewSyscallError("CreateIoCompletionPort", err)
  462. }
  463. watchEntry = &watch{
  464. ino: ino,
  465. path: dir,
  466. names: make(map[string]uint64),
  467. recurse: recurse,
  468. buf: make([]byte, bufsize),
  469. }
  470. w.mu.Lock()
  471. w.watches.set(ino, watchEntry)
  472. w.mu.Unlock()
  473. flags |= provisional
  474. } else {
  475. windows.CloseHandle(ino.handle)
  476. }
  477. if pathname == dir {
  478. watchEntry.mask |= flags
  479. } else {
  480. watchEntry.names[filepath.Base(pathname)] |= flags
  481. }
  482. err = w.startRead(watchEntry)
  483. if err != nil {
  484. return err
  485. }
  486. if pathname == dir {
  487. watchEntry.mask &= ^provisional
  488. } else {
  489. watchEntry.names[filepath.Base(pathname)] &= ^provisional
  490. }
  491. return nil
  492. }
  493. // Must run within the I/O thread.
  494. func (w *Watcher) remWatch(pathname string) error {
  495. pathname, recurse := recursivePath(pathname)
  496. dir, err := w.getDir(pathname)
  497. if err != nil {
  498. return err
  499. }
  500. ino, err := w.getIno(dir)
  501. if err != nil {
  502. return err
  503. }
  504. w.mu.Lock()
  505. watch := w.watches.get(ino)
  506. w.mu.Unlock()
  507. if recurse && !watch.recurse {
  508. return fmt.Errorf("can't use \\... with non-recursive watch %q", pathname)
  509. }
  510. err = windows.CloseHandle(ino.handle)
  511. if err != nil {
  512. w.sendError(os.NewSyscallError("CloseHandle", err))
  513. }
  514. if watch == nil {
  515. return fmt.Errorf("%w: %s", ErrNonExistentWatch, pathname)
  516. }
  517. if pathname == dir {
  518. w.sendEvent(watch.path, watch.mask&sysFSIGNORED)
  519. watch.mask = 0
  520. } else {
  521. name := filepath.Base(pathname)
  522. w.sendEvent(filepath.Join(watch.path, name), watch.names[name]&sysFSIGNORED)
  523. delete(watch.names, name)
  524. }
  525. return w.startRead(watch)
  526. }
  527. // Must run within the I/O thread.
  528. func (w *Watcher) deleteWatch(watch *watch) {
  529. for name, mask := range watch.names {
  530. if mask&provisional == 0 {
  531. w.sendEvent(filepath.Join(watch.path, name), mask&sysFSIGNORED)
  532. }
  533. delete(watch.names, name)
  534. }
  535. if watch.mask != 0 {
  536. if watch.mask&provisional == 0 {
  537. w.sendEvent(watch.path, watch.mask&sysFSIGNORED)
  538. }
  539. watch.mask = 0
  540. }
  541. }
  542. // Must run within the I/O thread.
  543. func (w *Watcher) startRead(watch *watch) error {
  544. err := windows.CancelIo(watch.ino.handle)
  545. if err != nil {
  546. w.sendError(os.NewSyscallError("CancelIo", err))
  547. w.deleteWatch(watch)
  548. }
  549. mask := w.toWindowsFlags(watch.mask)
  550. for _, m := range watch.names {
  551. mask |= w.toWindowsFlags(m)
  552. }
  553. if mask == 0 {
  554. err := windows.CloseHandle(watch.ino.handle)
  555. if err != nil {
  556. w.sendError(os.NewSyscallError("CloseHandle", err))
  557. }
  558. w.mu.Lock()
  559. delete(w.watches[watch.ino.volume], watch.ino.index)
  560. w.mu.Unlock()
  561. return nil
  562. }
  563. // We need to pass the array, rather than the slice.
  564. hdr := (*reflect.SliceHeader)(unsafe.Pointer(&watch.buf))
  565. rdErr := windows.ReadDirectoryChanges(watch.ino.handle,
  566. (*byte)(unsafe.Pointer(hdr.Data)), uint32(hdr.Len),
  567. watch.recurse, mask, nil, &watch.ov, 0)
  568. if rdErr != nil {
  569. err := os.NewSyscallError("ReadDirectoryChanges", rdErr)
  570. if rdErr == windows.ERROR_ACCESS_DENIED && watch.mask&provisional == 0 {
  571. // Watched directory was probably removed
  572. w.sendEvent(watch.path, watch.mask&sysFSDELETESELF)
  573. err = nil
  574. }
  575. w.deleteWatch(watch)
  576. w.startRead(watch)
  577. return err
  578. }
  579. return nil
  580. }
  581. // readEvents reads from the I/O completion port, converts the
  582. // received events into Event objects and sends them via the Events channel.
  583. // Entry point to the I/O thread.
  584. func (w *Watcher) readEvents() {
  585. var (
  586. n uint32
  587. key uintptr
  588. ov *windows.Overlapped
  589. )
  590. runtime.LockOSThread()
  591. for {
  592. // This error is handled after the watch == nil check below.
  593. qErr := windows.GetQueuedCompletionStatus(w.port, &n, &key, &ov, windows.INFINITE)
  594. watch := (*watch)(unsafe.Pointer(ov))
  595. if watch == nil {
  596. select {
  597. case ch := <-w.quit:
  598. w.mu.Lock()
  599. var indexes []indexMap
  600. for _, index := range w.watches {
  601. indexes = append(indexes, index)
  602. }
  603. w.mu.Unlock()
  604. for _, index := range indexes {
  605. for _, watch := range index {
  606. w.deleteWatch(watch)
  607. w.startRead(watch)
  608. }
  609. }
  610. err := windows.CloseHandle(w.port)
  611. if err != nil {
  612. err = os.NewSyscallError("CloseHandle", err)
  613. }
  614. close(w.Events)
  615. close(w.Errors)
  616. ch <- err
  617. return
  618. case in := <-w.input:
  619. switch in.op {
  620. case opAddWatch:
  621. in.reply <- w.addWatch(in.path, uint64(in.flags), in.bufsize)
  622. case opRemoveWatch:
  623. in.reply <- w.remWatch(in.path)
  624. }
  625. default:
  626. }
  627. continue
  628. }
  629. switch qErr {
  630. case nil:
  631. // No error
  632. case windows.ERROR_MORE_DATA:
  633. if watch == nil {
  634. w.sendError(errors.New("ERROR_MORE_DATA has unexpectedly null lpOverlapped buffer"))
  635. } else {
  636. // The i/o succeeded but the buffer is full.
  637. // In theory we should be building up a full packet.
  638. // In practice we can get away with just carrying on.
  639. n = uint32(unsafe.Sizeof(watch.buf))
  640. }
  641. case windows.ERROR_ACCESS_DENIED:
  642. // Watched directory was probably removed
  643. w.sendEvent(watch.path, watch.mask&sysFSDELETESELF)
  644. w.deleteWatch(watch)
  645. w.startRead(watch)
  646. continue
  647. case windows.ERROR_OPERATION_ABORTED:
  648. // CancelIo was called on this handle
  649. continue
  650. default:
  651. w.sendError(os.NewSyscallError("GetQueuedCompletionPort", qErr))
  652. continue
  653. }
  654. var offset uint32
  655. for {
  656. if n == 0 {
  657. w.sendError(ErrEventOverflow)
  658. break
  659. }
  660. // Point "raw" to the event in the buffer
  661. raw := (*windows.FileNotifyInformation)(unsafe.Pointer(&watch.buf[offset]))
  662. // Create a buf that is the size of the path name
  663. size := int(raw.FileNameLength / 2)
  664. var buf []uint16
  665. // TODO: Use unsafe.Slice in Go 1.17; https://stackoverflow.com/questions/51187973
  666. sh := (*reflect.SliceHeader)(unsafe.Pointer(&buf))
  667. sh.Data = uintptr(unsafe.Pointer(&raw.FileName))
  668. sh.Len = size
  669. sh.Cap = size
  670. name := windows.UTF16ToString(buf)
  671. fullname := filepath.Join(watch.path, name)
  672. var mask uint64
  673. switch raw.Action {
  674. case windows.FILE_ACTION_REMOVED:
  675. mask = sysFSDELETESELF
  676. case windows.FILE_ACTION_MODIFIED:
  677. mask = sysFSMODIFY
  678. case windows.FILE_ACTION_RENAMED_OLD_NAME:
  679. watch.rename = name
  680. case windows.FILE_ACTION_RENAMED_NEW_NAME:
  681. // Update saved path of all sub-watches.
  682. old := filepath.Join(watch.path, watch.rename)
  683. w.mu.Lock()
  684. for _, watchMap := range w.watches {
  685. for _, ww := range watchMap {
  686. if strings.HasPrefix(ww.path, old) {
  687. ww.path = filepath.Join(fullname, strings.TrimPrefix(ww.path, old))
  688. }
  689. }
  690. }
  691. w.mu.Unlock()
  692. if watch.names[watch.rename] != 0 {
  693. watch.names[name] |= watch.names[watch.rename]
  694. delete(watch.names, watch.rename)
  695. mask = sysFSMOVESELF
  696. }
  697. }
  698. sendNameEvent := func() {
  699. w.sendEvent(fullname, watch.names[name]&mask)
  700. }
  701. if raw.Action != windows.FILE_ACTION_RENAMED_NEW_NAME {
  702. sendNameEvent()
  703. }
  704. if raw.Action == windows.FILE_ACTION_REMOVED {
  705. w.sendEvent(fullname, watch.names[name]&sysFSIGNORED)
  706. delete(watch.names, name)
  707. }
  708. w.sendEvent(fullname, watch.mask&w.toFSnotifyFlags(raw.Action))
  709. if raw.Action == windows.FILE_ACTION_RENAMED_NEW_NAME {
  710. fullname = filepath.Join(watch.path, watch.rename)
  711. sendNameEvent()
  712. }
  713. // Move to the next event in the buffer
  714. if raw.NextEntryOffset == 0 {
  715. break
  716. }
  717. offset += raw.NextEntryOffset
  718. // Error!
  719. if offset >= n {
  720. //lint:ignore ST1005 Windows should be capitalized
  721. w.sendError(errors.New(
  722. "Windows system assumed buffer larger than it is, events have likely been missed"))
  723. break
  724. }
  725. }
  726. if err := w.startRead(watch); err != nil {
  727. w.sendError(err)
  728. }
  729. }
  730. }
  731. func (w *Watcher) toWindowsFlags(mask uint64) uint32 {
  732. var m uint32
  733. if mask&sysFSMODIFY != 0 {
  734. m |= windows.FILE_NOTIFY_CHANGE_LAST_WRITE
  735. }
  736. if mask&(sysFSMOVE|sysFSCREATE|sysFSDELETE) != 0 {
  737. m |= windows.FILE_NOTIFY_CHANGE_FILE_NAME | windows.FILE_NOTIFY_CHANGE_DIR_NAME
  738. }
  739. return m
  740. }
  741. func (w *Watcher) toFSnotifyFlags(action uint32) uint64 {
  742. switch action {
  743. case windows.FILE_ACTION_ADDED:
  744. return sysFSCREATE
  745. case windows.FILE_ACTION_REMOVED:
  746. return sysFSDELETE
  747. case windows.FILE_ACTION_MODIFIED:
  748. return sysFSMODIFY
  749. case windows.FILE_ACTION_RENAMED_OLD_NAME:
  750. return sysFSMOVEDFROM
  751. case windows.FILE_ACTION_RENAMED_NEW_NAME:
  752. return sysFSMOVEDTO
  753. }
  754. return 0
  755. }