if !ok {
return fmt.Errorf("can't remove non-existent inotify watch for: %s", name)
}
+ // inotify_rm_watch will return EINVAL if the file has been deleted;
+ // the inotify will already have been removed.
+ // That means we can safely delete it from our watches, whatever inotify_rm_watch does.
+ delete(w.watches, name)
success, errno := syscall.InotifyRmWatch(w.fd, watch.wd)
if success == -1 {
+ // TODO: Perhaps it's not helpful to return an error here in every case.
+ // the only two possible errors are:
+ // EBADF, which happens when w.fd is not a valid file descriptor of any kind.
+ // EINVAL, which is when fd is not an inotify descriptor or wd is not a valid watch descriptor.
+ // Watch descriptors are invalidated when they are removed explicitly or implicitly;
+ // explicitly by inotify_rm_watch, implicitly when the file they are watching is deleted.
return errno
}
- delete(w.watches, name)
return nil
}
}
}
}
+
+func TestInotifyRemoveTwice(t *testing.T) {
+ testDir := tempMkdir(t)
+ defer os.RemoveAll(testDir)
+ testFile := filepath.Join(testDir, "testfile")
+
+ handle, err := os.Create(testFile)
+ if err != nil {
+ t.Fatalf("Create failed: %v", err)
+ }
+ handle.Close()
+
+ w, err := NewWatcher()
+ if err != nil {
+ t.Fatalf("Failed to create watcher: %v", err)
+ }
+ defer w.Close()
+
+ err = w.Add(testFile)
+ if err != nil {
+ t.Fatalf("Failed to add testFile: %v", err)
+ }
+
+ err = os.Remove(testFile)
+ if err != nil {
+ t.Fatalf("Failed to remove testFile: %v", err)
+ }
+
+ err = w.Remove(testFile)
+ if err != syscall.EINVAL {
+ t.Fatalf("Expected EINVAL from Remove, got: %v", err)
+ }
+
+ err = w.Remove(testFile)
+ if err == syscall.EINVAL {
+ t.Fatalf("Got EINVAL again, watch was not removed")
+ }
+}