vendor/github.com/fsnotify/fsnotify/windows.go
changeset 242 2a9ec03fe5a1
child 260 445e01aede7e
equal deleted inserted replaced
241:e77dad242f4c 242:2a9ec03fe5a1
       
     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 }