Commit 7ecf6c99 authored by Hector Chu's avatar Hector Chu

exp/winfsnotify: filesystem watcher for Windows

R=rsc, alex.brainman, bradfitz
CC=bsiegert, go.peter.90, golang-dev
https://golang.org/cl/4188047
parent 033585d6
# Copyright 2011 The Go Authors. All rights reserved.
# Use of this source code is governed by a BSD-style
# license that can be found in the LICENSE file.
include ../../../Make.inc
TARG=exp/winfsnotify
GOFILES=\
winfsnotify.go\
include ../../../Make.pkg
This diff is collapsed.
// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package winfsnotify
import (
"os"
"time"
"testing"
)
func expect(t *testing.T, eventstream <-chan *Event, name string, mask uint32) {
t.Logf(`expected: "%s": 0x%x`, name, mask)
select {
case event := <-eventstream:
if event == nil {
t.Fatal("nil event received")
}
t.Logf("received: %s", event)
if event.Name != name || event.Mask != mask {
t.Fatal("did not receive expected event")
}
case <-time.After(1e9):
t.Fatal("timed out waiting for event")
}
}
func TestNotifyEvents(t *testing.T) {
watcher, err := NewWatcher()
if err != nil {
t.Fatalf("NewWatcher() failed: %s", err)
}
testDir := "TestNotifyEvents.testdirectory"
testFile := testDir + "/TestNotifyEvents.testfile"
testFile2 := testFile + ".new"
const mask = FS_ALL_EVENTS & ^(FS_ATTRIB|FS_CLOSE) | FS_IGNORED
// Add a watch for testDir
os.RemoveAll(testDir)
if err = os.Mkdir(testDir, 0777); err != nil {
t.Fatalf("Failed to create test directory", err)
}
defer os.RemoveAll(testDir)
err = watcher.AddWatch(testDir, mask)
if err != nil {
t.Fatalf("Watcher.Watch() failed: %s", err)
}
// Receive errors on the error channel on a separate goroutine
go func() {
for err := range watcher.Error {
t.Fatalf("error received: %s", err)
}
}()
// Create a file
file, err := os.Create(testFile)
if err != nil {
t.Fatalf("creating test file failed: %s", err)
}
expect(t, watcher.Event, testFile, FS_CREATE)
err = watcher.AddWatch(testFile, mask)
if err != nil {
t.Fatalf("Watcher.Watch() failed: %s", err)
}
if _, err = file.WriteString("hello, world"); err != nil {
t.Fatalf("failed to write to test file: %s", err)
}
if err = file.Sync(); err != nil {
t.Fatalf("failed to sync test file: %s", err)
}
expect(t, watcher.Event, testFile, FS_MODIFY)
expect(t, watcher.Event, testFile, FS_MODIFY)
if err = file.Close(); err != nil {
t.Fatalf("failed to close test file: %s", err)
}
if err = os.Rename(testFile, testFile2); err != nil {
t.Fatalf("failed to rename test file: %s", err)
}
expect(t, watcher.Event, testFile, FS_MOVED_FROM)
expect(t, watcher.Event, testFile2, FS_MOVED_TO)
expect(t, watcher.Event, testFile, FS_MOVE_SELF)
if err = os.RemoveAll(testDir); err != nil {
t.Fatalf("failed to remove test directory: %s", err)
}
expect(t, watcher.Event, testFile2, FS_DELETE_SELF)
expect(t, watcher.Event, testFile2, FS_IGNORED)
expect(t, watcher.Event, testFile2, FS_DELETE)
expect(t, watcher.Event, testDir, FS_DELETE_SELF)
expect(t, watcher.Event, testDir, FS_IGNORED)
t.Log("calling Close()")
if err = watcher.Close(); err != nil {
t.Fatalf("failed to close watcher: %s", err)
}
}
func TestNotifyClose(t *testing.T) {
watcher, _ := NewWatcher()
watcher.Close()
done := false
go func() {
watcher.Close()
done = true
}()
time.Sleep(50e6) // 50 ms
if !done {
t.Fatal("double Close() test failed: second Close() call didn't return")
}
err := watcher.Watch("_test")
if err == nil {
t.Fatal("expected error on Watch() after Close(), got nil")
}
}
......@@ -114,6 +114,7 @@ func NewCallback(fn interface{}) uintptr
//sys GetTimeZoneInformation(tzi *Timezoneinformation) (rc uint32, errno int) [failretval==0xffffffff]
//sys CreateIoCompletionPort(filehandle Handle, cphandle Handle, key uint32, threadcnt uint32) (handle Handle, errno int)
//sys GetQueuedCompletionStatus(cphandle Handle, qty *uint32, key *uint32, overlapped **Overlapped, timeout uint32) (errno int)
//sys PostQueuedCompletionStatus(cphandle Handle, qty uint32, key uint32, overlapped *Overlapped) (errno int)
//sys CancelIo(s Handle) (errno int)
//sys CreateProcess(appName *uint16, commandLine *uint16, procSecurity *SecurityAttributes, threadSecurity *SecurityAttributes, inheritHandles bool, creationFlags uint32, env *uint16, currentDir *uint16, startupInfo *StartupInfo, outProcInfo *ProcessInformation) (errno int) = CreateProcessW
//sys OpenProcess(da uint32, inheritHandle bool, pid uint32) (handle Handle, errno int)
......@@ -150,6 +151,7 @@ func NewCallback(fn interface{}) uintptr
//sys VirtualLock(addr uintptr, length uintptr) (errno int)
//sys VirtualUnlock(addr uintptr, length uintptr) (errno int)
//sys TransmitFile(s Handle, handle Handle, bytesToWrite uint32, bytsPerSend uint32, overlapped *Overlapped, transmitFileBuf *TransmitFileBuffers, flags uint32) (errno int) = mswsock.TransmitFile
//sys ReadDirectoryChanges(handle Handle, buf *byte, buflen uint32, watchSubTree bool, mask uint32, retlen *uint32, overlapped *Overlapped, completionRoutine uintptr) (errno int) = kernel32.ReadDirectoryChangesW
//sys CertOpenSystemStore(hprov Handle, name *uint16) (store Handle, errno int) = crypt32.CertOpenSystemStoreW
//sys CertEnumCertificatesInStore(store Handle, prevContext *CertContext) (context *CertContext) = crypt32.CertEnumCertificatesInStore
//sys CertCloseStore(store Handle, flags uint32) (errno int) = crypt32.CertCloseStore
......
......@@ -45,6 +45,7 @@ var (
procGetTimeZoneInformation = modkernel32.NewProc("GetTimeZoneInformation")
procCreateIoCompletionPort = modkernel32.NewProc("CreateIoCompletionPort")
procGetQueuedCompletionStatus = modkernel32.NewProc("GetQueuedCompletionStatus")
procPostQueuedCompletionStatus = modkernel32.NewProc("PostQueuedCompletionStatus")
procCancelIo = modkernel32.NewProc("CancelIo")
procCreateProcessW = modkernel32.NewProc("CreateProcessW")
procOpenProcess = modkernel32.NewProc("OpenProcess")
......@@ -81,6 +82,7 @@ var (
procVirtualLock = modkernel32.NewProc("VirtualLock")
procVirtualUnlock = modkernel32.NewProc("VirtualUnlock")
procTransmitFile = modmswsock.NewProc("TransmitFile")
procReadDirectoryChangesW = modkernel32.NewProc("ReadDirectoryChangesW")
procCertOpenSystemStoreW = modcrypt32.NewProc("CertOpenSystemStoreW")
procCertEnumCertificatesInStore = modcrypt32.NewProc("CertEnumCertificatesInStore")
procCertCloseStore = modcrypt32.NewProc("CertCloseStore")
......@@ -520,6 +522,20 @@ func GetQueuedCompletionStatus(cphandle Handle, qty *uint32, key *uint32, overla
return
}
func PostQueuedCompletionStatus(cphandle Handle, qty uint32, key uint32, overlapped *Overlapped) (errno int) {
r1, _, e1 := Syscall6(procPostQueuedCompletionStatus.Addr(), 4, uintptr(cphandle), uintptr(qty), uintptr(key), uintptr(unsafe.Pointer(overlapped)), 0, 0)
if int(r1) == 0 {
if e1 != 0 {
errno = int(e1)
} else {
errno = EINVAL
}
} else {
errno = 0
}
return
}
func CancelIo(s Handle) (errno int) {
r1, _, e1 := Syscall(procCancelIo.Addr(), 1, uintptr(s), 0, 0)
if int(r1) == 0 {
......@@ -1047,6 +1063,26 @@ func TransmitFile(s Handle, handle Handle, bytesToWrite uint32, bytsPerSend uint
return
}
func ReadDirectoryChanges(handle Handle, buf *byte, buflen uint32, watchSubTree bool, mask uint32, retlen *uint32, overlapped *Overlapped, completionRoutine uintptr) (errno int) {
var _p0 uint32
if watchSubTree {
_p0 = 1
} else {
_p0 = 0
}
r1, _, e1 := Syscall9(procReadDirectoryChangesW.Addr(), 8, uintptr(handle), uintptr(unsafe.Pointer(buf)), uintptr(buflen), uintptr(_p0), uintptr(mask), uintptr(unsafe.Pointer(retlen)), uintptr(unsafe.Pointer(overlapped)), uintptr(completionRoutine), 0)
if int(r1) == 0 {
if e1 != 0 {
errno = int(e1)
} else {
errno = EINVAL
}
} else {
errno = 0
}
return
}
func CertOpenSystemStore(hprov Handle, name *uint16) (store Handle, errno int) {
r0, _, e1 := Syscall(procCertOpenSystemStoreW.Addr(), 2, uintptr(hprov), uintptr(unsafe.Pointer(name)), 0)
store = Handle(r0)
......
......@@ -45,6 +45,7 @@ var (
procGetTimeZoneInformation = modkernel32.NewProc("GetTimeZoneInformation")
procCreateIoCompletionPort = modkernel32.NewProc("CreateIoCompletionPort")
procGetQueuedCompletionStatus = modkernel32.NewProc("GetQueuedCompletionStatus")
procPostQueuedCompletionStatus = modkernel32.NewProc("PostQueuedCompletionStatus")
procCancelIo = modkernel32.NewProc("CancelIo")
procCreateProcessW = modkernel32.NewProc("CreateProcessW")
procOpenProcess = modkernel32.NewProc("OpenProcess")
......@@ -81,6 +82,7 @@ var (
procVirtualLock = modkernel32.NewProc("VirtualLock")
procVirtualUnlock = modkernel32.NewProc("VirtualUnlock")
procTransmitFile = modmswsock.NewProc("TransmitFile")
procReadDirectoryChangesW = modkernel32.NewProc("ReadDirectoryChangesW")
procCertOpenSystemStoreW = modcrypt32.NewProc("CertOpenSystemStoreW")
procCertEnumCertificatesInStore = modcrypt32.NewProc("CertEnumCertificatesInStore")
procCertCloseStore = modcrypt32.NewProc("CertCloseStore")
......@@ -520,6 +522,20 @@ func GetQueuedCompletionStatus(cphandle Handle, qty *uint32, key *uint32, overla
return
}
func PostQueuedCompletionStatus(cphandle Handle, qty uint32, key uint32, overlapped *Overlapped) (errno int) {
r1, _, e1 := Syscall6(procPostQueuedCompletionStatus.Addr(), 4, uintptr(cphandle), uintptr(qty), uintptr(key), uintptr(unsafe.Pointer(overlapped)), 0, 0)
if int(r1) == 0 {
if e1 != 0 {
errno = int(e1)
} else {
errno = EINVAL
}
} else {
errno = 0
}
return
}
func CancelIo(s Handle) (errno int) {
r1, _, e1 := Syscall(procCancelIo.Addr(), 1, uintptr(s), 0, 0)
if int(r1) == 0 {
......@@ -1047,6 +1063,26 @@ func TransmitFile(s Handle, handle Handle, bytesToWrite uint32, bytsPerSend uint
return
}
func ReadDirectoryChanges(handle Handle, buf *byte, buflen uint32, watchSubTree bool, mask uint32, retlen *uint32, overlapped *Overlapped, completionRoutine uintptr) (errno int) {
var _p0 uint32
if watchSubTree {
_p0 = 1
} else {
_p0 = 0
}
r1, _, e1 := Syscall9(procReadDirectoryChangesW.Addr(), 8, uintptr(handle), uintptr(unsafe.Pointer(buf)), uintptr(buflen), uintptr(_p0), uintptr(mask), uintptr(unsafe.Pointer(retlen)), uintptr(unsafe.Pointer(overlapped)), uintptr(completionRoutine), 0)
if int(r1) == 0 {
if e1 != 0 {
errno = int(e1)
} else {
errno = EINVAL
}
} else {
errno = 0
}
return
}
func CertOpenSystemStore(hprov Handle, name *uint16) (store Handle, errno int) {
r0, _, e1 := Syscall(procCertOpenSystemStoreW.Addr(), 2, uintptr(hprov), uintptr(unsafe.Pointer(name)), 0)
store = Handle(r0)
......
......@@ -4,6 +4,7 @@ const (
// Windows errors.
ERROR_FILE_NOT_FOUND = 2
ERROR_PATH_NOT_FOUND = 3
ERROR_ACCESS_DENIED = 5
ERROR_NO_MORE_FILES = 18
ERROR_BROKEN_PIPE = 109
ERROR_BUFFER_OVERFLOW = 111
......@@ -54,6 +55,7 @@ const (
GENERIC_EXECUTE = 0x20000000
GENERIC_ALL = 0x10000000
FILE_LIST_DIRECTORY = 0x00000001
FILE_APPEND_DATA = 0x00000004
FILE_WRITE_ATTRIBUTES = 0x00000100
......@@ -75,6 +77,9 @@ const (
OPEN_ALWAYS = 4
TRUNCATE_EXISTING = 5
FILE_FLAG_BACKUP_SEMANTICS = 0x02000000
FILE_FLAG_OVERLAPPED = 0x40000000
HANDLE_FLAG_INHERIT = 0x00000001
STARTF_USESTDHANDLES = 0x00000100
STARTF_USESHOWWINDOW = 0x00000001
......@@ -133,6 +138,24 @@ const (
FILE_MAP_EXECUTE = 0x20
)
const (
FILE_NOTIFY_CHANGE_FILE_NAME = 1 << iota
FILE_NOTIFY_CHANGE_DIR_NAME
FILE_NOTIFY_CHANGE_ATTRIBUTES
FILE_NOTIFY_CHANGE_SIZE
FILE_NOTIFY_CHANGE_LAST_WRITE
FILE_NOTIFY_CHANGE_LAST_ACCESS
FILE_NOTIFY_CHANGE_CREATION
)
const (
FILE_ACTION_ADDED = iota + 1
FILE_ACTION_REMOVED
FILE_ACTION_MODIFIED
FILE_ACTION_RENAMED_OLD_NAME
FILE_ACTION_RENAMED_NEW_NAME
)
const (
// wincrypt.h
PROV_RSA_FULL = 1
......@@ -191,6 +214,13 @@ type Overlapped struct {
HEvent Handle
}
type FileNotifyInformation struct {
NextEntryOffset uint32
Action uint32
FileNameLength uint32
FileName uint16
}
type Filetime struct {
LowDateTime uint32
HighDateTime uint32
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment