| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582 | 
							- // Copyright (c) 2014-2015 The Notify Authors. All rights reserved.
 
- // Use of this source code is governed by the MIT license that can be
 
- // found in the LICENSE file.
 
- // +build windows
 
- package notify
 
- import (
 
- 	"errors"
 
- 	"runtime"
 
- 	"sync"
 
- 	"sync/atomic"
 
- 	"syscall"
 
- 	"unsafe"
 
- )
 
- // readBufferSize defines the size of an array in which read statuses are stored.
 
- // The buffer have to be DWORD-aligned and, if notify is used in monitoring a
 
- // directory over the network, its size must not be greater than 64KB. Each of
 
- // watched directories uses its own buffer for storing events.
 
- const readBufferSize = 4096
 
- // Since all operations which go through the Windows completion routine are done
 
- // asynchronously, filter may set one of the constants belor. They were defined
 
- // in order to distinguish whether current folder should be re-registered in
 
- // ReadDirectoryChangesW function or some control operations need to be executed.
 
- const (
 
- 	stateRewatch uint32 = 1 << (28 + iota)
 
- 	stateUnwatch
 
- 	stateCPClose
 
- )
 
- // Filter used in current implementation was split into four segments:
 
- //  - bits  0-11 store ReadDirectoryChangesW filters,
 
- //  - bits 12-19 store File notify actions,
 
- //  - bits 20-27 store notify specific events and flags,
 
- //  - bits 28-31 store states which are used in loop's FSM.
 
- // Constants below are used as masks to retrieve only specific filter parts.
 
- const (
 
- 	onlyNotifyChanges uint32 = 0x00000FFF
 
- 	onlyNGlobalEvents uint32 = 0x0FF00000
 
- 	onlyMachineStates uint32 = 0xF0000000
 
- )
 
- // grip represents a single watched directory. It stores the data required by
 
- // ReadDirectoryChangesW function. Only the filter, recursive, and handle members
 
- // may by modified by watcher implementation. Rest of the them have to remain
 
- // constant since they are used by Windows completion routine. This indicates that
 
- // grip can be removed only when all operations on the file handle are finished.
 
- type grip struct {
 
- 	handle    syscall.Handle
 
- 	filter    uint32
 
- 	recursive bool
 
- 	pathw     []uint16
 
- 	buffer    [readBufferSize]byte
 
- 	parent    *watched
 
- 	ovlapped  *overlappedEx
 
- }
 
- // overlappedEx stores information used in asynchronous input and output.
 
- // Additionally, overlappedEx contains a pointer to 'grip' item which is used in
 
- // order to gather the structure in which the overlappedEx object was created.
 
- type overlappedEx struct {
 
- 	syscall.Overlapped
 
- 	parent *grip
 
- }
 
- // newGrip creates a new file handle that can be used in overlapped operations.
 
- // Then, the handle is associated with I/O completion port 'cph' and its value
 
- // is stored in newly created 'grip' object.
 
- func newGrip(cph syscall.Handle, parent *watched, filter uint32) (*grip, error) {
 
- 	g := &grip{
 
- 		handle:    syscall.InvalidHandle,
 
- 		filter:    filter,
 
- 		recursive: parent.recursive,
 
- 		pathw:     parent.pathw,
 
- 		parent:    parent,
 
- 		ovlapped:  &overlappedEx{},
 
- 	}
 
- 	if err := g.register(cph); err != nil {
 
- 		return nil, err
 
- 	}
 
- 	g.ovlapped.parent = g
 
- 	return g, nil
 
- }
 
- // NOTE : Thread safe
 
- func (g *grip) register(cph syscall.Handle) (err error) {
 
- 	if g.handle, err = syscall.CreateFile(
 
- 		&g.pathw[0],
 
- 		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,
 
- 	); err != nil {
 
- 		return
 
- 	}
 
- 	if _, err = syscall.CreateIoCompletionPort(g.handle, cph, 0, 0); err != nil {
 
- 		syscall.CloseHandle(g.handle)
 
- 		return
 
- 	}
 
- 	return g.readDirChanges()
 
- }
 
