OpenCores
URL https://opencores.org/ocsvn/openrisc/openrisc/trunk

Subversion Repositories openrisc

[/] [openrisc/] [trunk/] [gnu-dev/] [or1k-gcc/] [libgo/] [go/] [exp/] [winfsnotify/] [winfsnotify.go] - Blame information for rev 747

Details | Compare with Previous | View Log

Line No. Rev Author Line
1 747 jeremybenn
// Copyright 2011 The Go Authors. All rights reserved.
2
// Use of this source code is governed by a BSD-style
3
// license that can be found in the LICENSE file.
4
 
5
// +build windows
6
 
7
// Package winfsnotify allows the user to receive
8
// file system event notifications on Windows.
9
package winfsnotify
10
 
11
import (
12
        "errors"
13
        "fmt"
14
        "os"
15
        "path/filepath"
16
        "runtime"
17
        "syscall"
18
        "unsafe"
19
)
20
 
21
// Event is the type of the notification messages
22
// received on the watcher's Event channel.
23
type Event struct {
24
        Mask   uint32 // Mask of events
25
        Cookie uint32 // Unique cookie associating related events (for rename)
26
        Name   string // File name (optional)
27
}
28
 
29
const (
30
        opAddWatch = iota
31
        opRemoveWatch
32
)
33
 
34
const (
35
        provisional uint64 = 1 << (32 + iota)
36
)
37
 
38
type input struct {
39
        op    int
40
        path  string
41
        flags uint32
42
        reply chan error
43
}
44
 
45
type inode struct {
46
        handle syscall.Handle
47
        volume uint32
48
        index  uint64
49
}
50
 
51
type watch struct {
52
        ov     syscall.Overlapped
53
        ino    *inode            // i-number
54
        path   string            // Directory path
55
        mask   uint64            // Directory itself is being watched with these notify flags
56
        names  map[string]uint64 // Map of names being watched and their notify flags
57
        rename string            // Remembers the old name while renaming a file
58
        buf    [4096]byte
59
}
60
 
61
type indexMap map[uint64]*watch
62
type watchMap map[uint32]indexMap
63
 
64
// A Watcher waits for and receives event notifications
65
// for a specific set of files and directories.
66
type Watcher struct {
67
        port     syscall.Handle // Handle to completion port
68
        watches  watchMap       // Map of watches (key: i-number)
69
        input    chan *input    // Inputs to the reader are sent on this channel
70
        Event    chan *Event    // Events are returned on this channel
71
        Error    chan error     // Errors are sent on this channel
72
        isClosed bool           // Set to true when Close() is first called
73
        quit     chan chan<- error
74
        cookie   uint32
75
}
76
 
77
// NewWatcher creates and returns a Watcher.
78
func NewWatcher() (*Watcher, error) {
79
        port, e := syscall.CreateIoCompletionPort(syscall.InvalidHandle, 0, 0, 0)
80
        if e != nil {
81
                return nil, os.NewSyscallError("CreateIoCompletionPort", e)
82
        }
83
        w := &Watcher{
84
                port:    port,
85
                watches: make(watchMap),
86
                input:   make(chan *input, 1),
87
                Event:   make(chan *Event, 50),
88
                Error:   make(chan error),
89
                quit:    make(chan chan<- error, 1),
90
        }
91
        go w.readEvents()
92
        return w, nil
93
}
94
 
95
// Close closes a Watcher.
96
// It sends a message to the reader goroutine to quit and removes all watches
97
// associated with the watcher.
98
func (w *Watcher) Close() error {
99
        if w.isClosed {
100
                return nil
101
        }
102
        w.isClosed = true
103
 
104
        // Send "quit" message to the reader goroutine
105
        ch := make(chan error)
106
        w.quit <- ch
107
        if err := w.wakeupReader(); err != nil {
108
                return err
109
        }
110
        return <-ch
111
}
112
 
113
// AddWatch adds path to the watched file set.
114
func (w *Watcher) AddWatch(path string, flags uint32) error {
115
        if w.isClosed {
116
                return errors.New("watcher already closed")
117
        }
118
        in := &input{
119
                op:    opAddWatch,
120
                path:  filepath.Clean(path),
121
                flags: flags,
122
                reply: make(chan error),
123
        }
124
        w.input <- in
125
        if err := w.wakeupReader(); err != nil {
126
                return err
127
        }
128
        return <-in.reply
129
}
130
 
