Source file
src/os/root_windows_test.go
1
2
3
4
5
6
7 package os_test
8
9 import (
10 "errors"
11 "fmt"
12 "internal/strconv"
13 "internal/syscall/windows"
14 "os"
15 "path/filepath"
16 "syscall"
17 "testing"
18 "unsafe"
19 )
20
21
22 func TestRootWindowsDeviceNames(t *testing.T) {
23 r, err := os.OpenRoot(t.TempDir())
24 if err != nil {
25 t.Fatal(err)
26 }
27 defer r.Close()
28 if f, err := r.Open("NUL"); err == nil {
29 t.Errorf(`r.Open("NUL") succeeded; want error"`)
30 f.Close()
31 }
32 }
33
34
35
36
37 func TestRootWindowsCaseInsensitivity(t *testing.T) {
38 dir := t.TempDir()
39 if err := os.WriteFile(filepath.Join(dir, "file"), nil, 0666); err != nil {
40 t.Fatal(err)
41 }
42 r, err := os.OpenRoot(dir)
43 if err != nil {
44 t.Fatal(err)
45 }
46 defer r.Close()
47 f, err := r.Open("FILE")
48 if err != nil {
49 t.Fatal(err)
50 }
51 f.Close()
52 if err := r.Remove("FILE"); err != nil {
53 t.Fatal(err)
54 }
55 if _, err := os.Stat(filepath.Join(dir, "file")); !errors.Is(err, os.ErrNotExist) {
56 t.Fatalf("os.Stat(file) after deletion: %v, want ErrNotFound", err)
57 }
58 }
59
60
61
62 func TestRootSymlinkRelativity(t *testing.T) {
63 dir := t.TempDir()
64 root, err := os.OpenRoot(dir)
65 if err != nil {
66 t.Fatal(err)
67 }
68 defer root.Close()
69
70 for i, test := range []struct {
71 name string
72 target string
73 }{{
74 name: "relative",
75 target: `foo`,
76 }, {
77 name: "absolute",
78 target: `C:\foo`,
79 }, {
80 name: "current working directory-relative",
81 target: `C:foo`,
82 }, {
83 name: "root-relative",
84 target: `\foo`,
85 }, {
86 name: "question prefix",
87 target: `\\?\foo`,
88 }, {
89 name: "relative with dot dot",
90 target: `a\..\b`,
91 }} {
92 t.Run(test.name, func(t *testing.T) {
93 name := fmt.Sprintf("symlink_%v", i)
94 if err := os.Symlink(test.target, filepath.Join(dir, name)); err != nil {
95 t.Fatal(err)
96 }
97 if err := root.Symlink(test.target, name+"_at"); err != nil {
98 t.Fatal(err)
99 }
100
101 osRDB, err := readSymlinkReparseData(filepath.Join(dir, name))
102 if err != nil {
103 t.Fatal(err)
104 }
105 rootRDB, err := readSymlinkReparseData(filepath.Join(dir, name+"_at"))
106 if err != nil {
107 t.Fatal(err)
108 }
109 if osRDB.Flags != rootRDB.Flags {
110 t.Errorf("symlink target %q: Symlink flags = %x, Root.Symlink flags = %x", test.target, osRDB.Flags, rootRDB.Flags)
111 }
112
113
114
115
116 osTarget, err := os.Readlink(filepath.Join(dir, name))
117 if err != nil {
118 t.Fatal(err)
119 }
120 rootTarget, err := os.Readlink(filepath.Join(dir, name+"_at"))
121 if err != nil {
122 t.Fatal(err)
123 }
124 if osTarget != rootTarget {
125 t.Errorf("symlink created with target %q: Symlink target = %q, Root.Symlink target = %q", test.target, osTarget, rootTarget)
126 }
127 })
128 }
129 }
130
131 func readSymlinkReparseData(name string) (*windows.SymbolicLinkReparseBuffer, error) {
132 nameu16, err := syscall.UTF16FromString(name)
133 if err != nil {
134 return nil, err
135 }
136 h, err := syscall.CreateFile(&nameu16[0], syscall.GENERIC_READ, 0, nil, syscall.OPEN_EXISTING,
137 syscall.FILE_FLAG_OPEN_REPARSE_POINT|syscall.FILE_FLAG_BACKUP_SEMANTICS, 0)
138 if err != nil {
139 return nil, err
140 }
141 defer syscall.CloseHandle(h)
142
143 var rdbbuf [syscall.MAXIMUM_REPARSE_DATA_BUFFER_SIZE]byte
144 var bytesReturned uint32
145 err = syscall.DeviceIoControl(h, syscall.FSCTL_GET_REPARSE_POINT, nil, 0, &rdbbuf[0], uint32(len(rdbbuf)), &bytesReturned, nil)
146 if err != nil {
147 return nil, err
148 }
149
150 rdb := (*windows.REPARSE_DATA_BUFFER)(unsafe.Pointer(&rdbbuf[0]))
151 if rdb.ReparseTag != syscall.IO_REPARSE_TAG_SYMLINK {
152 return nil, fmt.Errorf("%q: not a symlink", name)
153 }
154
155 bufoff := unsafe.Offsetof(rdb.DUMMYUNIONNAME)
156 symlinkBuf := (*windows.SymbolicLinkReparseBuffer)(unsafe.Pointer(&rdbbuf[bufoff]))
157
158 return symlinkBuf, nil
159 }
160
161
162
163 func TestRootSymlinkToDirectory(t *testing.T) {
164 dir := t.TempDir()
165 root, err := os.OpenRoot(dir)
166 if err != nil {
167 t.Fatal(err)
168 }
169 defer root.Close()
170
171 if err := os.Mkdir(filepath.Join(dir, "dir"), 0777); err != nil {
172 t.Fatal(err)
173 }
174 if err := os.WriteFile(filepath.Join(dir, "file"), nil, 0666); err != nil {
175 t.Fatal(err)
176 }
177
178 dir2 := t.TempDir()
179
180 for i, test := range []struct {
181 name string
182 target string
183 wantDir bool
184 }{{
185 name: "directory outside root",
186 target: dir2,
187 wantDir: false,
188 }, {
189 name: "directory inside root",
190 target: "dir",
191 wantDir: true,
192 }, {
193 name: "file inside root",
194 target: "file",
195 wantDir: false,
196 }, {
197 name: "nonexistent inside root",
198 target: "nonexistent",
199 wantDir: false,
200 }} {
201 t.Run(test.name, func(t *testing.T) {
202 name := fmt.Sprintf("symlink_%v", i)
203 if err := root.Symlink(test.target, name); err != nil {
204 t.Fatal(err)
205 }
206
207
208
209
210 nameu16, err := syscall.UTF16PtrFromString(filepath.Join(dir, name))
211 if err != nil {
212 t.Fatal(err)
213 }
214 h, err := syscall.CreateFile(nameu16, 0, 0, nil, syscall.OPEN_EXISTING,
215 syscall.FILE_FLAG_OPEN_REPARSE_POINT|syscall.FILE_FLAG_BACKUP_SEMANTICS, 0)
216 if err != nil {
217 t.Fatal(err)
218 }
219 defer syscall.CloseHandle(h)
220 var fi syscall.ByHandleFileInformation
221 if err := syscall.GetFileInformationByHandle(h, &fi); err != nil {
222 t.Fatal(err)
223 }
224 gotDir := fi.FileAttributes&syscall.FILE_ATTRIBUTE_DIRECTORY != 0
225
226 if got, want := gotDir, test.wantDir; got != want {
227 t.Errorf("link target %q: isDir = %v, want %v", test.target, got, want)
228 }
229 })
230 }
231 }
232
233 func TestRootOpenFileTruncateNamedPipe(t *testing.T) {
234 t.Parallel()
235 name := pipeName()
236 pipe := newBytePipe(t, name, false)
237 defer pipe.Close()
238
239 root, err := os.OpenRoot(filepath.Dir(name))
240 if err != nil {
241 t.Fatal(err)
242 }
243 defer root.Close()
244
245 f, err := root.OpenFile(filepath.Base(name), os.O_TRUNC|os.O_RDWR|os.O_CREATE, 0666)
246 if err != nil {
247 t.Fatal(err)
248 }
249 f.Close()
250 }
251
252 func TestRootOpenFileFlags(t *testing.T) {
253 t.Parallel()
254
255 dir := t.TempDir()
256 root, err := os.OpenRoot(dir)
257 if err != nil {
258 t.Fatal(err)
259 }
260 defer root.Close()
261
262
263
264
265
266
267
268 tests := []struct {
269 flag uint32
270 wantMode uint32
271 }{
272 {0, windows.FILE_SYNCHRONOUS_IO_NONALERT},
273 {windows.O_FILE_FLAG_OVERLAPPED, 0},
274 {windows.O_FILE_FLAG_NO_BUFFERING, windows.FILE_NO_INTERMEDIATE_BUFFERING | windows.FILE_SYNCHRONOUS_IO_NONALERT},
275 {windows.O_FILE_FLAG_NO_BUFFERING | windows.O_FILE_FLAG_OVERLAPPED, windows.FILE_NO_INTERMEDIATE_BUFFERING},
276 {windows.O_FILE_FLAG_SEQUENTIAL_SCAN, windows.FILE_SEQUENTIAL_ONLY | windows.FILE_SYNCHRONOUS_IO_NONALERT},
277 {windows.O_FILE_FLAG_WRITE_THROUGH, windows.FILE_WRITE_THROUGH | windows.FILE_SYNCHRONOUS_IO_NONALERT},
278 }
279 for i, tt := range tests {
280 t.Run(strconv.Itoa(i), func(t *testing.T) {
281 f, err := root.OpenFile(strconv.Itoa(i)+".txt", syscall.O_RDWR|syscall.O_CREAT|int(tt.flag), 0666)
282 if err != nil {
283 t.Fatal(err)
284 }
285 defer f.Close()
286 var info windows.FILE_MODE_INFORMATION
287 if err := windows.NtQueryInformationFile(syscall.Handle(f.Fd()), &windows.IO_STATUS_BLOCK{},
288 unsafe.Pointer(&info), uint32(unsafe.Sizeof(info)), windows.FileModeInformation); err != nil {
289 t.Fatal(err)
290 }
291 if info.Mode != tt.wantMode {
292 t.Errorf("file mode = 0x%x; want 0x%x", info.Mode, tt.wantMode)
293 }
294 })
295 }
296 }
297
298 func TestRootOpenFileDeleteOnClose(t *testing.T) {
299 t.Parallel()
300 dir := t.TempDir()
301 root, err := os.OpenRoot(dir)
302 if err != nil {
303 t.Fatal(err)
304 }
305 defer root.Close()
306 const name = "test.txt"
307 f, err := root.OpenFile(name, syscall.O_RDWR|syscall.O_CREAT|windows.O_FILE_FLAG_DELETE_ON_CLOSE, 0666)
308 if err != nil {
309 t.Fatal(err)
310 }
311 if err := f.Close(); err != nil {
312 t.Fatal(err)
313 }
314
315 if _, err := os.Stat(filepath.Join(dir, name)); !errors.Is(err, os.ErrNotExist) {
316 t.Errorf("expected file to be deleted, got %v", err)
317 }
318 }
319
320 func TestRootOpenFileFlagInvalid(t *testing.T) {
321 t.Parallel()
322 dir := t.TempDir()
323 root, err := os.OpenRoot(dir)
324 if err != nil {
325 t.Fatal(err)
326 }
327 defer root.Close()
328
329
330 const invalidFileFlag = 0x00400000
331 f, err := root.OpenFile("test.txt", syscall.O_RDWR|syscall.O_CREAT|invalidFileFlag, 0666)
332 if !errors.Is(err, os.ErrInvalid) {
333 t.Fatalf("expected os.ErrInvalid, got %v", err)
334 }
335 f.Close()
336 }
337
View as plain text