]> go.fuhry.dev Git - fsnotify.git/commitdiff
Windows - FSNotify flags
authorChris Howey <howeyc@gmail.com>
Tue, 3 Jul 2012 00:47:44 +0000 (19:47 -0500)
committerChris Howey <howeyc@gmail.com>
Tue, 3 Jul 2012 00:47:44 +0000 (19:47 -0500)
example_test.go [deleted file]
fsnotify_windows.go

diff --git a/example_test.go b/example_test.go
deleted file mode 100644 (file)
index 5f0c8b5..0000000
+++ /dev/null
@@ -1,29 +0,0 @@
-package fsnotify_test
-
-import (
-       "github.com/howeyc/fsnotify"
-       "log"
-)
-
-func ExampleNewWatcher() {
-       watcher, err := fsnotify.NewWatcher()
-       if err != nil {
-               log.Fatal(err)
-       }
-
-       go func() {
-               for {
-                       select {
-                       case ev := <-watcher.Event:
-                               log.Println("event:", ev)
-                       case err := <-watcher.Error:
-                               log.Println("error:", err)
-                       }
-               }
-       }()
-
-       err = watcher.Watch("/tmp/foo")
-       if err != nil {
-               log.Fatal(err)
-       }
-}
index 5ab54c0c859298a5c4b9df2a58ea9630ae6dc15c..1904271ee61709acd3e342cea8110c0674b90042 100644 (file)
 // Copyright 2011 The Go Authors. All rights reserved.\r
 // Use of this source code is governed by a BSD-style\r
 // license that can be found in the LICENSE file.\r
-\r
+
 // +build windows\r
-\r
+
 // Package fsnotify allows the user to receive\r
 // file system event notifications on Windows.\r
-package fsnotify\r
-\r
-import (\r
-        "errors"\r
-        "fmt"\r
-        "os"\r
-        "path/filepath"\r
-        "runtime"\r
-        "syscall"\r
-        "unsafe"\r
-)\r
-\r
+package fsnotify
+
+import (
+       "errors"
+       "fmt"
+       "os"
+       "path/filepath"
+       "runtime"
+       "syscall"
+       "unsafe"
+)
+
 // Event is the type of the notification messages\r
 // received on the watcher's Event channel.\r
-type FileEvent struct {\r
-        mask   uint32 // Mask of events\r
-        cookie uint32 // Unique cookie associating related events (for rename)\r
-        Name   string // File name (optional)\r
-}\r
-\r
+type FileEvent struct {
+       mask   uint32 // Mask of events\r
+       cookie uint32 // Unique cookie associating related events (for rename)\r
+       Name   string // File name (optional)\r
+}
+
 // IsCreate reports whether the FileEvent was triggerd by a creation\r
-func (e *FileEvent) IsCreate() bool { return (e.mask & FS_CREATE) == FS_CREATE}\r
-\r
+func (e *FileEvent) IsCreate() bool { return (e.mask & FS_CREATE) == FS_CREATE }
+
 // IsDelete reports whether the FileEvent was triggerd by a delete\r
-func (e *FileEvent) IsDelete() bool { return ((e.mask & FS_DELETE) == FS_DELETE || (e.mask & FS_DELETE_SELF) == FS_DELETE_SELF) }\r
-\r
+func (e *FileEvent) IsDelete() bool {
+       return ((e.mask&FS_DELETE) == FS_DELETE || (e.mask&FS_DELETE_SELF) == FS_DELETE_SELF)
+}
+
 // IsModify reports whether the FileEvent was triggerd by a file modification or attribute change\r
-func (e *FileEvent) IsModify() bool { return ((e.mask & FS_MODIFY) == FS_MODIFY || (e.mask & FS_ATTRIB) == FS_ATTRIB) }\r
-\r
+func (e *FileEvent) IsModify() bool {
+       return ((e.mask&FS_MODIFY) == FS_MODIFY || (e.mask&FS_ATTRIB) == FS_ATTRIB)
+}
+
 // IsRename reports whether the FileEvent was triggerd by a change name\r
-func (e *FileEvent) IsRename() bool { return ((e.mask & FS_MOVE) == FS_MOVE || (e.mask & FS_MOVE_SELF) == FS_MOVE_SELF || (e.mask & FS_MOVED_FROM) == FS_MOVED_FROM || (e.mask & FS_MOVED_TO) == FS_MOVED_TO) }\r
-\r
-const (\r
-        opAddWatch = iota\r
-        opRemoveWatch\r
-)\r
-\r
-const (\r
-        provisional uint64 = 1 << (32 + iota)\r
-)\r
-\r
-type input struct {\r
-        op    int\r
-        path  string\r
-        flags uint32\r
-        reply chan error\r
-}\r
-\r
-type inode struct {\r
-        handle syscall.Handle\r
-        volume uint32\r
-        index  uint64\r
-}\r
-\r
-type watch struct {\r
-        ov     syscall.Overlapped\r
-        ino    *inode            // i-number\r
-        path   string            // Directory path\r
-        mask   uint64            // Directory itself is being watched with these notify flags\r
-        names  map[string]uint64 // Map of names being watched and their notify flags\r
-        rename string            // Remembers the old name while renaming a file\r
-        buf    [4096]byte\r
-}\r
-\r
-type indexMap map[uint64]*watch\r
-type watchMap map[uint32]indexMap\r
-\r
+func (e *FileEvent) IsRename() bool {
+       return ((e.mask&FS_MOVE) == FS_MOVE || (e.mask&FS_MOVE_SELF) == FS_MOVE_SELF || (e.mask&FS_MOVED_FROM) == FS_MOVED_FROM || (e.mask&FS_MOVED_TO) == FS_MOVED_TO)
+}
+
+const (
+       opAddWatch = iota
+       opRemoveWatch
+)
+
+const (
+       provisional uint64 = 1 << (32 + iota)
+)
+
+type input struct {
+       op    int
+       path  string
+       flags uint32
+       reply chan error
+}
+
+type inode struct {
+       handle syscall.Handle
+       volume uint32
+       index  uint64
+}
+
+type watch struct {
+       ov     syscall.Overlapped
+       ino    *inode            // i-number\r
+       path   string            // Directory path\r
+       mask   uint64            // Directory itself is being watched with these notify flags\r
+       names  map[string]uint64 // Map of names being watched and their notify flags\r
+       rename string            // Remembers the old name while renaming a file\r
+       buf    [4096]byte
+}
+
+type indexMap map[uint64]*watch
+type watchMap map[uint32]indexMap
+
 // A Watcher waits for and receives event notifications\r
 // for a specific set of files and directories.\r