- // readDirChanges tells the system to store file change information in grip's
 
- // buffer. Directory changes that occur between calls to this function are added
 
- // to the buffer and then, returned with the next call.
 
- func (g *grip) readDirChanges() error {
 
- 	return syscall.ReadDirectoryChanges(
 
- 		g.handle,
 
- 		&g.buffer[0],
 
- 		uint32(unsafe.Sizeof(g.buffer)),
 
- 		g.recursive,
 
- 		encode(g.filter),
 
- 		nil,
 
- 		(*syscall.Overlapped)(unsafe.Pointer(g.ovlapped)),
 
- 		0,
 
- 	)
 
- }
 
- // encode transforms a generic filter, which contains platform independent and
 
- // implementation specific bit fields, to value that can be used as NotifyFilter
 
- // parameter in ReadDirectoryChangesW function.
 
- func encode(filter uint32) uint32 {
 
- 	e := Event(filter & (onlyNGlobalEvents | onlyNotifyChanges))
 
- 	if e&dirmarker != 0 {
 
- 		return uint32(FileNotifyChangeDirName)
 
- 	}
 
- 	if e&Create != 0 {
 
- 		e = (e ^ Create) | FileNotifyChangeFileName
 
- 	}
 
- 	if e&Remove != 0 {
 
- 		e = (e ^ Remove) | FileNotifyChangeFileName
 
- 	}
 
- 	if e&Write != 0 {
 
- 		e = (e ^ Write) | FileNotifyChangeAttributes | FileNotifyChangeSize |
 
- 			FileNotifyChangeCreation | FileNotifyChangeSecurity
 
- 	}
 
- 	if e&Rename != 0 {
 
- 		e = (e ^ Rename) | FileNotifyChangeFileName
 
- 	}
 
- 	return uint32(e)
 
- }
 
- // watched is made in order to check whether an action comes from a directory or
 
- // file. This approach requires two file handlers per single monitored folder. The
 
- // second grip handles actions which include creating or deleting a directory. If
 
- // these processes are not monitored, only the first grip is created.
 
- type watched struct {
 
- 	filter    uint32
 
- 	recursive bool
 
- 	count     uint8
 
- 	pathw     []uint16
 
- 	digrip    [2]*grip
 
- }
 
- // newWatched creates a new watched instance. It splits the filter variable into
 
- // two parts. The first part is responsible for watching all events which can be
 
- // created for a file in watched directory structure and the second one watches
 
- // only directory Create/Remove actions. If all operations succeed, the Create
 
- // message is sent to I/O completion port queue for further processing.
 
- func newWatched(cph syscall.Handle, filter uint32, recursive bool,
 
- 	path string) (wd *watched, err error) {
 
- 	wd = &watched{
 
- 		filter:    filter,
 
- 		recursive: recursive,
 
- 	}
 
- 	if wd.pathw, err = syscall.UTF16FromString(path); err != nil {
 
- 		return
 
- 	}
 
- 	if err = wd.recreate(cph); err != nil {
 
- 		return
 
- 	}
 
- 	return wd, nil
 
- }
 
- // TODO : doc
 
- func (wd *watched) recreate(cph syscall.Handle) (err error) {
 
- 	filefilter := wd.filter &^ uint32(FileNotifyChangeDirName)
 
- 	if err = wd.updateGrip(0, cph, filefilter == 0, filefilter); err != nil {
 
- 		return
 
- 	}
 
- 	dirfilter := wd.filter & uint32(FileNotifyChangeDirName|Create|Remove)
 
- 	if err = wd.updateGrip(1, cph, dirfilter == 0, wd.filter|uint32(dirmarker)); err != nil {
 
- 		return
 
- 	}
 
- 	wd.filter &^= onlyMachineStates
 
- 	return
 
- }
 
- // TODO : doc
 
