usesh: true
prepare: pkg install -y go
run: |
- go test -race ./...
+ pw user add -n action -m
+ su action -c 'go test -race ./...'
testOpenBSD:
runs-on: macos-12
uses: vmactions/openbsd-vm@v0.0.6
with:
prepare: pkg_add go
+ # No -race as the VM doesn't include the comp set.
+ #
+ # TODO: should probably add this, but on my local machine the tests
+ # time out with -race as the waits aren't long enough (OpenBSD
+ # is kind of slow), so should probably look into that first.
+ # Go 1.19 is supposed to have a much faster race detector, so
+ # maybe waiting until we have that is enough.
run: |
# Default of 512 leads to "too many open files".
ulimit -n 1024
-
- # No -race as the VM doesn't include the comp set.
- #
- # TODO: should probably add this, but on my local machine the tests
- # time out with -race as the waits aren't long enough (OpenBSD
- # is kind of slow), so should probably look into that first.
- # Go 1.19 is supposed to have a much faster race detector, so
- # maybe waiting until we have that is enough.
- go test ./...
+ useradd -mG wheel action
+ su action -c 'go test ./...'
testNetBSD:
runs-on: macos-12
uses: vmactions/netbsd-vm@v0.0.4
with:
prepare: pkg_add go
+ # TODO: no -race for the same reason as OpenBSD (the timing; it does run).
run: |
- # TODO: no -race for the same reason as OpenBSD (the timing; it does run).
- go117 test ./...
+ useradd -mG wheel action
+ su action -c 'go117 test ./...'
testillumos:
runs-on: macos-12
package fsnotify
import (
+ "errors"
"fmt"
"path/filepath"
"runtime"
"sync/atomic"
"testing"
"time"
+
+ "github.com/fsnotify/fsnotify/internal"
)
func TestWatch(t *testing.T) {
remove /sub
remove /file
`},
+
+ {"file in directory is not readable", func(t *testing.T, w *Watcher, tmp string) {
+ if runtime.GOOS == "windows" {
+ t.Skip("attributes don't work on Windows")
+ }
+
+ touch(t, tmp, "file-unreadable")
+ chmod(t, 0, tmp, "file-unreadable")
+ touch(t, tmp, "file")
+ addWatch(t, w, tmp)
+
+ cat(t, "hello", tmp, "file")
+ rm(t, tmp, "file")
+ rm(t, tmp, "file-unreadable")
+ }, `
+ WRITE "/file"
+ REMOVE "/file"
+ REMOVE "/file-unreadable"
+
+ # We never set up a watcher on the unreadable file, so we don't get
+ # the REMOVE.
+ kqueue:
+ WRITE "/file"
+ REMOVE "/file"
+ `},
}
for _, tt := range tests {
})
}
+func TestAdd(t *testing.T) {
+ t.Run("permission denied", func(t *testing.T) {
+ if runtime.GOOS == "windows" {
+ t.Skip("attributes don't work on Windows")
+ }
+
+ t.Parallel()
+
+ tmp := t.TempDir()
+ dir := filepath.Join(tmp, "dir-unreadable")
+ mkdir(t, dir)
+ touch(t, dir, "/file")
+ chmod(t, 0, dir)
+
+ w := newWatcher(t)
+ defer func() {
+ w.Close()
+ chmod(t, 0o755, dir) // Make TempDir() cleanup work
+ }()
+ err := w.Add(dir)
+ if err == nil {
+ t.Fatal("error is nil")
+ }
+ if !errors.Is(err, internal.UnixEACCES) {
+ t.Errorf("not unix.EACCESS: %T %#[1]v", err)
+ }
+ if !errors.Is(err, internal.SyscallEACCES) {
+ t.Errorf("not syscall.EACCESS: %T %#[1]v", err)
+ }
+ })
+}
+
// TODO: should also check internal state is correct/cleaned up; e.g. no
// left-over file descriptors or whatnot.
func TestRemove(t *testing.T) {
--- /dev/null
+//go:build !windows
+// +build !windows
+
+package internal
+
+import (
+ "syscall"
+
+ "golang.org/x/sys/unix"
+)
+
+var (
+ SyscallEACCES = syscall.EACCES
+ UnixEACCES = unix.EACCES
+)
--- /dev/null
+//go:build windows
+// +build windows
+
+package internal
+
+import (
+ "errors"
+)
+
+// Just a dummy.
+var (
+ SyscallEACCES = errors.New("dummy")
+ UnixEACCES = errors.New("dummy")
+)
return "", err
}
- // Don't watch sockets.
- if fi.Mode()&os.ModeSocket == os.ModeSocket {
- return "", nil
- }
-
- // Don't watch named pipes.
- if fi.Mode()&os.ModeNamedPipe == os.ModeNamedPipe {
+ // Don't watch sockets or named pipes
+ if (fi.Mode()&os.ModeSocket == os.ModeSocket) || (fi.Mode()&os.ModeNamedPipe == os.ModeNamedPipe) {
return "", nil
}
}
for _, fileInfo := range files {
- filePath := filepath.Join(dirPath, fileInfo.Name())
- filePath, err = w.internalWatch(filePath, fileInfo)
+ path := filepath.Join(dirPath, fileInfo.Name())
+
+ cleanPath, err := w.internalWatch(path, fileInfo)
if err != nil {
- return fmt.Errorf("%q: %w", filepath.Join(dirPath, fileInfo.Name()), err)
+ // No permission to read the file; that's not a problem: just skip.
+ // But do add it to w.fileExists to prevent it from being picked up
+ // as a "new" file later (it still shows up in the directory
+ // listing).
+ switch {
+ case errors.Is(err, unix.EACCES) || errors.Is(err, unix.EPERM):
+ cleanPath = filepath.Clean(path)
+ default:
+ return fmt.Errorf("%q: %w", filepath.Join(dirPath, fileInfo.Name()), err)
+ }
}
w.mu.Lock()
- w.fileExists[filePath] = struct{}{}
+ w.fileExists[cleanPath] = struct{}{}
w.mu.Unlock()
}