backend_kqueue.go 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707
  1. //go:build freebsd || openbsd || netbsd || dragonfly || darwin
  2. // +build freebsd openbsd netbsd dragonfly darwin
  3. package fsnotify
  4. import (
  5. "errors"
  6. "fmt"
  7. "io/ioutil"
  8. "os"
  9. "path/filepath"
  10. "sync"
  11. "golang.org/x/sys/unix"
  12. )
  13. // Watcher watches a set of paths, delivering events on a channel.
  14. //
  15. // A watcher should not be copied (e.g. pass it by pointer, rather than by
  16. // value).
  17. //
  18. // # Linux notes
  19. //
  20. // When a file is removed a Remove event won't be emitted until all file
  21. // descriptors are closed, and deletes will always emit a Chmod. For example:
  22. //
  23. // fp := os.Open("file")
  24. // os.Remove("file") // Triggers Chmod
  25. // fp.Close() // Triggers Remove
  26. //
  27. // This is the event that inotify sends, so not much can be changed about this.
  28. //
  29. // The fs.inotify.max_user_watches sysctl variable specifies the upper limit
  30. // for the number of watches per user, and fs.inotify.max_user_instances
  31. // specifies the maximum number of inotify instances per user. Every Watcher you
  32. // create is an "instance", and every path you add is a "watch".
  33. //
  34. // These are also exposed in /proc as /proc/sys/fs/inotify/max_user_watches and
  35. // /proc/sys/fs/inotify/max_user_instances
  36. //
  37. // To increase them you can use sysctl or write the value to the /proc file:
  38. //
  39. // # Default values on Linux 5.18
  40. // sysctl fs.inotify.max_user_watches=124983
  41. // sysctl fs.inotify.max_user_instances=128
  42. //
  43. // To make the changes persist on reboot edit /etc/sysctl.conf or
  44. // /usr/lib/sysctl.d/50-default.conf (details differ per Linux distro; check
  45. // your distro's documentation):
  46. //
  47. // fs.inotify.max_user_watches=124983
  48. // fs.inotify.max_user_instances=128
  49. //
  50. // Reaching the limit will result in a "no space left on device" or "too many open
  51. // files" error.
  52. //
  53. // # kqueue notes (macOS, BSD)
  54. //
  55. // kqueue requires opening a file descriptor for every file that's being watched;
  56. // so if you're watching a directory with five files then that's six file
  57. // descriptors. You will run in to your system's "max open files" limit faster on
  58. // these platforms.
  59. //
  60. // The sysctl variables kern.maxfiles and kern.maxfilesperproc can be used to
  61. // control the maximum number of open files, as well as /etc/login.conf on BSD
  62. // systems.
  63. //
  64. // # macOS notes
  65. //
  66. // Spotlight indexing on macOS can result in multiple events (see [#15]). A
  67. // temporary workaround is to add your folder(s) to the "Spotlight Privacy
  68. // Settings" until we have a native FSEvents implementation (see [#11]).
  69. //
  70. // [#11]: https://github.com/fsnotify/fsnotify/issues/11
  71. // [#15]: https://github.com/fsnotify/fsnotify/issues/15
  72. type Watcher struct {
  73. // Events sends the filesystem change events.
  74. //
  75. // fsnotify can send the following events; a "path" here can refer to a
  76. // file, directory, symbolic link, or special file like a FIFO.
  77. //
  78. // fsnotify.Create A new path was created; this may be followed by one
  79. // or more Write events if data also gets written to a
  80. // file.
  81. //
  82. // fsnotify.Remove A path was removed.
  83. //
  84. // fsnotify.Rename A path was renamed. A rename is always sent with the
  85. // old path as Event.Name, and a Create event will be
  86. // sent with the new name. Renames are only sent for
  87. // paths that are currently watched; e.g. moving an
  88. // unmonitored file into a monitored directory will
  89. // show up as just a Create. Similarly, renaming a file
  90. // to outside a monitored directory will show up as
  91. // only a Rename.
  92. //
  93. // fsnotify.Write A file or named pipe was written to. A Truncate will
  94. // also trigger a Write. A single "write action"
  95. // initiated by the user may show up as one or multiple
  96. // writes, depending on when the system syncs things to
  97. // disk. For example when compiling a large Go program
  98. // you may get hundreds of Write events, so you
  99. // probably want to wait until you've stopped receiving
  100. // them (see the dedup example in cmd/fsnotify).
  101. //
  102. // fsnotify.Chmod Attributes were changed. On Linux this is also sent
  103. // when a file is removed (or more accurately, when a
  104. // link to an inode is removed). On kqueue it's sent
  105. // and on kqueue when a file is truncated. On Windows
  106. // it's never sent.
  107. Events chan Event
  108. // Errors sends any errors.
  109. Errors chan error
  110. done chan struct{}
  111. kq int // File descriptor (as returned by the kqueue() syscall).
  112. closepipe [2]int // Pipe used for closing.
  113. mu sync.Mutex // Protects access to watcher data
  114. watches map[string]int // Watched file descriptors (key: path).
  115. watchesByDir map[string]map[int]struct{} // Watched file descriptors indexed by the parent directory (key: dirname(path)).
  116. userWatches map[string]struct{} // Watches added with Watcher.Add()
  117. dirFlags map[string]uint32 // Watched directories to fflags used in kqueue.
  118. paths map[int]pathInfo // File descriptors to path names for processing kqueue events.
  119. fileExists map[string]struct{} // Keep track of if we know this file exists (to stop duplicate create events).
  120. isClosed bool // Set to true when Close() is first called
  121. }
  122. type pathInfo struct {
  123. name string
  124. isDir bool
  125. }
  126. // NewWatcher creates a new Watcher.
  127. func NewWatcher() (*Watcher, error) {
  128. kq, closepipe, err := newKqueue()
  129. if err != nil {
  130. return nil, err
  131. }
  132. w := &Watcher{
  133. kq: kq,
  134. closepipe: closepipe,
  135. watches: make(map[string]int),
  136. watchesByDir: make(map[string]map[int]struct{}),
  137. dirFlags: make(map[string]uint32),
  138. paths: make(map[int]pathInfo),
  139. fileExists: make(map[string]struct{}),
  140. userWatches: make(map[string]struct{}),
  141. Events: make(chan Event),
  142. Errors: make(chan error),
  143. done: make(chan struct{}),
  144. }
  145. go w.readEvents()
  146. return w, nil
  147. }
  148. // newKqueue creates a new kernel event queue and returns a descriptor.
  149. //
  150. // This registers a new event on closepipe, which will trigger an event when
  151. // it's closed. This way we can use kevent() without timeout/polling; without
  152. // the closepipe, it would block forever and we wouldn't be able to stop it at
  153. // all.
  154. func newKqueue() (kq int, closepipe [2]int, err error) {
  155. kq, err = unix.Kqueue()
  156. if kq == -1 {
  157. return kq, closepipe, err
  158. }
  159. // Register the close pipe.
  160. err = unix.Pipe(closepipe[:])
  161. if err != nil {
  162. unix.Close(kq)
  163. return kq, closepipe, err
  164. }
  165. // Register changes to listen on the closepipe.
  166. changes := make([]unix.Kevent_t, 1)
  167. // SetKevent converts int to the platform-specific types.
  168. unix.SetKevent(&changes[0], closepipe[0], unix.EVFILT_READ,
  169. unix.EV_ADD|unix.EV_ENABLE|unix.EV_ONESHOT)
  170. ok, err := unix.Kevent(kq, changes, nil, nil)
  171. if ok == -1 {
  172. unix.Close(kq)
  173. unix.Close(closepipe[0])
  174. unix.Close(closepipe[1])
  175. return kq, closepipe, err
  176. }
  177. return kq, closepipe, nil
  178. }
  179. // Returns true if the event was sent, or false if watcher is closed.
  180. func (w *Watcher) sendEvent(e Event) bool {
  181. select {
  182. case w.Events <- e:
  183. return true
  184. case <-w.done:
  185. }
  186. return false
  187. }
  188. // Returns true if the error was sent, or false if watcher is closed.
  189. func (w *Watcher) sendError(err error) bool {
  190. select {
  191. case w.Errors <- err:
  192. return true
  193. case <-w.done:
  194. }
  195. return false
  196. }
  197. // Close removes all watches and closes the events channel.
  198. func (w *Watcher) Close() error {
  199. w.mu.Lock()
  200. if w.isClosed {
  201. w.mu.Unlock()
  202. return nil
  203. }
  204. w.isClosed = true
  205. // copy paths to remove while locked
  206. pathsToRemove := make([]string, 0, len(w.watches))
  207. for name := range w.watches {
  208. pathsToRemove = append(pathsToRemove, name)
  209. }
  210. w.mu.Unlock() // Unlock before calling Remove, which also locks
  211. for _, name := range pathsToRemove {
  212. w.Remove(name)
  213. }
  214. // Send "quit" message to the reader goroutine.
  215. unix.Close(w.closepipe[1])
  216. close(w.done)
  217. return nil
  218. }
  219. // Add starts monitoring the path for changes.
  220. //
  221. // A path can only be watched once; attempting to watch it more than once will
  222. // return an error. Paths that do not yet exist on the filesystem cannot be
  223. // added. A watch will be automatically removed if the path is deleted.
  224. //
  225. // A path will remain watched if it gets renamed to somewhere else on the same
  226. // filesystem, but the monitor will get removed if the path gets deleted and
  227. // re-created, or if it's moved to a different filesystem.
  228. //
  229. // Notifications on network filesystems (NFS, SMB, FUSE, etc.) or special
  230. // filesystems (/proc, /sys, etc.) generally don't work.
  231. //
  232. // # Watching directories
  233. //
  234. // All files in a directory are monitored, including new files that are created
  235. // after the watcher is started. Subdirectories are not watched (i.e. it's
  236. // non-recursive).
  237. //
  238. // # Watching files
  239. //
  240. // Watching individual files (rather than directories) is generally not
  241. // recommended as many tools update files atomically. Instead of "just" writing
  242. // to the file a temporary file will be written to first, and if successful the
  243. // temporary file is moved to to destination removing the original, or some
  244. // variant thereof. The watcher on the original file is now lost, as it no
  245. // longer exists.
  246. //
  247. // Instead, watch the parent directory and use Event.Name to filter out files
  248. // you're not interested in. There is an example of this in [cmd/fsnotify/file.go].
  249. func (w *Watcher) Add(name string) error {
  250. w.mu.Lock()
  251. w.userWatches[name] = struct{}{}
  252. w.mu.Unlock()
  253. _, err := w.addWatch(name, noteAllEvents)
  254. return err
  255. }
  256. // Remove stops monitoring the path for changes.
  257. //
  258. // Directories are always removed non-recursively. For example, if you added
  259. // /tmp/dir and /tmp/dir/subdir then you will need to remove both.
  260. //
  261. // Removing a path that has not yet been added returns [ErrNonExistentWatch].
  262. func (w *Watcher) Remove(name string) error {
  263. name = filepath.Clean(name)
  264. w.mu.Lock()
  265. watchfd, ok := w.watches[name]
  266. w.mu.Unlock()
  267. if !ok {
  268. return fmt.Errorf("%w: %s", ErrNonExistentWatch, name)
  269. }
  270. err := w.register([]int{watchfd}, unix.EV_DELETE, 0)
  271. if err != nil {
  272. return err
  273. }
  274. unix.Close(watchfd)
  275. w.mu.Lock()
  276. isDir := w.paths[watchfd].isDir
  277. delete(w.watches, name)
  278. delete(w.userWatches, name)
  279. parentName := filepath.Dir(name)
  280. delete(w.watchesByDir[parentName], watchfd)
  281. if len(w.watchesByDir[parentName]) == 0 {
  282. delete(w.watchesByDir, parentName)
  283. }
  284. delete(w.paths, watchfd)
  285. delete(w.dirFlags, name)
  286. delete(w.fileExists, name)
  287. w.mu.Unlock()
  288. // Find all watched paths that are in this directory that are not external.
  289. if isDir {
  290. var pathsToRemove []string
  291. w.mu.Lock()
  292. for fd := range w.watchesByDir[name] {
  293. path := w.paths[fd]
  294. if _, ok := w.userWatches[path.name]; !ok {
  295. pathsToRemove = append(pathsToRemove, path.name)
  296. }
  297. }
  298. w.mu.Unlock()
  299. for _, name := range pathsToRemove {
  300. // Since these are internal, not much sense in propagating error
  301. // to the user, as that will just confuse them with an error about
  302. // a path they did not explicitly watch themselves.
  303. w.Remove(name)
  304. }
  305. }
  306. return nil
  307. }
  308. // WatchList returns all paths added with [Add] (and are not yet removed).
  309. func (w *Watcher) WatchList() []string {
  310. w.mu.Lock()
  311. defer w.mu.Unlock()
  312. entries := make([]string, 0, len(w.userWatches))
  313. for pathname := range w.userWatches {
  314. entries = append(entries, pathname)
  315. }
  316. return entries
  317. }
  318. // Watch all events (except NOTE_EXTEND, NOTE_LINK, NOTE_REVOKE)
  319. const noteAllEvents = unix.NOTE_DELETE | unix.NOTE_WRITE | unix.NOTE_ATTRIB | unix.NOTE_RENAME
  320. // addWatch adds name to the watched file set.
  321. // The flags are interpreted as described in kevent(2).
  322. // 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.
  323. func (w *Watcher) addWatch(name string, flags uint32) (string, error) {
  324. var isDir bool
  325. // Make ./name and name equivalent
  326. name = filepath.Clean(name)
  327. w.mu.Lock()
  328. if w.isClosed {
  329. w.mu.Unlock()
  330. return "", errors.New("kevent instance already closed")
  331. }
  332. watchfd, alreadyWatching := w.watches[name]
  333. // We already have a watch, but we can still override flags.
  334. if alreadyWatching {
  335. isDir = w.paths[watchfd].isDir
  336. }
  337. w.mu.Unlock()
  338. if !alreadyWatching {
  339. fi, err := os.Lstat(name)
  340. if err != nil {
  341. return "", err
  342. }
  343. // Don't watch sockets or named pipes
  344. if (fi.Mode()&os.ModeSocket == os.ModeSocket) || (fi.Mode()&os.ModeNamedPipe == os.ModeNamedPipe) {
  345. return "", nil
  346. }
  347. // Follow Symlinks
  348. //
  349. // Linux can add unresolvable symlinks to the watch list without issue,
  350. // and Windows can't do symlinks period. To maintain consistency, we
  351. // will act like everything is fine if the link can't be resolved.
  352. // There will simply be no file events for broken symlinks. Hence the
  353. // returns of nil on errors.
  354. if fi.Mode()&os.ModeSymlink == os.ModeSymlink {
  355. name, err = filepath.EvalSymlinks(name)
  356. if err != nil {
  357. return "", nil
  358. }
  359. w.mu.Lock()
  360. _, alreadyWatching = w.watches[name]
  361. w.mu.Unlock()
  362. if alreadyWatching {
  363. return name, nil
  364. }
  365. fi, err = os.Lstat(name)
  366. if err != nil {
  367. return "", nil
  368. }
  369. }
  370. // Retry on EINTR; open() can return EINTR in practice on macOS.
  371. // See #354, and go issues 11180 and 39237.
  372. for {
  373. watchfd, err = unix.Open(name, openMode, 0)
  374. if err == nil {
  375. break
  376. }
  377. if errors.Is(err, unix.EINTR) {
  378. continue
  379. }
  380. return "", err
  381. }
  382. isDir = fi.IsDir()
  383. }
  384. err := w.register([]int{watchfd}, unix.EV_ADD|unix.EV_CLEAR|unix.EV_ENABLE, flags)
  385. if err != nil {
  386. unix.Close(watchfd)
  387. return "", err
  388. }
  389. if !alreadyWatching {
  390. w.mu.Lock()
  391. parentName := filepath.Dir(name)
  392. w.watches[name] = watchfd
  393. watchesByDir, ok := w.watchesByDir[parentName]
  394. if !ok {
  395. watchesByDir = make(map[int]struct{}, 1)
  396. w.watchesByDir[parentName] = watchesByDir
  397. }
  398. watchesByDir[watchfd] = struct{}{}
  399. w.paths[watchfd] = pathInfo{name: name, isDir: isDir}
  400. w.mu.Unlock()
  401. }
  402. if isDir {
  403. // Watch the directory if it has not been watched before,
  404. // or if it was watched before, but perhaps only a NOTE_DELETE (watchDirectoryFiles)
  405. w.mu.Lock()
  406. watchDir := (flags&unix.NOTE_WRITE) == unix.NOTE_WRITE &&
  407. (!alreadyWatching || (w.dirFlags[name]&unix.NOTE_WRITE) != unix.NOTE_WRITE)
  408. // Store flags so this watch can be updated later
  409. w.dirFlags[name] = flags
  410. w.mu.Unlock()
  411. if watchDir {
  412. if err := w.watchDirectoryFiles(name); err != nil {
  413. return "", err
  414. }
  415. }
  416. }
  417. return name, nil
  418. }
  419. // readEvents reads from kqueue and converts the received kevents into
  420. // Event values that it sends down the Events channel.
  421. func (w *Watcher) readEvents() {
  422. defer func() {
  423. err := unix.Close(w.kq)
  424. if err != nil {
  425. w.Errors <- err
  426. }
  427. unix.Close(w.closepipe[0])
  428. close(w.Events)
  429. close(w.Errors)
  430. }()
  431. eventBuffer := make([]unix.Kevent_t, 10)
  432. for closed := false; !closed; {
  433. kevents, err := w.read(eventBuffer)
  434. // EINTR is okay, the syscall was interrupted before timeout expired.
  435. if err != nil && err != unix.EINTR {
  436. if !w.sendError(fmt.Errorf("fsnotify.readEvents: %w", err)) {
  437. closed = true
  438. }
  439. continue
  440. }
  441. // Flush the events we received to the Events channel
  442. for _, kevent := range kevents {
  443. var (
  444. watchfd = int(kevent.Ident)
  445. mask = uint32(kevent.Fflags)
  446. )
  447. // Shut down the loop when the pipe is closed, but only after all
  448. // other events have been processed.
  449. if watchfd == w.closepipe[0] {
  450. closed = true
  451. continue
  452. }
  453. w.mu.Lock()
  454. path := w.paths[watchfd]
  455. w.mu.Unlock()
  456. event := w.newEvent(path.name, mask)
  457. if path.isDir && !event.Has(Remove) {
  458. // Double check to make sure the directory exists. This can
  459. // happen when we do a rm -fr on a recursively watched folders
  460. // and we receive a modification event first but the folder has
  461. // been deleted and later receive the delete event.
  462. if _, err := os.Lstat(event.Name); os.IsNotExist(err) {
  463. event.Op |= Remove
  464. }
  465. }
  466. if event.Has(Rename) || event.Has(Remove) {
  467. w.Remove(event.Name)
  468. w.mu.Lock()
  469. delete(w.fileExists, event.Name)
  470. w.mu.Unlock()
  471. }
  472. if path.isDir && event.Has(Write) && !event.Has(Remove) {
  473. w.sendDirectoryChangeEvents(event.Name)
  474. } else {
  475. if !w.sendEvent(event) {
  476. closed = true
  477. continue
  478. }
  479. }
  480. if event.Has(Remove) {
  481. // Look for a file that may have overwritten this.
  482. // For example, mv f1 f2 will delete f2, then create f2.
  483. if path.isDir {
  484. fileDir := filepath.Clean(event.Name)
  485. w.mu.Lock()
  486. _, found := w.watches[fileDir]
  487. w.mu.Unlock()
  488. if found {
  489. // make sure the directory exists before we watch for changes. When we
  490. // do a recursive watch and perform rm -fr, the parent directory might
  491. // have gone missing, ignore the missing directory and let the
  492. // upcoming delete event remove the watch from the parent directory.
  493. if _, err := os.Lstat(fileDir); err == nil {
  494. w.sendDirectoryChangeEvents(fileDir)
  495. }
  496. }
  497. } else {
  498. filePath := filepath.Clean(event.Name)
  499. if fileInfo, err := os.Lstat(filePath); err == nil {
  500. w.sendFileCreatedEventIfNew(filePath, fileInfo)
  501. }
  502. }
  503. }
  504. }
  505. }
  506. }
  507. // newEvent returns an platform-independent Event based on kqueue Fflags.
  508. func (w *Watcher) newEvent(name string, mask uint32) Event {
  509. e := Event{Name: name}
  510. if mask&unix.NOTE_DELETE == unix.NOTE_DELETE {
  511. e.Op |= Remove
  512. }
  513. if mask&unix.NOTE_WRITE == unix.NOTE_WRITE {
  514. e.Op |= Write
  515. }
  516. if mask&unix.NOTE_RENAME == unix.NOTE_RENAME {
  517. e.Op |= Rename
  518. }
  519. if mask&unix.NOTE_ATTRIB == unix.NOTE_ATTRIB {
  520. e.Op |= Chmod
  521. }
  522. return e
  523. }
  524. // watchDirectoryFiles to mimic inotify when adding a watch on a directory
  525. func (w *Watcher) watchDirectoryFiles(dirPath string) error {
  526. // Get all files
  527. files, err := ioutil.ReadDir(dirPath)
  528. if err != nil {
  529. return err
  530. }
  531. for _, fileInfo := range files {
  532. path := filepath.Join(dirPath, fileInfo.Name())
  533. cleanPath, err := w.internalWatch(path, fileInfo)
  534. if err != nil {
  535. // No permission to read the file; that's not a problem: just skip.
  536. // But do add it to w.fileExists to prevent it from being picked up
  537. // as a "new" file later (it still shows up in the directory
  538. // listing).
  539. switch {
  540. case errors.Is(err, unix.EACCES) || errors.Is(err, unix.EPERM):
  541. cleanPath = filepath.Clean(path)
  542. default:
  543. return fmt.Errorf("%q: %w", filepath.Join(dirPath, fileInfo.Name()), err)
  544. }
  545. }
  546. w.mu.Lock()
  547. w.fileExists[cleanPath] = struct{}{}
  548. w.mu.Unlock()
  549. }
  550. return nil
  551. }
  552. // Search the directory for new files and send an event for them.
  553. //
  554. // This functionality is to have the BSD watcher match the inotify, which sends
  555. // a create event for files created in a watched directory.
  556. func (w *Watcher) sendDirectoryChangeEvents(dir string) {
  557. // Get all files
  558. files, err := ioutil.ReadDir(dir)
  559. if err != nil {
  560. if !w.sendError(fmt.Errorf("fsnotify.sendDirectoryChangeEvents: %w", err)) {
  561. return
  562. }
  563. }
  564. // Search for new files
  565. for _, fi := range files {
  566. err := w.sendFileCreatedEventIfNew(filepath.Join(dir, fi.Name()), fi)
  567. if err != nil {
  568. return
  569. }
  570. }
  571. }
  572. // sendFileCreatedEvent sends a create event if the file isn't already being tracked.
  573. func (w *Watcher) sendFileCreatedEventIfNew(filePath string, fileInfo os.FileInfo) (err error) {
  574. w.mu.Lock()
  575. _, doesExist := w.fileExists[filePath]
  576. w.mu.Unlock()
  577. if !doesExist {
  578. if !w.sendEvent(Event{Name: filePath, Op: Create}) {
  579. return
  580. }
  581. }
  582. // like watchDirectoryFiles (but without doing another ReadDir)
  583. filePath, err = w.internalWatch(filePath, fileInfo)
  584. if err != nil {
  585. return err
  586. }
  587. w.mu.Lock()
  588. w.fileExists[filePath] = struct{}{}
  589. w.mu.Unlock()
  590. return nil
  591. }
  592. func (w *Watcher) internalWatch(name string, fileInfo os.FileInfo) (string, error) {
  593. if fileInfo.IsDir() {
  594. // mimic Linux providing delete events for subdirectories
  595. // but preserve the flags used if currently watching subdirectory
  596. w.mu.Lock()
  597. flags := w.dirFlags[name]
  598. w.mu.Unlock()
  599. flags |= unix.NOTE_DELETE | unix.NOTE_RENAME
  600. return w.addWatch(name, flags)
  601. }
  602. // watch file to mimic Linux inotify
  603. return w.addWatch(name, noteAllEvents)
  604. }
  605. // Register events with the queue.
  606. func (w *Watcher) register(fds []int, flags int, fflags uint32) error {
  607. changes := make([]unix.Kevent_t, len(fds))
  608. for i, fd := range fds {
  609. // SetKevent converts int to the platform-specific types.
  610. unix.SetKevent(&changes[i], fd, unix.EVFILT_VNODE, flags)
  611. changes[i].Fflags = fflags
  612. }
  613. // Register the events.
  614. success, err := unix.Kevent(w.kq, changes, nil, nil)
  615. if success == -1 {
  616. return err
  617. }
  618. return nil
  619. }
  620. // read retrieves pending events, or waits until an event occurs.
  621. func (w *Watcher) read(events []unix.Kevent_t) ([]unix.Kevent_t, error) {
  622. n, err := unix.Kevent(w.kq, nil, events, nil)
  623. if err != nil {
  624. return nil, err
  625. }
  626. return events[0:n], nil
  627. }