- func (wd *watched) updateGrip(idx int, cph syscall.Handle, reset bool,
 
- 	newflag uint32) (err error) {
 
- 	if reset {
 
- 		wd.digrip[idx] = nil
 
- 	} else {
 
- 		if wd.digrip[idx] == nil {
 
- 			if wd.digrip[idx], err = newGrip(cph, wd, newflag); err != nil {
 
- 				wd.closeHandle()
 
- 				return
 
- 			}
 
- 		} else {
 
- 			wd.digrip[idx].filter = newflag
 
- 			wd.digrip[idx].recursive = wd.recursive
 
- 			if err = wd.digrip[idx].register(cph); err != nil {
 
- 				wd.closeHandle()
 
- 				return
 
- 			}
 
- 		}
 
- 		wd.count++
 
- 	}
 
- 	return
 
- }
 
- // closeHandle closes handles that are stored in digrip array. Function always
 
- // tries to close all of the handlers before it exits, even when there are errors
 
- // returned from the operating system kernel.
 
- func (wd *watched) closeHandle() (err error) {
 
- 	for _, g := range wd.digrip {
 
- 		if g != nil && g.handle != syscall.InvalidHandle {
 
- 			switch suberr := syscall.CloseHandle(g.handle); {
 
- 			case suberr == nil:
 
- 				g.handle = syscall.InvalidHandle
 
- 			case err == nil:
 
- 				err = suberr
 
- 			}
 
- 		}
 
- 	}
 
- 	return
 
- }
 
- // watcher implements Watcher interface. It stores a set of watched directories.
 
- // All operations which remove watched objects from map `m` must be performed in
 
- // loop goroutine since these structures are used internally by operating system.
 
- type readdcw struct {
 
- 	sync.Mutex
 
- 	m     map[string]*watched
 
- 	cph   syscall.Handle
 
- 	start bool
 
- 	wg    sync.WaitGroup
 
- 	c     chan<- EventInfo
 
- }
 
- // NewWatcher creates new non-recursive watcher backed by ReadDirectoryChangesW.
 
- func newWatcher(c chan<- EventInfo) watcher {
 
- 	r := &readdcw{
 
- 		m:   make(map[string]*watched),
 
- 		cph: syscall.InvalidHandle,
 
- 		c:   c,
 
- 	}
 
- 	runtime.SetFinalizer(r, func(r *readdcw) {
 
- 		if r.cph != syscall.InvalidHandle {
 
- 			syscall.CloseHandle(r.cph)
 
- 		}
 
- 	})
 
- 	return r
 
- }
 
- // Watch implements notify.Watcher interface.
 
- func (r *readdcw) Watch(path string, event Event) error {
 
- 	return r.watch(path, event, false)
 
- }
 
- // RecursiveWatch implements notify.RecursiveWatcher interface.
 
- func (r *readdcw) RecursiveWatch(path string, event Event) error {
 
- 	return r.watch(path, event, true)
 
- }
 
- // watch inserts a directory to the group of watched folders. If watched folder
 
- // already exists, function tries to rewatch it with new filters(NOT VALID). Moreover,
 
- // watch starts the main event loop goroutine when called for the first time.
 
- func (r *readdcw) watch(path string, event Event, recursive bool) (err error) {
 
- 	if event&^(All|fileNotifyChangeAll) != 0 {
 
- 		return errors.New("notify: unknown event")
 
- 	}
 
- 	r.Lock()
 
- 	wd, ok := r.m[path]
 
- 	r.Unlock()
 
- 	if !ok {
 
- 		if err = r.lazyinit(); err != nil {
 
- 			return
 
- 		}
 
- 		r.Lock()
 
- 		defer r.Unlock()
 
- 		if wd, ok = r.m[path]; ok {
 
- 			dbgprint("watch: exists already")
 
- 			return
 
- 		}
 
- 		if wd, err = newWatched(r.cph, uint32(event), recursive, path); err != nil {
 
- 			return
 
- 		}
 
- 		r.m[path] = wd
 
- 		dbgprint("watch: new watch added")
 
- 	} else {
 
- 		dbgprint("watch: exists already")
 
- 	}
 
- 	return nil
 
- }
 
