vendor/github.com/fsnotify/fsnotify/backend_windows.go
changeset 265 05c40b36d3b2
equal deleted inserted replaced
264:8f478162d991 265:05c40b36d3b2
       
     1 //go:build windows
       
     2 // +build windows
       
     3 
       
     4 package fsnotify
       
     5 
       
     6 import (
       
     7 	"errors"
       
     8 	"fmt"
       
     9 	"os"
       
    10 	"path/filepath"
       
    11 	"reflect"
       
    12 	"runtime"
       
    13 	"strings"
       
    14 	"sync"
       
    15 	"unsafe"
       
    16 
       
    17 	"golang.org/x/sys/windows"
       
    18 )
       
    19 
       
    20 // Watcher watches a set of paths, delivering events on a channel.
       
    21 //
       
    22 // A watcher should not be copied (e.g. pass it by pointer, rather than by
       
    23 // value).
       
    24 //
       
    25 // # Linux notes
       
    26 //
       
    27 // When a file is removed a Remove event won't be emitted until all file
       
    28 // descriptors are closed, and deletes will always emit a Chmod. For example:
       
    29 //
       
    30 //     fp := os.Open("file")
       
    31 //     os.Remove("file")        // Triggers Chmod
       
    32 //     fp.Close()               // Triggers Remove
       
    33 //
       
    34 // This is the event that inotify sends, so not much can be changed about this.
       
    35 //
       
    36 // The fs.inotify.max_user_watches sysctl variable specifies the upper limit
       
    37 // for the number of watches per user, and fs.inotify.max_user_instances
       
    38 // specifies the maximum number of inotify instances per user. Every Watcher you
       
    39 // create is an "instance", and every path you add is a "watch".
       
    40 //
       
    41 // These are also exposed in /proc as /proc/sys/fs/inotify/max_user_watches and
       
    42 // /proc/sys/fs/inotify/max_user_instances
       
    43 //
       
    44 // To increase them you can use sysctl or write the value to the /proc file:
       
    45 //
       
    46 //     # Default values on Linux 5.18
       
    47 //     sysctl fs.inotify.max_user_watches=124983
       
    48 //     sysctl fs.inotify.max_user_instances=128
       
    49 //
       
    50 // To make the changes persist on reboot edit /etc/sysctl.conf or
       
    51 // /usr/lib/sysctl.d/50-default.conf (details differ per Linux distro; check
       
    52 // your distro's documentation):
       
    53 //
       
    54 //     fs.inotify.max_user_watches=124983
       
    55 //     fs.inotify.max_user_instances=128
       
    56 //
       
    57 // Reaching the limit will result in a "no space left on device" or "too many open
       
    58 // files" error.
       
    59 //
       
    60 // # kqueue notes (macOS, BSD)
       
    61 //
       
    62 // kqueue requires opening a file descriptor for every file that's being watched;
       
    63 // so if you're watching a directory with five files then that's six file
       
    64 // descriptors. You will run in to your system's "max open files" limit faster on
       
    65 // these platforms.
       
    66 //
       
    67 // The sysctl variables kern.maxfiles and kern.maxfilesperproc can be used to
       
    68 // control the maximum number of open files, as well as /etc/login.conf on BSD
       
    69 // systems.
       
    70 //
       
    71 // # macOS notes
       
    72 //
       
    73 // Spotlight indexing on macOS can result in multiple events (see [#15]). A
       
    74 // temporary workaround is to add your folder(s) to the "Spotlight Privacy
       
    75 // Settings" until we have a native FSEvents implementation (see [#11]).
       
    76 //
       
    77 // [#11]: https://github.com/fsnotify/fsnotify/issues/11
       
    78 // [#15]: https://github.com/fsnotify/fsnotify/issues/15
       
    79 type Watcher struct {
       
    80 	// Events sends the filesystem change events.
       
    81 	//
       
    82 	// fsnotify can send the following events; a "path" here can refer to a
       
    83 	// file, directory, symbolic link, or special file like a FIFO.
       
    84 	//
       
    85 	//   fsnotify.Create    A new path was created; this may be followed by one
       
    86 	//                      or more Write events if data also gets written to a
       
    87 	//                      file.
       
    88 	//
       
    89 	//   fsnotify.Remove    A path was removed.
       
    90 	//
       
    91 	//   fsnotify.Rename    A path was renamed. A rename is always sent with the
       
    92 	//                      old path as Event.Name, and a Create event will be
       
    93 	//                      sent with the new name. Renames are only sent for
       
    94 	//                      paths that are currently watched; e.g. moving an
       
    95 	//                      unmonitored file into a monitored directory will
       
    96 	//                      show up as just a Create. Similarly, renaming a file
       
    97 	//                      to outside a monitored directory will show up as
       
    98 	//                      only a Rename.
       
    99 	//
       
   100 	//   fsnotify.Write     A file or named pipe was written to. A Truncate will
       
   101 	//                      also trigger a Write. A single "write action"
       
   102 	//                      initiated by the user may show up as one or multiple
       
   103 	//                      writes, depending on when the system syncs things to
       
   104 	//                      disk. For example when compiling a large Go program
       
   105 	//                      you may get hundreds of Write events, so you
       
   106 	//                      probably want to wait until you've stopped receiving
       
   107 	//                      them (see the dedup example in cmd/fsnotify).
       
   108 	//
       
   109 	//   fsnotify.Chmod     Attributes were changed. On Linux this is also sent
       
   110 	//                      when a file is removed (or more accurately, when a
       
   111 	//                      link to an inode is removed). On kqueue it's sent
       
   112 	//                      and on kqueue when a file is truncated. On Windows
       
   113 	//                      it's never sent.
       
   114 	Events chan Event
       
   115 
       
   116 	// Errors sends any errors.
       
   117 	Errors chan error
       
   118 
       
   119 	port  windows.Handle // Handle to completion port
       
   120 	input chan *input    // Inputs to the reader are sent on this channel
       
   121 	quit  chan chan<- error
       
   122 
       
   123 	mu       sync.Mutex // Protects access to watches, isClosed
       
   124 	watches  watchMap   // Map of watches (key: i-number)
       
   125 	isClosed bool       // Set to true when Close() is first called
       
   126 }
       
   127 
       
   128 // NewWatcher creates a new Watcher.
       
   129 func NewWatcher() (*Watcher, error) {
       
   130 	port, err := windows.CreateIoCompletionPort(windows.InvalidHandle, 0, 0, 0)
       
   131 	if err != nil {
       
   132 		return nil, os.NewSyscallError("CreateIoCompletionPort", err)
       
   133 	}
       
   134 	w := &Watcher{
       
   135 		port:    port,
       
   136 		watches: make(watchMap),
       
   137 		input:   make(chan *input, 1),
       
   138 		Events:  make(chan Event, 50),
       
   139 		Errors:  make(chan error),
       
   140 		quit:    make(chan chan<- error, 1),
       
   141 	}
       
   142 	go w.readEvents()
       
   143 	return w, nil
       
   144 }
       
   145 
       
   146 func (w *Watcher) sendEvent(name string, mask uint64) bool {
       
   147 	if mask == 0 {
       
   148 		return false
       
   149 	}
       
   150 
       
   151 	event := w.newEvent(name, uint32(mask))
       
   152 	select {
       
   153 	case ch := <-w.quit:
       
   154 		w.quit <- ch
       
   155 	case w.Events <- event:
       
   156 	}
       
   157 	return true
       
   158 }
       
   159 
       
   160 // Returns true if the error was sent, or false if watcher is closed.
       
   161 func (w *Watcher) sendError(err error) bool {
       
   162 	select {
       
   163 	case w.Errors <- err:
       
   164 		return true
       
   165 	case <-w.quit:
       
   166 	}
       
   167 	return false
       
   168 }
       
   169 
       
   170 // Close removes all watches and closes the events channel.
       
   171 func (w *Watcher) Close() error {
       
   172 	w.mu.Lock()
       
   173 	if w.isClosed {
       
   174 		w.mu.Unlock()
       
   175 		return nil
       
   176 	}
       
   177 	w.isClosed = true
       
   178 	w.mu.Unlock()
       
   179 
       
   180 	// Send "quit" message to the reader goroutine
       
   181 	ch := make(chan error)
       
   182 	w.quit <- ch
       
   183 	if err := w.wakeupReader(); err != nil {
       
   184 		return err
       
   185 	}
       
   186 	return <-ch
       
   187 }
       
   188 
       
   189 // Add starts monitoring the path for changes.
       
   190 //
       
   191 // A path can only be watched once; attempting to watch it more than once will
       
   192 // return an error. Paths that do not yet exist on the filesystem cannot be
       
   193 // added. A watch will be automatically removed if the path is deleted.
       
   194 //
       
   195 // A path will remain watched if it gets renamed to somewhere else on the same
       
   196 // filesystem, but the monitor will get removed if the path gets deleted and
       
   197 // re-created, or if it's moved to a different filesystem.
       
   198 //
       
   199 // Notifications on network filesystems (NFS, SMB, FUSE, etc.) or special
       
   200 // filesystems (/proc, /sys, etc.) generally don't work.
       
   201 //
       
   202 // # Watching directories
       
   203 //
       
   204 // All files in a directory are monitored, including new files that are created
       
   205 // after the watcher is started. Subdirectories are not watched (i.e. it's
       
   206 // non-recursive).
       
   207 //
       
   208 // # Watching files
       
   209 //
       
   210 // Watching individual files (rather than directories) is generally not
       
   211 // recommended as many tools update files atomically. Instead of "just" writing
       
   212 // to the file a temporary file will be written to first, and if successful the
       
   213 // temporary file is moved to to destination removing the original, or some
       
   214 // variant thereof. The watcher on the original file is now lost, as it no
       
   215 // longer exists.
       
   216 //
       
   217 // Instead, watch the parent directory and use Event.Name to filter out files
       
   218 // you're not interested in. There is an example of this in [cmd/fsnotify/file.go].
       
   219 func (w *Watcher) Add(name string) error {
       
   220 	w.mu.Lock()
       
   221 	if w.isClosed {
       
   222 		w.mu.Unlock()
       
   223 		return errors.New("watcher already closed")
       
   224 	}
       
   225 	w.mu.Unlock()
       
   226 
       
   227 	in := &input{
       
   228 		op:    opAddWatch,
       
   229 		path:  filepath.Clean(name),
       
   230 		flags: sysFSALLEVENTS,
       
   231 		reply: make(chan error),
       
   232 	}
       
   233 	w.input <- in
       
   234 	if err := w.wakeupReader(); err != nil {
       
   235 		return err
       
   236 	}
       
   237 	return <-in.reply
       
   238 }
       
   239 
       
   240 // Remove stops monitoring the path for changes.
       
   241 //
       
   242 // Directories are always removed non-recursively. For example, if you added
       
   243 // /tmp/dir and /tmp/dir/subdir then you will need to remove both.
       
   244 //
       
   245 // Removing a path that has not yet been added returns [ErrNonExistentWatch].
       
   246 func (w *Watcher) Remove(name string) error {
       
   247 	in := &input{
       
   248 		op:    opRemoveWatch,
       
   249 		path:  filepath.Clean(name),
       
   250 		reply: make(chan error),
       
   251 	}
       
   252 	w.input <- in
       
   253 	if err := w.wakeupReader(); err != nil {
       
   254 		return err
       
   255 	}
       
   256 	return <-in.reply
       
   257 }
       
   258 
       
   259 // WatchList returns all paths added with [Add] (and are not yet removed).
       
   260 func (w *Watcher) WatchList() []string {
       
   261 	w.mu.Lock()
       
   262 	defer w.mu.Unlock()
       
   263 
       
   264 	entries := make([]string, 0, len(w.watches))
       
   265 	for _, entry := range w.watches {
       
   266 		for _, watchEntry := range entry {
       
   267 			entries = append(entries, watchEntry.path)
       
   268 		}
       
   269 	}
       
   270 
       
   271 	return entries
       
   272 }
       
   273 
       
   274 // These options are from the old golang.org/x/exp/winfsnotify, where you could
       
   275 // add various options to the watch. This has long since been removed.
       
   276 //
       
   277 // The "sys" in the name is misleading as they're not part of any "system".
       
   278 //
       
   279 // This should all be removed at some point, and just use windows.FILE_NOTIFY_*
       
   280 const (
       
   281 	sysFSALLEVENTS  = 0xfff
       
   282 	sysFSATTRIB     = 0x4
       
   283 	sysFSCREATE     = 0x100
       
   284 	sysFSDELETE     = 0x200
       
   285 	sysFSDELETESELF = 0x400
       
   286 	sysFSMODIFY     = 0x2
       
   287 	sysFSMOVE       = 0xc0
       
   288 	sysFSMOVEDFROM  = 0x40
       
   289 	sysFSMOVEDTO    = 0x80
       
   290 	sysFSMOVESELF   = 0x800
       
   291 	sysFSIGNORED    = 0x8000
       
   292 )
       
   293 
       
   294 func (w *Watcher) newEvent(name string, mask uint32) Event {
       
   295 	e := Event{Name: name}
       
   296 	if mask&sysFSCREATE == sysFSCREATE || mask&sysFSMOVEDTO == sysFSMOVEDTO {
       
   297 		e.Op |= Create
       
   298 	}
       
   299 	if mask&sysFSDELETE == sysFSDELETE || mask&sysFSDELETESELF == sysFSDELETESELF {
       
   300 		e.Op |= Remove
       
   301 	}
       
   302 	if mask&sysFSMODIFY == sysFSMODIFY {
       
   303 		e.Op |= Write
       
   304 	}
       
   305 	if mask&sysFSMOVE == sysFSMOVE || mask&sysFSMOVESELF == sysFSMOVESELF || mask&sysFSMOVEDFROM == sysFSMOVEDFROM {
       
   306 		e.Op |= Rename
       
   307 	}
       
   308 	if mask&sysFSATTRIB == sysFSATTRIB {
       
   309 		e.Op |= Chmod
       
   310 	}
       
   311 	return e
       
   312 }
       
   313 
       
   314 const (
       
   315 	opAddWatch = iota
       
   316 	opRemoveWatch
       
   317 )
       
   318 
       
   319 const (
       
   320 	provisional uint64 = 1 << (32 + iota)
       
   321 )
       
   322 
       
   323 type input struct {
       
   324 	op    int
       
   325 	path  string
       
   326 	flags uint32
       
   327 	reply chan error
       
   328 }
       
   329 
       
   330 type inode struct {
       
   331 	handle windows.Handle
       
   332 	volume uint32
       
   333 	index  uint64
       
   334 }
       
   335 
       
   336 type watch struct {
       
   337 	ov     windows.Overlapped
       
   338 	ino    *inode            // i-number
       
   339 	path   string            // Directory path
       
   340 	mask   uint64            // Directory itself is being watched with these notify flags
       
   341 	names  map[string]uint64 // Map of names being watched and their notify flags
       
   342 	rename string            // Remembers the old name while renaming a file
       
   343 	buf    [65536]byte       // 64K buffer
       
   344 }
       
   345 
       
   346 type (
       
   347 	indexMap map[uint64]*watch
       
   348 	watchMap map[uint32]indexMap
       
   349 )
       
   350 
       
   351 func (w *Watcher) wakeupReader() error {
       
   352 	err := windows.PostQueuedCompletionStatus(w.port, 0, 0, nil)
       
   353 	if err != nil {
       
   354 		return os.NewSyscallError("PostQueuedCompletionStatus", err)
       
   355 	}
       
   356 	return nil
       
   357 }
       
   358 
       
   359 func (w *Watcher) getDir(pathname string) (dir string, err error) {
       
   360 	attr, err := windows.GetFileAttributes(windows.StringToUTF16Ptr(pathname))
       
   361 	if err != nil {
       
   362 		return "", os.NewSyscallError("GetFileAttributes", err)
       
   363 	}
       
   364 	if attr&windows.FILE_ATTRIBUTE_DIRECTORY != 0 {
       
   365 		dir = pathname
       
   366 	} else {
       
   367 		dir, _ = filepath.Split(pathname)
       
   368 		dir = filepath.Clean(dir)
       
   369 	}
       
   370 	return
       
   371 }
       
   372 
       
   373 func (w *Watcher) getIno(path string) (ino *inode, err error) {
       
   374 	h, err := windows.CreateFile(windows.StringToUTF16Ptr(path),
       
   375 		windows.FILE_LIST_DIRECTORY,
       
   376 		windows.FILE_SHARE_READ|windows.FILE_SHARE_WRITE|windows.FILE_SHARE_DELETE,
       
   377 		nil, windows.OPEN_EXISTING,
       
   378 		windows.FILE_FLAG_BACKUP_SEMANTICS|windows.FILE_FLAG_OVERLAPPED, 0)
       
   379 	if err != nil {
       
   380 		return nil, os.NewSyscallError("CreateFile", err)
       
   381 	}
       
   382 
       
   383 	var fi windows.ByHandleFileInformation
       
   384 	err = windows.GetFileInformationByHandle(h, &fi)
       
   385 	if err != nil {
       
   386 		windows.CloseHandle(h)
       
   387 		return nil, os.NewSyscallError("GetFileInformationByHandle", err)
       
   388 	}
       
   389 	ino = &inode{
       
   390 		handle: h,
       
   391 		volume: fi.VolumeSerialNumber,
       
   392 		index:  uint64(fi.FileIndexHigh)<<32 | uint64(fi.FileIndexLow),
       
   393 	}
       
   394 	return ino, nil
       
   395 }
       
   396 
       
   397 // Must run within the I/O thread.
       
   398 func (m watchMap) get(ino *inode) *watch {
       
   399 	if i := m[ino.volume]; i != nil {
       
   400 		return i[ino.index]
       
   401 	}
       
   402 	return nil
       
   403 }
       
   404 
       
   405 // Must run within the I/O thread.
       
   406 func (m watchMap) set(ino *inode, watch *watch) {
       
   407 	i := m[ino.volume]
       
   408 	if i == nil {
       
   409 		i = make(indexMap)
       
   410 		m[ino.volume] = i
       
   411 	}
       
   412 	i[ino.index] = watch
       
   413 }
       
   414 
       
   415 // Must run within the I/O thread.
       
   416 func (w *Watcher) addWatch(pathname string, flags uint64) error {
       
   417 	dir, err := w.getDir(pathname)
       
   418 	if err != nil {
       
   419 		return err
       
   420 	}
       
   421 
       
   422 	ino, err := w.getIno(dir)
       
   423 	if err != nil {
       
   424 		return err
       
   425 	}
       
   426 	w.mu.Lock()
       
   427 	watchEntry := w.watches.get(ino)
       
   428 	w.mu.Unlock()
       
   429 	if watchEntry == nil {
       
   430 		_, err := windows.CreateIoCompletionPort(ino.handle, w.port, 0, 0)
       
   431 		if err != nil {
       
   432 			windows.CloseHandle(ino.handle)
       
   433 			return os.NewSyscallError("CreateIoCompletionPort", err)
       
   434 		}
       
   435 		watchEntry = &watch{
       
   436 			ino:   ino,
       
   437 			path:  dir,
       
   438 			names: make(map[string]uint64),
       
   439 		}
       
   440 		w.mu.Lock()
       
   441 		w.watches.set(ino, watchEntry)
       
   442 		w.mu.Unlock()
       
   443 		flags |= provisional
       
   444 	} else {
       
   445 		windows.CloseHandle(ino.handle)
       
   446 	}
       
   447 	if pathname == dir {
       
   448 		watchEntry.mask |= flags
       
   449 	} else {
       
   450 		watchEntry.names[filepath.Base(pathname)] |= flags
       
   451 	}
       
   452 
       
   453 	err = w.startRead(watchEntry)
       
   454 	if err != nil {
       
   455 		return err
       
   456 	}
       
   457 
       
   458 	if pathname == dir {
       
   459 		watchEntry.mask &= ^provisional
       
   460 	} else {
       
   461 		watchEntry.names[filepath.Base(pathname)] &= ^provisional
       
   462 	}
       
   463 	return nil
       
   464 }
       
   465 
       
   466 // Must run within the I/O thread.
       
   467 func (w *Watcher) remWatch(pathname string) error {
       
   468 	dir, err := w.getDir(pathname)
       
   469 	if err != nil {
       
   470 		return err
       
   471 	}
       
   472 	ino, err := w.getIno(dir)
       
   473 	if err != nil {
       
   474 		return err
       
   475 	}
       
   476 
       
   477 	w.mu.Lock()
       
   478 	watch := w.watches.get(ino)
       
   479 	w.mu.Unlock()
       
   480 
       
   481 	err = windows.CloseHandle(ino.handle)
       
   482 	if err != nil {
       
   483 		w.sendError(os.NewSyscallError("CloseHandle", err))
       
   484 	}
       
   485 	if watch == nil {
       
   486 		return fmt.Errorf("%w: %s", ErrNonExistentWatch, pathname)
       
   487 	}
       
   488 	if pathname == dir {
       
   489 		w.sendEvent(watch.path, watch.mask&sysFSIGNORED)
       
   490 		watch.mask = 0
       
   491 	} else {
       
   492 		name := filepath.Base(pathname)
       
   493 		w.sendEvent(filepath.Join(watch.path, name), watch.names[name]&sysFSIGNORED)
       
   494 		delete(watch.names, name)
       
   495 	}
       
   496 
       
   497 	return w.startRead(watch)
       
   498 }
       
   499 
       
   500 // Must run within the I/O thread.
       
   501 func (w *Watcher) deleteWatch(watch *watch) {
       
   502 	for name, mask := range watch.names {
       
   503 		if mask&provisional == 0 {
       
   504 			w.sendEvent(filepath.Join(watch.path, name), mask&sysFSIGNORED)
       
   505 		}
       
   506 		delete(watch.names, name)
       
   507 	}
       
   508 	if watch.mask != 0 {
       
   509 		if watch.mask&provisional == 0 {
       
   510 			w.sendEvent(watch.path, watch.mask&sysFSIGNORED)
       
   511 		}
       
   512 		watch.mask = 0
       
   513 	}
       
   514 }
       
   515 
       
   516 // Must run within the I/O thread.
       
   517 func (w *Watcher) startRead(watch *watch) error {
       
   518 	err := windows.CancelIo(watch.ino.handle)
       
   519 	if err != nil {
       
   520 		w.sendError(os.NewSyscallError("CancelIo", err))
       
   521 		w.deleteWatch(watch)
       
   522 	}
       
   523 	mask := w.toWindowsFlags(watch.mask)
       
   524 	for _, m := range watch.names {
       
   525 		mask |= w.toWindowsFlags(m)
       
   526 	}
       
   527 	if mask == 0 {
       
   528 		err := windows.CloseHandle(watch.ino.handle)
       
   529 		if err != nil {
       
   530 			w.sendError(os.NewSyscallError("CloseHandle", err))
       
   531 		}
       
   532 		w.mu.Lock()
       
   533 		delete(w.watches[watch.ino.volume], watch.ino.index)
       
   534 		w.mu.Unlock()
       
   535 		return nil
       
   536 	}
       
   537 
       
   538 	rdErr := windows.ReadDirectoryChanges(watch.ino.handle, &watch.buf[0],
       
   539 		uint32(unsafe.Sizeof(watch.buf)), false, mask, nil, &watch.ov, 0)
       
   540 	if rdErr != nil {
       
   541 		err := os.NewSyscallError("ReadDirectoryChanges", rdErr)
       
   542 		if rdErr == windows.ERROR_ACCESS_DENIED && watch.mask&provisional == 0 {
       
   543 			// Watched directory was probably removed
       
   544 			w.sendEvent(watch.path, watch.mask&sysFSDELETESELF)
       
   545 			err = nil
       
   546 		}
       
   547 		w.deleteWatch(watch)
       
   548 		w.startRead(watch)
       
   549 		return err
       
   550 	}
       
   551 	return nil
       
   552 }
       
   553 
       
   554 // readEvents reads from the I/O completion port, converts the
       
   555 // received events into Event objects and sends them via the Events channel.
       
   556 // Entry point to the I/O thread.
       
   557 func (w *Watcher) readEvents() {
       
   558 	var (
       
   559 		n   uint32
       
   560 		key uintptr
       
   561 		ov  *windows.Overlapped
       
   562 	)
       
   563 	runtime.LockOSThread()
       
   564 
       
   565 	for {
       
   566 		qErr := windows.GetQueuedCompletionStatus(w.port, &n, &key, &ov, windows.INFINITE)
       
   567 		// This error is handled after the watch == nil check below. NOTE: this
       
   568 		// seems odd, note sure if it's correct.
       
   569 
       
   570 		watch := (*watch)(unsafe.Pointer(ov))
       
   571 		if watch == nil {
       
   572 			select {
       
   573 			case ch := <-w.quit:
       
   574 				w.mu.Lock()
       
   575 				var indexes []indexMap
       
   576 				for _, index := range w.watches {
       
   577 					indexes = append(indexes, index)
       
   578 				}
       
   579 				w.mu.Unlock()
       
   580 				for _, index := range indexes {
       
   581 					for _, watch := range index {
       
   582 						w.deleteWatch(watch)
       
   583 						w.startRead(watch)
       
   584 					}
       
   585 				}
       
   586 
       
   587 				err := windows.CloseHandle(w.port)
       
   588 				if err != nil {
       
   589 					err = os.NewSyscallError("CloseHandle", err)
       
   590 				}
       
   591 				close(w.Events)
       
   592 				close(w.Errors)
       
   593 				ch <- err
       
   594 				return
       
   595 			case in := <-w.input:
       
   596 				switch in.op {
       
   597 				case opAddWatch:
       
   598 					in.reply <- w.addWatch(in.path, uint64(in.flags))
       
   599 				case opRemoveWatch:
       
   600 					in.reply <- w.remWatch(in.path)
       
   601 				}
       
   602 			default:
       
   603 			}
       
   604 			continue
       
   605 		}
       
   606 
       
   607 		switch qErr {
       
   608 		case windows.ERROR_MORE_DATA:
       
   609 			if watch == nil {
       
   610 				w.sendError(errors.New("ERROR_MORE_DATA has unexpectedly null lpOverlapped buffer"))
       
   611 			} else {
       
   612 				// The i/o succeeded but the buffer is full.
       
   613 				// In theory we should be building up a full packet.
       
   614 				// In practice we can get away with just carrying on.
       
   615 				n = uint32(unsafe.Sizeof(watch.buf))
       
   616 			}
       
   617 		case windows.ERROR_ACCESS_DENIED:
       
   618 			// Watched directory was probably removed
       
   619 			w.sendEvent(watch.path, watch.mask&sysFSDELETESELF)
       
   620 			w.deleteWatch(watch)
       
   621 			w.startRead(watch)
       
   622 			continue
       
   623 		case windows.ERROR_OPERATION_ABORTED:
       
   624 			// CancelIo was called on this handle
       
   625 			continue
       
   626 		default:
       
   627 			w.sendError(os.NewSyscallError("GetQueuedCompletionPort", qErr))
       
   628 			continue
       
   629 		case nil:
       
   630 		}
       
   631 
       
   632 		var offset uint32
       
   633 		for {
       
   634 			if n == 0 {
       
   635 				w.sendError(errors.New("short read in readEvents()"))
       
   636 				break
       
   637 			}
       
   638 
       
   639 			// Point "raw" to the event in the buffer
       
   640 			raw := (*windows.FileNotifyInformation)(unsafe.Pointer(&watch.buf[offset]))
       
   641 
       
   642 			// Create a buf that is the size of the path name
       
   643 			size := int(raw.FileNameLength / 2)
       
   644 			var buf []uint16
       
   645 			// TODO: Use unsafe.Slice in Go 1.17; https://stackoverflow.com/questions/51187973
       
   646 			sh := (*reflect.SliceHeader)(unsafe.Pointer(&buf))
       
   647 			sh.Data = uintptr(unsafe.Pointer(&raw.FileName))
       
   648 			sh.Len = size
       
   649 			sh.Cap = size
       
   650 			name := windows.UTF16ToString(buf)
       
   651 			fullname := filepath.Join(watch.path, name)
       
   652 
       
   653 			var mask uint64
       
   654 			switch raw.Action {
       
   655 			case windows.FILE_ACTION_REMOVED:
       
   656 				mask = sysFSDELETESELF
       
   657 			case windows.FILE_ACTION_MODIFIED:
       
   658 				mask = sysFSMODIFY
       
   659 			case windows.FILE_ACTION_RENAMED_OLD_NAME:
       
   660 				watch.rename = name
       
   661 			case windows.FILE_ACTION_RENAMED_NEW_NAME:
       
   662 				// Update saved path of all sub-watches.
       
   663 				old := filepath.Join(watch.path, watch.rename)
       
   664 				w.mu.Lock()
       
   665 				for _, watchMap := range w.watches {
       
   666 					for _, ww := range watchMap {
       
   667 						if strings.HasPrefix(ww.path, old) {
       
   668 							ww.path = filepath.Join(fullname, strings.TrimPrefix(ww.path, old))
       
   669 						}
       
   670 					}
       
   671 				}
       
   672 				w.mu.Unlock()
       
   673 
       
   674 				if watch.names[watch.rename] != 0 {
       
   675 					watch.names[name] |= watch.names[watch.rename]
       
   676 					delete(watch.names, watch.rename)
       
   677 					mask = sysFSMOVESELF
       
   678 				}
       
   679 			}
       
   680 
       
   681 			sendNameEvent := func() {
       
   682 				w.sendEvent(fullname, watch.names[name]&mask)
       
   683 			}
       
   684 			if raw.Action != windows.FILE_ACTION_RENAMED_NEW_NAME {
       
   685 				sendNameEvent()
       
   686 			}
       
   687 			if raw.Action == windows.FILE_ACTION_REMOVED {
       
   688 				w.sendEvent(fullname, watch.names[name]&sysFSIGNORED)
       
   689 				delete(watch.names, name)
       
   690 			}
       
   691 
       
   692 			w.sendEvent(fullname, watch.mask&w.toFSnotifyFlags(raw.Action))
       
   693 			if raw.Action == windows.FILE_ACTION_RENAMED_NEW_NAME {
       
   694 				fullname = filepath.Join(watch.path, watch.rename)
       
   695 				sendNameEvent()
       
   696 			}
       
   697 
       
   698 			// Move to the next event in the buffer
       
   699 			if raw.NextEntryOffset == 0 {
       
   700 				break
       
   701 			}
       
   702 			offset += raw.NextEntryOffset
       
   703 
       
   704 			// Error!
       
   705 			if offset >= n {
       
   706 				w.sendError(errors.New(
       
   707 					"Windows system assumed buffer larger than it is, events have likely been missed."))
       
   708 				break
       
   709 			}
       
   710 		}
       
   711 
       
   712 		if err := w.startRead(watch); err != nil {
       
   713 			w.sendError(err)
       
   714 		}
       
   715 	}
       
   716 }
       
   717 
       
   718 func (w *Watcher) toWindowsFlags(mask uint64) uint32 {
       
   719 	var m uint32
       
   720 	if mask&sysFSMODIFY != 0 {
       
   721 		m |= windows.FILE_NOTIFY_CHANGE_LAST_WRITE
       
   722 	}
       
   723 	if mask&sysFSATTRIB != 0 {
       
   724 		m |= windows.FILE_NOTIFY_CHANGE_ATTRIBUTES
       
   725 	}
       
   726 	if mask&(sysFSMOVE|sysFSCREATE|sysFSDELETE) != 0 {
       
   727 		m |= windows.FILE_NOTIFY_CHANGE_FILE_NAME | windows.FILE_NOTIFY_CHANGE_DIR_NAME
       
   728 	}
       
   729 	return m
       
   730 }
       
   731 
       
   732 func (w *Watcher) toFSnotifyFlags(action uint32) uint64 {
       
   733 	switch action {
       
   734 	case windows.FILE_ACTION_ADDED:
       
   735 		return sysFSCREATE
       
   736 	case windows.FILE_ACTION_REMOVED:
       
   737 		return sysFSDELETE
       
   738 	case windows.FILE_ACTION_MODIFIED:
       
   739 		return sysFSMODIFY
       
   740 	case windows.FILE_ACTION_RENAMED_OLD_NAME:
       
   741 		return sysFSMOVEDFROM
       
   742 	case windows.FILE_ACTION_RENAMED_NEW_NAME:
       
   743 		return sysFSMOVEDTO
       
   744 	}
       
   745 	return 0
       
   746 }