forked from cloudfoundry/gosigar
-
Notifications
You must be signed in to change notification settings - Fork 0
/
volume_windows.go
134 lines (114 loc) · 3.53 KB
/
volume_windows.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
134
package sigar
// #include <stdlib.h>
// #include <windows.h>
import "C"
import (
"fmt"
"syscall"
)
type WindowsVolumeIterator struct {
handle C.HANDLE
buffer []uint16
bufferSize C.DWORD
volume FileSystem
err error
hasNext bool
}
// Get the first mount point for the volume. If we get an ERROR_MORE_DATA,
// don't return an error, but return the expected buffer size.
// You can attempt to call the method again with the larger buffer size
func getVolumePathName(wideName []uint16, bufferSize uint16) (string, error, uint16) {
charsLen := C.DWORD(bufferSize)
expectedBuffer := C.DWORD(0)
chars := make([]uint16, charsLen)
if C.GetVolumePathNamesForVolumeNameW((*C.WCHAR)(&wideName[0]), (*C.WCHAR)(&chars[0]), charsLen, &expectedBuffer) == C.FALSE {
errno := syscall.GetLastError()
err, ok := errno.(syscall.Errno)
if !ok || err != C.ERROR_MORE_DATA {
return "", errno, 0
} else if err == C.ERROR_MORE_DATA {
return "", nil, uint16(expectedBuffer)
}
}
volumePaths := syscall.UTF16ToString(chars)
return volumePaths, nil, 0
}
func NewWindowsVolumeIterator() (*WindowsVolumeIterator, error) {
bufSize := C.DWORD(C.MAX_PATH)
buf := make([]uint16, bufSize)
// Get a handle to iterate over all the attached disks
diskHandle := C.FindFirstVolumeW((*C.WCHAR)(&buf[0]), bufSize)
if diskHandle == C.HANDLE(uintptr(0xFF)) {
err := syscall.GetLastError()
return nil, fmt.Errorf("Got bad handle when calling FindFirstVolume - %v", err)
}
return &WindowsVolumeIterator{
handle: diskHandle,
buffer: buf,
bufferSize: bufSize,
hasNext: true,
}, nil
}
// Get the last volume from the iterator
func (self *WindowsVolumeIterator) Volume() FileSystem {
return self.volume
}
// Get the last error that stopped iteration
func (self *WindowsVolumeIterator) Error() error {
return self.err
}
/* Iterate over the attached volumes. Returns true if there
is a volume available from Volume(). Always check Error()
when you're finished iterating. */
func (self *WindowsVolumeIterator) Next() bool {
/* Because of the weird structure of `FindFirstVolume`,
we have a volume buffered before the first `Next()`. */
if !self.hasNext {
return false
}
// Convert the currently buffered event to a Go struct
volume := syscall.UTF16ToString(self.buffer)
// Get the first mount point for the buffered volume
volumeMount, err, newSize := getVolumePathName(self.buffer, C.MAX_PATH)
if newSize > 0 {
// Retry once if we get an error about buffers being too small
volumeMount, err, _ = getVolumePathName(self.buffer, newSize)
}
if err != nil {
self.err = fmt.Errorf("Error getting volume mountpoints: %v", err)
return false
}
self.volume = FileSystem{
DevName: volume,
DirName: volumeMount,
}
// Put the next volume in the buffer
err, hasNext := self.getNextVolume()
if !hasNext {
// If there is no buffered event, the next call to Next() will return false
self.hasNext = false
}
// If there was an error getting the next volume, fail immediately
if err != nil {
self.err = err
return false
}
return true
}
func (self *WindowsVolumeIterator) Close() {
C.FindVolumeClose(self.handle)
}
func (self *WindowsVolumeIterator) getNextVolume() (error, bool) {
if C.FindNextVolumeW(self.handle, (*C.WCHAR)(&self.buffer[0]), self.bufferSize) == C.FALSE {
err := syscall.GetLastError()
errno, _ := err.(syscall.Errno)
if err == nil || errno == C.ERROR_NO_MORE_FILES {
// There are no more volumes
return nil, false
} else {
// Unknown error
return err, false
}
}
return nil, true
}