OSDN Git Service

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