-type Watcher struct {\r
-        port     syscall.Handle // Handle to completion port\r
-        watches  watchMap       // Map of watches (key: i-number)\r
-        input    chan *input    // Inputs to the reader are sent on this channel\r
-        Event    chan *FileEvent// Events are returned on this channel\r
-        Error    chan error     // Errors are sent on this channel\r
-        isClosed bool           // Set to true when Close() is first called\r
-        quit     chan chan<- error\r
-        cookie   uint32\r
-}\r
-\r
+type Watcher struct {
+       port          syscall.Handle    // Handle to completion port\r
+       watches       watchMap          // Map of watches (key: i-number)\r
+       fsnFlags      map[string]uint32 // Map of watched files to flags used for filter\r
+       input         chan *input       // Inputs to the reader are sent on this channel\r
+       internalEvent chan *FileEvent   // Events are queued on this channel\r
+       Event         chan *FileEvent   // Events are returned on this channel\r
+       Error         chan error        // Errors are sent on this channel\r
+       isClosed      bool              // Set to true when Close() is first called\r
+       quit          chan chan<- error
+       cookie        uint32
+}
+
 // NewWatcher creates and returns a Watcher.\r
-func NewWatcher() (*Watcher, error) {\r
-        port, e := syscall.CreateIoCompletionPort(syscall.InvalidHandle, 0, 0, 0)\r
-        if e != nil {\r
-                return nil, os.NewSyscallError("CreateIoCompletionPort", e)\r
-        }\r
-        w := &Watcher{\r
-                port:    port,\r
-                watches: make(watchMap),\r
-                input:   make(chan *input, 1),\r
-                Event:   make(chan *FileEvent, 50),\r
-                Error:   make(chan error),\r
-                quit:    make(chan chan<- error, 1),\r
-        }\r
-        go w.readEvents()\r
-        return w, nil\r
-}\r
-\r
+func NewWatcher() (*Watcher, error) {
+       port, e := syscall.CreateIoCompletionPort(syscall.InvalidHandle, 0, 0, 0)
+       if e != nil {
+               return nil, os.NewSyscallError("CreateIoCompletionPort", e)
+       }
+       w := &Watcher{
+               port:          port,
+               watches:       make(watchMap),
+               fsnFlags:      make(map[string]uint32),
+               input:         make(chan *input, 1),
+               Event:         make(chan *FileEvent, 50),
+               internalEvent: make(chan *FileEvent),
+               Error:         make(chan error),
+               quit:          make(chan chan<- error, 1),
+       }
+       go w.readEvents()
+       go w.purgeEvents()
+       return w, nil
+}
+
 // Close closes a Watcher.\r
 // It sends a message to the reader goroutine to quit and removes all watches\r
 // associated with the watcher.\r
-func (w *Watcher) Close() error {\r
-        if w.isClosed {\r
-                return nil\r
-        }\r
-        w.isClosed = true\r
-\r
-        // Send "quit" message to the reader goroutine\r
-        ch := make(chan error)\r
-        w.quit <- ch\r
-        if err := w.wakeupReader(); err != nil {\r
-                return err\r
-        }\r
-        return <-ch\r
-}\r
-\r
+func (w *Watcher) Close() error {
+       if w.isClosed {
+               return nil
+       }
+       w.isClosed = true
+
+       // Send "quit" message to the reader goroutine\r
+       ch := make(chan error)
+       w.quit <- ch
+       if err := w.wakeupReader(); err != nil {
+               return err
+       }
+       return <-ch
+}
+
 // AddWatch adds path to the watched file set.\r
-func (w *Watcher) AddWatch(path string, flags uint32) error {\r
-        if w.isClosed {\r
-                return errors.New("watcher already closed")\r
-        }\r
-        in := &input{\r
-                op:    opAddWatch,\r
-                path:  filepath.Clean(path),\r
-                flags: flags,\r
-                reply: make(chan error),\r
-        }\r
-        w.input <- in\r
-        if err := w.wakeupReader(); err != nil {\r
-                return err\r
-        }\r
-        return <-in.reply\r
-}\r
-\r
+func (w *Watcher) AddWatch(path string, flags uint32) error {
+       if w.isClosed {
+               return errors.New("watcher already closed")
+       }
+       in := &input{
+               op:    opAddWatch,
+               path:  filepath.Clean(path),
+               flags: flags,
+               reply: make(chan error),
+       }
+       w.input <- in
+       if err := w.wakeupReader(); err != nil {
+               return err
+       }
+       return <-in.reply
+}
+
 // Watch adds path to the watched file set, watching all events.\r