- // lazyinit creates an I/O completion port and starts the main event processing
 
- // loop. This method uses Double-Checked Locking optimization.
 
- func (r *readdcw) lazyinit() (err error) {
 
- 	invalid := uintptr(syscall.InvalidHandle)
 
- 	if atomic.LoadUintptr((*uintptr)(&r.cph)) == invalid {
 
- 		r.Lock()
 
- 		defer r.Unlock()
 
- 		if atomic.LoadUintptr((*uintptr)(&r.cph)) == invalid {
 
- 			cph := syscall.InvalidHandle
 
- 			if cph, err = syscall.CreateIoCompletionPort(cph, 0, 0, 0); err != nil {
 
- 				return
 
- 			}
 
- 			r.cph, r.start = cph, true
 
- 			go r.loop()
 
- 		}
 
- 	}
 
- 	return
 
- }
 
- // TODO(pknap) : doc
 
- func (r *readdcw) loop() {
 
- 	var n, key uint32
 
- 	var overlapped *syscall.Overlapped
 
- 	for {
 
- 		err := syscall.GetQueuedCompletionStatus(r.cph, &n, &key, &overlapped, syscall.INFINITE)
 
- 		if key == stateCPClose {
 
- 			r.Lock()
 
- 			handle := r.cph
 
- 			r.cph = syscall.InvalidHandle
 
- 			r.Unlock()
 
- 			syscall.CloseHandle(handle)
 
- 			r.wg.Done()
 
- 			return
 
- 		}
 
- 		if overlapped == nil {
 
- 			// TODO: check key == rewatch delete or 0(panic)
 
- 			continue
 
- 		}
 
- 		overEx := (*overlappedEx)(unsafe.Pointer(overlapped))
 
- 		if n != 0 {
 
- 			r.loopevent(n, overEx)
 
- 			if err = overEx.parent.readDirChanges(); err != nil {
 
- 				// TODO: error handling
 
- 			}
 
- 		}
 
- 		r.loopstate(overEx)
 
- 	}
 
- }
 
- // TODO(pknap) : doc
 
- func (r *readdcw) loopstate(overEx *overlappedEx) {
 
- 	r.Lock()
 
- 	defer r.Unlock()
 
- 	filter := overEx.parent.parent.filter
 
- 	if filter&onlyMachineStates == 0 {
 
- 		return
 
- 	}
 
- 	if overEx.parent.parent.count--; overEx.parent.parent.count == 0 {
 
- 		switch filter & onlyMachineStates {
 
- 		case stateRewatch:
 
- 			dbgprint("loopstate rewatch")
 
- 			overEx.parent.parent.recreate(r.cph)
 
- 		case stateUnwatch:
 
- 			dbgprint("loopstate unwatch")
 
- 			delete(r.m, syscall.UTF16ToString(overEx.parent.pathw))
 
- 		case stateCPClose:
 
- 		default:
 
- 			panic(`notify: windows loopstate logic error`)
 
- 		}
 
- 	}
 
- }
 
- // TODO(pknap) : doc
 
- func (r *readdcw) loopevent(n uint32, overEx *overlappedEx) {
 
- 	events := []*event{}
 
- 	var currOffset uint32
 
- 	for {
 
- 		raw := (*syscall.FileNotifyInformation)(unsafe.Pointer(&overEx.parent.buffer[currOffset]))
 
- 		name := syscall.UTF16ToString((*[syscall.MAX_LONG_PATH]uint16)(unsafe.Pointer(&raw.FileName))[:raw.FileNameLength>>1])
 
- 		events = append(events, &event{
 
- 			pathw:  overEx.parent.pathw,
 
- 			filter: overEx.parent.filter,
 
- 			action: raw.Action,
 
- 			name:   name,
 
- 		})
 
- 		if raw.NextEntryOffset == 0 {
 
- 			break
 
- 		}
 
- 		if currOffset += raw.NextEntryOffset; currOffset >= n {
 
- 			break
 
- 		}
 
- 	}
 
- 	r.send(events)
 
- }
 
