fsnotify_windows.go 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598
  1. // Copyright 2011 The Go Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style
  3. // license that can be found in the LICENSE file.
  4. // +build windows
  5. package fsnotify
  6. import (
  7. "errors"
  8. "fmt"
  9. "os"
  10. "path/filepath"
  11. "runtime"
  12. "sync"
  13. "syscall"
  14. "unsafe"
  15. )
  16. const (
  17. // Options for AddWatch
  18. sys_FS_ONESHOT = 0x80000000
  19. sys_FS_ONLYDIR = 0x1000000
  20. // Events
  21. sys_FS_ACCESS = 0x1
  22. sys_FS_ALL_EVENTS = 0xfff
  23. sys_FS_ATTRIB = 0x4
  24. sys_FS_CLOSE = 0x18
  25. sys_FS_CREATE = 0x100
  26. sys_FS_DELETE = 0x200
  27. sys_FS_DELETE_SELF = 0x400
  28. sys_FS_MODIFY = 0x2
  29. sys_FS_MOVE = 0xc0
  30. sys_FS_MOVED_FROM = 0x40
  31. sys_FS_MOVED_TO = 0x80
  32. sys_FS_MOVE_SELF = 0x800
  33. // Special events
  34. sys_FS_IGNORED = 0x8000
  35. sys_FS_Q_OVERFLOW = 0x4000
  36. )
  37. const (
  38. // TODO(nj): Use syscall.ERROR_MORE_DATA from ztypes_windows in Go 1.3+
  39. sys_ERROR_MORE_DATA syscall.Errno = 234
  40. )
  41. // Event is the type of the notification messages
  42. // received on the watcher's Event channel.
  43. type FileEvent struct {
  44. mask uint32 // Mask of events
  45. cookie uint32 // Unique cookie associating related events (for rename)
  46. Name string // File name (optional)
  47. }
  48. // IsCreate reports whether the FileEvent was triggered by a creation
  49. func (e *FileEvent) IsCreate() bool { return (e.mask & sys_FS_CREATE) == sys_FS_CREATE }
  50. // IsDelete reports whether the FileEvent was triggered by a delete
  51. func (e *FileEvent) IsDelete() bool {
  52. return ((e.mask&sys_FS_DELETE) == sys_FS_DELETE || (e.mask&sys_FS_DELETE_SELF) == sys_FS_DELETE_SELF)
  53. }
  54. // IsModify reports whether the FileEvent was triggered by a file modification or attribute change
  55. func (e *FileEvent) IsModify() bool {
  56. return ((e.mask&sys_FS_MODIFY) == sys_FS_MODIFY || (e.mask&sys_FS_ATTRIB) == sys_FS_ATTRIB)
  57. }
  58. // IsRename reports whether the FileEvent was triggered by a change name
  59. func (e *FileEvent) IsRename() bool {
  60. return ((e.mask&sys_FS_MOVE) == sys_FS_MOVE || (e.mask&sys_FS_MOVE_SELF) == sys_FS_MOVE_SELF || (e.mask&sys_FS_MOVED_FROM) == sys_FS_MOVED_FROM || (e.mask&sys_FS_MOVED_TO) == sys_FS_MOVED_TO)
  61. }
  62. // IsAttrib reports whether the FileEvent was triggered by a change in the file metadata.
  63. func (e *FileEvent) IsAttrib() bool {
  64. return (e.mask & sys_FS_ATTRIB) == sys_FS_ATTRIB
  65. }
  66. const (
  67. opAddWatch = iota
  68. opRemoveWatch
  69. )
  70. const (
  71. provisional uint64 = 1 << (32 + iota)
  72. )
  73. type input struct {
  74. op int
  75. path string
  76. flags uint32
  77. reply chan error
  78. }
  79. type inode struct {
  80. handle syscall.Handle
  81. volume uint32
  82. index uint64
  83. }
  84. type watch struct {
  85. ov syscall.Overlapped
  86. ino *inode // i-number
  87. path string // Directory path
  88. mask uint64 // Directory itself is being watched with these notify flags
  89. names map[string]uint64 // Map of names being watched and their notify flags
  90. rename string // Remembers the old name while renaming a file
  91. buf [4096]byte
  92. }
  93. type indexMap map[uint64]*watch
  94. type watchMap map[uint32]indexMap
  95. // A Watcher waits for and receives event notifications
  96. // for a specific set of files and directories.
  97. type Watcher struct {
  98. mu sync.Mutex // Map access
  99. port syscall.Handle // Handle to completion port
  100. watches watchMap // Map of watches (key: i-number)
  101. fsnFlags map[string]uint32 // Map of watched files to flags used for filter
  102. fsnmut sync.Mutex // Protects access to fsnFlags.
  103. input chan *input // Inputs to the reader are sent on this channel
  104. internalEvent chan *FileEvent // Events are queued on this channel
  105. Event chan *FileEvent // Events are returned on this channel
  106. Error chan error // Errors are sent on this channel
  107. isClosed bool // Set to true when Close() is first called
  108. quit chan chan<- error
  109. cookie uint32
  110. }
  111. // NewWatcher creates and returns a Watcher.
  112. func NewWatcher() (*Watcher, error) {
  113. port, e := syscall.CreateIoCompletionPort(syscall.InvalidHandle, 0, 0, 0)
  114. if e != nil {
  115. return nil, os.NewSyscallError("CreateIoCompletionPort", e)
  116. }
  117. w := &Watcher{
  118. port: port,
  119. watches: make(watchMap),
  120. fsnFlags: make(map[string]uint32),
  121. input: make(chan *input, 1),
  122. Event: make(chan *FileEvent, 50),
  123. internalEvent: make(chan *FileEvent),
  124. Error: make(chan error),
  125. quit: make(chan chan<- error, 1),
  126. }
  127. go w.readEvents()
  128. go w.purgeEvents()
  129. return w, nil
  130. }
  131. // Close closes a Watcher.
  132. // It sends a message to the reader goroutine to quit and removes all watches
  133. // associated with the watcher.
  134. func (w *Watcher) Close() error {
  135. if w.isClosed {
  136. return nil
  137. }
  138. w.isClosed = true
  139. // Send "quit" message to the reader goroutine
  140. ch := make(chan error)
  141. w.quit <- ch
  142. if err := w.wakeupReader(); err != nil {
  143. return err
  144. }
  145. return <-ch
  146. }
  147. // AddWatch adds path to the watched file set.
  148. func (w *Watcher) AddWatch(path string, flags uint32) error {
  149. if w.isClosed {
  150. return errors.New("watcher already closed")
  151. }
  152. in := &input{
  153. op: opAddWatch,
  154. path: filepath.Clean(path),
  155. flags: flags,
  156. reply: make(chan error),
  157. }
  158. w.input <- in
  159. if err := w.wakeupReader(); err != nil {
  160. return err
  161. }
  162. return <-in.reply
  163. }
  164. // Watch adds path to the watched file set, watching all events.
  165. func (w *Watcher) watch(path string) error {
  166. return w.AddWatch(path, sys_FS_ALL_EVENTS)
  167. }
  168. // RemoveWatch removes path from the watched file set.
  169. func (w *Watcher) removeWatch(path string) error {
  170. in := &input{
  171. op: opRemoveWatch,
  172. path: filepath.Clean(path),
  173. reply: make(chan error),
  174. }
  175. w.input <- in
  176. if err := w.wakeupReader(); err != nil {
  177. return err
  178. }
  179. return <-in.reply
  180. }
  181. func (w *Watcher) wakeupReader() error {
  182. e := syscall.PostQueuedCompletionStatus(w.port, 0, 0, nil)
  183. if e != nil {
  184. return os.NewSyscallError("PostQueuedCompletionStatus", e)
  185. }
  186. return nil
  187. }
  188. func getDir(pathname string) (dir string, err error) {
  189. attr, e := syscall.GetFileAttributes(syscall.StringToUTF16Ptr(pathname))
  190. if e != nil {
  191. return "", os.NewSyscallError("GetFileAttributes", e)
  192. }
  193. if attr&syscall.FILE_ATTRIBUTE_DIRECTORY != 0 {
  194. dir = pathname
  195. } else {
  196. dir, _ = filepath.Split(pathname)
  197. dir = filepath.Clean(dir)
  198. }
  199. return
  200. }
  201. func getIno(path string) (ino *inode, err error) {
  202. h, e := syscall.CreateFile(syscall.StringToUTF16Ptr(path),
  203. syscall.FILE_LIST_DIRECTORY,
  204. syscall.FILE_SHARE_READ|syscall.FILE_SHARE_WRITE|syscall.FILE_SHARE_DELETE,
  205. nil, syscall.OPEN_EXISTING,
  206. syscall.FILE_FLAG_BACKUP_SEMANTICS|syscall.FILE_FLAG_OVERLAPPED, 0)
  207. if e != nil {
  208. return nil, os.NewSyscallError("CreateFile", e)
  209. }
  210. var fi syscall.ByHandleFileInformation
  211. if e = syscall.GetFileInformationByHandle(h, &fi); e != nil {
  212. syscall.CloseHandle(h)
  213. return nil, os.NewSyscallError("GetFileInformationByHandle", e)
  214. }
  215. ino = &inode{
  216. handle: h,
  217. volume: fi.VolumeSerialNumber,
  218. index: uint64(fi.FileIndexHigh)<<32 | uint64(fi.FileIndexLow),
  219. }
  220. return ino, nil
  221. }
  222. // Must run within the I/O thread.
  223. func (m watchMap) get(ino *inode) *watch {
  224. if i := m[ino.volume]; i != nil {
  225. return i[ino.index]
  226. }
  227. return nil
  228. }
  229. // Must run within the I/O thread.
  230. func (m watchMap) set(ino *inode, watch *watch) {
  231. i := m[ino.volume]
  232. if i == nil {
  233. i = make(indexMap)
  234. m[ino.volume] = i
  235. }
  236. i[ino.index] = watch
  237. }
  238. // Must run within the I/O thread.
  239. func (w *Watcher) addWatch(pathname string, flags uint64) error {
  240. dir, err := getDir(pathname)
  241. if err != nil {
  242. return err
  243. }
  244. if flags&sys_FS_ONLYDIR != 0 && pathname != dir {
  245. return nil
  246. }
  247. ino, err := getIno(dir)
  248. if err != nil {
  249. return err
  250. }
  251. w.mu.Lock()
  252. watchEntry := w.watches.get(ino)
  253. w.mu.Unlock()
  254. if watchEntry == nil {
  255. if _, e := syscall.CreateIoCompletionPort(ino.handle, w.port, 0, 0); e != nil {
  256. syscall.CloseHandle(ino.handle)
  257. return os.NewSyscallError("CreateIoCompletionPort", e)
  258. }
  259. watchEntry = &watch{
  260. ino: ino,
  261. path: dir,
  262. names: make(map[string]uint64),
  263. }
  264. w.mu.Lock()
  265. w.watches.set(ino, watchEntry)
  266. w.mu.Unlock()
  267. flags |= provisional
  268. } else {
  269. syscall.CloseHandle(ino.handle)
  270. }
  271. if pathname == dir {
  272. watchEntry.mask |= flags
  273. } else {
  274. watchEntry.names[filepath.Base(pathname)] |= flags
  275. }
  276. if err = w.startRead(watchEntry); err != nil {
  277. return err
  278. }
  279. if pathname == dir {
  280. watchEntry.mask &= ^provisional
  281. } else {
  282. watchEntry.names[filepath.Base(pathname)] &= ^provisional
  283. }
  284. return nil
  285. }
  286. // Must run within the I/O thread.
  287. func (w *Watcher) remWatch(pathname string) error {
  288. dir, err := getDir(pathname)
  289. if err != nil {
  290. return err
  291. }
  292. ino, err := getIno(dir)
  293. if err != nil {
  294. return err
  295. }
  296. w.mu.Lock()
  297. watch := w.watches.get(ino)
  298. w.mu.Unlock()
  299. if watch == nil {
  300. return fmt.Errorf("can't remove non-existent watch for: %s", pathname)
  301. }
  302. if pathname == dir {
  303. w.sendEvent(watch.path, watch.mask&sys_FS_IGNORED)
  304. watch.mask = 0
  305. } else {
  306. name := filepath.Base(pathname)
  307. w.sendEvent(watch.path+"\\"+name, watch.names[name]&sys_FS_IGNORED)
  308. delete(watch.names, name)
  309. }
  310. return w.startRead(watch)
  311. }
  312. // Must run within the I/O thread.
  313. func (w *Watcher) deleteWatch(watch *watch) {
  314. for name, mask := range watch.names {
  315. if mask&provisional == 0 {
  316. w.sendEvent(watch.path+"\\"+name, mask&sys_FS_IGNORED)
  317. }
  318. delete(watch.names, name)
  319. }
  320. if watch.mask != 0 {
  321. if watch.mask&provisional == 0 {
  322. w.sendEvent(watch.path, watch.mask&sys_FS_IGNORED)
  323. }
  324. watch.mask = 0
  325. }
  326. }
  327. // Must run within the I/O thread.
  328. func (w *Watcher) startRead(watch *watch) error {
  329. if e := syscall.CancelIo(watch.ino.handle); e != nil {
  330. w.Error <- os.NewSyscallError("CancelIo", e)
  331. w.deleteWatch(watch)
  332. }
  333. mask := toWindowsFlags(watch.mask)
  334. for _, m := range watch.names {
  335. mask |= toWindowsFlags(m)
  336. }
  337. if mask == 0 {
  338. if e := syscall.CloseHandle(watch.ino.handle); e != nil {
  339. w.Error <- os.NewSyscallError("CloseHandle", e)
  340. }
  341. w.mu.Lock()
  342. delete(w.watches[watch.ino.volume], watch.ino.index)
  343. w.mu.Unlock()
  344. return nil
  345. }
  346. e := syscall.ReadDirectoryChanges(watch.ino.handle, &watch.buf[0],
  347. uint32(unsafe.Sizeof(watch.buf)), false, mask, nil, &watch.ov, 0)
  348. if e != nil {
  349. err := os.NewSyscallError("ReadDirectoryChanges", e)
  350. if e == syscall.ERROR_ACCESS_DENIED && watch.mask&provisional == 0 {
  351. // Watched directory was probably removed
  352. if w.sendEvent(watch.path, watch.mask&sys_FS_DELETE_SELF) {
  353. if watch.mask&sys_FS_ONESHOT != 0 {
  354. watch.mask = 0
  355. }
  356. }
  357. err = nil
  358. }
  359. w.deleteWatch(watch)
  360. w.startRead(watch)
  361. return err
  362. }
  363. return nil
  364. }
  365. // readEvents reads from the I/O completion port, converts the
  366. // received events into Event objects and sends them via the Event channel.
  367. // Entry point to the I/O thread.
  368. func (w *Watcher) readEvents() {
  369. var (
  370. n, key uint32
  371. ov *syscall.Overlapped
  372. )
  373. runtime.LockOSThread()
  374. for {
  375. e := syscall.GetQueuedCompletionStatus(w.port, &n, &key, &ov, syscall.INFINITE)
  376. watch := (*watch)(unsafe.Pointer(ov))
  377. if watch == nil {
  378. select {
  379. case ch := <-w.quit:
  380. w.mu.Lock()
  381. var indexes []indexMap
  382. for _, index := range w.watches {
  383. indexes = append(indexes, index)
  384. }
  385. w.mu.Unlock()
  386. for _, index := range indexes {
  387. for _, watch := range index {
  388. w.deleteWatch(watch)
  389. w.startRead(watch)
  390. }
  391. }
  392. var err error
  393. if e := syscall.CloseHandle(w.port); e != nil {
  394. err = os.NewSyscallError("CloseHandle", e)
  395. }
  396. close(w.internalEvent)
  397. close(w.Error)
  398. ch <- err
  399. return
  400. case in := <-w.input:
  401. switch in.op {
  402. case opAddWatch:
  403. in.reply <- w.addWatch(in.path, uint64(in.flags))
  404. case opRemoveWatch:
  405. in.reply <- w.remWatch(in.path)
  406. }
  407. default:
  408. }
  409. continue
  410. }
  411. switch e {
  412. case sys_ERROR_MORE_DATA:
  413. if watch == nil {
  414. w.Error <- errors.New("ERROR_MORE_DATA has unexpectedly null lpOverlapped buffer")
  415. } else {
  416. // The i/o succeeded but the buffer is full.
  417. // In theory we should be building up a full packet.
  418. // In practice we can get away with just carrying on.
  419. n = uint32(unsafe.Sizeof(watch.buf))
  420. }
  421. case syscall.ERROR_ACCESS_DENIED:
  422. // Watched directory was probably removed
  423. w.sendEvent(watch.path, watch.mask&sys_FS_DELETE_SELF)
  424. w.deleteWatch(watch)
  425. w.startRead(watch)
  426. continue
  427. case syscall.ERROR_OPERATION_ABORTED:
  428. // CancelIo was called on this handle
  429. continue
  430. default:
  431. w.Error <- os.NewSyscallError("GetQueuedCompletionPort", e)
  432. continue
  433. case nil:
  434. }
  435. var offset uint32
  436. for {
  437. if n == 0 {
  438. w.internalEvent <- &FileEvent{mask: sys_FS_Q_OVERFLOW}
  439. w.Error <- errors.New("short read in readEvents()")
  440. break
  441. }
  442. // Point "raw" to the event in the buffer
  443. raw := (*syscall.FileNotifyInformation)(unsafe.Pointer(&watch.buf[offset]))
  444. buf := (*[syscall.MAX_PATH]uint16)(unsafe.Pointer(&raw.FileName))
  445. name := syscall.UTF16ToString(buf[:raw.FileNameLength/2])
  446. fullname := watch.path + "\\" + name
  447. var mask uint64
  448. switch raw.Action {
  449. case syscall.FILE_ACTION_REMOVED:
  450. mask = sys_FS_DELETE_SELF
  451. case syscall.FILE_ACTION_MODIFIED:
  452. mask = sys_FS_MODIFY
  453. case syscall.FILE_ACTION_RENAMED_OLD_NAME:
  454. watch.rename = name
  455. case syscall.FILE_ACTION_RENAMED_NEW_NAME:
  456. if watch.names[watch.rename] != 0 {
  457. watch.names[name] |= watch.names[watch.rename]
  458. delete(watch.names, watch.rename)
  459. mask = sys_FS_MOVE_SELF
  460. }
  461. }
  462. sendNameEvent := func() {
  463. if w.sendEvent(fullname, watch.names[name]&mask) {
  464. if watch.names[name]&sys_FS_ONESHOT != 0 {
  465. delete(watch.names, name)
  466. }
  467. }
  468. }
  469. if raw.Action != syscall.FILE_ACTION_RENAMED_NEW_NAME {
  470. sendNameEvent()
  471. }
  472. if raw.Action == syscall.FILE_ACTION_REMOVED {
  473. w.sendEvent(fullname, watch.names[name]&sys_FS_IGNORED)
  474. delete(watch.names, name)
  475. }
  476. if w.sendEvent(fullname, watch.mask&toFSnotifyFlags(raw.Action)) {
  477. if watch.mask&sys_FS_ONESHOT != 0 {
  478. watch.mask = 0
  479. }
  480. }
  481. if raw.Action == syscall.FILE_ACTION_RENAMED_NEW_NAME {
  482. fullname = watch.path + "\\" + watch.rename
  483. sendNameEvent()
  484. }
  485. // Move to the next event in the buffer
  486. if raw.NextEntryOffset == 0 {
  487. break
  488. }
  489. offset += raw.NextEntryOffset
  490. // Error!
  491. if offset >= n {
  492. w.Error <- errors.New("Windows system assumed buffer larger than it is, events have likely been missed.")
  493. break
  494. }
  495. }
  496. if err := w.startRead(watch); err != nil {
  497. w.Error <- err
  498. }
  499. }
  500. }
  501. func (w *Watcher) sendEvent(name string, mask uint64) bool {
  502. if mask == 0 {
  503. return false
  504. }
  505. event := &FileEvent{mask: uint32(mask), Name: name}
  506. if mask&sys_FS_MOVE != 0 {
  507. if mask&sys_FS_MOVED_FROM != 0 {
  508. w.cookie++
  509. }
  510. event.cookie = w.cookie
  511. }
  512. select {
  513. case ch := <-w.quit:
  514. w.quit <- ch
  515. case w.Event <- event:
  516. }
  517. return true
  518. }
  519. func toWindowsFlags(mask uint64) uint32 {
  520. var m uint32
  521. if mask&sys_FS_ACCESS != 0 {
  522. m |= syscall.FILE_NOTIFY_CHANGE_LAST_ACCESS
  523. }
  524. if mask&sys_FS_MODIFY != 0 {
  525. m |= syscall.FILE_NOTIFY_CHANGE_LAST_WRITE
  526. }
  527. if mask&sys_FS_ATTRIB != 0 {
  528. m |= syscall.FILE_NOTIFY_CHANGE_ATTRIBUTES
  529. }
  530. if mask&(sys_FS_MOVE|sys_FS_CREATE|sys_FS_DELETE) != 0 {
  531. m |= syscall.FILE_NOTIFY_CHANGE_FILE_NAME | syscall.FILE_NOTIFY_CHANGE_DIR_NAME
  532. }
  533. return m
  534. }
  535. func toFSnotifyFlags(action uint32) uint64 {
  536. switch action {
  537. case syscall.FILE_ACTION_ADDED:
  538. return sys_FS_CREATE
  539. case syscall.FILE_ACTION_REMOVED:
  540. return sys_FS_DELETE
  541. case syscall.FILE_ACTION_MODIFIED:
  542. return sys_FS_MODIFY
  543. case syscall.FILE_ACTION_RENAMED_OLD_NAME:
  544. return sys_FS_MOVED_FROM
  545. case syscall.FILE_ACTION_RENAMED_NEW_NAME:
  546. return sys_FS_MOVED_TO
  547. }
  548. return 0
  549. }