131
// Watch adds path to the watched file set, watching all events.
132
func (w *Watcher) Watch(path string) error {
133
        return w.AddWatch(path, FS_ALL_EVENTS)
134
}
135
 
136
// RemoveWatch removes path from the watched file set.
137
func (w *Watcher) RemoveWatch(path string) error {
138
        in := &input{
139
                op:    opRemoveWatch,
140
                path:  filepath.Clean(path),
141
                reply: make(chan error),
142
        }
143
        w.input <- in
144
        if err := w.wakeupReader(); err != nil {
145
                return err
146
        }
147
        return <-in.reply
148
}
149
 
150
func (w *Watcher) wakeupReader() error {
151
        e := syscall.PostQueuedCompletionStatus(w.port, 0, 0, nil)
152
        if e != nil {
153
                return os.NewSyscallError("PostQueuedCompletionStatus", e)
154
        }
155
        return nil
156
}
157
 
158
func getDir(pathname string) (dir string, err error) {
159
        attr, e := syscall.GetFileAttributes(syscall.StringToUTF16Ptr(pathname))
160
        if e != nil {
161
                return "", os.NewSyscallError("GetFileAttributes", e)
162
        }
163
        if attr&syscall.FILE_ATTRIBUTE_DIRECTORY != 0 {
164
                dir = pathname
165
        } else {
166
                dir, _ = filepath.Split(pathname)
167
                dir = filepath.Clean(dir)
168
        }
169
        return
170
}
171
 
172
func getIno(path string) (ino *inode, err error) {
173
        h, e := syscall.CreateFile(syscall.StringToUTF16Ptr(path),
174
                syscall.FILE_LIST_DIRECTORY,
175
                syscall.FILE_SHARE_READ|syscall.FILE_SHARE_WRITE|syscall.FILE_SHARE_DELETE,
176
                nil, syscall.OPEN_EXISTING,
177
                syscall.FILE_FLAG_BACKUP_SEMANTICS|syscall.FILE_FLAG_OVERLAPPED, 0)
178
        if e != nil {
179
                return nil, os.NewSyscallError("CreateFile", e)
180
        }
181
        var fi syscall.ByHandleFileInformation
182
        if e = syscall.GetFileInformationByHandle(h, &fi); e != nil {
183
                syscall.CloseHandle(h)
184
                return nil, os.NewSyscallError("GetFileInformationByHandle", e)
185
        }
186
        ino = &inode{
187
                handle: h,
188
                volume: fi.VolumeSerialNumber,
189
                index:  uint64(fi.FileIndexHigh)<<32 | uint64(fi.FileIndexLow),
190
        }
191
        return ino, nil
192
}
193
 
194
// Must run within the I/O thread.
195
func (m watchMap) get(ino *inode) *watch {
196
        if i := m[ino.volume]; i != nil {
197
                return i[ino.index]
198
        }
199
        return nil
200
}
201
 
202
// Must run within the I/O thread.
203
func (m watchMap) set(ino *inode, watch *watch) {
204
        i := m[ino.volume]
205
        if i == nil {
206
                i = make(indexMap)
207
                m[ino.volume] = i
208
        }
209
        i[ino.index] = watch
210
}
211
 
212
// Must run within the I/O thread.
213
func (w *Watcher) addWatch(pathname string, flags uint64) error {
214
        dir, err := getDir(pathname)
215
        if err != nil {
216
                return err
217
        }
218
        if flags&FS_ONLYDIR != 0 && pathname != dir {
219
                return nil
220
        }
221
        ino, err := getIno(dir)
222
        if err != nil {
223
                return err
224
        }
225
        watchEntry := w.watches.get(ino)
226
        if watchEntry == nil {
227
                if _, e := syscall.CreateIoCompletionPort(ino.handle, w.port, 0, 0); e != nil {
228
                        syscall.CloseHandle(ino.handle)
229
                        return os.NewSyscallError("CreateIoCompletionPort", e)
230
                }
231
                watchEntry = &watch{
232
                        ino:   ino,
233
                        path:  dir,
234
                        names: make(map[string]uint64),
235
                }
236
                w.watches.set(ino, watchEntry)
237
                flags |= provisional
238
        } else {
239
                syscall.CloseHandle(ino.handle)
240
        }
241
        if pathname == dir {
242
                watchEntry.mask |= flags
243
        } else {
244
                watchEntry.names[filepath.Base(pathname)] |= flags
245
        }
246
        if err = w.startRead(watchEntry); err != nil {
247
                return err
248
        }
249
        if pathname == dir {
250
                watchEntry.mask &= ^provisional
251
        } else {
252
                watchEntry.names[filepath.Base(pathname)] &= ^provisional
253
        }
254
        return nil
255
}
256
 
