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