-func (w *Watcher) Watch(path string) error {\r
-        return w.AddWatch(path, FS_ALL_EVENTS)\r
-}\r
-\r
+func (w *Watcher) watch(path string) error {
+       return w.AddWatch(path, FS_ALL_EVENTS)
+}
+
 // RemoveWatch removes path from the watched file set.\r
-func (w *Watcher) RemoveWatch(path string) error {\r
-        in := &input{\r
-                op:    opRemoveWatch,\r
-                path:  filepath.Clean(path),\r
-                reply: make(chan error),\r
-        }\r
-        w.input <- in\r
-        if err := w.wakeupReader(); err != nil {\r
-                return err\r
-        }\r
-        return <-in.reply\r
-}\r
-\r
-func (w *Watcher) wakeupReader() error {\r
-        e := syscall.PostQueuedCompletionStatus(w.port, 0, 0, nil)\r
-        if e != nil {\r
-                return os.NewSyscallError("PostQueuedCompletionStatus", e)\r
-        }\r
-        return nil\r
-}\r
-\r
-func getDir(pathname string) (dir string, err error) {\r
-        attr, e := syscall.GetFileAttributes(syscall.StringToUTF16Ptr(pathname))\r
-        if e != nil {\r
-                return "", os.NewSyscallError("GetFileAttributes", e)\r
-        }\r
-        if attr&syscall.FILE_ATTRIBUTE_DIRECTORY != 0 {\r
-                dir = pathname\r
-        } else {\r
-                dir, _ = filepath.Split(pathname)\r
-                dir = filepath.Clean(dir)\r
-        }\r
-        return\r
-}\r
-\r
-func getIno(path string) (ino *inode, err error) {\r
-        h, e := syscall.CreateFile(syscall.StringToUTF16Ptr(path),\r
-                syscall.FILE_LIST_DIRECTORY,\r
-                syscall.FILE_SHARE_READ|syscall.FILE_SHARE_WRITE|syscall.FILE_SHARE_DELETE,\r
-                nil, syscall.OPEN_EXISTING,\r
-                syscall.FILE_FLAG_BACKUP_SEMANTICS|syscall.FILE_FLAG_OVERLAPPED, 0)\r
-        if e != nil {\r
-                return nil, os.NewSyscallError("CreateFile", e)\r
-        }\r
-        var fi syscall.ByHandleFileInformation\r
-        if e = syscall.GetFileInformationByHandle(h, &fi); e != nil {\r
-                syscall.CloseHandle(h)\r
-                return nil, os.NewSyscallError("GetFileInformationByHandle", e)\r
-        }\r
-        ino = &inode{\r
-                handle: h,\r
-                volume: fi.VolumeSerialNumber,\r
-                index:  uint64(fi.FileIndexHigh)<<32 | uint64(fi.FileIndexLow),\r
-        }\r
-        return ino, nil\r
-}\r
-\r
+func (w *Watcher) removeWatch(path string) error {
+       in := &input{
+               op:    opRemoveWatch,
+               path:  filepath.Clean(path),
+               reply: make(chan error),
+       }
+       w.input <- in
+       if err := w.wakeupReader(); err != nil {
+               return err
+       }
+       return <-in.reply
+}
+
+func (w *Watcher) wakeupReader() error {
+       e := syscall.PostQueuedCompletionStatus(w.port, 0, 0, nil)
+       if e != nil {
+               return os.NewSyscallError("PostQueuedCompletionStatus", e)
+       }
+       return nil
+}
+
+func getDir(pathname string) (dir string, err error) {
+       attr, e := syscall.GetFileAttributes(syscall.StringToUTF16Ptr(pathname))
+       if e != nil {
+               return "", os.NewSyscallError("GetFileAttributes", e)
+       }
+       if attr&syscall.FILE_ATTRIBUTE_DIRECTORY != 0 {
+               dir = pathname
+       } else {
+               dir, _ = filepath.Split(pathname)
+               dir = filepath.Clean(dir)
+       }
+       return
+}
+
+func getIno(path string) (ino *inode, err error) {
+       h, e := syscall.CreateFile(syscall.StringToUTF16Ptr(path),
+               syscall.FILE_LIST_DIRECTORY,
+               syscall.FILE_SHARE_READ|syscall.FILE_SHARE_WRITE|syscall.FILE_SHARE_DELETE,
+               nil, syscall.OPEN_EXISTING,
+               syscall.FILE_FLAG_BACKUP_SEMANTICS|syscall.FILE_FLAG_OVERLAPPED, 0)
+       if e != nil {
+               return nil, os.NewSyscallError("CreateFile", e)
+       }
+       var fi syscall.ByHandleFileInformation
+       if e = syscall.GetFileInformationByHandle(h, &fi); e != nil {
+               syscall.CloseHandle(h)
+               return nil, os.NewSyscallError("GetFileInformationByHandle", e)
+       }
+       ino = &inode{
+               handle: h,
+               volume: fi.VolumeSerialNumber,
+               index:  uint64(fi.FileIndexHigh)<<32 | uint64(fi.FileIndexLow),
+       }
+       return ino, nil
+}
+
 // Must run within the I/O thread.\r
