OSDN Git Service

new repo
[bytom/vapor.git] / vendor / github.com / rjeczalik / notify / watcher_fsevents.go
1 // Copyright (c) 2014-2015 The Notify Authors. All rights reserved.
2 // Use of this source code is governed by the MIT license that can be
3 // found in the LICENSE file.
4
5 // +build darwin,!kqueue
6
7 package notify
8
9 import (
10         "errors"
11         "strings"
12         "sync/atomic"
13 )
14
15 // TODO(rjeczalik): get rid of calls to canonical, it's tree responsibility
16
17 const (
18         failure = uint32(FSEventsMustScanSubDirs | FSEventsUserDropped | FSEventsKernelDropped)
19         filter  = uint32(FSEventsCreated | FSEventsRemoved | FSEventsRenamed |
20                 FSEventsModified | FSEventsInodeMetaMod)
21 )
22
23 // FSEvent represents single file event. It is created out of values passed by
24 // FSEvents to FSEventStreamCallback function.
25 type FSEvent struct {
26         Path  string // real path of the file or directory
27         ID    uint64 // ID of the event (FSEventStreamEventId)
28         Flags uint32 // joint FSEvents* flags (FSEventStreamEventFlags)
29 }
30
31 // splitflags separates event flags from single set into slice of flags.
32 func splitflags(set uint32) (e []uint32) {
33         for i := uint32(1); set != 0; i, set = i<<1, set>>1 {
34                 if (set & 1) != 0 {
35                         e = append(e, i)
36                 }
37         }
38         return
39 }
40
41 // watch represents a filesystem watchpoint. It is a higher level abstraction
42 // over FSEvents' stream, which implements filtering of file events based
43 // on path and event set. It emulates non-recursive watch-point by filtering out
44 // events which paths are more than 1 level deeper than the watched path.
45 type watch struct {
46         // prev stores last event set  per path in order to filter out old flags
47         // for new events, which appratenly FSEvents likes to retain. It's a disgusting
48         // hack, it should be researched how to get rid of it.
49         prev    map[string]uint32
50         c       chan<- EventInfo
51         stream  *stream
52         path    string
53         events  uint32
54         isrec   int32
55         flushed bool
56 }
57
58 // Example format:
59 //
60 //   ~ $ (trigger command) # (event set) -> (effective event set)
61 //
62 // Heuristics:
63 //
64 // 1. Create event is removed when it was present in previous event set.
65 // Example:
66 //
67 //   ~ $ echo > file # Create|Write -> Create|Write
68 //   ~ $ echo > file # Create|Write|InodeMetaMod -> Write|InodeMetaMod
69 //
70 // 2. Remove event is removed if it was present in previouse event set.
71 // Example:
72 //
73 //   ~ $ touch file # Create -> Create
74 //   ~ $ rm file    # Create|Remove -> Remove
75 //   ~ $ touch file # Create|Remove -> Create
76 //
77 // 3. Write event is removed if not followed by InodeMetaMod on existing
78 // file. Example:
79 //
80 //   ~ $ echo > file   # Create|Write -> Create|Write
81 //   ~ $ chmod +x file # Create|Write|ChangeOwner -> ChangeOwner
82 //
83 // 4. Write&InodeMetaMod is removed when effective event set contain Remove event.
84 // Example:
85 //
86 //   ~ $ echo > file # Write|InodeMetaMod -> Write|InodeMetaMod
87 //   ~ $ rm file     # Remove|Write|InodeMetaMod -> Remove
88 //
89 func (w *watch) strip(base string, set uint32) uint32 {
90         const (
91                 write = FSEventsModified | FSEventsInodeMetaMod
92                 both  = FSEventsCreated | FSEventsRemoved
93         )
94         switch w.prev[base] {
95         case FSEventsCreated:
96                 set &^= FSEventsCreated
97                 if set&FSEventsRemoved != 0 {
98                         w.prev[base] = FSEventsRemoved
99                         set &^= write
100                 }
101         case FSEventsRemoved:
102                 set &^= FSEventsRemoved
103                 if set&FSEventsCreated != 0 {
104                         w.prev[base] = FSEventsCreated
105                 }
106         default:
107                 switch set & both {
108                 case FSEventsCreated:
109                         w.prev[base] = FSEventsCreated
110                 case FSEventsRemoved:
111                         w.prev[base] = FSEventsRemoved
112                         set &^= write
113                 }
114         }
115         dbgprintf("split()=%v\n", Event(set))
116         return set
117 }
118
119 // Dispatch is a stream function which forwards given file events for the watched
120 // path to underlying FileInfo channel.
121 func (w *watch) Dispatch(ev []FSEvent) {
122         events := atomic.LoadUint32(&w.events)
123         isrec := (atomic.LoadInt32(&w.isrec) == 1)
124         for i := range ev {
125                 if ev[i].Flags&FSEventsHistoryDone != 0 {
126                         w.flushed = true
127                         continue
128                 }
129                 if !w.flushed {
130                         continue
131                 }
132                 dbgprintf("%v (0x%x) (%s, i=%d, ID=%d, len=%d)\n", Event(ev[i].Flags),
133                         ev[i].Flags, ev[i].Path, i, ev[i].ID, len(ev))
134                 if ev[i].Flags&failure != 0 {
135                         // TODO(rjeczalik): missing error handling
136                         continue
137                 }
138                 if !strings.HasPrefix(ev[i].Path, w.path) {
139                         continue
140                 }
141                 n := len(w.path)
142                 base := ""
143                 if len(ev[i].Path) > n {
144                         if ev[i].Path[n] != '/' {
145                                 continue
146                         }
147                         base = ev[i].Path[n+1:]
148                         if !isrec && strings.IndexByte(base, '/') != -1 {
149                                 continue
150                         }
151                 }
152                 // TODO(rjeczalik): get diff only from filtered events?
153                 e := w.strip(string(base), ev[i].Flags) & events
154                 if e == 0 {
155                         continue
156                 }
157                 for _, e := range splitflags(e) {
158                         dbgprintf("%d: single event: %v", ev[i].ID, Event(e))
159                         w.c <- &event{
160                                 fse:   ev[i],
161                                 event: Event(e),
162                         }
163                 }
164         }
165 }
166
167 // Stop closes underlying FSEvents stream and stops dispatching events.
168 func (w *watch) Stop() {
169         w.stream.Stop()
170         // TODO(rjeczalik): make (*stream).Stop flush synchronously undelivered events,
171         // so the following hack can be removed. It should flush all the streams
172         // concurrently as we care not to block too much here.
173         atomic.StoreUint32(&w.events, 0)
174         atomic.StoreInt32(&w.isrec, 0)
175 }
176
177 // fsevents implements Watcher and RecursiveWatcher interfaces backed by FSEvents
178 // framework.
179 type fsevents struct {
180         watches map[string]*watch
181         c       chan<- EventInfo
182 }
183
184 func newWatcher(c chan<- EventInfo) watcher {
185         return &fsevents{
186                 watches: make(map[string]*watch),
187                 c:       c,
188         }
189 }
190
191 func (fse *fsevents) watch(path string, event Event, isrec int32) (err error) {
192         if path, err = canonical(path); err != nil {
193                 return err
194         }
195         if _, ok := fse.watches[path]; ok {
196                 return errAlreadyWatched
197         }
198         w := &watch{
199                 prev:   make(map[string]uint32),
200                 c:      fse.c,
201                 path:   path,
202                 events: uint32(event),
203                 isrec:  isrec,
204         }
205         w.stream = newStream(path, w.Dispatch)
206         if err = w.stream.Start(); err != nil {
207                 return err
208         }
209         fse.watches[path] = w
210         return nil
211 }
212
213 func (fse *fsevents) unwatch(path string) (err error) {
214         if path, err = canonical(path); err != nil {
215                 return
216         }
217         w, ok := fse.watches[path]
218         if !ok {
219                 return errNotWatched
220         }
221         w.stream.Stop()
222         delete(fse.watches, path)
223         return nil
224 }
225
226 // Watch implements Watcher interface. It fails with non-nil error when setting
227 // the watch-point by FSEvents fails or with errAlreadyWatched error when
228 // the given path is already watched.
229 func (fse *fsevents) Watch(path string, event Event) error {
230         return fse.watch(path, event, 0)
231 }
232
233 // Unwatch implements Watcher interface. It fails with errNotWatched when
234 // the given path is not being watched.
235 func (fse *fsevents) Unwatch(path string) error {
236         return fse.unwatch(path)
237 }
238
239 // Rewatch implements Watcher interface. It fails with errNotWatched when
240 // the given path is not being watched or with errInvalidEventSet when oldevent
241 // does not match event set the watch-point currently holds.
242 func (fse *fsevents) Rewatch(path string, oldevent, newevent Event) error {
243         w, ok := fse.watches[path]
244         if !ok {
245                 return errNotWatched
246         }
247         if !atomic.CompareAndSwapUint32(&w.events, uint32(oldevent), uint32(newevent)) {
248                 return errInvalidEventSet
249         }
250         atomic.StoreInt32(&w.isrec, 0)
251         return nil
252 }
253
254 // RecursiveWatch implements RecursiveWatcher interface. It fails with non-nil
255 // error when setting the watch-point by FSEvents fails or with errAlreadyWatched
256 // error when the given path is already watched.
257 func (fse *fsevents) RecursiveWatch(path string, event Event) error {
258         return fse.watch(path, event, 1)
259 }
260
261 // RecursiveUnwatch implements RecursiveWatcher interface. It fails with
262 // errNotWatched when the given path is not being watched.
263 //
264 // TODO(rjeczalik): fail if w.isrec == 0?
265 func (fse *fsevents) RecursiveUnwatch(path string) error {
266         return fse.unwatch(path)
267 }
268
269 // RecrusiveRewatch implements RecursiveWatcher interface. It fails:
270 //
271 //   * with errNotWatched when the given path is not being watched
272 //   * with errInvalidEventSet when oldevent does not match the current event set
273 //   * with errAlreadyWatched when watch-point given by the oldpath was meant to
274 //     be relocated to newpath, but the newpath is already watched
275 //   * a non-nil error when setting the watch-point with FSEvents fails
276 //
277 // TODO(rjeczalik): Improve handling of watch-point relocation? See two TODOs
278 // that follows.
279 func (fse *fsevents) RecursiveRewatch(oldpath, newpath string, oldevent, newevent Event) error {
280         switch [2]bool{oldpath == newpath, oldevent == newevent} {
281         case [2]bool{true, true}:
282                 w, ok := fse.watches[oldpath]
283                 if !ok {
284                         return errNotWatched
285                 }
286                 atomic.StoreInt32(&w.isrec, 1)
287                 return nil
288         case [2]bool{true, false}:
289                 w, ok := fse.watches[oldpath]
290                 if !ok {
291                         return errNotWatched
292                 }
293                 if !atomic.CompareAndSwapUint32(&w.events, uint32(oldevent), uint32(newevent)) {
294                         return errors.New("invalid event state diff")
295                 }
296                 atomic.StoreInt32(&w.isrec, 1)
297                 return nil
298         default:
299                 // TODO(rjeczalik): rewatch newpath only if exists?
300                 // TODO(rjeczalik): migrate w.prev to new watch?
301                 if _, ok := fse.watches[newpath]; ok {
302                         return errAlreadyWatched
303                 }
304                 if err := fse.Unwatch(oldpath); err != nil {
305                         return err
306                 }
307                 // TODO(rjeczalik): revert unwatch if watch fails?
308                 return fse.watch(newpath, newevent, 1)
309         }
310 }
311
312 // Close unwatches all watch-points.
313 func (fse *fsevents) Close() error {
314         for _, w := range fse.watches {
315                 w.Stop()
316         }
317         fse.watches = nil
318         return nil
319 }