257
// Must run within the I/O thread.
258
func (w *Watcher) removeWatch(pathname string) error {
259
        dir, err := getDir(pathname)
260
        if err != nil {
261
                return err
262
        }
263
        ino, err := getIno(dir)
264
        if err != nil {
265
                return err
266
        }
267
        watch := w.watches.get(ino)
268
        if watch == nil {
269
                return fmt.Errorf("can't remove non-existent watch for: %s", pathname)
270
        }
271
        if pathname == dir {
272
                w.sendEvent(watch.path, watch.mask&FS_IGNORED)
273
                watch.mask = 0
274
        } else {
275
                name := filepath.Base(pathname)
276
                w.sendEvent(watch.path+"/"+name, watch.names[name]&FS_IGNORED)
277
                delete(watch.names, name)
278
        }
279
        return w.startRead(watch)
280
}
281
 
282
// Must run within the I/O thread.
283
func (w *Watcher) deleteWatch(watch *watch) {
284
        for name, mask := range watch.names {
285
                if mask&provisional == 0 {
286
                        w.sendEvent(watch.path+"/"+name, mask&FS_IGNORED)
287
                }
288
                delete(watch.names, name)
289
        }
290
        if watch.mask != 0 {
291
                if watch.mask&provisional == 0 {
292
                        w.sendEvent(watch.path, watch.mask&FS_IGNORED)
293
                }
294
                watch.mask = 0
295
        }
296
}
297
 
298
// Must run within the I/O thread.
299
func (w *Watcher) startRead(watch *watch) error {
300
        if e := syscall.CancelIo(watch.ino.handle); e != nil {
301
                w.Error <- os.NewSyscallError("CancelIo", e)
302
                w.deleteWatch(watch)
303
        }
304
        mask := toWindowsFlags(watch.mask)
305
        for _, m := range watch.names {
306
                mask |= toWindowsFlags(m)
307
        }
308
        if mask == 0 {
309
                if e := syscall.CloseHandle(watch.ino.handle); e != nil {
310
                        w.Error <- os.NewSyscallError("CloseHandle", e)
311
                }
312
                delete(w.watches[watch.ino.volume], watch.ino.index)
313
                return nil
314
        }
315
        e := syscall.ReadDirectoryChanges(watch.ino.handle, &watch.buf[0],
316
                uint32(unsafe.Sizeof(watch.buf)), false, mask, nil, &watch.ov, 0)
317
        if e != nil {
318
                err := os.NewSyscallError("ReadDirectoryChanges", e)
319
                if e == syscall.ERROR_ACCESS_DENIED && watch.mask&provisional == 0 {
320
                        // Watched directory was probably removed
321
                        if w.sendEvent(watch.path, watch.mask&FS_DELETE_SELF) {
322
                                if watch.mask&FS_ONESHOT != 0 {
323
                                        watch.mask = 0
324
                                }
325
                        }
326
                        err = nil
327
                }
328
                w.deleteWatch(watch)
329
                w.startRead(watch)
330
                return err
331
        }
332
        return nil
333
}
334
 