-func (m watchMap) get(ino *inode) *watch {\r
-        if i := m[ino.volume]; i != nil {\r
-                return i[ino.index]\r
-        }\r
-        return nil\r
-}\r
-\r
+func (m watchMap) get(ino *inode) *watch {
+       if i := m[ino.volume]; i != nil {
+               return i[ino.index]
+       }
+       return nil
+}
+
 // Must run within the I/O thread.\r
-func (m watchMap) set(ino *inode, watch *watch) {\r
-        i := m[ino.volume]\r
-        if i == nil {\r
-                i = make(indexMap)\r
-                m[ino.volume] = i\r
-        }\r
-        i[ino.index] = watch\r
-}\r
-\r
+func (m watchMap) set(ino *inode, watch *watch) {
+       i := m[ino.volume]
+       if i == nil {
+               i = make(indexMap)
+               m[ino.volume] = i
+       }
+       i[ino.index] = watch
+}
+
 // Must run within the I/O thread.\r
-func (w *Watcher) addWatch(pathname string, flags uint64) error {\r
-        dir, err := getDir(pathname)\r
-        if err != nil {\r
-                return err\r
-        }\r
-        if flags&FS_ONLYDIR != 0 && pathname != dir {\r
-                return nil\r
-        }\r
-        ino, err := getIno(dir)\r
-        if err != nil {\r
-                return err\r
-        }\r
-        watchEntry := w.watches.get(ino)\r
-        if watchEntry == nil {\r
-                if _, e := syscall.CreateIoCompletionPort(ino.handle, w.port, 0, 0); e != nil {\r
-                        syscall.CloseHandle(ino.handle)\r
-                        return os.NewSyscallError("CreateIoCompletionPort", e)\r
-                }\r
-                watchEntry = &watch{\r
-                        ino:   ino,\r
-                        path:  dir,\r
-                        names: make(map[string]uint64),\r
-                }\r
-                w.watches.set(ino, watchEntry)\r
-                flags |= provisional\r
-        } else {\r
-                syscall.CloseHandle(ino.handle)\r
-        }\r
-        if pathname == dir {\r
-                watchEntry.mask |= flags\r
-        } else {\r
-                watchEntry.names[filepath.Base(pathname)] |= flags\r
-        }\r
-        if err = w.startRead(watchEntry); err != nil {\r
-                return err\r
-        }\r
-        if pathname == dir {\r
-                watchEntry.mask &= ^provisional\r
-        } else {\r
-                watchEntry.names[filepath.Base(pathname)] &= ^provisional\r
-        }\r
-        return nil\r
-}\r
-\r
+func (w *Watcher) addWatch(pathname string, flags uint64) error {
+       dir, err := getDir(pathname)
+       if err != nil {
+               return err
+       }
+       if flags&FS_ONLYDIR != 0 && pathname != dir {
+               return nil
+       }
+       ino, err := getIno(dir)
+       if err != nil {
+               return err
+       }
+       watchEntry := w.watches.get(ino)
+       if watchEntry == nil {
+               if _, e := syscall.CreateIoCompletionPort(ino.handle, w.port, 0, 0); e != nil {
+                       syscall.CloseHandle(ino.handle)
+                       return os.NewSyscallError("CreateIoCompletionPort", e)
+               }
+               watchEntry = &watch{
+                       ino:   ino,
+                       path:  dir,
+                       names: make(map[string]uint64),
+               }
+               w.watches.set(ino, watchEntry)
+               flags |= provisional
+       } else {
+               syscall.CloseHandle(ino.handle)
+       }
+       if pathname == dir {
+               watchEntry.mask |= flags
+       } else {
+               watchEntry.names[filepath.Base(pathname)] |= flags
+       }
+       if err = w.startRead(watchEntry); err != nil {
+               return err
+       }
+       if pathname == dir {
+               watchEntry.mask &= ^provisional
+       } else {
+               watchEntry.names[filepath.Base(pathname)] &= ^provisional
+       }
+       return nil
+}
+
 // Must run within the I/O thread.\r
-func (w *Watcher) removeWatch(pathname string) error {\r
-        dir, err := getDir(pathname)\r
-        if err != nil {\r
-                return err\r
-        }\r
-        ino, err := getIno(dir)\r
-        if err != nil {\r
-                return err\r
-        }\r
-        watch := w.watches.get(ino)\r
-        if watch == nil {\r
-                return fmt.Errorf("can't remove non-existent watch for: %s", pathname)\r
-        }\r
-        if pathname == dir {\r
-                w.sendEvent(watch.path, watch.mask&FS_IGNORED)\r
-                watch.mask = 0\r
-        } else {\r
-                name := filepath.Base(pathname)\r
-                w.sendEvent(watch.path+"/"+name, watch.names[name]&FS_IGNORED)\r
-                delete(watch.names, name)\r
-        }\r
-        return w.startRead(watch)\r
-}\r
-\r
+func (w *Watcher) remWatch(pathname string) error {
+       dir, err := getDir(pathname)
+       if err != nil {
+               return err
+       }
+       ino, err := getIno(dir)
+       if err != nil {
+               return err
+       }
+       watch := w.watches.get(ino)
+       if watch == nil {
+               return fmt.Errorf("can't remove non-existent watch for: %s", pathname)
+       }
+       if pathname == dir {
+               w.sendEvent(watch.path, watch.mask&FS_IGNORED)
+               watch.mask = 0
+       } else {
+               name := filepath.Base(pathname)
+               w.sendEvent(watch.path+"/"+name, watch.names[name]&FS_IGNORED)
+               delete(watch.names, name)
+       }
+       return w.startRead(watch)
+}
+
 // Must run within the I/O thread.\r
