Source file
src/path/filepath/path_windows_test.go
1
2
3
4
5 package filepath_test
6
7 import (
8 "flag"
9 "fmt"
10 "internal/testenv"
11 "io/ioutil"
12 "os"
13 "os/exec"
14 "path/filepath"
15 "reflect"
16 "runtime/debug"
17 "strings"
18 "testing"
19 )
20
21 func TestWinSplitListTestsAreValid(t *testing.T) {
22 comspec := os.Getenv("ComSpec")
23 if comspec == "" {
24 t.Fatal("%ComSpec% must be set")
25 }
26
27 for ti, tt := range winsplitlisttests {
28 testWinSplitListTestIsValid(t, ti, tt, comspec)
29 }
30 }
31
32 func testWinSplitListTestIsValid(t *testing.T, ti int, tt SplitListTest,
33 comspec string) {
34
35 const (
36 cmdfile = `printdir.cmd`
37 perm os.FileMode = 0700
38 )
39
40 tmp, err := ioutil.TempDir("", "testWinSplitListTestIsValid")
41 if err != nil {
42 t.Fatalf("TempDir failed: %v", err)
43 }
44 defer os.RemoveAll(tmp)
45
46 for i, d := range tt.result {
47 if d == "" {
48 continue
49 }
50 if cd := filepath.Clean(d); filepath.VolumeName(cd) != "" ||
51 cd[0] == '\\' || cd == ".." || (len(cd) >= 3 && cd[0:3] == `..\`) {
52 t.Errorf("%d,%d: %#q refers outside working directory", ti, i, d)
53 return
54 }
55 dd := filepath.Join(tmp, d)
56 if _, err := os.Stat(dd); err == nil {
57 t.Errorf("%d,%d: %#q already exists", ti, i, d)
58 return
59 }
60 if err = os.MkdirAll(dd, perm); err != nil {
61 t.Errorf("%d,%d: MkdirAll(%#q) failed: %v", ti, i, dd, err)
62 return
63 }
64 fn, data := filepath.Join(dd, cmdfile), []byte("@echo "+d+"\r\n")
65 if err = ioutil.WriteFile(fn, data, perm); err != nil {
66 t.Errorf("%d,%d: WriteFile(%#q) failed: %v", ti, i, fn, err)
67 return
68 }
69 }
70
71
72 systemRoot := os.Getenv("SystemRoot")
73
74 for i, d := range tt.result {
75 if d == "" {
76 continue
77 }
78 exp := []byte(d + "\r\n")
79 cmd := &exec.Cmd{
80 Path: comspec,
81 Args: []string{`/c`, cmdfile},
82 Env: []string{`Path=` + systemRoot + "/System32;" + tt.list, `SystemRoot=` + systemRoot},
83 Dir: tmp,
84 }
85 out, err := cmd.CombinedOutput()
86 switch {
87 case err != nil:
88 t.Errorf("%d,%d: execution error %v\n%q", ti, i, err, out)
89 return
90 case !reflect.DeepEqual(out, exp):
91 t.Errorf("%d,%d: expected %#q, got %#q", ti, i, exp, out)
92 return
93 default:
94
95 err = os.Remove(filepath.Join(tmp, d, cmdfile))
96 if err != nil {
97 t.Fatalf("Remove test command failed: %v", err)
98 }
99 }
100 }
101 }
102
103 func TestWindowsEvalSymlinks(t *testing.T) {
104 testenv.MustHaveSymlink(t)
105
106 tmpDir, err := ioutil.TempDir("", "TestWindowsEvalSymlinks")
107 if err != nil {
108 t.Fatal(err)
109 }
110 defer os.RemoveAll(tmpDir)
111
112
113
114 tmpDir, err = filepath.EvalSymlinks(tmpDir)
115 if err != nil {
116 t.Fatal(err)
117 }
118
119 if len(tmpDir) < 3 {
120 t.Fatalf("tmpDir path %q is too short", tmpDir)
121 }
122 if tmpDir[1] != ':' {
123 t.Fatalf("tmpDir path %q must have drive letter in it", tmpDir)
124 }
125 test := EvalSymlinksTest{"test/linkabswin", tmpDir[:3]}
126
127
128 testdirs := append(EvalSymlinksTestDirs, test)
129 for _, d := range testdirs {
130 var err error
131 path := simpleJoin(tmpDir, d.path)
132 if d.dest == "" {
133 err = os.Mkdir(path, 0755)
134 } else {
135 err = os.Symlink(d.dest, path)
136 }
137 if err != nil {
138 t.Fatal(err)
139 }
140 }
141
142 path := simpleJoin(tmpDir, test.path)
143
144 testEvalSymlinks(t, path, test.dest)
145
146 testEvalSymlinksAfterChdir(t, path, ".", test.dest)
147
148 testEvalSymlinksAfterChdir(t,
149 path,
150 filepath.VolumeName(tmpDir)+".",
151 test.dest)
152
153 testEvalSymlinksAfterChdir(t,
154 simpleJoin(tmpDir, "test"),
155 simpleJoin("..", test.path),
156 test.dest)
157
158 testEvalSymlinksAfterChdir(t, tmpDir, test.path, test.dest)
159 }
160
161
162
163 func TestEvalSymlinksCanonicalNames(t *testing.T) {
164 tmp, err := ioutil.TempDir("", "evalsymlinkcanonical")
165 if err != nil {
166 t.Fatal("creating temp dir:", err)
167 }
168 defer os.RemoveAll(tmp)
169
170
171 cTmpName, err := filepath.EvalSymlinks(tmp)
172 if err != nil {
173 t.Errorf("EvalSymlinks(%q) error: %v", tmp, err)
174 }
175
176 dirs := []string{
177 "test",
178 "test/dir",
179 "testing_long_dir",
180 "TEST2",
181 }
182
183 for _, d := range dirs {
184 dir := filepath.Join(cTmpName, d)
185 err := os.Mkdir(dir, 0755)
186 if err != nil {
187 t.Fatal(err)
188 }
189 cname, err := filepath.EvalSymlinks(dir)
190 if err != nil {
191 t.Errorf("EvalSymlinks(%q) error: %v", dir, err)
192 continue
193 }
194 if dir != cname {
195 t.Errorf("EvalSymlinks(%q) returns %q, but should return %q", dir, cname, dir)
196 continue
197 }
198
199 test := strings.ToUpper(dir)
200 p, err := filepath.EvalSymlinks(test)
201 if err != nil {
202 t.Errorf("EvalSymlinks(%q) error: %v", test, err)
203 continue
204 }
205 if p != cname {
206 t.Errorf("EvalSymlinks(%q) returns %q, but should return %q", test, p, cname)
207 continue
208 }
209
210 test = strings.ToLower(dir)
211 p, err = filepath.EvalSymlinks(test)
212 if err != nil {
213 t.Errorf("EvalSymlinks(%q) error: %v", test, err)
214 continue
215 }
216 if p != cname {
217 t.Errorf("EvalSymlinks(%q) returns %q, but should return %q", test, p, cname)
218 continue
219 }
220 }
221 }
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237 func checkVolume8dot3Setting(vol string, enabled bool) error {
238
239
240 out, _ := exec.Command("fsutil", "8dot3name", "query", vol).CombinedOutput()
241
242 expected := "The registry state of NtfsDisable8dot3NameCreation is 2, the default (Volume level setting)"
243 if !strings.Contains(string(out), expected) {
244
245 expectedWindow10 := "The registry state is: 2 (Per volume setting - the default)"
246 if !strings.Contains(string(out), expectedWindow10) {
247 return fmt.Errorf("fsutil output should contain %q, but is %q", expected, string(out))
248 }
249 }
250
251 expected = "Based on the above two settings, 8dot3 name creation is %s on %s"
252 if enabled {
253 expected = fmt.Sprintf(expected, "enabled", vol)
254 } else {
255 expected = fmt.Sprintf(expected, "disabled", vol)
256 }
257 if !strings.Contains(string(out), expected) {
258 return fmt.Errorf("unexpected fsutil output: %q", string(out))
259 }
260 return nil
261 }
262
263 func setVolume8dot3Setting(vol string, enabled bool) error {
264 cmd := []string{"fsutil", "8dot3name", "set", vol}
265 if enabled {
266 cmd = append(cmd, "0")
267 } else {
268 cmd = append(cmd, "1")
269 }
270
271
272 out, _ := exec.Command(cmd[0], cmd[1:]...).CombinedOutput()
273 if string(out) != "\r\nSuccessfully set 8dot3name behavior.\r\n" {
274
275 expectedWindow10 := "Successfully %s 8dot3name generation on %s\r\n"
276 if enabled {
277 expectedWindow10 = fmt.Sprintf(expectedWindow10, "enabled", vol)
278 } else {
279 expectedWindow10 = fmt.Sprintf(expectedWindow10, "disabled", vol)
280 }
281 if string(out) != expectedWindow10 {
282 return fmt.Errorf("%v command failed: %q", cmd, string(out))
283 }
284 }
285 return nil
286 }
287
288 var runFSModifyTests = flag.Bool("run_fs_modify_tests", false, "run tests which modify filesystem parameters")
289
290
291
292 func TestEvalSymlinksCanonicalNamesWith8dot3Disabled(t *testing.T) {
293 if !*runFSModifyTests {
294 t.Skip("skipping test that modifies file system setting; enable with -run_fs_modify_tests")
295 }
296 tempVol := filepath.VolumeName(os.TempDir())
297 if len(tempVol) != 2 {
298 t.Fatalf("unexpected temp volume name %q", tempVol)
299 }
300
301 err := checkVolume8dot3Setting(tempVol, true)
302 if err != nil {
303 t.Fatal(err)
304 }
305 err = setVolume8dot3Setting(tempVol, false)
306 if err != nil {
307 t.Fatal(err)
308 }
309 defer func() {
310 err := setVolume8dot3Setting(tempVol, true)
311 if err != nil {
312 t.Fatal(err)
313 }
314 err = checkVolume8dot3Setting(tempVol, true)
315 if err != nil {
316 t.Fatal(err)
317 }
318 }()
319 err = checkVolume8dot3Setting(tempVol, false)
320 if err != nil {
321 t.Fatal(err)
322 }
323 TestEvalSymlinksCanonicalNames(t)
324 }
325
326 func TestToNorm(t *testing.T) {
327 stubBase := func(path string) (string, error) {
328 vol := filepath.VolumeName(path)
329 path = path[len(vol):]
330
331 if strings.Contains(path, "/") {
332 return "", fmt.Errorf("invalid path is given to base: %s", vol+path)
333 }
334
335 if path == "" || path == "." || path == `\` {
336 return "", fmt.Errorf("invalid path is given to base: %s", vol+path)
337 }
338
339 i := strings.LastIndexByte(path, filepath.Separator)
340 if i == len(path)-1 {
341 return "", fmt.Errorf("invalid path is given to base: %s", vol+path)
342 }
343 if i == -1 {
344 return strings.ToUpper(path), nil
345 }
346
347 return strings.ToUpper(path[i+1:]), nil
348 }
349
350
351 tests := []struct {
352 arg string
353 want string
354 }{
355 {"", ""},
356 {".", "."},
357 {"./foo/bar", `FOO\BAR`},
358 {"/", `\`},
359 {"/foo/bar", `\FOO\BAR`},
360 {"/foo/bar/baz/qux", `\FOO\BAR\BAZ\QUX`},
361 {"foo/bar", `FOO\BAR`},
362 {"C:/foo/bar", `C:\FOO\BAR`},
363 {"C:foo/bar", `C:FOO\BAR`},
364 {"c:/foo/bar", `C:\FOO\BAR`},
365 {"C:/foo/bar", `C:\FOO\BAR`},
366 {"C:/foo/bar/", `C:\FOO\BAR`},
367 {`C:\foo\bar`, `C:\FOO\BAR`},
368 {`C:\foo/bar\`, `C:\FOO\BAR`},
369 {"C:/ふー/バー", `C:\ふー\バー`},
370 }
371
372 for _, test := range tests {
373 got, err := filepath.ToNorm(test.arg, stubBase)
374 if err != nil {
375 t.Errorf("toNorm(%s) failed: %v\n", test.arg, err)
376 } else if got != test.want {
377 t.Errorf("toNorm(%s) returns %s, but %s expected\n", test.arg, got, test.want)
378 }
379 }
380
381 testPath := `{{tmp}}\test\foo\bar`
382
383 testsDir := []struct {
384 wd string
385 arg string
386 want string
387 }{
388
389 {".", `{{tmp}}\test\foo\bar`, `{{tmp}}\test\foo\bar`},
390 {".", `{{tmp}}\.\test/foo\bar`, `{{tmp}}\test\foo\bar`},
391 {".", `{{tmp}}\test\..\test\foo\bar`, `{{tmp}}\test\foo\bar`},
392 {".", `{{tmp}}\TEST\FOO\BAR`, `{{tmp}}\test\foo\bar`},
393
394
395 {`{{tmp}}\test`, `{{tmpvol}}.`, `{{tmpvol}}.`},
396 {`{{tmp}}\test`, `{{tmpvol}}..`, `{{tmpvol}}..`},
397 {`{{tmp}}\test`, `{{tmpvol}}foo\bar`, `{{tmpvol}}foo\bar`},
398 {`{{tmp}}\test`, `{{tmpvol}}.\foo\bar`, `{{tmpvol}}foo\bar`},
399 {`{{tmp}}\test`, `{{tmpvol}}foo\..\foo\bar`, `{{tmpvol}}foo\bar`},
400 {`{{tmp}}\test`, `{{tmpvol}}FOO\BAR`, `{{tmpvol}}foo\bar`},
401
402
403 {"{{tmp}}", `{{tmpnovol}}\test\foo\bar`, `{{tmpnovol}}\test\foo\bar`},
404 {"{{tmp}}", `{{tmpnovol}}\.\test\foo\bar`, `{{tmpnovol}}\test\foo\bar`},
405 {"{{tmp}}", `{{tmpnovol}}\test\..\test\foo\bar`, `{{tmpnovol}}\test\foo\bar`},
406 {"{{tmp}}", `{{tmpnovol}}\TEST\FOO\BAR`, `{{tmpnovol}}\test\foo\bar`},
407
408
409 {`{{tmp}}\test`, ".", `.`},
410 {`{{tmp}}\test`, "..", `..`},
411 {`{{tmp}}\test`, `foo\bar`, `foo\bar`},
412 {`{{tmp}}\test`, `.\foo\bar`, `foo\bar`},
413 {`{{tmp}}\test`, `foo\..\foo\bar`, `foo\bar`},
414 {`{{tmp}}\test`, `FOO\BAR`, `foo\bar`},
415 }
416
417 tmp, err := ioutil.TempDir("", "testToNorm")
418 if err != nil {
419 t.Fatal(err)
420 }
421 defer func() {
422 err := os.RemoveAll(tmp)
423 if err != nil {
424 t.Fatal(err)
425 }
426 }()
427
428
429 ctmp, err := filepath.EvalSymlinks(tmp)
430 if err != nil {
431 t.Fatal(err)
432 }
433
434 err = os.MkdirAll(strings.ReplaceAll(testPath, "{{tmp}}", ctmp), 0777)
435 if err != nil {
436 t.Fatal(err)
437 }
438
439 cwd, err := os.Getwd()
440 if err != nil {
441 t.Fatal(err)
442 }
443 defer func() {
444 err := os.Chdir(cwd)
445 if err != nil {
446 t.Fatal(err)
447 }
448 }()
449
450 tmpVol := filepath.VolumeName(ctmp)
451 if len(tmpVol) != 2 {
452 t.Fatalf("unexpected temp volume name %q", tmpVol)
453 }
454
455 tmpNoVol := ctmp[len(tmpVol):]
456
457 replacer := strings.NewReplacer("{{tmp}}", ctmp, "{{tmpvol}}", tmpVol, "{{tmpnovol}}", tmpNoVol)
458
459 for _, test := range testsDir {
460 wd := replacer.Replace(test.wd)
461 arg := replacer.Replace(test.arg)
462 want := replacer.Replace(test.want)
463
464 if test.wd == "." {
465 err := os.Chdir(cwd)
466 if err != nil {
467 t.Error(err)
468
469 continue
470 }
471 } else {
472 err := os.Chdir(wd)
473 if err != nil {
474 t.Error(err)
475
476 continue
477 }
478 }
479
480 got, err := filepath.ToNorm(arg, filepath.NormBase)
481 if err != nil {
482 t.Errorf("toNorm(%s) failed: %v (wd=%s)\n", arg, err, wd)
483 } else if got != want {
484 t.Errorf("toNorm(%s) returns %s, but %s expected (wd=%s)\n", arg, got, want, wd)
485 }
486 }
487 }
488
489 func TestUNC(t *testing.T) {
490
491
492 defer debug.SetMaxStack(debug.SetMaxStack(1e6))
493 filepath.Glob(`\\?\c:\*`)
494 }
495
496 func testWalkMklink(t *testing.T, linktype string) {
497 output, _ := exec.Command("cmd", "/c", "mklink", "/?").Output()
498 if !strings.Contains(string(output), fmt.Sprintf(" /%s ", linktype)) {
499 t.Skipf(`skipping test; mklink does not supports /%s parameter`, linktype)
500 }
501 testWalkSymlink(t, func(target, link string) error {
502 output, err := exec.Command("cmd", "/c", "mklink", "/"+linktype, link, target).CombinedOutput()
503 if err != nil {
504 return fmt.Errorf(`"mklink /%s %v %v" command failed: %v\n%v`, linktype, link, target, err, string(output))
505 }
506 return nil
507 })
508 }
509
510 func TestWalkDirectoryJunction(t *testing.T) {
511 testenv.MustHaveSymlink(t)
512 testWalkMklink(t, "J")
513 }
514
515 func TestWalkDirectorySymlink(t *testing.T) {
516 testenv.MustHaveSymlink(t)
517 testWalkMklink(t, "D")
518 }
519
520 func TestNTNamespaceSymlink(t *testing.T) {
521 output, _ := exec.Command("cmd", "/c", "mklink", "/?").Output()
522 if !strings.Contains(string(output), " /J ") {
523 t.Skip("skipping test because mklink command does not support junctions")
524 }
525
526 tmpdir, err := ioutil.TempDir("", "TestNTNamespaceSymlink")
527 if err != nil {
528 t.Fatal(err)
529 }
530 defer os.RemoveAll(tmpdir)
531
532
533 tmpdir, err = filepath.EvalSymlinks(tmpdir)
534 if err != nil {
535 t.Fatal(err)
536 }
537
538 vol := filepath.VolumeName(tmpdir)
539 output, err = exec.Command("cmd", "/c", "mountvol", vol, "/L").CombinedOutput()
540 if err != nil {
541 t.Fatalf("failed to run mountvol %v /L: %v %q", vol, err, output)
542 }
543 target := strings.Trim(string(output), " \n\r")
544
545 dirlink := filepath.Join(tmpdir, "dirlink")
546 output, err = exec.Command("cmd", "/c", "mklink", "/J", dirlink, target).CombinedOutput()
547 if err != nil {
548 t.Fatalf("failed to run mklink %v %v: %v %q", dirlink, target, err, output)
549 }
550
551 got, err := filepath.EvalSymlinks(dirlink)
552 if err != nil {
553 t.Fatal(err)
554 }
555 if want := vol + `\`; got != want {
556 t.Errorf(`EvalSymlinks(%q): got %q, want %q`, dirlink, got, want)
557 }
558
559
560 testenv.MustHaveSymlink(t)
561
562 file := filepath.Join(tmpdir, "file")
563 err = ioutil.WriteFile(file, []byte(""), 0666)
564 if err != nil {
565 t.Fatal(err)
566 }
567
568 target += file[len(filepath.VolumeName(file)):]
569
570 filelink := filepath.Join(tmpdir, "filelink")
571 output, err = exec.Command("cmd", "/c", "mklink", filelink, target).CombinedOutput()
572 if err != nil {
573 t.Fatalf("failed to run mklink %v %v: %v %q", filelink, target, err, output)
574 }
575
576 got, err = filepath.EvalSymlinks(filelink)
577 if err != nil {
578 t.Fatal(err)
579 }
580 if want := file; got != want {
581 t.Errorf(`EvalSymlinks(%q): got %q, want %q`, filelink, got, want)
582 }
583 }
584
View as plain text