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