Source file
src/os/removeall_test.go
Documentation: os
1
2
3
4
5 package os_test
6
7 import (
8 "bytes"
9 "errors"
10 "fmt"
11 "internal/testenv"
12 . "os"
13 "path/filepath"
14 "runtime"
15 "strconv"
16 "strings"
17 "testing"
18 )
19
20 func TestRemoveAll(t *testing.T) {
21 t.Parallel()
22
23 tmpDir := t.TempDir()
24 if err := RemoveAll(""); err != nil {
25 t.Errorf("RemoveAll(\"\"): %v; want nil", err)
26 }
27
28 file := filepath.Join(tmpDir, "file")
29 path := filepath.Join(tmpDir, "_TestRemoveAll_")
30 fpath := filepath.Join(path, "file")
31 dpath := filepath.Join(path, "dir")
32
33
34 fd, err := Create(file)
35 if err != nil {
36 t.Fatalf("create %q: %s", file, err)
37 }
38 fd.Close()
39 if err = RemoveAll(file); err != nil {
40 t.Fatalf("RemoveAll %q (first): %s", file, err)
41 }
42 if _, err = Lstat(file); err == nil {
43 t.Fatalf("Lstat %q succeeded after RemoveAll (first)", file)
44 }
45
46
47 if err := MkdirAll(path, 0777); err != nil {
48 t.Fatalf("MkdirAll %q: %s", path, err)
49 }
50 fd, err = Create(fpath)
51 if err != nil {
52 t.Fatalf("create %q: %s", fpath, err)
53 }
54 fd.Close()
55 if err = RemoveAll(path); err != nil {
56 t.Fatalf("RemoveAll %q (second): %s", path, err)
57 }
58 if _, err = Lstat(path); err == nil {
59 t.Fatalf("Lstat %q succeeded after RemoveAll (second)", path)
60 }
61
62
63 if err = MkdirAll(dpath, 0777); err != nil {
64 t.Fatalf("MkdirAll %q: %s", dpath, err)
65 }
66 fd, err = Create(fpath)
67 if err != nil {
68 t.Fatalf("create %q: %s", fpath, err)
69 }
70 fd.Close()
71 fd, err = Create(dpath + "/file")
72 if err != nil {
73 t.Fatalf("create %q: %s", fpath, err)
74 }
75 fd.Close()
76 if err = RemoveAll(path); err != nil {
77 t.Fatalf("RemoveAll %q (third): %s", path, err)
78 }
79 if _, err := Lstat(path); err == nil {
80 t.Fatalf("Lstat %q succeeded after RemoveAll (third)", path)
81 }
82
83
84 if runtime.GOOS != "windows" && runtime.GOOS != "wasip1" && Getuid() != 0 {
85
86 if err = MkdirAll(dpath, 0777); err != nil {
87 t.Fatalf("MkdirAll %q: %s", dpath, err)
88 }
89
90 for _, s := range []string{fpath, dpath + "/file1", path + "/zzz"} {
91 fd, err = Create(s)
92 if err != nil {
93 t.Fatalf("create %q: %s", s, err)
94 }
95 fd.Close()
96 }
97 if err = Chmod(dpath, 0); err != nil {
98 t.Fatalf("Chmod %q 0: %s", dpath, err)
99 }
100
101
102
103
104
105
106
107
108 RemoveAll(path)
109 Chmod(dpath, 0777)
110
111 for _, s := range []string{fpath, path + "/zzz"} {
112 if _, err = Lstat(s); err == nil {
113 t.Fatalf("Lstat %q succeeded after partial RemoveAll", s)
114 }
115 }
116 }
117 if err = RemoveAll(path); err != nil {
118 t.Fatalf("RemoveAll %q after partial RemoveAll: %s", path, err)
119 }
120 if _, err = Lstat(path); err == nil {
121 t.Fatalf("Lstat %q succeeded after RemoveAll (final)", path)
122 }
123 }
124
125
126 func TestRemoveAllLarge(t *testing.T) {
127 if testing.Short() {
128 t.Skip("skipping in short mode")
129 }
130 t.Parallel()
131
132 tmpDir := t.TempDir()
133 path := filepath.Join(tmpDir, "_TestRemoveAllLarge_")
134
135
136 if err := MkdirAll(path, 0777); err != nil {
137 t.Fatalf("MkdirAll %q: %s", path, err)
138 }
139 for i := 0; i < 1000; i++ {
140 fpath := fmt.Sprintf("%s/file%d", path, i)
141 fd, err := Create(fpath)
142 if err != nil {
143 t.Fatalf("create %q: %s", fpath, err)
144 }
145 fd.Close()
146 }
147 if err := RemoveAll(path); err != nil {
148 t.Fatalf("RemoveAll %q: %s", path, err)
149 }
150 if _, err := Lstat(path); err == nil {
151 t.Fatalf("Lstat %q succeeded after RemoveAll", path)
152 }
153 }
154
155 func TestRemoveAllLongPath(t *testing.T) {
156 switch runtime.GOOS {
157 case "aix", "darwin", "ios", "dragonfly", "freebsd", "linux", "netbsd", "openbsd", "illumos", "solaris":
158 break
159 default:
160 t.Skip("skipping for not implemented platforms")
161 }
162
163 startPath := t.TempDir()
164 t.Chdir(startPath)
165
166
167 name := strings.Repeat("a", 100)
168 for i := 0; i < 41; i++ {
169 if err := Mkdir(name, 0755); err != nil {
170 t.Fatalf("Could not mkdir %s: %s", name, err)
171 }
172 if err := Chdir(name); err != nil {
173 t.Fatalf("Could not chdir %s: %s", name, err)
174 }
175 }
176
177
178
179 err := Chdir(filepath.Join(startPath, ".."))
180 if err != nil {
181 t.Fatalf("Could not chdir: %s", err)
182 }
183
184 err = RemoveAll(startPath)
185 if err != nil {
186 t.Errorf("RemoveAll could not remove long file path %s: %s", startPath, err)
187 }
188 }
189
190 func TestRemoveAllDot(t *testing.T) {
191 t.Chdir(t.TempDir())
192
193 if err := RemoveAll("."); err == nil {
194 t.Errorf("RemoveAll succeed to remove .")
195 }
196 }
197
198 func TestRemoveAllDotDot(t *testing.T) {
199 t.Parallel()
200
201 tempDir := t.TempDir()
202 subdir := filepath.Join(tempDir, "x")
203 subsubdir := filepath.Join(subdir, "y")
204 if err := MkdirAll(subsubdir, 0777); err != nil {
205 t.Fatal(err)
206 }
207 if err := RemoveAll(filepath.Join(subsubdir, "..")); err != nil {
208 t.Error(err)
209 }
210 for _, dir := range []string{subsubdir, subdir} {
211 if _, err := Stat(dir); err == nil {
212 t.Errorf("%s: exists after RemoveAll", dir)
213 }
214 }
215 }
216
217
218 func TestRemoveReadOnlyDir(t *testing.T) {
219 t.Parallel()
220
221 tempDir := t.TempDir()
222 subdir := filepath.Join(tempDir, "x")
223 if err := Mkdir(subdir, 0); err != nil {
224 t.Fatal(err)
225 }
226
227
228
229 defer Chmod(subdir, 0777)
230
231 if err := RemoveAll(subdir); err != nil {
232 t.Fatal(err)
233 }
234
235 if _, err := Stat(subdir); err == nil {
236 t.Error("subdirectory was not removed")
237 }
238 }
239
240
241 func TestRemoveAllButReadOnlyAndPathError(t *testing.T) {
242 testRequiresPermissions(t)
243
244 t.Parallel()
245
246 tempDir := t.TempDir()
247 dirs := []string{
248 "a",
249 "a/x",
250 "a/x/1",
251 "b",
252 "b/y",
253 "b/y/2",
254 "c",
255 "c/z",
256 "c/z/3",
257 }
258 readonly := []string{
259 "b",
260 }
261 inReadonly := func(d string) bool {
262 for _, ro := range readonly {
263 if d == ro {
264 return true
265 }
266 dd, _ := filepath.Split(d)
267 if filepath.Clean(dd) == ro {
268 return true
269 }
270 }
271 return false
272 }
273
274 for _, dir := range dirs {
275 if err := Mkdir(filepath.Join(tempDir, dir), 0777); err != nil {
276 t.Fatal(err)
277 }
278 }
279 for _, dir := range readonly {
280 d := filepath.Join(tempDir, dir)
281 if err := Chmod(d, 0555); err != nil {
282 t.Fatal(err)
283 }
284
285
286
287 defer Chmod(d, 0777)
288 }
289
290 err := RemoveAll(tempDir)
291 if err == nil {
292 t.Fatal("RemoveAll succeeded unexpectedly")
293 }
294
295
296
297 if pathErr, ok := err.(*PathError); ok {
298 want := filepath.Join(tempDir, "b", "y")
299 if pathErr.Path != want {
300 t.Errorf("RemoveAll(%q): err.Path=%q, want %q", tempDir, pathErr.Path, want)
301 }
302 } else {
303 t.Errorf("RemoveAll(%q): error has type %T, want *fs.PathError", tempDir, err)
304 }
305
306 for _, dir := range dirs {
307 _, err := Stat(filepath.Join(tempDir, dir))
308 if inReadonly(dir) {
309 if err != nil {
310 t.Errorf("file %q was deleted but should still exist", dir)
311 }
312 } else {
313 if err == nil {
314 t.Errorf("file %q still exists but should have been deleted", dir)
315 }
316 }
317 }
318 }
319
320 func TestRemoveUnreadableDir(t *testing.T) {
321 switch runtime.GOOS {
322 case "js":
323 t.Skipf("skipping test on %s", runtime.GOOS)
324 }
325
326 if Getuid() == 0 {
327 t.Skip("skipping test when running as root")
328 }
329
330 t.Parallel()
331
332 tempDir := t.TempDir()
333 target := filepath.Join(tempDir, "d0", "d1", "d2")
334 if err := MkdirAll(target, 0755); err != nil {
335 t.Fatal(err)
336 }
337 if err := Chmod(target, 0300); err != nil {
338 t.Fatal(err)
339 }
340 if err := RemoveAll(filepath.Join(tempDir, "d0")); err != nil {
341 t.Fatal(err)
342 }
343 }
344
345
346 func TestRemoveAllWithMoreErrorThanReqSize(t *testing.T) {
347 if testing.Short() {
348 t.Skip("skipping in short mode")
349 }
350 t.Parallel()
351
352 tmpDir := t.TempDir()
353 path := filepath.Join(tmpDir, "_TestRemoveAllWithMoreErrorThanReqSize_")
354
355
356 if err := MkdirAll(path, 0777); err != nil {
357 t.Fatalf("MkdirAll %q: %s", path, err)
358 }
359 for i := 0; i < 1025; i++ {
360 fpath := filepath.Join(path, fmt.Sprintf("file%d", i))
361 fd, err := Create(fpath)
362 if err != nil {
363 t.Fatalf("create %q: %s", fpath, err)
364 }
365 fd.Close()
366 }
367
368
369
370 if err := Chmod(path, 0555); err != nil {
371 t.Fatal(err)
372 }
373 defer Chmod(path, 0755)
374
375
376
377 err := RemoveAll(path)
378
379 if Getuid() == 0 {
380
381 return
382 }
383 if err == nil {
384 if runtime.GOOS == "windows" || runtime.GOOS == "wasip1" {
385
386
387
388
389
390 return
391 }
392 t.Fatal("RemoveAll(<read-only directory>) = nil; want error")
393 }
394
395 dir, err := Open(path)
396 if err != nil {
397 t.Fatal(err)
398 }
399 defer dir.Close()
400
401 names, _ := dir.Readdirnames(1025)
402 if len(names) < 1025 {
403 t.Fatalf("RemoveAll(<read-only directory>) unexpectedly removed %d read-only files from that directory", 1025-len(names))
404 }
405 }
406
407 func TestRemoveAllNoFcntl(t *testing.T) {
408 if testing.Short() {
409 t.Skip("skipping in short mode")
410 }
411
412 const env = "GO_TEST_REMOVE_ALL_NO_FCNTL"
413 if dir := Getenv(env); dir != "" {
414 if err := RemoveAll(dir); err != nil {
415 t.Fatal(err)
416 }
417 return
418 }
419
420
421
422
423 if runtime.GOOS != "linux" {
424 t.Skipf("skipping test on %s", runtime.GOOS)
425 }
426 if _, err := Stat("/bin/strace"); err != nil {
427 t.Skipf("skipping test because /bin/strace not found: %v", err)
428 }
429 me, err := Executable()
430 if err != nil {
431 t.Skipf("skipping because Executable failed: %v", err)
432 }
433
434
435
436
437 tmpdir := t.TempDir()
438 subdir := filepath.Join(tmpdir, "subdir")
439 if err := Mkdir(subdir, 0o755); err != nil {
440 t.Fatal(err)
441 }
442 for i := 0; i < 100; i++ {
443 subsubdir := filepath.Join(subdir, strconv.Itoa(i))
444 if err := Mkdir(filepath.Join(subdir, strconv.Itoa(i)), 0o755); err != nil {
445 t.Fatal(err)
446 }
447 if err := WriteFile(filepath.Join(subsubdir, "file"), nil, 0o644); err != nil {
448 t.Fatal(err)
449 }
450 }
451
452 cmd := testenv.Command(t, "/bin/strace", "-f", "-e", "fcntl", me, "-test.run=^TestRemoveAllNoFcntl$")
453 cmd = testenv.CleanCmdEnv(cmd)
454 cmd.Env = append(cmd.Env, env+"="+subdir)
455 out, err := cmd.CombinedOutput()
456 if len(out) > 0 {
457 t.Logf("%s", out)
458 }
459 if err != nil {
460 t.Fatal(err)
461 }
462
463 if got := bytes.Count(out, []byte("fcntl")); got >= 100 {
464 t.Errorf("found %d fcntl calls, want < 100", got)
465 }
466 }
467
468 func TestRemoveAllTrailingSlash(t *testing.T) {
469 slashes := []string{"/"}
470 if runtime.GOOS == "windows" {
471 slashes = append(slashes, `\`)
472 }
473 for _, slash := range slashes {
474 dir := makefs(t, []string{
475 "dir/a/file1",
476 "dir/a/file2",
477 "dir/file3",
478 })
479 path := dir + "/dir"
480 if err := RemoveAll(path + slash); err != nil {
481 t.Fatal(err)
482 }
483 if _, err := Stat(path); !IsNotExist(err) {
484 t.Errorf("after RemoveAll(%q), directory still exists", path+slash)
485 }
486 }
487 }
488
489 func TestRemoveAllSymlinkRemovalFailure(t *testing.T) {
490 testRequiresPermissions(t)
491 dir := makefs(t, []string{
492 "parent/",
493 "parent/link => target",
494 })
495 if err := Chmod(dir+"/parent", 0o555); err != nil {
496 t.Fatal(err)
497 }
498 defer func() {
499 Chmod(dir+"/parent", 0o755)
500 }()
501
502 err := RemoveAll(dir + "/parent/link")
503 if !errors.Is(err, ErrPermission) {
504 t.Fatalf("RemoveAll = %v; want ErrPermission", err)
505 }
506
507 _ = err.Error()
508 }
509
510 func testRequiresPermissions(t *testing.T) {
511 t.Helper()
512 switch runtime.GOOS {
513 case "js", "wasip1", "windows":
514 t.Skipf("skipping test: %s does not support chmod", runtime.GOOS)
515 }
516 if Getuid() == 0 {
517 t.Skip("skipping test: running as root (which ignores file permissions)")
518 }
519 }
520
521 func BenchmarkRemoveAll(b *testing.B) {
522 tmpDir := filepath.Join(b.TempDir(), "target")
523 b.ReportAllocs()
524 b.ResetTimer()
525 for i := 0; i < b.N; i++ {
526 b.StopTimer()
527 err := CopyFS(tmpDir, DirFS("."))
528 if err != nil {
529 b.Fatal(err)
530 }
531 b.StartTimer()
532 if err := RemoveAll(tmpDir); err != nil {
533 b.Fatal(err)
534 }
535 }
536 }
537
View as plain text