OSDN Git Service

Merge pull request #41 from Bytom/dev
[bytom/vapor.git] / vendor / github.com / fsnotify / fsnotify / inotify.go
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
7 package fsnotify
8
9 import (
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.
23 type 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.
36 func 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
63 func (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.
73 func (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).
91 func (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).
126 func (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
165 type 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
172 func (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.
300 func (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.
319 func 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 }