mirror of
https://github.com/golang/go
synced 2024-11-21 22:34:48 -07:00
exp/winfsnotify: filesystem watcher for Windows
R=rsc, alex.brainman, bradfitz CC=bsiegert, go.peter.90, golang-dev https://golang.org/cl/4188047
This commit is contained in:
parent
033585d675
commit
7ecf6c997e
11
src/pkg/exp/winfsnotify/Makefile
Normal file
11
src/pkg/exp/winfsnotify/Makefile
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
# 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
|
569
src/pkg/exp/winfsnotify/winfsnotify.go
Normal file
569
src/pkg/exp/winfsnotify/winfsnotify.go
Normal file
@ -0,0 +1,569 @@
|
|||||||
|
// 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 allows the user to receive
|
||||||
|
// file system event notifications on Windows.
|
||||||
|
package winfsnotify
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"runtime"
|
||||||
|
"syscall"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Event is the type of the notification messages
|
||||||
|
// received on the watcher's Event channel.
|
||||||
|
type Event struct {
|
||||||
|
Mask uint32 // Mask of events
|
||||||
|
Cookie uint32 // Unique cookie associating related events (for rename)
|
||||||
|
Name string // File name (optional)
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
opAddWatch = iota
|
||||||
|
opRemoveWatch
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
provisional uint64 = 1 << (32 + iota)
|
||||||
|
)
|
||||||
|
|
||||||
|
type input struct {
|
||||||
|
op int
|
||||||
|
path string
|
||||||
|
flags uint32
|
||||||
|
reply chan os.Error
|
||||||
|
}
|
||||||
|
|
||||||
|
type inode struct {
|
||||||
|
handle syscall.Handle
|
||||||
|
volume uint32
|
||||||
|
index uint64
|
||||||
|
}
|
||||||
|
|
||||||
|
type watch struct {
|
||||||
|
ov syscall.Overlapped
|
||||||
|
ino *inode // i-number
|
||||||
|
path string // Directory path
|
||||||
|
mask uint64 // Directory itself is being watched with these notify flags
|
||||||
|
names map[string]uint64 // Map of names being watched and their notify flags
|
||||||
|
rename string // Remembers the old name while renaming a file
|
||||||
|
buf [4096]byte
|
||||||
|
}
|
||||||
|
|
||||||
|
type indexMap map[uint64]*watch
|
||||||
|
type watchMap map[uint32]indexMap
|
||||||
|
|
||||||
|
// A Watcher waits for and receives event notifications
|
||||||
|
// for a specific set of files and directories.
|
||||||
|
type Watcher struct {
|
||||||
|
port syscall.Handle // Handle to completion port
|
||||||
|
watches watchMap // Map of watches (key: i-number)
|
||||||
|
input chan *input // Inputs to the reader are sent on this channel
|
||||||
|
Event chan *Event // Events are returned on this channel
|
||||||
|
Error chan os.Error // Errors are sent on this channel
|
||||||
|
isClosed bool // Set to true when Close() is first called
|
||||||
|
quit chan chan<- os.Error
|
||||||
|
cookie uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewWatcher creates and returns a Watcher.
|
||||||
|
func NewWatcher() (*Watcher, os.Error) {
|
||||||
|
port, e := syscall.CreateIoCompletionPort(syscall.InvalidHandle, 0, 0, 0)
|
||||||
|
if e != 0 {
|
||||||
|
return nil, os.NewSyscallError("CreateIoCompletionPort", e)
|
||||||
|
}
|
||||||
|
w := &Watcher{
|
||||||
|
port: port,
|
||||||
|
watches: make(watchMap),
|
||||||
|
input: make(chan *input, 1),
|
||||||
|
Event: make(chan *Event, 50),
|
||||||
|
Error: make(chan os.Error),
|
||||||
|
quit: make(chan chan<- os.Error, 1),
|
||||||
|
}
|
||||||
|
go w.readEvents()
|
||||||
|
return w, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close closes a Watcher.
|
||||||
|
// It sends a message to the reader goroutine to quit and removes all watches
|
||||||
|
// associated with the watcher.
|
||||||
|
func (w *Watcher) Close() os.Error {
|
||||||
|
if w.isClosed {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
w.isClosed = true
|
||||||
|
|
||||||
|
// Send "quit" message to the reader goroutine
|
||||||
|
ch := make(chan os.Error)
|
||||||
|
w.quit <- ch
|
||||||
|
if err := w.wakeupReader(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return <-ch
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddWatch adds path to the watched file set.
|
||||||
|
func (w *Watcher) AddWatch(path string, flags uint32) os.Error {
|
||||||
|
if w.isClosed {
|
||||||
|
return os.NewError("watcher already closed")
|
||||||
|
}
|
||||||
|
in := &input{
|
||||||
|
op: opAddWatch,
|
||||||
|
path: filepath.Clean(path),
|
||||||
|
flags: flags,
|
||||||
|
reply: make(chan os.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.
|
||||||
|
func (w *Watcher) Watch(path string) os.Error {
|
||||||
|
return w.AddWatch(path, FS_ALL_EVENTS)
|
||||||
|
}
|
||||||
|
|
||||||
|
// RemoveWatch removes path from the watched file set.
|
||||||
|
func (w *Watcher) RemoveWatch(path string) os.Error {
|
||||||
|
in := &input{
|
||||||
|
op: opRemoveWatch,
|
||||||
|
path: filepath.Clean(path),
|
||||||
|
reply: make(chan os.Error),
|
||||||
|
}
|
||||||
|
w.input <- in
|
||||||
|
if err := w.wakeupReader(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return <-in.reply
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *Watcher) wakeupReader() os.Error {
|
||||||
|
e := syscall.PostQueuedCompletionStatus(w.port, 0, 0, nil)
|
||||||
|
if e != 0 {
|
||||||
|
return os.NewSyscallError("PostQueuedCompletionStatus", e)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getDir(pathname string) (dir string, err os.Error) {
|
||||||
|
attr, e := syscall.GetFileAttributes(syscall.StringToUTF16Ptr(pathname))
|
||||||
|
if e != 0 {
|
||||||
|
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 os.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 != 0 {
|
||||||
|
return nil, os.NewSyscallError("CreateFile", e)
|
||||||
|
}
|
||||||
|
var fi syscall.ByHandleFileInformation
|
||||||
|
if e = syscall.GetFileInformationByHandle(h, &fi); e != 0 {
|
||||||
|
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.
|
||||||
|
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.
|
||||||
|
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.
|
||||||
|
func (w *Watcher) addWatch(pathname string, flags uint64) os.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 != 0 {
|
||||||
|
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.
|
||||||
|
func (w *Watcher) removeWatch(pathname string) os.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.
|
||||||
|
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.
|
||||||
|
func (w *Watcher) startRead(watch *watch) os.Error {
|
||||||
|
if e := syscall.CancelIo(watch.ino.handle); e != 0 {
|
||||||
|
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 != 0 {
|
||||||
|
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 != 0 {
|
||||||
|
err := os.NewSyscallError("ReadDirectoryChanges", e)
|
||||||
|
if e == syscall.ERROR_ACCESS_DENIED && watch.mask&provisional == 0 {
|
||||||
|
// Watched directory was probably removed
|
||||||
|
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
|
||||||
|
// received events into Event objects and sends them via the Event channel.
|
||||||
|
// Entry point to the I/O thread.
|
||||||
|
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 os.Error
|
||||||
|
if e := syscall.CloseHandle(w.port); e != 0 {
|
||||||
|
err = os.NewSyscallError("CloseHandle", e)
|
||||||
|
}
|
||||||
|
close(w.Event)
|
||||||
|
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.removeWatch(in.path)
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
switch e {
|
||||||
|
case syscall.ERROR_ACCESS_DENIED:
|
||||||
|
// Watched directory was probably removed
|
||||||
|
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
|
||||||
|
continue
|
||||||
|
default:
|
||||||
|
w.Error <- os.NewSyscallError("GetQueuedCompletionPort", e)
|
||||||
|
continue
|
||||||
|
case 0:
|
||||||
|
}
|
||||||
|
|
||||||
|
var offset uint32
|
||||||
|
for {
|
||||||
|
if n == 0 {
|
||||||
|
w.Event <- &Event{Mask: FS_Q_OVERFLOW}
|
||||||
|
w.Error <- os.NewError("short read in readEvents()")
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
// Point "raw" to the event in the buffer
|
||||||
|
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
|
||||||
|
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 := &Event{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
|
||||||
|
}
|
||||||
|
|
||||||
|
// String formats the event e in the form
|
||||||
|
// "filename: 0xEventMask = FS_ACCESS|FS_ATTRIB_|..."
|
||||||
|
func (e *Event) String() string {
|
||||||
|
var events string
|
||||||
|
m := e.Mask
|
||||||
|
for _, b := range eventBits {
|
||||||
|
if m&b.Value != 0 {
|
||||||
|
m &^= b.Value
|
||||||
|
events += "|" + b.Name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if m != 0 {
|
||||||
|
events += fmt.Sprintf("|%#x", m)
|
||||||
|
}
|
||||||
|
if len(events) > 0 {
|
||||||
|
events = " == " + events[1:]
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("%q: %#x%s", e.Name, e.Mask, events)
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
FS_ONESHOT = 0x80000000
|
||||||
|
FS_ONLYDIR = 0x1000000
|
||||||
|
|
||||||
|
// Events
|
||||||
|
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
|
||||||
|
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"},
|
||||||
|
}
|
124
src/pkg/exp/winfsnotify/winfsnotify_test.go
Normal file
124
src/pkg/exp/winfsnotify/winfsnotify_test.go
Normal file
@ -0,0 +1,124 @@
|
|||||||
|
// 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 GetTimeZoneInformation(tzi *Timezoneinformation) (rc uint32, errno int) [failretval==0xffffffff]
|
||||||
//sys CreateIoCompletionPort(filehandle Handle, cphandle Handle, key uint32, threadcnt uint32) (handle Handle, errno int)
|
//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 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 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 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)
|
//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 VirtualLock(addr uintptr, length uintptr) (errno int)
|
||||||
//sys VirtualUnlock(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 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 CertOpenSystemStore(hprov Handle, name *uint16) (store Handle, errno int) = crypt32.CertOpenSystemStoreW
|
||||||
//sys CertEnumCertificatesInStore(store Handle, prevContext *CertContext) (context *CertContext) = crypt32.CertEnumCertificatesInStore
|
//sys CertEnumCertificatesInStore(store Handle, prevContext *CertContext) (context *CertContext) = crypt32.CertEnumCertificatesInStore
|
||||||
//sys CertCloseStore(store Handle, flags uint32) (errno int) = crypt32.CertCloseStore
|
//sys CertCloseStore(store Handle, flags uint32) (errno int) = crypt32.CertCloseStore
|
||||||
|
@ -45,6 +45,7 @@ var (
|
|||||||
procGetTimeZoneInformation = modkernel32.NewProc("GetTimeZoneInformation")
|
procGetTimeZoneInformation = modkernel32.NewProc("GetTimeZoneInformation")
|
||||||
procCreateIoCompletionPort = modkernel32.NewProc("CreateIoCompletionPort")
|
procCreateIoCompletionPort = modkernel32.NewProc("CreateIoCompletionPort")
|
||||||
procGetQueuedCompletionStatus = modkernel32.NewProc("GetQueuedCompletionStatus")
|
procGetQueuedCompletionStatus = modkernel32.NewProc("GetQueuedCompletionStatus")
|
||||||
|
procPostQueuedCompletionStatus = modkernel32.NewProc("PostQueuedCompletionStatus")
|
||||||
procCancelIo = modkernel32.NewProc("CancelIo")
|
procCancelIo = modkernel32.NewProc("CancelIo")
|
||||||
procCreateProcessW = modkernel32.NewProc("CreateProcessW")
|
procCreateProcessW = modkernel32.NewProc("CreateProcessW")
|
||||||
procOpenProcess = modkernel32.NewProc("OpenProcess")
|
procOpenProcess = modkernel32.NewProc("OpenProcess")
|
||||||
@ -81,6 +82,7 @@ var (
|
|||||||
procVirtualLock = modkernel32.NewProc("VirtualLock")
|
procVirtualLock = modkernel32.NewProc("VirtualLock")
|
||||||
procVirtualUnlock = modkernel32.NewProc("VirtualUnlock")
|
procVirtualUnlock = modkernel32.NewProc("VirtualUnlock")
|
||||||
procTransmitFile = modmswsock.NewProc("TransmitFile")
|
procTransmitFile = modmswsock.NewProc("TransmitFile")
|
||||||
|
procReadDirectoryChangesW = modkernel32.NewProc("ReadDirectoryChangesW")
|
||||||
procCertOpenSystemStoreW = modcrypt32.NewProc("CertOpenSystemStoreW")
|
procCertOpenSystemStoreW = modcrypt32.NewProc("CertOpenSystemStoreW")
|
||||||
procCertEnumCertificatesInStore = modcrypt32.NewProc("CertEnumCertificatesInStore")
|
procCertEnumCertificatesInStore = modcrypt32.NewProc("CertEnumCertificatesInStore")
|
||||||
procCertCloseStore = modcrypt32.NewProc("CertCloseStore")
|
procCertCloseStore = modcrypt32.NewProc("CertCloseStore")
|
||||||
@ -520,6 +522,20 @@ func GetQueuedCompletionStatus(cphandle Handle, qty *uint32, key *uint32, overla
|
|||||||
return
|
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) {
|
func CancelIo(s Handle) (errno int) {
|
||||||
r1, _, e1 := Syscall(procCancelIo.Addr(), 1, uintptr(s), 0, 0)
|
r1, _, e1 := Syscall(procCancelIo.Addr(), 1, uintptr(s), 0, 0)
|
||||||
if int(r1) == 0 {
|
if int(r1) == 0 {
|
||||||
@ -1047,6 +1063,26 @@ func TransmitFile(s Handle, handle Handle, bytesToWrite uint32, bytsPerSend uint
|
|||||||
return
|
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) {
|
func CertOpenSystemStore(hprov Handle, name *uint16) (store Handle, errno int) {
|
||||||
r0, _, e1 := Syscall(procCertOpenSystemStoreW.Addr(), 2, uintptr(hprov), uintptr(unsafe.Pointer(name)), 0)
|
r0, _, e1 := Syscall(procCertOpenSystemStoreW.Addr(), 2, uintptr(hprov), uintptr(unsafe.Pointer(name)), 0)
|
||||||
store = Handle(r0)
|
store = Handle(r0)
|
||||||
|
@ -45,6 +45,7 @@ var (
|
|||||||
procGetTimeZoneInformation = modkernel32.NewProc("GetTimeZoneInformation")
|
procGetTimeZoneInformation = modkernel32.NewProc("GetTimeZoneInformation")
|
||||||
procCreateIoCompletionPort = modkernel32.NewProc("CreateIoCompletionPort")
|
procCreateIoCompletionPort = modkernel32.NewProc("CreateIoCompletionPort")
|
||||||
procGetQueuedCompletionStatus = modkernel32.NewProc("GetQueuedCompletionStatus")
|
procGetQueuedCompletionStatus = modkernel32.NewProc("GetQueuedCompletionStatus")
|
||||||
|
procPostQueuedCompletionStatus = modkernel32.NewProc("PostQueuedCompletionStatus")
|
||||||
procCancelIo = modkernel32.NewProc("CancelIo")
|
procCancelIo = modkernel32.NewProc("CancelIo")
|
||||||
procCreateProcessW = modkernel32.NewProc("CreateProcessW")
|
procCreateProcessW = modkernel32.NewProc("CreateProcessW")
|
||||||
procOpenProcess = modkernel32.NewProc("OpenProcess")
|
procOpenProcess = modkernel32.NewProc("OpenProcess")
|
||||||
@ -81,6 +82,7 @@ var (
|
|||||||
procVirtualLock = modkernel32.NewProc("VirtualLock")
|
procVirtualLock = modkernel32.NewProc("VirtualLock")
|
||||||
procVirtualUnlock = modkernel32.NewProc("VirtualUnlock")
|
procVirtualUnlock = modkernel32.NewProc("VirtualUnlock")
|
||||||
procTransmitFile = modmswsock.NewProc("TransmitFile")
|
procTransmitFile = modmswsock.NewProc("TransmitFile")
|
||||||
|
procReadDirectoryChangesW = modkernel32.NewProc("ReadDirectoryChangesW")
|
||||||
procCertOpenSystemStoreW = modcrypt32.NewProc("CertOpenSystemStoreW")
|
procCertOpenSystemStoreW = modcrypt32.NewProc("CertOpenSystemStoreW")
|
||||||
procCertEnumCertificatesInStore = modcrypt32.NewProc("CertEnumCertificatesInStore")
|
procCertEnumCertificatesInStore = modcrypt32.NewProc("CertEnumCertificatesInStore")
|
||||||
procCertCloseStore = modcrypt32.NewProc("CertCloseStore")
|
procCertCloseStore = modcrypt32.NewProc("CertCloseStore")
|
||||||
@ -520,6 +522,20 @@ func GetQueuedCompletionStatus(cphandle Handle, qty *uint32, key *uint32, overla
|
|||||||
return
|
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) {
|
func CancelIo(s Handle) (errno int) {
|
||||||
r1, _, e1 := Syscall(procCancelIo.Addr(), 1, uintptr(s), 0, 0)
|
r1, _, e1 := Syscall(procCancelIo.Addr(), 1, uintptr(s), 0, 0)
|
||||||
if int(r1) == 0 {
|
if int(r1) == 0 {
|
||||||
@ -1047,6 +1063,26 @@ func TransmitFile(s Handle, handle Handle, bytesToWrite uint32, bytsPerSend uint
|
|||||||
return
|
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) {
|
func CertOpenSystemStore(hprov Handle, name *uint16) (store Handle, errno int) {
|
||||||
r0, _, e1 := Syscall(procCertOpenSystemStoreW.Addr(), 2, uintptr(hprov), uintptr(unsafe.Pointer(name)), 0)
|
r0, _, e1 := Syscall(procCertOpenSystemStoreW.Addr(), 2, uintptr(hprov), uintptr(unsafe.Pointer(name)), 0)
|
||||||
store = Handle(r0)
|
store = Handle(r0)
|
||||||
|
@ -4,6 +4,7 @@ const (
|
|||||||
// Windows errors.
|
// Windows errors.
|
||||||
ERROR_FILE_NOT_FOUND = 2
|
ERROR_FILE_NOT_FOUND = 2
|
||||||
ERROR_PATH_NOT_FOUND = 3
|
ERROR_PATH_NOT_FOUND = 3
|
||||||
|
ERROR_ACCESS_DENIED = 5
|
||||||
ERROR_NO_MORE_FILES = 18
|
ERROR_NO_MORE_FILES = 18
|
||||||
ERROR_BROKEN_PIPE = 109
|
ERROR_BROKEN_PIPE = 109
|
||||||
ERROR_BUFFER_OVERFLOW = 111
|
ERROR_BUFFER_OVERFLOW = 111
|
||||||
@ -54,6 +55,7 @@ const (
|
|||||||
GENERIC_EXECUTE = 0x20000000
|
GENERIC_EXECUTE = 0x20000000
|
||||||
GENERIC_ALL = 0x10000000
|
GENERIC_ALL = 0x10000000
|
||||||
|
|
||||||
|
FILE_LIST_DIRECTORY = 0x00000001
|
||||||
FILE_APPEND_DATA = 0x00000004
|
FILE_APPEND_DATA = 0x00000004
|
||||||
FILE_WRITE_ATTRIBUTES = 0x00000100
|
FILE_WRITE_ATTRIBUTES = 0x00000100
|
||||||
|
|
||||||
@ -75,6 +77,9 @@ const (
|
|||||||
OPEN_ALWAYS = 4
|
OPEN_ALWAYS = 4
|
||||||
TRUNCATE_EXISTING = 5
|
TRUNCATE_EXISTING = 5
|
||||||
|
|
||||||
|
FILE_FLAG_BACKUP_SEMANTICS = 0x02000000
|
||||||
|
FILE_FLAG_OVERLAPPED = 0x40000000
|
||||||
|
|
||||||
HANDLE_FLAG_INHERIT = 0x00000001
|
HANDLE_FLAG_INHERIT = 0x00000001
|
||||||
STARTF_USESTDHANDLES = 0x00000100
|
STARTF_USESTDHANDLES = 0x00000100
|
||||||
STARTF_USESHOWWINDOW = 0x00000001
|
STARTF_USESHOWWINDOW = 0x00000001
|
||||||
@ -133,6 +138,24 @@ const (
|
|||||||
FILE_MAP_EXECUTE = 0x20
|
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 (
|
const (
|
||||||
// wincrypt.h
|
// wincrypt.h
|
||||||
PROV_RSA_FULL = 1
|
PROV_RSA_FULL = 1
|
||||||
@ -191,6 +214,13 @@ type Overlapped struct {
|
|||||||
HEvent Handle
|
HEvent Handle
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type FileNotifyInformation struct {
|
||||||
|
NextEntryOffset uint32
|
||||||
|
Action uint32
|
||||||
|
FileNameLength uint32
|
||||||
|
FileName uint16
|
||||||
|
}
|
||||||
|
|
||||||
type Filetime struct {
|
type Filetime struct {
|
||||||
LowDateTime uint32
|
LowDateTime uint32
|
||||||
HighDateTime uint32
|
HighDateTime uint32
|
||||||
|
Loading…
Reference in New Issue
Block a user