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/syscall/windows"
13 "os"
14 "path/filepath"
15 "syscall"
16 "testing"
17 "unsafe"
18 )
19
20
21 func TestRootWindowsDeviceNames(t *testing.T) {
22 r, err := os.OpenRoot(t.TempDir())
23 if err != nil {
24 t.Fatal(err)
25 }
26 defer r.Close()
27 if f, err := r.Open("NUL"); err == nil {
28 t.Errorf(`r.Open("NUL") succeeded; want error"`)
29 f.Close()
30 }
31 }
32
33
34
35
36 func TestRootWindowsCaseInsensitivity(t *testing.T) {
37 dir := t.TempDir()
38 if err := os.WriteFile(filepath.Join(dir, "file"), nil, 0666); err != nil {
39 t.Fatal(err)
40 }
41 r, err := os.OpenRoot(dir)
42 if err != nil {
43 t.Fatal(err)
44 }
45 defer r.Close()
46 f, err := r.Open("FILE")
47 if err != nil {
48 t.Fatal(err)
49 }
50 f.Close()
51 if err := r.Remove("FILE"); err != nil {
52 t.Fatal(err)
53 }
54 if _, err := os.Stat(filepath.Join(dir, "file")); !errors.Is(err, os.ErrNotExist) {
55 t.Fatalf("os.Stat(file) after deletion: %v, want ErrNotFound", err)
56 }
57 }
58
59
60
61 func TestRootSymlinkRelativity(t *testing.T) {
62 dir := t.TempDir()
63 root, err := os.OpenRoot(dir)
64 if err != nil {
65 t.Fatal(err)
66 }
67 defer root.Close()
68
69 for i, test := range []struct {
70 name string
71 target string
72 }{{
73 name: "relative",
74 target: `foo`,
75 }, {
76 name: "absolute",
77 target: `C:\foo`,
78 }, {
79 name: "current working directory-relative",
80 target: `C:foo`,
81 }, {
82 name: "root-relative",
83 target: `\foo`,
84 }, {
85 name: "question prefix",
86 target: `\\?\foo`,
87 }, {
88 name: "relative with dot dot",
89 target: `a\..\b`,
90 }} {
91 t.Run(test.name, func(t *testing.T) {
92 name := fmt.Sprintf("symlink_%v", i)
93 if err := os.Symlink(test.target, filepath.Join(dir, name)); err != nil {
94 t.Fatal(err)
95 }
96 if err := root.Symlink(test.target, name+"_at"); err != nil {
97 t.Fatal(err)
98 }
99
100 osRDB, err := readSymlinkReparseData(filepath.Join(dir, name))
101 if err != nil {
102 t.Fatal(err)
103 }
104 rootRDB, err := readSymlinkReparseData(filepath.Join(dir, name+"_at"))
105 if err != nil {
106 t.Fatal(err)
107 }
108 if osRDB.Flags != rootRDB.Flags {
109 t.Errorf("symlink target %q: Symlink flags = %x, Root.Symlink flags = %x", test.target, osRDB.Flags, rootRDB.Flags)
110 }
111
112
113
114
115 osTarget, err := os.Readlink(filepath.Join(dir, name))
116 if err != nil {
117 t.Fatal(err)
118 }
119 rootTarget, err := os.Readlink(filepath.Join(dir, name+"_at"))
120 if err != nil {
121 t.Fatal(err)
122 }
123 if osTarget != rootTarget {
124 t.Errorf("symlink created with target %q: Symlink target = %q, Root.Symlink target = %q", test.target, osTarget, rootTarget)
125 }
126 })
127 }
128 }
129
130 func readSymlinkReparseData(name string) (*windows.SymbolicLinkReparseBuffer, error) {
131 nameu16, err := syscall.UTF16FromString(name)
132 if err != nil {
133 return nil, err
134 }
135 h, err := syscall.CreateFile(&nameu16[0], syscall.GENERIC_READ, 0, nil, syscall.OPEN_EXISTING,
136 syscall.FILE_FLAG_OPEN_REPARSE_POINT|syscall.FILE_FLAG_BACKUP_SEMANTICS, 0)
137 if err != nil {
138 return nil, err
139 }
140 defer syscall.CloseHandle(h)
141
142 var rdbbuf [syscall.MAXIMUM_REPARSE_DATA_BUFFER_SIZE]byte
143 var bytesReturned uint32
144 err = syscall.DeviceIoControl(h, syscall.FSCTL_GET_REPARSE_POINT, nil, 0, &rdbbuf[0], uint32(len(rdbbuf)), &bytesReturned, nil)
145 if err != nil {
146 return nil, err
147 }
148
149 rdb := (*windows.REPARSE_DATA_BUFFER)(unsafe.Pointer(&rdbbuf[0]))
150 if rdb.ReparseTag != syscall.IO_REPARSE_TAG_SYMLINK {
151 return nil, fmt.Errorf("%q: not a symlink", name)
152 }
153
154 bufoff := unsafe.Offsetof(rdb.DUMMYUNIONNAME)
155 symlinkBuf := (*windows.SymbolicLinkReparseBuffer)(unsafe.Pointer(&rdbbuf[bufoff]))
156
157 return symlinkBuf, nil
158 }
159
160
161
162 func TestRootSymlinkToDirectory(t *testing.T) {
163 dir := t.TempDir()
164 root, err := os.OpenRoot(dir)
165 if err != nil {
166 t.Fatal(err)
167 }
168 defer root.Close()
169
170 if err := os.Mkdir(filepath.Join(dir, "dir"), 0777); err != nil {
171 t.Fatal(err)
172 }
173 if err := os.WriteFile(filepath.Join(dir, "file"), nil, 0666); err != nil {
174 t.Fatal(err)
175 }
176
177 dir2 := t.TempDir()
178
179 for i, test := range []struct {
180 name string
181 target string
182 wantDir bool
183 }{{
184 name: "directory outside root",
185 target: dir2,
186 wantDir: false,
187 }, {
188 name: "directory inside root",
189 target: "dir",
190 wantDir: true,
191 }, {
192 name: "file inside root",
193 target: "file",
194 wantDir: false,
195 }, {
196 name: "nonexistent inside root",
197 target: "nonexistent",
198 wantDir: false,
199 }} {
200 t.Run(test.name, func(t *testing.T) {
201 name := fmt.Sprintf("symlink_%v", i)
202 if err := root.Symlink(test.target, name); err != nil {
203 t.Fatal(err)
204 }
205
206
207
208
209 nameu16, err := syscall.UTF16PtrFromString(filepath.Join(dir, name))
210 if err != nil {
211 t.Fatal(err)
212 }
213 h, err := syscall.CreateFile(nameu16, 0, 0, nil, syscall.OPEN_EXISTING,
214 syscall.FILE_FLAG_OPEN_REPARSE_POINT|syscall.FILE_FLAG_BACKUP_SEMANTICS, 0)
215 if err != nil {
216 t.Fatal(err)
217 }
218 defer syscall.CloseHandle(h)
219 var fi syscall.ByHandleFileInformation
220 if err := syscall.GetFileInformationByHandle(h, &fi); err != nil {
221 t.Fatal(err)
222 }
223 gotDir := fi.FileAttributes&syscall.FILE_ATTRIBUTE_DIRECTORY != 0
224
225 if got, want := gotDir, test.wantDir; got != want {
226 t.Errorf("link target %q: isDir = %v, want %v", test.target, got, want)
227 }
228 })
229 }
230 }
231
View as plain text