# Changelog
+## v1.3.0 / 2016-04-19
+
+* Support linux/arm64 by [patching](https://go-review.googlesource.com/#/c/21971/) x/sys/unix and switching to to it from syscall (thanks @suihkulokki) [#135](https://github.com/fsnotify/fsnotify/pull/135)
+
## v1.2.10 / 2016-03-02
* Fix golint errors in windows.go [#121](https://github.com/fsnotify/fsnotify/pull/121) (thanks @tiffanyfj)
[](https://godoc.org/github.com/fsnotify/fsnotify) [](https://goreportcard.com/report/github.com/fsnotify/fsnotify) [](http://gocover.io/github.com/fsnotify/fsnotify)
-Go 1.3+ required.
+fsnotify utilizes [golang.org/x/sys](https://godoc.org/golang.org/x/sys) rather than `syscall` from the standard library. Ensure you have the latest version installed by running:
+
+```console
+go get -u golang.org/x/sys/...
+```
Cross platform: Windows, Linux, BSD and OS X.
All [releases](https://github.com/fsnotify/fsnotify/releases) are tagged based on [Semantic Versioning](http://semver.org/). Further API changes are [planned](https://github.com/fsnotify/fsnotify/milestones), and will be tagged with a new major revision number.
-Go 1.6 supports dependencies located in the `vendor/` folder. Unless you are creating a library, it is recommended that you copy fsnotify into `vendor/github.com/fsnotify/fsnotify` within your project.
+Go 1.6 supports dependencies located in the `vendor/` folder. Unless you are creating a library, it is recommended that you copy fsnotify into `vendor/github.com/fsnotify/fsnotify` within your project, and likewise for `golang.org/x/sys`.
## Contributing
"path/filepath"
"strings"
"sync"
- "syscall"
"unsafe"
+
+ "golang.org/x/sys/unix"
)
// Watcher watches a set of files, delivering events to a channel.
// NewWatcher establishes a new watcher with the underlying OS and begins waiting for events.
func NewWatcher() (*Watcher, error) {
// Create inotify fd
- fd, errno := syscall.InotifyInit()
+ fd, errno := unix.InotifyInit()
if fd == -1 {
return nil, errno
}
// Create epoll
poller, err := newFdPoller(fd)
if err != nil {
- syscall.Close(fd)
+ unix.Close(fd)
return nil, err
}
w := &Watcher{
return errors.New("inotify instance already closed")
}
- const agnosticEvents = syscall.IN_MOVED_TO | syscall.IN_MOVED_FROM |
- syscall.IN_CREATE | syscall.IN_ATTRIB | syscall.IN_MODIFY |
- syscall.IN_MOVE_SELF | syscall.IN_DELETE | syscall.IN_DELETE_SELF
+ const agnosticEvents = unix.IN_MOVED_TO | unix.IN_MOVED_FROM |
+ unix.IN_CREATE | unix.IN_ATTRIB | unix.IN_MODIFY |
+ unix.IN_MOVE_SELF | unix.IN_DELETE | unix.IN_DELETE_SELF
var flags uint32 = agnosticEvents
w.mu.Unlock()
if found {
watchEntry.flags |= flags
- flags |= syscall.IN_MASK_ADD
+ flags |= unix.IN_MASK_ADD
}
- wd, errno := syscall.InotifyAddWatch(w.fd, name, flags)
+ wd, errno := unix.InotifyAddWatch(w.fd, name, flags)
if wd == -1 {
return errno
}
// by calling inotify_rm_watch() below. e.g. readEvents() goroutine receives IN_IGNORE
// so that EINVAL means that the wd is being rm_watch()ed or its file removed
// by another thread and we have not received IN_IGNORE event.
- success, errno := syscall.InotifyRmWatch(w.fd, watch.wd)
+ success, errno := unix.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:
// received events into Event objects and sends them via the Events channel
func (w *Watcher) readEvents() {
var (
- buf [syscall.SizeofInotifyEvent * 4096]byte // Buffer for a maximum of 4096 raw events
- n int // Number of bytes read with read()
- errno error // Syscall errno
- ok bool // For poller.wait
+ buf [unix.SizeofInotifyEvent * 4096]byte // Buffer for a maximum of 4096 raw events
+ n int // Number of bytes read with read()
+ errno error // Syscall errno
+ ok bool // For poller.wait
)
defer close(w.doneResp)
defer close(w.Errors)
defer close(w.Events)
- defer syscall.Close(w.fd)
+ defer unix.Close(w.fd)
defer w.poller.close()
for {
continue
}
- n, errno = syscall.Read(w.fd, buf[:])
+ n, errno = unix.Read(w.fd, buf[:])
// If a signal interrupted execution, see if we've been asked to close, and try again.
// http://man7.org/linux/man-pages/man7/signal.7.html :
// "Before Linux 3.8, reads from an inotify(7) file descriptor were not restartable"
- if errno == syscall.EINTR {
+ if errno == unix.EINTR {
continue
}
- // syscall.Read might have been woken up by Close. If so, we're done.
+ // unix.Read might have been woken up by Close. If so, we're done.
if w.isClosed() {
return
}
- if n < syscall.SizeofInotifyEvent {
+ if n < unix.SizeofInotifyEvent {
var err error
if n == 0 {
// If EOF is received. This should really never happen.
var offset uint32
// We don't know how many events we just read into the buffer
// While the offset points to at least one whole event...
- for offset <= uint32(n-syscall.SizeofInotifyEvent) {
+ for offset <= uint32(n-unix.SizeofInotifyEvent) {
// Point "raw" to the event in the buffer
- raw := (*syscall.InotifyEvent)(unsafe.Pointer(&buf[offset]))
+ raw := (*unix.InotifyEvent)(unsafe.Pointer(&buf[offset]))
mask := uint32(raw.Mask)
nameLen := uint32(raw.Len)
w.mu.Unlock()
if nameLen > 0 {
// Point "bytes" at the first byte of the filename
- bytes := (*[syscall.PathMax]byte)(unsafe.Pointer(&buf[offset+syscall.SizeofInotifyEvent]))
+ bytes := (*[unix.PathMax]byte)(unsafe.Pointer(&buf[offset+unix.SizeofInotifyEvent]))
// The filename is padded with NULL bytes. TrimRight() gets rid of those.
name += "/" + strings.TrimRight(string(bytes[0:nameLen]), "\000")
}
}
// Move to the next event in the buffer
- offset += syscall.SizeofInotifyEvent + nameLen
+ offset += unix.SizeofInotifyEvent + nameLen
}
}
}
// against files that do not exist.
func (e *Event) ignoreLinux(w *Watcher, wd int32, mask uint32) bool {
// Ignore anything the inotify API says to ignore
- if mask&syscall.IN_IGNORED == syscall.IN_IGNORED {
+ if mask&unix.IN_IGNORED == unix.IN_IGNORED {
w.mu.Lock()
defer w.mu.Unlock()
name := w.paths[int(wd)]
// newEvent returns an platform-independent Event based on an inotify mask.
func newEvent(name string, mask uint32) Event {
e := Event{Name: name}
- if mask&syscall.IN_CREATE == syscall.IN_CREATE || mask&syscall.IN_MOVED_TO == syscall.IN_MOVED_TO {
+ if mask&unix.IN_CREATE == unix.IN_CREATE || mask&unix.IN_MOVED_TO == unix.IN_MOVED_TO {
e.Op |= Create
}
- if mask&syscall.IN_DELETE_SELF == syscall.IN_DELETE_SELF || mask&syscall.IN_DELETE == syscall.IN_DELETE {
+ if mask&unix.IN_DELETE_SELF == unix.IN_DELETE_SELF || mask&unix.IN_DELETE == unix.IN_DELETE {
e.Op |= Remove
}
- if mask&syscall.IN_MODIFY == syscall.IN_MODIFY {
+ if mask&unix.IN_MODIFY == unix.IN_MODIFY {
e.Op |= Write
}
- if mask&syscall.IN_MOVE_SELF == syscall.IN_MOVE_SELF || mask&syscall.IN_MOVED_FROM == syscall.IN_MOVED_FROM {
+ if mask&unix.IN_MOVE_SELF == unix.IN_MOVE_SELF || mask&unix.IN_MOVED_FROM == unix.IN_MOVED_FROM {
e.Op |= Rename
}
- if mask&syscall.IN_ATTRIB == syscall.IN_ATTRIB {
+ if mask&unix.IN_ATTRIB == unix.IN_ATTRIB {
e.Op |= Chmod
}
return e
import (
"errors"
- "syscall"
+
+ "golang.org/x/sys/unix"
)
type fdPoller struct {
poller.fd = fd
// Create epoll fd
- poller.epfd, errno = syscall.EpollCreate1(0)
+ poller.epfd, errno = unix.EpollCreate1(0)
if poller.epfd == -1 {
return nil, errno
}
// Create pipe; pipe[0] is the read end, pipe[1] the write end.
- errno = syscall.Pipe2(poller.pipe[:], syscall.O_NONBLOCK)
+ errno = unix.Pipe2(poller.pipe[:], unix.O_NONBLOCK)
if errno != nil {
return nil, errno
}
// Register inotify fd with epoll
- event := syscall.EpollEvent{
+ event := unix.EpollEvent{
Fd: int32(poller.fd),
- Events: syscall.EPOLLIN,
+ Events: unix.EPOLLIN,
}
- errno = syscall.EpollCtl(poller.epfd, syscall.EPOLL_CTL_ADD, poller.fd, &event)
+ errno = unix.EpollCtl(poller.epfd, unix.EPOLL_CTL_ADD, poller.fd, &event)
if errno != nil {
return nil, errno
}
// Register pipe fd with epoll
- event = syscall.EpollEvent{
+ event = unix.EpollEvent{
Fd: int32(poller.pipe[0]),
- Events: syscall.EPOLLIN,
+ Events: unix.EPOLLIN,
}
- errno = syscall.EpollCtl(poller.epfd, syscall.EPOLL_CTL_ADD, poller.pipe[0], &event)
+ errno = unix.EpollCtl(poller.epfd, unix.EPOLL_CTL_ADD, poller.pipe[0], &event)
if errno != nil {
return nil, errno
}
// I don't know whether epoll_wait returns the number of events returned,
// or the total number of events ready.
// I decided to catch both by making the buffer one larger than the maximum.
- events := make([]syscall.EpollEvent, 7)
+ events := make([]unix.EpollEvent, 7)
for {
- n, errno := syscall.EpollWait(poller.epfd, events, -1)
+ n, errno := unix.EpollWait(poller.epfd, events, -1)
if n == -1 {
- if errno == syscall.EINTR {
+ if errno == unix.EINTR {
continue
}
return false, errno
epollin := false
for _, event := range ready {
if event.Fd == int32(poller.fd) {
- if event.Events&syscall.EPOLLHUP != 0 {
+ if event.Events&unix.EPOLLHUP != 0 {
// This should not happen, but if it does, treat it as a wakeup.
epollhup = true
}
- if event.Events&syscall.EPOLLERR != 0 {
+ if event.Events&unix.EPOLLERR != 0 {
// If an error is waiting on the file descriptor, we should pretend
- // something is ready to read, and let syscall.Read pick up the error.
+ // something is ready to read, and let unix.Read pick up the error.
epollerr = true
}
- if event.Events&syscall.EPOLLIN != 0 {
+ if event.Events&unix.EPOLLIN != 0 {
// There is data to read.
epollin = true
}
}
if event.Fd == int32(poller.pipe[0]) {
- if event.Events&syscall.EPOLLHUP != 0 {
+ if event.Events&unix.EPOLLHUP != 0 {
// Write pipe descriptor was closed, by us. This means we're closing down the
// watcher, and we should wake up.
}
- if event.Events&syscall.EPOLLERR != 0 {
+ if event.Events&unix.EPOLLERR != 0 {
// If an error is waiting on the pipe file descriptor.
// This is an absolute mystery, and should never ever happen.
return false, errors.New("Error on the pipe descriptor.")
}
- if event.Events&syscall.EPOLLIN != 0 {
+ if event.Events&unix.EPOLLIN != 0 {
// This is a regular wakeup, so we have to clear the buffer.
err := poller.clearWake()
if err != nil {
// Close the write end of the poller.
func (poller *fdPoller) wake() error {
buf := make([]byte, 1)
- n, errno := syscall.Write(poller.pipe[1], buf)
+ n, errno := unix.Write(poller.pipe[1], buf)
if n == -1 {
- if errno == syscall.EAGAIN {
+ if errno == unix.EAGAIN {
// Buffer is full, poller will wake.
return nil
}
func (poller *fdPoller) clearWake() error {
// You have to be woken up a LOT in order to get to 100!
buf := make([]byte, 100)
- n, errno := syscall.Read(poller.pipe[0], buf)
+ n, errno := unix.Read(poller.pipe[0], buf)
if n == -1 {
- if errno == syscall.EAGAIN {
+ if errno == unix.EAGAIN {
// Buffer is empty, someone else cleared our wake.
return nil
}
// Close all poller file descriptors, but not the one passed to it.
func (poller *fdPoller) close() {
if poller.pipe[1] != -1 {
- syscall.Close(poller.pipe[1])
+ unix.Close(poller.pipe[1])
}
if poller.pipe[0] != -1 {
- syscall.Close(poller.pipe[0])
+ unix.Close(poller.pipe[0])
}
if poller.epfd != -1 {
- syscall.Close(poller.epfd)
+ unix.Close(poller.epfd)
}
}
package fsnotify
import (
- "syscall"
"testing"
"time"
+
+ "golang.org/x/sys/unix"
)
type testFd [2]int
func makeTestFd(t *testing.T) testFd {
var tfd testFd
- errno := syscall.Pipe(tfd[:])
+ errno := unix.Pipe(tfd[:])
if errno != nil {
t.Fatalf("Failed to create pipe: %v", errno)
}
}
func (tfd testFd) closeWrite(t *testing.T) {
- errno := syscall.Close(tfd[1])
+ errno := unix.Close(tfd[1])
if errno != nil {
t.Fatalf("Failed to close write end of pipe: %v", errno)
}
func (tfd testFd) put(t *testing.T) {
buf := make([]byte, 10)
- _, errno := syscall.Write(tfd[1], buf)
+ _, errno := unix.Write(tfd[1], buf)
if errno != nil {
t.Fatalf("Failed to write to pipe: %v", errno)
}
func (tfd testFd) get(t *testing.T) {
buf := make([]byte, 10)
- _, errno := syscall.Read(tfd[0], buf)
+ _, errno := unix.Read(tfd[0], buf)
if errno != nil {
t.Fatalf("Failed to read from pipe: %v", errno)
}
}
func (tfd testFd) close() {
- syscall.Close(tfd[1])
- syscall.Close(tfd[0])
+ unix.Close(tfd[1])
+ unix.Close(tfd[0])
}
func makePoller(t *testing.T) (testFd, *fdPoller) {
func TestPollerWithBadFd(t *testing.T) {
_, err := newFdPoller(-1)
- if err != syscall.EBADF {
+ if err != unix.EBADF {
t.Fatalf("Expected EBADF, got: %v", err)
}
}
"fmt"
"os"
"path/filepath"
- "syscall"
"testing"
"time"
+
+ "golang.org/x/sys/unix"
)
func TestInotifyCloseRightAway(t *testing.T) {
t.Fatalf("Failed to create watcher")
}
- // Close immediately; it won't even reach the first syscall.Read.
+ // Close immediately; it won't even reach the first unix.Read.
w.Close()
// Wait for the close to complete.
t.Fatalf("Failed to create watcher")
}
- // Wait until readEvents has reached syscall.Read, and Close.
+ // Wait until readEvents has reached unix.Read, and Close.
<-time.After(50 * time.Millisecond)
w.Close()
}
w.Add(testDir)
- // Wait until readEvents has reached syscall.Read, and Close.
+ // Wait until readEvents has reached unix.Read, and Close.
<-time.After(50 * time.Millisecond)
w.Close()
}
// At this point, we've received one event, so the goroutine is ready.
- // It's also blocking on syscall.Read.
+ // It's also blocking on unix.Read.
// Now we try to swap the file descriptor under its nose.
w.Close()
w, err = NewWatcher()
for {
select {
case <-time.After(5 * time.Millisecond):
- err := proc.Signal(syscall.SIGUSR1)
+ err := proc.Signal(unix.SIGUSR1)
if err != nil {
t.Fatalf("Signal failed: %v", err)
}
import (
"os"
"path/filepath"
- "syscall"
"testing"
"time"
+
+ "golang.org/x/sys/unix"
)
// testExchangedataForWatcher tests the watcher with the exchangedata operation on OS X.
createAndSyncFile(t, intermediate)
// 1. Swap
- if err := syscall.Exchangedata(intermediate, resolved, 0); err != nil {
+ if err := unix.Exchangedata(intermediate, resolved, 0); err != nil {
t.Fatalf("[%d] exchangedata failed: %s", i, err)
}
"os"
"path/filepath"
"sync"
- "syscall"
"time"
+
+ "golang.org/x/sys/unix"
)
// Watcher watches a set of files, delivering events to a channel.
return fmt.Errorf("can't remove non-existent kevent watch for: %s", name)
}
- const registerRemove = syscall.EV_DELETE
+ const registerRemove = unix.EV_DELETE
if err := register(w.kq, []int{watchfd}, registerRemove, 0); err != nil {
return err
}
- syscall.Close(watchfd)
+ unix.Close(watchfd)
w.mu.Lock()
isDir := w.paths[watchfd].isDir
}
// Watch all events (except NOTE_EXTEND, NOTE_LINK, NOTE_REVOKE)
-const noteAllEvents = syscall.NOTE_DELETE | syscall.NOTE_WRITE | syscall.NOTE_ATTRIB | syscall.NOTE_RENAME
+const noteAllEvents = unix.NOTE_DELETE | unix.NOTE_WRITE | unix.NOTE_ATTRIB | unix.NOTE_RENAME
// keventWaitTime to block on each read from kevent
var keventWaitTime = durationToTimespec(100 * time.Millisecond)
}
}
- watchfd, err = syscall.Open(name, openMode, 0700)
+ watchfd, err = unix.Open(name, openMode, 0700)
if watchfd == -1 {
return "", err
}
isDir = fi.IsDir()
}
- const registerAdd = syscall.EV_ADD | syscall.EV_CLEAR | syscall.EV_ENABLE
+ const registerAdd = unix.EV_ADD | unix.EV_CLEAR | unix.EV_ENABLE
if err := register(w.kq, []int{watchfd}, registerAdd, flags); err != nil {
- syscall.Close(watchfd)
+ unix.Close(watchfd)
return "", err
}
// or if it was watched before, but perhaps only a NOTE_DELETE (watchDirectoryFiles)
w.mu.Lock()
- watchDir := (flags&syscall.NOTE_WRITE) == syscall.NOTE_WRITE &&
- (!alreadyWatching || (w.dirFlags[name]&syscall.NOTE_WRITE) != syscall.NOTE_WRITE)
+ watchDir := (flags&unix.NOTE_WRITE) == unix.NOTE_WRITE &&
+ (!alreadyWatching || (w.dirFlags[name]&unix.NOTE_WRITE) != unix.NOTE_WRITE)
// Store flags so this watch can be updated later
w.dirFlags[name] = flags
w.mu.Unlock()
// readEvents reads from kqueue and converts the received kevents into
// Event values that it sends down the Events channel.
func (w *Watcher) readEvents() {
- eventBuffer := make([]syscall.Kevent_t, 10)
+ eventBuffer := make([]unix.Kevent_t, 10)
for {
// See if there is a message on the "done" channel
select {
case <-w.done:
- err := syscall.Close(w.kq)
+ err := unix.Close(w.kq)
if err != nil {
w.Errors <- err
}
// Get new events
kevents, err := read(w.kq, eventBuffer, &keventWaitTime)
// EINTR is okay, the syscall was interrupted before timeout expired.
- if err != nil && err != syscall.EINTR {
+ if err != nil && err != unix.EINTR {
w.Errors <- err
continue
}
// newEvent returns an platform-independent Event based on kqueue Fflags.
func newEvent(name string, mask uint32) Event {
e := Event{Name: name}
- if mask&syscall.NOTE_DELETE == syscall.NOTE_DELETE {
+ if mask&unix.NOTE_DELETE == unix.NOTE_DELETE {
e.Op |= Remove
}
- if mask&syscall.NOTE_WRITE == syscall.NOTE_WRITE {
+ if mask&unix.NOTE_WRITE == unix.NOTE_WRITE {
e.Op |= Write
}
- if mask&syscall.NOTE_RENAME == syscall.NOTE_RENAME {
+ if mask&unix.NOTE_RENAME == unix.NOTE_RENAME {
e.Op |= Rename
}
- if mask&syscall.NOTE_ATTRIB == syscall.NOTE_ATTRIB {
+ if mask&unix.NOTE_ATTRIB == unix.NOTE_ATTRIB {
e.Op |= Chmod
}
return e
flags := w.dirFlags[name]
w.mu.Unlock()
- flags |= syscall.NOTE_DELETE | syscall.NOTE_RENAME
+ flags |= unix.NOTE_DELETE | unix.NOTE_RENAME
return w.addWatch(name, flags)
}
// kqueue creates a new kernel event queue and returns a descriptor.
func kqueue() (kq int, err error) {
- kq, err = syscall.Kqueue()
+ kq, err = unix.Kqueue()
if kq == -1 {
return kq, err
}
// register events with the queue
func register(kq int, fds []int, flags int, fflags uint32) error {
- changes := make([]syscall.Kevent_t, len(fds))
+ changes := make([]unix.Kevent_t, len(fds))
for i, fd := range fds {
// SetKevent converts int to the platform-specific types:
- syscall.SetKevent(&changes[i], fd, syscall.EVFILT_VNODE, flags)
+ unix.SetKevent(&changes[i], fd, unix.EVFILT_VNODE, flags)
changes[i].Fflags = fflags
}
// register the events
- success, err := syscall.Kevent(kq, changes, nil, nil)
+ success, err := unix.Kevent(kq, changes, nil, nil)
if success == -1 {
return err
}
// read retrieves pending events, or waits until an event occurs.
// A timeout of nil blocks indefinitely, while 0 polls the queue.
-func read(kq int, events []syscall.Kevent_t, timeout *syscall.Timespec) ([]syscall.Kevent_t, error) {
- n, err := syscall.Kevent(kq, nil, events, timeout)
+func read(kq int, events []unix.Kevent_t, timeout *unix.Timespec) ([]unix.Kevent_t, error) {
+ n, err := unix.Kevent(kq, nil, events, timeout)
if err != nil {
return nil, err
}
}
// durationToTimespec prepares a timeout value
-func durationToTimespec(d time.Duration) syscall.Timespec {
- return syscall.NsecToTimespec(d.Nanoseconds())
+func durationToTimespec(d time.Duration) unix.Timespec {
+ return unix.NsecToTimespec(d.Nanoseconds())
}
package fsnotify
-import "syscall"
+import "golang.org/x/sys/unix"
-const openMode = syscall.O_NONBLOCK | syscall.O_RDONLY
+const openMode = unix.O_NONBLOCK | unix.O_RDONLY
package fsnotify
-import "syscall"
+import "golang.org/x/sys/unix"
// note: this constant is not defined on BSD
-const openMode = syscall.O_EVTONLY
+const openMode = unix.O_EVTONLY