backend_windows.go 20 KB

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