-func (w *Watcher) deleteWatch(watch *watch) {\r
-        for name, mask := range watch.names {\r
-                if mask&provisional == 0 {\r
-                        w.sendEvent(watch.path+"/"+name, mask&FS_IGNORED)\r
-                }\r
-                delete(watch.names, name)\r
-        }\r
-        if watch.mask != 0 {\r
-                if watch.mask&provisional == 0 {\r
-                        w.sendEvent(watch.path, watch.mask&FS_IGNORED)\r
-                }\r
-                watch.mask = 0\r
-        }\r
-}\r
-\r
+func (w *Watcher) deleteWatch(watch *watch) {
+       for name, mask := range watch.names {
+               if mask&provisional == 0 {
+                       w.sendEvent(watch.path+"/"+name, mask&FS_IGNORED)
+               }
+               delete(watch.names, name)
+       }
+       if watch.mask != 0 {
+               if watch.mask&provisional == 0 {
+                       w.sendEvent(watch.path, watch.mask&FS_IGNORED)
+               }
+               watch.mask = 0
+       }
+}
+
 // Must run within the I/O thread.\r
-func (w *Watcher) startRead(watch *watch) error {\r
-        if e := syscall.CancelIo(watch.ino.handle); e != nil {\r
-                w.Error <- os.NewSyscallError("CancelIo", e)\r
-                w.deleteWatch(watch)\r
-        }\r
-        mask := toWindowsFlags(watch.mask)\r
-        for _, m := range watch.names {\r
-                mask |= toWindowsFlags(m)\r
-        }\r
-        if mask == 0 {\r
-                if e := syscall.CloseHandle(watch.ino.handle); e != nil {\r
-                        w.Error <- os.NewSyscallError("CloseHandle", e)\r
-                }\r
-                delete(w.watches[watch.ino.volume], watch.ino.index)\r
-                return nil\r
-        }\r
-        e := syscall.ReadDirectoryChanges(watch.ino.handle, &watch.buf[0],\r
-                uint32(unsafe.Sizeof(watch.buf)), false, mask, nil, &watch.ov, 0)\r
-        if e != nil {\r
-                err := os.NewSyscallError("ReadDirectoryChanges", e)\r
-                if e == syscall.ERROR_ACCESS_DENIED && watch.mask&provisional == 0 {\r
-                        // Watched directory was probably removed\r
-                        if w.sendEvent(watch.path, watch.mask&FS_DELETE_SELF) {\r
-                                if watch.mask&FS_ONESHOT != 0 {\r
-                                        watch.mask = 0\r
-                                }\r
-                        }\r
-                        err = nil\r
-                }\r
-                w.deleteWatch(watch)\r
-                w.startRead(watch)\r
-                return err\r
-        }\r
-        return nil\r
-}\r
-\r
+func (w *Watcher) startRead(watch *watch) error {
+       if e := syscall.CancelIo(watch.ino.handle); e != nil {
+               w.Error <- os.NewSyscallError("CancelIo", e)
+               w.deleteWatch(watch)
+       }
+       mask := toWindowsFlags(watch.mask)
+       for _, m := range watch.names {
+               mask |= toWindowsFlags(m)
+       }
+       if mask == 0 {
+               if e := syscall.CloseHandle(watch.ino.handle); e != nil {
+                       w.Error <- os.NewSyscallError("CloseHandle", e)
+               }
+               delete(w.watches[watch.ino.volume], watch.ino.index)
+               return nil
+       }
+       e := syscall.ReadDirectoryChanges(watch.ino.handle, &watch.buf[0],
+               uint32(unsafe.Sizeof(watch.buf)), false, mask, nil, &watch.ov, 0)
+       if e != nil {
+               err := os.NewSyscallError("ReadDirectoryChanges", e)
+               if e == syscall.ERROR_ACCESS_DENIED && watch.mask&provisional == 0 {
+                       // Watched directory was probably removed\r
+                       if w.sendEvent(watch.path, watch.mask&FS_DELETE_SELF) {
+                               if watch.mask&FS_ONESHOT != 0 {
+                                       watch.mask = 0
+                               }
+                       }
+                       err = nil
+               }
+               w.deleteWatch(watch)
+               w.startRead(watch)
+               return err
+       }
+       return nil
+}
+
 // readEvents reads from the I/O completion port, converts the\r
 // received events into Event objects and sends them via the Event channel.\r
 // Entry point to the I/O thread.\r
