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