335
// readEvents reads from the I/O completion port, converts the
336
// received events into Event objects and sends them via the Event channel.
337
// Entry point to the I/O thread.
338
func (w *Watcher) readEvents() {
339
        var (
340
                n, key uint32
341
                ov     *syscall.Overlapped
342
        )
343
        runtime.LockOSThread()
344
 
345
        for {
346
                e := syscall.GetQueuedCompletionStatus(w.port, &n, &key, &ov, syscall.INFINITE)
347
                watch := (*watch)(unsafe.Pointer(ov))
348
 
349
                if watch == nil {
350
                        select {
351
                        case ch := <-w.quit:
352
                                for _, index := range w.watches {
353
                                        for _, watch := range index {
354
                                                w.deleteWatch(watch)
355
                                                w.startRead(watch)
356
                                        }
357
                                }
358
                                var err error
359
                                if e := syscall.CloseHandle(w.port); e != nil {
360
                                        err = os.NewSyscallError("CloseHandle", e)
361
                                }
362
                                close(w.Event)
363
                                close(w.Error)
364
                                ch <- err
365
                                return
366
                        case in := <-w.input:
367
                                switch in.op {
368
                                case opAddWatch:
369
                                        in.reply <- w.addWatch(in.path, uint64(in.flags))
370
                                case opRemoveWatch:
371
                                        in.reply <- w.removeWatch(in.path)
372
                                }
373
                        default:
374
                        }
375
                        continue
376
                }
377
 
378
                switch e {
379
                case syscall.ERROR_ACCESS_DENIED:
380
                        // Watched directory was probably removed
381
                        w.sendEvent(watch.path, watch.mask&FS_DELETE_SELF)
382
                        w.deleteWatch(watch)
383
                        w.startRead(watch)
384
                        continue
385
                case syscall.ERROR_OPERATION_ABORTED:
386
                        // CancelIo was called on this handle
387
                        continue
388
                default:
389
                        w.Error <- os.NewSyscallError("GetQueuedCompletionPort", e)
390
                        continue
391
                case nil:
392
                }
393
 
394
                var offset uint32
395
                for {
396
                        if n == 0 {
397
                                w.Event <- &Event{Mask: FS_Q_OVERFLOW}
398
                                w.Error <- errors.New("short read in readEvents()")
399
                                break
400
                        }
401
 
402
                        // Point "raw" to the event in the buffer
403
                        raw := (*syscall.FileNotifyInformation)(unsafe.Pointer(&watch.buf[offset]))
404
                        buf := (*[syscall.MAX_PATH]uint16)(unsafe.Pointer(&raw.FileName))
405
                        name := syscall.UTF16ToString(buf[:raw.FileNameLength/2])
406
                        fullname := watch.path + "/" + name
407
 
408
                        var mask uint64
409
                        switch raw.Action {
410
                        case syscall.FILE_ACTION_REMOVED:
411
                                mask = FS_DELETE_SELF
412
                        case syscall.FILE_ACTION_MODIFIED:
413
                                mask = FS_MODIFY
414
                        case syscall.FILE_ACTION_RENAMED_OLD_NAME:
415
                                watch.rename = name
416
                        case syscall.FILE_ACTION_RENAMED_NEW_NAME:
417
                                if watch.names[watch.rename] != 0 {
418
                                        watch.names[name] |= watch.names[watch.rename]
419
                                        delete(watch.names, watch.rename)
420
                                        mask = FS_MOVE_SELF
421
                                }
422
                        }
423
 
424
                        sendNameEvent := func() {
425
                                if w.sendEvent(fullname, watch.names[name]&mask) {
426
                                        if watch.names[name]&FS_ONESHOT != 0 {
427
                                                delete(watch.names, name)
428
                                        }
429
                                }
430
                        }
431
                        if raw.Action != syscall.FILE_ACTION_RENAMED_NEW_NAME {
432
                                sendNameEvent()
433
                        }
434
                        if raw.Action == syscall.FILE_ACTION_REMOVED {
435
                                w.sendEvent(fullname, watch.names[name]&FS_IGNORED)
436
                                delete(watch.names, name)
437
                        }
438
                        if w.sendEvent(fullname, watch.mask&toFSnotifyFlags(raw.Action)) {
439
                                if watch.mask&FS_ONESHOT != 0 {
440
                                        watch.mask = 0
441
                                }
442
                        }
443
                        if raw.Action == syscall.FILE_ACTION_RENAMED_NEW_NAME {
444
                                fullname = watch.path + "/" + watch.rename
445
                                sendNameEvent()
446
                        }
447
 
448
                        // Move to the next event in the buffer
449
                        if raw.NextEntryOffset == 0 {
450
                                break
451
                        }
452
                        offset += raw.NextEntryOffset
453
                }
454
 
455
                if err := w.startRead(watch); err != nil {
456
                        w.Error <- err
457
                }
458
        }
459
}
460
 