- // TODO(pknap) : doc
 
- func (r *readdcw) send(es []*event) {
 
- 	for _, e := range es {
 
- 		var syse Event
 
- 		if e.e, syse = decode(e.filter, e.action); e.e == 0 && syse == 0 {
 
- 			continue
 
- 		}
 
- 		switch {
 
- 		case e.action == syscall.FILE_ACTION_MODIFIED:
 
- 			e.ftype = fTypeUnknown
 
- 		case e.filter&uint32(dirmarker) != 0:
 
- 			e.ftype = fTypeDirectory
 
- 		default:
 
- 			e.ftype = fTypeFile
 
- 		}
 
- 		switch {
 
- 		case e.e == 0:
 
- 			e.e = syse
 
- 		case syse != 0:
 
- 			r.c <- &event{
 
- 				pathw:  e.pathw,
 
- 				name:   e.name,
 
- 				ftype:  e.ftype,
 
- 				action: e.action,
 
- 				filter: e.filter,
 
- 				e:      syse,
 
- 			}
 
- 		}
 
- 		r.c <- e
 
- 	}
 
- }
 
- // Rewatch implements notify.Rewatcher interface.
 
- func (r *readdcw) Rewatch(path string, oldevent, newevent Event) error {
 
- 	return r.rewatch(path, uint32(oldevent), uint32(newevent), false)
 
- }
 
- // RecursiveRewatch implements notify.RecursiveRewatcher interface.
 
- func (r *readdcw) RecursiveRewatch(oldpath, newpath string, oldevent,
 
- 	newevent Event) error {
 
- 	if oldpath != newpath {
 
- 		if err := r.unwatch(oldpath); err != nil {
 
- 			return err
 
- 		}
 
- 		return r.watch(newpath, newevent, true)
 
- 	}
 
- 	return r.rewatch(newpath, uint32(oldevent), uint32(newevent), true)
 
- }
 
- // TODO : (pknap) doc.
 
- func (r *readdcw) rewatch(path string, oldevent, newevent uint32, recursive bool) (err error) {
 
- 	if Event(newevent)&^(All|fileNotifyChangeAll) != 0 {
 
- 		return errors.New("notify: unknown event")
 
- 	}
 
- 	var wd *watched
 
- 	r.Lock()
 
- 	defer r.Unlock()
 
- 	if wd, err = r.nonStateWatchedLocked(path); err != nil {
 
- 		return
 
- 	}
 
- 	if wd.filter&(onlyNotifyChanges|onlyNGlobalEvents) != oldevent {
 
- 		panic(`notify: windows re-watcher logic error`)
 
- 	}
 
- 	wd.filter = stateRewatch | newevent
 
- 	wd.recursive, recursive = recursive, wd.recursive
 
- 	if err = wd.closeHandle(); err != nil {
 
- 		wd.filter = oldevent
 
- 		wd.recursive = recursive
 
- 		return
 
- 	}
 
- 	return
 
- }
 
- // TODO : pknap
 
- func (r *readdcw) nonStateWatchedLocked(path string) (wd *watched, err error) {
 
- 	wd, ok := r.m[path]
 
- 	if !ok || wd == nil {
 
- 		err = errors.New(`notify: ` + path + ` path is unwatched`)
 
- 		return
 
- 	}
 
- 	if wd.filter&onlyMachineStates != 0 {
 
- 		err = errors.New(`notify: another re/unwatching operation in progress`)
 
- 		return
 
- 	}
 
- 	return
 
- }
 
- // Unwatch implements notify.Watcher interface.
 
- func (r *readdcw) Unwatch(path string) error {
 
- 	return r.unwatch(path)
 
- }
 
- // RecursiveUnwatch implements notify.RecursiveWatcher interface.
 
- func (r *readdcw) RecursiveUnwatch(path string) error {
 
- 	return r.unwatch(path)
 
- }
 
- // TODO : pknap
 