-func (w *Watcher) readEvents() {\r
-        var (\r
-                n, key uint32\r
-                ov     *syscall.Overlapped\r
-        )\r
-        runtime.LockOSThread()\r
-\r
-        for {\r
-                e := syscall.GetQueuedCompletionStatus(w.port, &n, &key, &ov, syscall.INFINITE)\r
-                watch := (*watch)(unsafe.Pointer(ov))\r
-\r
-                if watch == nil {\r
-                        select {\r
-                        case ch := <-w.quit:\r
-                                for _, index := range w.watches {\r
-                                        for _, watch := range index {\r
-                                                w.deleteWatch(watch)\r
-                                                w.startRead(watch)\r
-                                        }\r
-                                }\r
-                                var err error\r
-                                if e := syscall.CloseHandle(w.port); e != nil {\r
-                                        err = os.NewSyscallError("CloseHandle", e)\r
-                                }\r
-                                close(w.Event)\r
-                                close(w.Error)\r
-                                ch <- err\r
-                                return\r
-                        case in := <-w.input:\r
-                                switch in.op {\r
-                                case opAddWatch:\r
-                                        in.reply <- w.addWatch(in.path, uint64(in.flags))\r
-                                case opRemoveWatch:\r
-                                        in.reply <- w.removeWatch(in.path)\r
-                                }\r
-                        default:\r
-                        }\r
-                        continue\r
-                }\r
-\r
-                switch e {\r
-                case syscall.ERROR_ACCESS_DENIED:\r
-                        // Watched directory was probably removed\r
-                        w.sendEvent(watch.path, watch.mask&FS_DELETE_SELF)\r
-                        w.deleteWatch(watch)\r
-                        w.startRead(watch)\r
-                        continue\r
-                case syscall.ERROR_OPERATION_ABORTED:\r
-                        // CancelIo was called on this handle\r
-                        continue\r
-                default:\r
-                        w.Error <- os.NewSyscallError("GetQueuedCompletionPort", e)\r
-                        continue\r
-                case nil:\r
-                }\r
-\r
-                var offset uint32\r
-                for {\r
-                        if n == 0 {\r
-                                w.Event <- &FileEvent{mask: FS_Q_OVERFLOW}\r
-                                w.Error <- errors.New("short read in readEvents()")\r
-                                break\r
-                        }\r
-\r
-                        // Point "raw" to the event in the buffer\r
-                        raw := (*syscall.FileNotifyInformation)(unsafe.Pointer(&watch.buf[offset]))\r
-                        buf := (*[syscall.MAX_PATH]uint16)(unsafe.Pointer(&raw.FileName))\r
-                        name := syscall.UTF16ToString(buf[:raw.FileNameLength/2])\r
-                        fullname := watch.path + "/" + name\r
-\r
-                        var mask uint64\r
-                        switch raw.Action {\r
-                        case syscall.FILE_ACTION_REMOVED:\r
-                                mask = FS_DELETE_SELF\r
-                        case syscall.FILE_ACTION_MODIFIED:\r
-                                mask = FS_MODIFY\r
-                        case syscall.FILE_ACTION_RENAMED_OLD_NAME:\r
-                                watch.rename = name\r
-                        case syscall.FILE_ACTION_RENAMED_NEW_NAME:\r
-                                if watch.names[watch.rename] != 0 {\r
-                                        watch.names[name] |= watch.names[watch.rename]\r
-                                        delete(watch.names, watch.rename)\r
-                                        mask = FS_MOVE_SELF\r
-                                }\r
-                        }\r
-\r
-                        sendNameEvent := func() {\r
-                                if w.sendEvent(fullname, watch.names[name]&mask) {\r
-                                        if watch.names[name]&FS_ONESHOT != 0 {\r
-                                                delete(watch.names, name)\r
-                                        }\r
-                                }\r
-                        }\r
-                        if raw.Action != syscall.FILE_ACTION_RENAMED_NEW_NAME {\r
-                                sendNameEvent()\r
-                        }\r
-                        if raw.Action == syscall.FILE_ACTION_REMOVED {\r
-                                w.sendEvent(fullname, watch.names[name]&FS_IGNORED)\r
-                                delete(watch.names, name)\r
-                        }\r
-                        if w.sendEvent(fullname, watch.mask&toFSnotifyFlags(raw.Action)) {\r
-                                if watch.mask&FS_ONESHOT != 0 {\r
-                                        watch.mask = 0\r
-                                }\r
-                        }\r
-                        if raw.Action == syscall.FILE_ACTION_RENAMED_NEW_NAME {\r
-                                fullname = watch.path + "/" + watch.rename\r
-                                sendNameEvent()\r
-                        }\r
-\r
-                        // Move to the next event in the buffer\r
-                        if raw.NextEntryOffset == 0 {\r
-                                break\r
-                        }\r
-                        offset += raw.NextEntryOffset\r
-                }\r
-\r
-                if err := w.startRead(watch); err != nil {\r
-                        w.Error <- err\r
-                }\r
-        }\r
-}\r
-\r
-func (w *Watcher) sendEvent(name string, mask uint64) bool {\r
-        if mask == 0 {\r
-                return false\r
-        }\r
-        event := &FileEvent{mask: uint32(mask), Name: name}\r
-        if mask&FS_MOVE != 0 {\r
-                if mask&FS_MOVED_FROM != 0 {\r
-                        w.cookie++\r
-                }\r
-                event.cookie = w.cookie\r
-        }\r
-        select {\r
-        case ch := <-w.quit:\r
-                w.quit <- ch\r
-        case w.Event <- event:\r
-        }\r
-        return true\r
-}\r
-\r
-func toWindowsFlags(mask uint64) uint32 {\r
-        var m uint32\r
-        if mask&FS_ACCESS != 0 {\r
-                m |= syscall.FILE_NOTIFY_CHANGE_LAST_ACCESS\r
-        }\r
-        if mask&FS_MODIFY != 0 {\r
-                m |= syscall.FILE_NOTIFY_CHANGE_LAST_WRITE\r
-        }\r
-        if mask&FS_ATTRIB != 0 {\r
-                m |= syscall.FILE_NOTIFY_CHANGE_ATTRIBUTES\r
-        }\r
-        if mask&(FS_MOVE|FS_CREATE|FS_DELETE) != 0 {\r
-                m |= syscall.FILE_NOTIFY_CHANGE_FILE_NAME | syscall.FILE_NOTIFY_CHANGE_DIR_NAME\r
-        }\r
-        return m\r
-}\r
-\r
-func toFSnotifyFlags(action uint32) uint64 {\r
-        switch action {\r
-        case syscall.FILE_ACTION_ADDED:\r
-                return FS_CREATE\r
-        case syscall.FILE_ACTION_REMOVED:\r
-                return FS_DELETE\r
-        case syscall.FILE_ACTION_MODIFIED:\r
-                return FS_MODIFY\r
-        case syscall.FILE_ACTION_RENAMED_OLD_NAME:\r
-                return FS_MOVED_FROM\r
-        case syscall.FILE_ACTION_RENAMED_NEW_NAME:\r
-                return FS_MOVED_TO\r
-        }\r
-        return 0\r
-}\r
-\r
-const (\r
-        // Options for AddWatch\r
-        FS_ONESHOT = 0x80000000\r
-        FS_ONLYDIR = 0x1000000\r
-\r
-        // Events\r
-        FS_ACCESS      = 0x1\r
-        FS_ALL_EVENTS  = 0xfff\r
-        FS_ATTRIB      = 0x4\r
-        FS_CLOSE       = 0x18\r
-        FS_CREATE      = 0x100\r
-        FS_DELETE      = 0x200\r
-        FS_DELETE_SELF = 0x400\r
-        FS_MODIFY      = 0x2\r
-        FS_MOVE        = 0xc0\r
-        FS_MOVED_FROM  = 0x40\r
-        FS_MOVED_TO    = 0x80\r
-        FS_MOVE_SELF   = 0x800\r
-\r
-        // Special events\r
-        FS_IGNORED    = 0x8000\r
-        FS_Q_OVERFLOW = 0x4000\r
-)\r
-\r
-var eventBits = []struct {\r
-        Value uint32\r
-        Name  string\r
-}{\r
-        {FS_ACCESS, "FS_ACCESS"},\r
-        {FS_ATTRIB, "FS_ATTRIB"},\r
-        {FS_CREATE, "FS_CREATE"},\r
-        {FS_DELETE, "FS_DELETE"},\r
-        {FS_DELETE_SELF, "FS_DELETE_SELF"},\r
-        {FS_MODIFY, "FS_MODIFY"},\r
-        {FS_MOVED_FROM, "FS_MOVED_FROM"},\r
-        {FS_MOVED_TO, "FS_MOVED_TO"},\r
-        {FS_MOVE_SELF, "FS_MOVE_SELF"},\r
-        {FS_IGNORED, "FS_IGNORED"},\r
-        {FS_Q_OVERFLOW, "FS_Q_OVERFLOW"},\r
-}\r
+func (w *Watcher) readEvents() {
+       var (
+               n, key uint32
+               ov     *syscall.Overlapped
+       )
+       runtime.LockOSThread()
+
+       for {
+               e := syscall.GetQueuedCompletionStatus(w.port, &n, &key, &ov, syscall.INFINITE)
+               watch := (*watch)(unsafe.Pointer(ov))
+
+               if watch == nil {
+                       select {
+                       case ch := <-w.quit:
+                               for _, index := range w.watches {
+                                       for _, watch := range index {
+                                               w.deleteWatch(watch)
+                                               w.startRead(watch)
+                                       }
+                               }
+                               var err error
+                               if e := syscall.CloseHandle(w.port); e != nil {
+                                       err = os.NewSyscallError("CloseHandle", e)
+                               }
+                               close(w.internalEvent)
+                               close(w.Error)
+                               ch <- err
+                               return
+                       case in := <-w.input:
+                               switch in.op {
+                               case opAddWatch:
+                                       in.reply <- w.addWatch(in.path, uint64(in.flags))
+                               case opRemoveWatch:
+                                       in.reply <- w.remWatch(in.path)
+                               }
+                       default:
+                       }
+                       continue
+               }
+
+               switch e {
+               case syscall.ERROR_ACCESS_DENIED:
+                       // Watched directory was probably removed\r
+                       w.sendEvent(watch.path, watch.mask&FS_DELETE_SELF)
+                       w.deleteWatch(watch)
+                       w.startRead(watch)
+                       continue
+               case syscall.ERROR_OPERATION_ABORTED:
+                       // CancelIo was called on this handle\r
+                       continue
+               default:
+                       w.Error <- os.NewSyscallError("GetQueuedCompletionPort", e)
+                       continue
+               case nil:
+               }
+
+               var offset uint32
+               for {
+                       if n == 0 {
+                               w.internalEvent <- &FileEvent{mask: FS_Q_OVERFLOW}
+                               w.Error <- errors.New("short read in readEvents()")
+                               break
+                       }
+
+                       // Point "raw" to the event in the buffer\r
+                       raw := (*syscall.FileNotifyInformation)(unsafe.Pointer(&watch.buf[offset]))
+                       buf := (*[syscall.MAX_PATH]uint16)(unsafe.Pointer(&raw.FileName))
+                       name := syscall.UTF16ToString(buf[:raw.FileNameLength/2])
+                       fullname := watch.path + "/" + name
+
+                       var mask uint64
+                       switch raw.Action {
+                       case syscall.FILE_ACTION_REMOVED:
+                               mask = FS_DELETE_SELF
+                       case syscall.FILE_ACTION_MODIFIED:
+                               mask = FS_MODIFY
+                       case syscall.FILE_ACTION_RENAMED_OLD_NAME:
+                               watch.rename = name
+                       case syscall.FILE_ACTION_RENAMED_NEW_NAME:
+                               if watch.names[watch.rename] != 0 {
+                                       watch.names[name] |= watch.names[watch.rename]
+                                       delete(watch.names, watch.rename)
+                                       mask = FS_MOVE_SELF
+                               }
+                       }
+
+                       sendNameEvent := func() {
+                               if w.sendEvent(fullname, watch.names[name]&mask) {
+                                       if watch.names[name]&FS_ONESHOT != 0 {
+                                               delete(watch.names, name)
+                                       }
+                               }
+                       }
+                       if raw.Action != syscall.FILE_ACTION_RENAMED_NEW_NAME {
+                               sendNameEvent()
+                       }
+                       if raw.Action == syscall.FILE_ACTION_REMOVED {
+                               w.sendEvent(fullname, watch.names[name]&FS_IGNORED)
+                               delete(watch.names, name)
+                       }
+                       if w.sendEvent(fullname, watch.mask&toFSnotifyFlags(raw.Action)) {
+                               if watch.mask&FS_ONESHOT != 0 {
+                                       watch.mask = 0
+                               }
+                       }
+                       if raw.Action == syscall.FILE_ACTION_RENAMED_NEW_NAME {
+                               fullname = watch.path + "/" + watch.rename
+                               sendNameEvent()
+                       }
+
+                       // Move to the next event in the buffer\r
+                       if raw.NextEntryOffset == 0 {
+                               break
+                       }
+                       offset += raw.NextEntryOffset
+               }
+
+               if err := w.startRead(watch); err != nil {
+                       w.Error <- err
+               }
+       }
+}
+
+func (w *Watcher) sendEvent(name string, mask uint64) bool {
+       if mask == 0 {
+               return false
+       }
+       event := &FileEvent{mask: uint32(mask), Name: name}
+       if mask&FS_MOVE != 0 {
+               if mask&FS_MOVED_FROM != 0 {
+                       w.cookie++
+               }
+               event.cookie = w.cookie
+       }
+       select {
+       case ch := <-w.quit:
+               w.quit <- ch
+       case w.Event <- event:
+       }
+       return true
+}
+
+func toWindowsFlags(mask uint64) uint32 {
+       var m uint32
+       if mask&FS_ACCESS != 0 {
+               m |= syscall.FILE_NOTIFY_CHANGE_LAST_ACCESS
+       }
+       if mask&FS_MODIFY != 0 {
+               m |= syscall.FILE_NOTIFY_CHANGE_LAST_WRITE
+       }
+       if mask&FS_ATTRIB != 0 {
+               m |= syscall.FILE_NOTIFY_CHANGE_ATTRIBUTES
+       }
+       if mask&(FS_MOVE|FS_CREATE|FS_DELETE) != 0 {
+               m |= syscall.FILE_NOTIFY_CHANGE_FILE_NAME | syscall.FILE_NOTIFY_CHANGE_DIR_NAME
+       }
+       return m
+}
+
+func toFSnotifyFlags(action uint32) uint64 {
+       switch action {
+       case syscall.FILE_ACTION_ADDED:
+               return FS_CREATE
+       case syscall.FILE_ACTION_REMOVED:
+               return FS_DELETE
+       case syscall.FILE_ACTION_MODIFIED:
+               return FS_MODIFY
+       case syscall.FILE_ACTION_RENAMED_OLD_NAME:
+               return FS_MOVED_FROM
+       case syscall.FILE_ACTION_RENAMED_NEW_NAME:
+               return FS_MOVED_TO
+       }
+       return 0
+}
+
+const (
+       // Options for AddWatch\r
+       FS_ONESHOT = 0x80000000
+       FS_ONLYDIR = 0x1000000
+
+       // Events\r
+       FS_ACCESS      = 0x1
+       FS_ALL_EVENTS  = 0xfff
+       FS_ATTRIB      = 0x4
+       FS_CLOSE       = 0x18
+       FS_CREATE      = 0x100
+       FS_DELETE      = 0x200
+       FS_DELETE_SELF = 0x400
+       FS_MODIFY      = 0x2
+       FS_MOVE        = 0xc0
+       FS_MOVED_FROM  = 0x40
+       FS_MOVED_TO    = 0x80
+       FS_MOVE_SELF   = 0x800
+
+       // Special events\r
+       FS_IGNORED    = 0x8000
+       FS_Q_OVERFLOW = 0x4000
+)
+
+var eventBits = []struct {
+       Value uint32
+       Name  string
+}{
+       {FS_ACCESS, "FS_ACCESS"},
+       {FS_ATTRIB, "FS_ATTRIB"},
+       {FS_CREATE, "FS_CREATE"},
+       {FS_DELETE, "FS_DELETE"},
+       {FS_DELETE_SELF, "FS_DELETE_SELF"},
+       {FS_MODIFY, "FS_MODIFY"},
+       {FS_MOVED_FROM, "FS_MOVED_FROM"},
+       {FS_MOVED_TO, "FS_MOVED_TO"},
+       {FS_MOVE_SELF, "FS_MOVE_SELF"},
+       {FS_IGNORED, "FS_IGNORED"},
+       {FS_Q_OVERFLOW, "FS_Q_OVERFLOW"},
+}