461
func (w *Watcher) sendEvent(name string, mask uint64) bool {
462
        if mask == 0 {
463
                return false
464
        }
465
        event := &Event{Mask: uint32(mask), Name: name}
466
        if mask&FS_MOVE != 0 {
467
                if mask&FS_MOVED_FROM != 0 {
468
                        w.cookie++
469
                }
470
                event.Cookie = w.cookie
471
        }
472
        select {
473
        case ch := <-w.quit:
474
                w.quit <- ch
475
        case w.Event <- event:
476
        }
477
        return true
478
}
479
 
480
// String formats the event e in the form
481
// "filename: 0xEventMask = FS_ACCESS|FS_ATTRIB_|..."
482
func (e *Event) String() string {
483
        var events string
484
        m := e.Mask
485
        for _, b := range eventBits {
486
                if m&b.Value != 0 {
487
                        m &^= b.Value
488
                        events += "|" + b.Name
489
                }
490
        }
491
        if m != 0 {
492
                events += fmt.Sprintf("|%#x", m)
493
        }
494
        if len(events) > 0 {
495
                events = " == " + events[1:]
496
        }
497
        return fmt.Sprintf("%q: %#x%s", e.Name, e.Mask, events)
498
}
499
 
500
func toWindowsFlags(mask uint64) uint32 {
501
        var m uint32
502
        if mask&FS_ACCESS != 0 {
503
                m |= syscall.FILE_NOTIFY_CHANGE_LAST_ACCESS
504
        }
505
        if mask&FS_MODIFY != 0 {
506
                m |= syscall.FILE_NOTIFY_CHANGE_LAST_WRITE
507
        }
508
        if mask&FS_ATTRIB != 0 {
509
                m |= syscall.FILE_NOTIFY_CHANGE_ATTRIBUTES
510
        }
511
        if mask&(FS_MOVE|FS_CREATE|FS_DELETE) != 0 {
512
                m |= syscall.FILE_NOTIFY_CHANGE_FILE_NAME | syscall.FILE_NOTIFY_CHANGE_DIR_NAME
513
        }
514
        return m
515
}
516
 
517
func toFSnotifyFlags(action uint32) uint64 {
518
        switch action {
519
        case syscall.FILE_ACTION_ADDED:
520
                return FS_CREATE
521
        case syscall.FILE_ACTION_REMOVED:
522
                return FS_DELETE
523
        case syscall.FILE_ACTION_MODIFIED:
524
                return FS_MODIFY
525
        case syscall.FILE_ACTION_RENAMED_OLD_NAME:
526
                return FS_MOVED_FROM
527
        case syscall.FILE_ACTION_RENAMED_NEW_NAME:
528
                return FS_MOVED_TO
529
        }
530
        return 0
531
}
532
 
533
const (
534
        // Options for AddWatch
535
        FS_ONESHOT = 0x80000000
536
        FS_ONLYDIR = 0x1000000
537
 
538
        // Events
539
        FS_ACCESS      = 0x1
540
        FS_ALL_EVENTS  = 0xfff
541
        FS_ATTRIB      = 0x4
542
        FS_CLOSE       = 0x18
543
        FS_CREATE      = 0x100
544
        FS_DELETE      = 0x200
545
        FS_DELETE_SELF = 0x400
546
        FS_MODIFY      = 0x2
547
        FS_MOVE        = 0xc0
548
        FS_MOVED_FROM  = 0x40
549
        FS_MOVED_TO    = 0x80
550
        FS_MOVE_SELF   = 0x800
551
 
552
        // Special events
553
        FS_IGNORED    = 0x8000
554
        FS_Q_OVERFLOW = 0x4000
555
)
556
 
557
var eventBits = []struct {
558
        Value uint32
559
        Name  string
560
}{
561
        {FS_ACCESS, "FS_ACCESS"},
562
        {FS_ATTRIB, "FS_ATTRIB"},
563
        {FS_CREATE, "FS_CREATE"},
564
        {FS_DELETE, "FS_DELETE"},
565
        {FS_DELETE_SELF, "FS_DELETE_SELF"},
566
        {FS_MODIFY, "FS_MODIFY"},
567
        {FS_MOVED_FROM, "FS_MOVED_FROM"},
568
        {FS_MOVED_TO, "FS_MOVED_TO"},
569
        {FS_MOVE_SELF, "FS_MOVE_SELF"},
570
        {FS_IGNORED, "FS_IGNORED"},
571
        {FS_Q_OVERFLOW, "FS_Q_OVERFLOW"},
572
}

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.