- func (r *readdcw) unwatch(path string) (err error) {
 
- 	var wd *watched
 
- 	r.Lock()
 
- 	defer r.Unlock()
 
- 	if wd, err = r.nonStateWatchedLocked(path); err != nil {
 
- 		return
 
- 	}
 
- 	wd.filter |= stateUnwatch
 
- 	if err = wd.closeHandle(); err != nil {
 
- 		wd.filter &^= stateUnwatch
 
- 		return
 
- 	}
 
- 	if _, attrErr := syscall.GetFileAttributes(&wd.pathw[0]); attrErr != nil {
 
- 		for _, g := range wd.digrip {
 
- 			if g != nil {
 
- 				dbgprint("unwatch: posting")
 
- 				if err = syscall.PostQueuedCompletionStatus(r.cph, 0, 0, (*syscall.Overlapped)(unsafe.Pointer(g.ovlapped))); err != nil {
 
- 					wd.filter &^= stateUnwatch
 
- 					return
 
- 				}
 
- 			}
 
- 		}
 
- 	}
 
- 	return
 
- }
 
- // Close resets the whole watcher object, closes all existing file descriptors,
 
- // and sends stateCPClose state as completion key to the main watcher's loop.
 
- func (r *readdcw) Close() (err error) {
 
- 	r.Lock()
 
- 	if !r.start {
 
- 		r.Unlock()
 
- 		return nil
 
- 	}
 
- 	for _, wd := range r.m {
 
- 		wd.filter &^= onlyMachineStates
 
- 		wd.filter |= stateCPClose
 
- 		if e := wd.closeHandle(); e != nil && err == nil {
 
- 			err = e
 
- 		}
 
- 	}
 
- 	r.start = false
 
- 	r.Unlock()
 
- 	r.wg.Add(1)
 
- 	if e := syscall.PostQueuedCompletionStatus(r.cph, 0, stateCPClose, nil); e != nil && err == nil {
 
- 		return e
 
- 	}
 
- 	r.wg.Wait()
 
- 	return
 
- }
 
- // decode creates a notify event from both non-raw filter and action which was
 
- // returned from completion routine. Function may return Event(0) in case when
 
- // filter was replaced by a new value which does not contain fields that are
 
- // valid with passed action.
 
- func decode(filter, action uint32) (Event, Event) {
 
- 	switch action {
 
- 	case syscall.FILE_ACTION_ADDED:
 
- 		return gensys(filter, Create, FileActionAdded)
 
- 	case syscall.FILE_ACTION_REMOVED:
 
- 		return gensys(filter, Remove, FileActionRemoved)
 
- 	case syscall.FILE_ACTION_MODIFIED:
 
- 		return gensys(filter, Write, FileActionModified)
 
- 	case syscall.FILE_ACTION_RENAMED_OLD_NAME:
 
- 		return gensys(filter, Rename, FileActionRenamedOldName)
 
- 	case syscall.FILE_ACTION_RENAMED_NEW_NAME:
 
- 		return gensys(filter, Rename, FileActionRenamedNewName)
 
- 	}
 
- 	panic(`notify: cannot decode internal mask`)
 
- }
 
- // gensys decides whether the Windows action, system-independent event or both
 
- // of them should be returned. Since the grip's filter may be atomically changed
 
- // during watcher lifetime, it is possible that neither Windows nor notify masks
 
- // are watched by the user when this function is called.
 
- func gensys(filter uint32, ge, se Event) (gene, syse Event) {
 
- 	isdir := filter&uint32(dirmarker) != 0
 
- 	if isdir && filter&uint32(FileNotifyChangeDirName) != 0 ||
 
- 		!isdir && filter&uint32(FileNotifyChangeFileName) != 0 ||
 
- 		filter&uint32(fileNotifyChangeModified) != 0 {
 
- 		syse = se
 
- 	}
 
- 	if filter&uint32(ge) != 0 {
 
- 		gene = ge
 
- 	}
 
- 	return
 
- }
 
 
  |