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 5// +build linux 6 7package fsnotify 8 9import ( 10 "errors" 11 "fmt" 12 "io" 13 "os" 14 "path/filepath" 15 "strings" 16 "sync" 17 "unsafe" 18 19 "golang.org/x/sys/unix" 20) 21 22// Watcher watches a set of files, delivering events to a channel. 23type Watcher struct { 24 Events chan Event 25 Errors chan error 26 mu sync.Mutex // Map access 27 fd int 28 poller *fdPoller 29 watches map[string]*watch // Map of inotify watches (key: path) 30 paths map[int]string // Map of watched paths (key: watch descriptor) 31 done chan struct{} // Channel for sending a "quit message" to the reader goroutine 32 doneResp chan struct{} // Channel to respond to Close 33} 34 35// NewWatcher establishes a new watcher with the underlying OS and begins waiting for events. 36func NewWatcher() (*Watcher, error) { 37 // Create inotify fd 38 fd, errno := unix.InotifyInit1(unix.IN_CLOEXEC) 39 if fd == -1 { 40 return nil, errno 41 } 42 // Create epoll 43 poller, err := newFdPoller(fd) 44 if err != nil { 45 unix.Close(fd) 46 return nil, err 47 } 48 w := &Watcher{ 49 fd: fd, 50 poller: poller, 51 watches: make(map[string]*watch), 52 paths: make(map[int]string), 53 Events: make(chan Event), 54 Errors: make(chan error), 55 done: make(chan struct{}), 56 doneResp: make(chan struct{}), 57 } 58 59 go w.readEvents() 60 return w, nil 61} 62 63func (w *Watcher) isClosed() bool { 64 select { 65 case <-w.done: 66 return true 67 default: 68 return false 69 } 70} 71 72// Close removes all watches and closes the events channel. 73func (w *Watcher) Close() error { 74 if w.isClosed() { 75 return nil 76 } 77 78 // Send 'close' signal to goroutine, and set the Watcher to closed. 79 close(w.done) 80 81 // Wake up goroutine 82 w.poller.wake() 83 84 // Wait for goroutine to close 85 <-w.doneResp 86 87 return nil 88} 89 90// Add starts watching the named file or directory (non-recursively). 91func (w *Watcher) Add(name string) error { 92 name = filepath.Clean(name) 93 if w.isClosed() { 94 return errors.New("inotify instance already closed") 95 } 96 97 const agnosticEvents = unix.IN_MOVED_TO | unix.IN_MOVED_FROM | 98 unix.IN_CREATE | unix.IN_ATTRIB | unix.IN_MODIFY | 99 unix.IN_MOVE_SELF | unix.IN_DELETE | unix.IN_DELETE_SELF 100 101 var flags uint32 = agnosticEvents 102 103 w.mu.Lock() 104 defer w.mu.Unlock() 105 watchEntry := w.watches[name] 106 if watchEntry != nil { 107 flags |= watchEntry.flags | unix.IN_MASK_ADD 108 } 109 wd, errno := unix.InotifyAddWatch(w.fd, name, flags) 110 if wd == -1 { 111 return errno 112 } 113 114 if watchEntry == nil { 115 w.watches[name] = &watch{wd: uint32(wd), flags: flags} 116 w.paths[wd] = name 117 } else { 118 watchEntry.wd = uint32(wd) 119 watchEntry.flags = flags 120 } 121 122 return nil 123} 124 125// Remove stops watching the named file or directory (non-recursively). 126func (w *Watcher) Remove(name string) error { 127 name = filepath.Clean(name) 128 129 // Fetch the watch. 130 w.mu.Lock() 131 defer w.mu.Unlock() 132 watch, ok := w.watches[name] 133 134 // Remove it from inotify. 135 if !ok { 136 return fmt.Errorf("can't remove non-existent inotify watch for: %s", name) 137 } 138 139 // We successfully removed the watch if InotifyRmWatch doesn't return an 140 // error, we need to clean up our internal state to ensure it matches 141 // inotify's kernel state. 142 delete(w.paths, int(watch.wd)) 143 delete(w.watches, name) 144 145 // inotify_rm_watch will return EINVAL if the file has been deleted; 146 // the inotify will already have been removed. 147 // watches and pathes are deleted in ignoreLinux() implicitly and asynchronously 148 // by calling inotify_rm_watch() below. e.g. readEvents() goroutine receives IN_IGNORE 149 // so that EINVAL means that the wd is being rm_watch()ed or its file removed 150 // by another thread and we have not received IN_IGNORE event. 151 success, errno := unix.InotifyRmWatch(w.fd, watch.wd) 152 if success == -1 { 153 // TODO: Perhaps it's not helpful to return an error here in every case. 154 // the only two possible errors are: 155 // EBADF, which happens when w.fd is not a valid file descriptor of any kind. 156 // EINVAL, which is when fd is not an inotify descriptor or wd is not a valid watch descriptor. 157 // Watch descriptors are invalidated when they are removed explicitly or implicitly; 158 // explicitly by inotify_rm_watch, implicitly when the file they are watching is deleted. 159 return errno 160 } 161 162 return nil 163} 164 165type watch struct { 166 wd uint32 // Watch descriptor (as returned by the inotify_add_watch() syscall) 167 flags uint32 // inotify flags of this watch (see inotify(7) for the list of valid flags) 168} 169 170// readEvents reads from the inotify file descriptor, converts the 171// received events into Event objects and sends them via the Events channel 172func (w *Watcher) readEvents() { 173 var ( 174 buf [unix.SizeofInotifyEvent * 4096]byte // Buffer for a maximum of 4096 raw events 175 n int // Number of bytes read with read() 176 errno error // Syscall errno 177 ok bool // For poller.wait 178 ) 179 180 defer close(w.doneResp) 181 defer close(w.Errors) 182 defer close(w.Events) 183 defer unix.Close(w.fd) 184 defer w.poller.close() 185 186 for { 187 // See if we have been closed. 188 if w.isClosed() { 189 return 190 } 191 192 ok, errno = w.poller.wait() 193 if errno != nil { 194 select { 195 case w.Errors <- errno: 196 case <-w.done: 197 return 198 } 199 continue 200 } 201 202 if !ok { 203 continue 204 } 205 206 n, errno = unix.Read(w.fd, buf[:]) 207 // If a signal interrupted execution, see if we've been asked to close, and try again. 208 // http://man7.org/linux/man-pages/man7/signal.7.html : 209 // "Before Linux 3.8, reads from an inotify(7) file descriptor were not restartable" 210 if errno == unix.EINTR { 211 continue 212 } 213 214 // unix.Read might have been woken up by Close. If so, we're done. 215 if w.isClosed() { 216 return 217 } 218 219 if n < unix.SizeofInotifyEvent { 220 var err error 221 if n == 0 { 222 // If EOF is received. This should really never happen. 223 err = io.EOF 224 } else if n < 0 { 225 // If an error occurred while reading. 226 err = errno 227 } else { 228 // Read was too short. 229 err = errors.New("notify: short read in readEvents()") 230 } 231 select { 232 case w.Errors <- err: 233 case <-w.done: 234 return 235 } 236 continue 237 } 238 239 var offset uint32 240 // We don't know how many events we just read into the buffer 241 // While the offset points to at least one whole event... 242 for offset <= uint32(n-unix.SizeofInotifyEvent) { 243 // Point "raw" to the event in the buffer 244 raw := (*unix.InotifyEvent)(unsafe.Pointer(&buf[offset])) 245 246 mask := uint32(raw.Mask) 247 nameLen := uint32(raw.Len) 248 249 if mask&unix.IN_Q_OVERFLOW != 0 { 250 select { 251 case w.Errors <- ErrEventOverflow: 252 case <-w.done: 253 return 254 } 255 } 256 257 // If the event happened to the watched directory or the watched file, the kernel 258 // doesn't append the filename to the event, but we would like to always fill the 259 // the "Name" field with a valid filename. We retrieve the path of the watch from 260 // the "paths" map. 261 w.mu.Lock() 262 name, ok := w.paths[int(raw.Wd)] 263 // IN_DELETE_SELF occurs when the file/directory being watched is removed. 264 // This is a sign to clean up the maps, otherwise we are no longer in sync 265 // with the inotify kernel state which has already deleted the watch 266 // automatically. 267 if ok && mask&unix.IN_DELETE_SELF == unix.IN_DELETE_SELF { 268 delete(w.paths, int(raw.Wd)) 269 delete(w.watches, name) 270 } 271 w.mu.Unlock() 272 273 if nameLen > 0 { 274 // Point "bytes" at the first byte of the filename 275 bytes := (*[unix.PathMax]byte)(unsafe.Pointer(&buf[offset+unix.SizeofInotifyEvent])) 276 // The filename is padded with NULL bytes. TrimRight() gets rid of those. 277 name += "/" + strings.TrimRight(string(bytes[0:nameLen]), "\000") 278 } 279 280 event := newEvent(name, mask) 281 282 // Send the events that are not ignored on the events channel 283 if !event.ignoreLinux(mask) { 284 select { 285 case w.Events <- event: 286 case <-w.done: 287 return 288 } 289 } 290 291 // Move to the next event in the buffer 292 offset += unix.SizeofInotifyEvent + nameLen 293 } 294 } 295} 296 297// Certain types of events can be "ignored" and not sent over the Events 298// channel. Such as events marked ignore by the kernel, or MODIFY events 299// against files that do not exist. 300func (e *Event) ignoreLinux(mask uint32) bool { 301 // Ignore anything the inotify API says to ignore 302 if mask&unix.IN_IGNORED == unix.IN_IGNORED { 303 return true 304 } 305 306 // If the event is not a DELETE or RENAME, the file must exist. 307 // Otherwise the event is ignored. 308 // *Note*: this was put in place because it was seen that a MODIFY 309 // event was sent after the DELETE. This ignores that MODIFY and 310 // assumes a DELETE will come or has come if the file doesn't exist. 311 if !(e.Op&Remove == Remove || e.Op&Rename == Rename) { 312 _, statErr := os.Lstat(e.Name) 313 return os.IsNotExist(statErr) 314 } 315 return false 316} 317 318// newEvent returns an platform-independent Event based on an inotify mask. 319func newEvent(name string, mask uint32) Event { 320 e := Event{Name: name} 321 if mask&unix.IN_CREATE == unix.IN_CREATE || mask&unix.IN_MOVED_TO == unix.IN_MOVED_TO { 322 e.Op |= Create 323 } 324 if mask&unix.IN_DELETE_SELF == unix.IN_DELETE_SELF || mask&unix.IN_DELETE == unix.IN_DELETE { 325 e.Op |= Remove 326 } 327 if mask&unix.IN_MODIFY == unix.IN_MODIFY { 328 e.Op |= Write 329 } 330 if mask&unix.IN_MOVE_SELF == unix.IN_MOVE_SELF || mask&unix.IN_MOVED_FROM == unix.IN_MOVED_FROM { 331 e.Op |= Rename 332 } 333 if mask&unix.IN_ATTRIB == unix.IN_ATTRIB { 334 e.Op |= Chmod 335 } 336 return e 337} 338