fsnotify_linux.go 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304
  1. // Copyright 2010 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 linux
  5. package fsnotify
  6. import (
  7. "errors"
  8. "fmt"
  9. "os"
  10. "strings"
  11. "sync"
  12. "syscall"
  13. "unsafe"
  14. )
  15. const (
  16. // Options for inotify_init() are not exported
  17. // sys_IN_CLOEXEC uint32 = syscall.IN_CLOEXEC
  18. // sys_IN_NONBLOCK uint32 = syscall.IN_NONBLOCK
  19. // Options for AddWatch
  20. sys_IN_DONT_FOLLOW uint32 = syscall.IN_DONT_FOLLOW
  21. sys_IN_ONESHOT uint32 = syscall.IN_ONESHOT
  22. sys_IN_ONLYDIR uint32 = syscall.IN_ONLYDIR
  23. // The "sys_IN_MASK_ADD" option is not exported, as AddWatch
  24. // adds it automatically, if there is already a watch for the given path
  25. // sys_IN_MASK_ADD uint32 = syscall.IN_MASK_ADD
  26. // Events
  27. sys_IN_ACCESS uint32 = syscall.IN_ACCESS
  28. sys_IN_ALL_EVENTS uint32 = syscall.IN_ALL_EVENTS
  29. sys_IN_ATTRIB uint32 = syscall.IN_ATTRIB
  30. sys_IN_CLOSE uint32 = syscall.IN_CLOSE
  31. sys_IN_CLOSE_NOWRITE uint32 = syscall.IN_CLOSE_NOWRITE
  32. sys_IN_CLOSE_WRITE uint32 = syscall.IN_CLOSE_WRITE
  33. sys_IN_CREATE uint32 = syscall.IN_CREATE
  34. sys_IN_DELETE uint32 = syscall.IN_DELETE
  35. sys_IN_DELETE_SELF uint32 = syscall.IN_DELETE_SELF
  36. sys_IN_MODIFY uint32 = syscall.IN_MODIFY
  37. sys_IN_MOVE uint32 = syscall.IN_MOVE
  38. sys_IN_MOVED_FROM uint32 = syscall.IN_MOVED_FROM
  39. sys_IN_MOVED_TO uint32 = syscall.IN_MOVED_TO
  40. sys_IN_MOVE_SELF uint32 = syscall.IN_MOVE_SELF
  41. sys_IN_OPEN uint32 = syscall.IN_OPEN
  42. sys_AGNOSTIC_EVENTS = sys_IN_MOVED_TO | sys_IN_MOVED_FROM | sys_IN_CREATE | sys_IN_ATTRIB | sys_IN_MODIFY | sys_IN_MOVE_SELF | sys_IN_DELETE | sys_IN_DELETE_SELF
  43. // Special events
  44. sys_IN_ISDIR uint32 = syscall.IN_ISDIR
  45. sys_IN_IGNORED uint32 = syscall.IN_IGNORED
  46. sys_IN_Q_OVERFLOW uint32 = syscall.IN_Q_OVERFLOW
  47. sys_IN_UNMOUNT uint32 = syscall.IN_UNMOUNT
  48. )
  49. type FileEvent struct {
  50. mask uint32 // Mask of events
  51. cookie uint32 // Unique cookie associating related events (for rename(2))
  52. Name string // File name (optional)
  53. }
  54. // IsCreate reports whether the FileEvent was triggered by a creation
  55. func (e *FileEvent) IsCreate() bool {
  56. return (e.mask&sys_IN_CREATE) == sys_IN_CREATE || (e.mask&sys_IN_MOVED_TO) == sys_IN_MOVED_TO
  57. }
  58. // IsDelete reports whether the FileEvent was triggered by a delete
  59. func (e *FileEvent) IsDelete() bool {
  60. return (e.mask&sys_IN_DELETE_SELF) == sys_IN_DELETE_SELF || (e.mask&sys_IN_DELETE) == sys_IN_DELETE
  61. }
  62. // IsModify reports whether the FileEvent was triggered by a file modification or attribute change
  63. func (e *FileEvent) IsModify() bool {
  64. return ((e.mask&sys_IN_MODIFY) == sys_IN_MODIFY || (e.mask&sys_IN_ATTRIB) == sys_IN_ATTRIB)
  65. }
  66. // IsRename reports whether the FileEvent was triggered by a change name
  67. func (e *FileEvent) IsRename() bool {
  68. return ((e.mask&sys_IN_MOVE_SELF) == sys_IN_MOVE_SELF || (e.mask&sys_IN_MOVED_FROM) == sys_IN_MOVED_FROM)
  69. }
  70. // IsAttrib reports whether the FileEvent was triggered by a change in the file metadata.
  71. func (e *FileEvent) IsAttrib() bool {
  72. return (e.mask & sys_IN_ATTRIB) == sys_IN_ATTRIB
  73. }
  74. type watch struct {
  75. wd uint32 // Watch descriptor (as returned by the inotify_add_watch() syscall)
  76. flags uint32 // inotify flags of this watch (see inotify(7) for the list of valid flags)
  77. }
  78. type Watcher struct {
  79. mu sync.Mutex // Map access
  80. fd int // File descriptor (as returned by the inotify_init() syscall)
  81. watches map[string]*watch // Map of inotify watches (key: path)
  82. fsnFlags map[string]uint32 // Map of watched files to flags used for filter
  83. fsnmut sync.Mutex // Protects access to fsnFlags.
  84. paths map[int]string // Map of watched paths (key: watch descriptor)
  85. Error chan error // Errors are sent on this channel
  86. internalEvent chan *FileEvent // Events are queued on this channel
  87. Event chan *FileEvent // Events are returned on this channel
  88. done chan bool // Channel for sending a "quit message" to the reader goroutine
  89. isClosed bool // Set to true when Close() is first called
  90. }
  91. // NewWatcher creates and returns a new inotify instance using inotify_init(2)
  92. func NewWatcher() (*Watcher, error) {
  93. fd, errno := syscall.InotifyInit()
  94. if fd == -1 {
  95. return nil, os.NewSyscallError("inotify_init", errno)
  96. }
  97. w := &Watcher{
  98. fd: fd,
  99. watches: make(map[string]*watch),
  100. fsnFlags: make(map[string]uint32),
  101. paths: make(map[int]string),
  102. internalEvent: make(chan *FileEvent),
  103. Event: make(chan *FileEvent),
  104. Error: make(chan error),
  105. done: make(chan bool, 1),
  106. }
  107. go w.readEvents()
  108. go w.purgeEvents()
  109. return w, nil
  110. }
  111. // Close closes an inotify watcher instance
  112. // It sends a message to the reader goroutine to quit and removes all watches
  113. // associated with the inotify instance
  114. func (w *Watcher) Close() error {
  115. if w.isClosed {
  116. return nil
  117. }
  118. w.isClosed = true
  119. // Remove all watches
  120. for path := range w.watches {
  121. w.RemoveWatch(path)
  122. }
  123. // Send "quit" message to the reader goroutine
  124. w.done <- true
  125. return nil
  126. }
  127. // AddWatch adds path to the watched file set.
  128. // The flags are interpreted as described in inotify_add_watch(2).
  129. func (w *Watcher) addWatch(path string, flags uint32) error {
  130. if w.isClosed {
  131. return errors.New("inotify instance already closed")
  132. }
  133. w.mu.Lock()
  134. watchEntry, found := w.watches[path]
  135. w.mu.Unlock()
  136. if found {
  137. watchEntry.flags |= flags
  138. flags |= syscall.IN_MASK_ADD
  139. }
  140. wd, errno := syscall.InotifyAddWatch(w.fd, path, flags)
  141. if wd == -1 {
  142. return errno
  143. }
  144. w.mu.Lock()
  145. w.watches[path] = &watch{wd: uint32(wd), flags: flags}
  146. w.paths[wd] = path
  147. w.mu.Unlock()
  148. return nil
  149. }
  150. // Watch adds path to the watched file set, watching all events.
  151. func (w *Watcher) watch(path string) error {
  152. return w.addWatch(path, sys_AGNOSTIC_EVENTS)
  153. }
  154. // RemoveWatch removes path from the watched file set.
  155. func (w *Watcher) removeWatch(path string) error {
  156. w.mu.Lock()
  157. defer w.mu.Unlock()
  158. watch, ok := w.watches[path]
  159. if !ok {
  160. return errors.New(fmt.Sprintf("can't remove non-existent inotify watch for: %s", path))
  161. }
  162. success, errno := syscall.InotifyRmWatch(w.fd, watch.wd)
  163. if success == -1 {
  164. return os.NewSyscallError("inotify_rm_watch", errno)
  165. }
  166. delete(w.watches, path)
  167. return nil
  168. }
  169. // readEvents reads from the inotify file descriptor, converts the
  170. // received events into Event objects and sends them via the Event channel
  171. func (w *Watcher) readEvents() {
  172. var (
  173. buf [syscall.SizeofInotifyEvent * 4096]byte // Buffer for a maximum of 4096 raw events
  174. n int // Number of bytes read with read()
  175. errno error // Syscall errno
  176. )
  177. for {
  178. // See if there is a message on the "done" channel
  179. select {
  180. case <-w.done:
  181. syscall.Close(w.fd)
  182. close(w.internalEvent)
  183. close(w.Error)
  184. return
  185. default:
  186. }
  187. n, errno = syscall.Read(w.fd, buf[:])
  188. // If EOF is received
  189. if n == 0 {
  190. syscall.Close(w.fd)
  191. close(w.internalEvent)
  192. close(w.Error)
  193. return
  194. }
  195. if n < 0 {
  196. w.Error <- os.NewSyscallError("read", errno)
  197. continue
  198. }
  199. if n < syscall.SizeofInotifyEvent {
  200. w.Error <- errors.New("inotify: short read in readEvents()")
  201. continue
  202. }
  203. var offset uint32 = 0
  204. // We don't know how many events we just read into the buffer
  205. // While the offset points to at least one whole event...
  206. for offset <= uint32(n-syscall.SizeofInotifyEvent) {
  207. // Point "raw" to the event in the buffer
  208. raw := (*syscall.InotifyEvent)(unsafe.Pointer(&buf[offset]))
  209. event := new(FileEvent)
  210. event.mask = uint32(raw.Mask)
  211. event.cookie = uint32(raw.Cookie)
  212. nameLen := uint32(raw.Len)
  213. // If the event happened to the watched directory or the watched file, the kernel
  214. // doesn't append the filename to the event, but we would like to always fill the
  215. // the "Name" field with a valid filename. We retrieve the path of the watch from
  216. // the "paths" map.
  217. w.mu.Lock()
  218. event.Name = w.paths[int(raw.Wd)]
  219. w.mu.Unlock()
  220. watchedName := event.Name
  221. if nameLen > 0 {
  222. // Point "bytes" at the first byte of the filename
  223. bytes := (*[syscall.PathMax]byte)(unsafe.Pointer(&buf[offset+syscall.SizeofInotifyEvent]))
  224. // The filename is padded with NUL bytes. TrimRight() gets rid of those.
  225. event.Name += "/" + strings.TrimRight(string(bytes[0:nameLen]), "\000")
  226. }
  227. // Send the events that are not ignored on the events channel
  228. if !event.ignoreLinux() {
  229. // Setup FSNotify flags (inherit from directory watch)
  230. w.fsnmut.Lock()
  231. if _, fsnFound := w.fsnFlags[event.Name]; !fsnFound {
  232. if fsnFlags, watchFound := w.fsnFlags[watchedName]; watchFound {
  233. w.fsnFlags[event.Name] = fsnFlags
  234. } else {
  235. w.fsnFlags[event.Name] = FSN_ALL
  236. }
  237. }
  238. w.fsnmut.Unlock()
  239. w.internalEvent <- event
  240. }
  241. // Move to the next event in the buffer
  242. offset += syscall.SizeofInotifyEvent + nameLen
  243. }
  244. }
  245. }
  246. // Certain types of events can be "ignored" and not sent over the Event
  247. // channel. Such as events marked ignore by the kernel, or MODIFY events
  248. // against files that do not exist.
  249. func (e *FileEvent) ignoreLinux() bool {
  250. // Ignore anything the inotify API says to ignore
  251. if e.mask&sys_IN_IGNORED == sys_IN_IGNORED {
  252. return true
  253. }
  254. // If the event is not a DELETE or RENAME, the file must exist.
  255. // Otherwise the event is ignored.
  256. // *Note*: this was put in place because it was seen that a MODIFY
  257. // event was sent after the DELETE. This ignores that MODIFY and
  258. // assumes a DELETE will come or has come if the file doesn't exist.
  259. if !(e.IsDelete() || e.IsRename()) {
  260. _, statErr := os.Lstat(e.Name)
  261. return os.IsNotExist(statErr)
  262. }
  263. return false
  264. }