-
Notifications
You must be signed in to change notification settings - Fork 3
/
volume.go
133 lines (112 loc) · 2.87 KB
/
volume.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
package filewatcher
import (
"github.com/y4v8/errors"
"github.com/y4v8/filewatcher/win"
"syscall"
)
const (
systemBufferSize uint32 = 64 * 1024
)
type volumes map[uint32]*volume
type volume struct {
ov syscall.Overlapped
handle syscall.Handle
watches watches
urd win.READ_USN_JOURNAL_DATA_V1
buffer []byte
}
func newVolume(path string, mask win.UsnReason) (*volume, error) {
hVolume, err := openVolume(path)
if err != nil {
return nil, errors.Wrap(err)
}
ujd, err := win.GetUsnJournalData(hVolume)
if err != nil {
syscall.CloseHandle(hVolume)
return nil, errors.Wrap(err)
}
v := volume{
handle: hVolume,
urd: win.READ_USN_JOURNAL_DATA_V1{
READ_USN_JOURNAL_DATA_V0: win.READ_USN_JOURNAL_DATA_V0{
UsnJournalID: ujd.UsnJournalID,
StartUsn: ujd.NextUsn,
BytesToWaitFor: 1,
// TODO ReturnOnlyOnClose
//ReturnOnlyOnClose: 1,
ReasonMask: mask,
},
MinMajorVersion: 2,
MaxMajorVersion: 2,
},
watches: make(watches),
buffer: make([]byte, systemBufferSize),
}
return &v, nil
}
func openVolume(path string) (syscall.Handle, error) {
name, err := getVolumeName(path)
if err != nil {
return syscall.InvalidHandle, errors.Wrap(err)
}
nameUTF16, err := syscall.UTF16PtrFromString(name)
if err != nil {
return syscall.InvalidHandle, errors.Wrap(err)
}
hVolume, err := syscall.CreateFile(nameUTF16,
win.FILE_LIST_DIRECTORY,
syscall.FILE_SHARE_READ|syscall.FILE_SHARE_WRITE,
nil,
syscall.OPEN_EXISTING,
syscall.FILE_FLAG_OVERLAPPED,
0)
if err != nil {
return syscall.InvalidHandle, errors.Wrap(err)
}
supported, err := win.IsSupportedUsnJournal(hVolume)
if err != nil {
syscall.CloseHandle(hVolume)
return syscall.InvalidHandle, errors.Wrap(err)
}
if !supported {
syscall.CloseHandle(hVolume)
return syscall.InvalidHandle, errors.New("The USN journal is not supported.")
}
return hVolume, nil
}
func getVolumePathName(path string) (string, error) {
pathUTF16, err := syscall.UTF16PtrFromString(path)
if err != nil {
return "", errors.Wrap(err)
}
lengthPathName := 256
bufferPathName := make([]uint16, lengthPathName)
err = win.GetVolumePathName(pathUTF16, &bufferPathName[0], uint32(lengthPathName))
if err != nil {
return "", errors.Wrap(err)
}
return syscall.UTF16ToString(bufferPathName), nil
}
func getVolumeName(path string) (string, error) {
pathName, err := getVolumePathName(path)
if err != nil {
return "", errors.Wrap(err)
}
bufferPathName, err := syscall.UTF16PtrFromString(pathName)
if err != nil {
return "", errors.Wrap(err)
}
lengthName := 256
bufferName := make([]uint16, lengthName)
err = win.GetVolumeNameForVolumeMountPoint(bufferPathName, &bufferName[0], uint32(lengthName))
if err != nil {
return "", errors.Wrap(err)
}
for i, v := range bufferName {
if v == 0 {
bufferName[i-1] = 0
break
}
}
return syscall.UTF16ToString(bufferName), nil
}