github.com/lovishpuri/go-40569/src@v0.0.0-20230519171745-f8623e7c56cf/os/removeall_test.go (about) 1 // Copyright 2018 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 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 // Make a regular file and remove 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 // Make directory with 1 file and remove. 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 // Make directory with file and subdirectory and remove. 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 // Chmod is not supported under Windows or wasip1 and test fails as root. 83 if runtime.GOOS != "windows" && runtime.GOOS != "wasip1" && Getuid() != 0 { 84 // Make directory with file and subdirectory and trigger error. 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 // No error checking here: either RemoveAll 101 // will or won't be able to remove dpath; 102 // either way we want to see if it removes fpath 103 // and path/zzz. Reasons why RemoveAll might 104 // succeed in removing dpath as well include: 105 // * running as root 106 // * running on a file system without permissions (FAT) 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 // Test RemoveAll on a large directory. 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 // Make directory with 1000 files and remove. 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 prevDir, err := Getwd() 163 if err != nil { 164 t.Fatalf("Could not get wd: %s", err) 165 } 166 167 startPath, err := MkdirTemp("", "TestRemoveAllLongPath-") 168 if err != nil { 169 t.Fatalf("Could not create TempDir: %s", err) 170 } 171 defer RemoveAll(startPath) 172 173 err = Chdir(startPath) 174 if err != nil { 175 t.Fatalf("Could not chdir %s: %s", startPath, err) 176 } 177 178 // Removing paths with over 4096 chars commonly fails 179 for i := 0; i < 41; i++ { 180 name := strings.Repeat("a", 100) 181 182 err = Mkdir(name, 0755) 183 if err != nil { 184 t.Fatalf("Could not mkdir %s: %s", name, err) 185 } 186 187 err = Chdir(name) 188 if err != nil { 189 t.Fatalf("Could not chdir %s: %s", name, err) 190 } 191 } 192 193 err = Chdir(prevDir) 194 if err != nil { 195 t.Fatalf("Could not chdir %s: %s", prevDir, err) 196 } 197 198 err = RemoveAll(startPath) 199 if err != nil { 200 t.Errorf("RemoveAll could not remove long file path %s: %s", startPath, err) 201 } 202 } 203 204 func TestRemoveAllDot(t *testing.T) { 205 prevDir, err := Getwd() 206 if err != nil { 207 t.Fatalf("Could not get wd: %s", err) 208 } 209 tempDir, err := MkdirTemp("", "TestRemoveAllDot-") 210 if err != nil { 211 t.Fatalf("Could not create TempDir: %s", err) 212 } 213 defer RemoveAll(tempDir) 214 215 err = Chdir(tempDir) 216 if err != nil { 217 t.Fatalf("Could not chdir to tempdir: %s", err) 218 } 219 220 err = RemoveAll(".") 221 if err == nil { 222 t.Errorf("RemoveAll succeed to remove .") 223 } 224 225 err = Chdir(prevDir) 226 if err != nil { 227 t.Fatalf("Could not chdir %s: %s", prevDir, err) 228 } 229 } 230 231 func TestRemoveAllDotDot(t *testing.T) { 232 t.Parallel() 233 234 tempDir := t.TempDir() 235 subdir := filepath.Join(tempDir, "x") 236 subsubdir := filepath.Join(subdir, "y") 237 if err := MkdirAll(subsubdir, 0777); err != nil { 238 t.Fatal(err) 239 } 240 if err := RemoveAll(filepath.Join(subsubdir, "..")); err != nil { 241 t.Error(err) 242 } 243 for _, dir := range []string{subsubdir, subdir} { 244 if _, err := Stat(dir); err == nil { 245 t.Errorf("%s: exists after RemoveAll", dir) 246 } 247 } 248 } 249 250 // Issue #29178. 251 func TestRemoveReadOnlyDir(t *testing.T) { 252 t.Parallel() 253 254 tempDir := t.TempDir() 255 subdir := filepath.Join(tempDir, "x") 256 if err := Mkdir(subdir, 0); err != nil { 257 t.Fatal(err) 258 } 259 260 // If an error occurs make it more likely that removing the 261 // temporary directory will succeed. 262 defer Chmod(subdir, 0777) 263 264 if err := RemoveAll(subdir); err != nil { 265 t.Fatal(err) 266 } 267 268 if _, err := Stat(subdir); err == nil { 269 t.Error("subdirectory was not removed") 270 } 271 } 272 273 // Issue #29983. 274 func TestRemoveAllButReadOnlyAndPathError(t *testing.T) { 275 switch runtime.GOOS { 276 case "js", "wasip1", "windows": 277 t.Skipf("skipping test on %s", runtime.GOOS) 278 } 279 280 if Getuid() == 0 { 281 t.Skip("skipping test when running as root") 282 } 283 284 t.Parallel() 285 286 tempDir := t.TempDir() 287 dirs := []string{ 288 "a", 289 "a/x", 290 "a/x/1", 291 "b", 292 "b/y", 293 "b/y/2", 294 "c", 295 "c/z", 296 "c/z/3", 297 } 298 readonly := []string{ 299 "b", 300 } 301 inReadonly := func(d string) bool { 302 for _, ro := range readonly { 303 if d == ro { 304 return true 305 } 306 dd, _ := filepath.Split(d) 307 if filepath.Clean(dd) == ro { 308 return true 309 } 310 } 311 return false 312 } 313 314 for _, dir := range dirs { 315 if err := Mkdir(filepath.Join(tempDir, dir), 0777); err != nil { 316 t.Fatal(err) 317 } 318 } 319 for _, dir := range readonly { 320 d := filepath.Join(tempDir, dir) 321 if err := Chmod(d, 0555); err != nil { 322 t.Fatal(err) 323 } 324 325 // Defer changing the mode back so that the deferred 326 // RemoveAll(tempDir) can succeed. 327 defer Chmod(d, 0777) 328 } 329 330 err := RemoveAll(tempDir) 331 if err == nil { 332 t.Fatal("RemoveAll succeeded unexpectedly") 333 } 334 335 // The error should be of type *PathError. 336 // see issue 30491 for details. 337 if pathErr, ok := err.(*PathError); ok { 338 want := filepath.Join(tempDir, "b", "y") 339 if pathErr.Path != want { 340 t.Errorf("RemoveAll(%q): err.Path=%q, want %q", tempDir, pathErr.Path, want) 341 } 342 } else { 343 t.Errorf("RemoveAll(%q): error has type %T, want *fs.PathError", tempDir, err) 344 } 345 346 for _, dir := range dirs { 347 _, err := Stat(filepath.Join(tempDir, dir)) 348 if inReadonly(dir) { 349 if err != nil { 350 t.Errorf("file %q was deleted but should still exist", dir) 351 } 352 } else { 353 if err == nil { 354 t.Errorf("file %q still exists but should have been deleted", dir) 355 } 356 } 357 } 358 } 359 360 func TestRemoveUnreadableDir(t *testing.T) { 361 switch runtime.GOOS { 362 case "js": 363 t.Skipf("skipping test on %s", runtime.GOOS) 364 } 365 366 if Getuid() == 0 { 367 t.Skip("skipping test when running as root") 368 } 369 370 t.Parallel() 371 372 tempDir := t.TempDir() 373 target := filepath.Join(tempDir, "d0", "d1", "d2") 374 if err := MkdirAll(target, 0755); err != nil { 375 t.Fatal(err) 376 } 377 if err := Chmod(target, 0300); err != nil { 378 t.Fatal(err) 379 } 380 if err := RemoveAll(filepath.Join(tempDir, "d0")); err != nil { 381 t.Fatal(err) 382 } 383 } 384 385 // Issue 29921 386 func TestRemoveAllWithMoreErrorThanReqSize(t *testing.T) { 387 if testing.Short() { 388 t.Skip("skipping in short mode") 389 } 390 t.Parallel() 391 392 tmpDir := t.TempDir() 393 path := filepath.Join(tmpDir, "_TestRemoveAllWithMoreErrorThanReqSize_") 394 395 // Make directory with 1025 read-only files. 396 if err := MkdirAll(path, 0777); err != nil { 397 t.Fatalf("MkdirAll %q: %s", path, err) 398 } 399 for i := 0; i < 1025; i++ { 400 fpath := filepath.Join(path, fmt.Sprintf("file%d", i)) 401 fd, err := Create(fpath) 402 if err != nil { 403 t.Fatalf("create %q: %s", fpath, err) 404 } 405 fd.Close() 406 } 407 408 // Make the parent directory read-only. On some platforms, this is what 409 // prevents Remove from removing the files within that directory. 410 if err := Chmod(path, 0555); err != nil { 411 t.Fatal(err) 412 } 413 defer Chmod(path, 0755) 414 415 // This call should not hang, even on a platform that disallows file deletion 416 // from read-only directories. 417 err := RemoveAll(path) 418 419 if Getuid() == 0 { 420 // On many platforms, root can remove files from read-only directories. 421 return 422 } 423 if err == nil { 424 if runtime.GOOS == "windows" || runtime.GOOS == "wasip1" { 425 // Marking a directory as read-only in Windows does not prevent the RemoveAll 426 // from creating or removing files within it. 427 // 428 // For wasip1, there is no support for file permissions so we cannot prevent 429 // RemoveAll from removing the files. 430 return 431 } 432 t.Fatal("RemoveAll(<read-only directory>) = nil; want error") 433 } 434 435 dir, err := Open(path) 436 if err != nil { 437 t.Fatal(err) 438 } 439 defer dir.Close() 440 441 names, _ := dir.Readdirnames(1025) 442 if len(names) < 1025 { 443 t.Fatalf("RemoveAll(<read-only directory>) unexpectedly removed %d read-only files from that directory", 1025-len(names)) 444 } 445 } 446 447 func TestRemoveAllNoFcntl(t *testing.T) { 448 if testing.Short() { 449 t.Skip("skipping in short mode") 450 } 451 452 const env = "GO_TEST_REMOVE_ALL_NO_FCNTL" 453 if dir := Getenv(env); dir != "" { 454 if err := RemoveAll(dir); err != nil { 455 t.Fatal(err) 456 } 457 return 458 } 459 460 // Only test on Linux so that we can assume we have strace. 461 // The code is OS-independent so if it passes on Linux 462 // it should pass on other Unix systems. 463 if runtime.GOOS != "linux" { 464 t.Skipf("skipping test on %s", runtime.GOOS) 465 } 466 if _, err := Stat("/bin/strace"); err != nil { 467 t.Skipf("skipping test because /bin/strace not found: %v", err) 468 } 469 me, err := Executable() 470 if err != nil { 471 t.Skipf("skipping because Executable failed: %v", err) 472 } 473 474 // Create 100 directories. 475 // The test is that we can remove them without calling fcntl 476 // on each one. 477 tmpdir := t.TempDir() 478 subdir := filepath.Join(tmpdir, "subdir") 479 if err := Mkdir(subdir, 0o755); err != nil { 480 t.Fatal(err) 481 } 482 for i := 0; i < 100; i++ { 483 subsubdir := filepath.Join(subdir, strconv.Itoa(i)) 484 if err := Mkdir(filepath.Join(subdir, strconv.Itoa(i)), 0o755); err != nil { 485 t.Fatal(err) 486 } 487 if err := WriteFile(filepath.Join(subsubdir, "file"), nil, 0o644); err != nil { 488 t.Fatal(err) 489 } 490 } 491 492 cmd := testenv.Command(t, "/bin/strace", "-f", "-e", "fcntl", me, "-test.run=TestRemoveAllNoFcntl") 493 cmd = testenv.CleanCmdEnv(cmd) 494 cmd.Env = append(cmd.Env, env+"="+subdir) 495 out, err := cmd.CombinedOutput() 496 if len(out) > 0 { 497 t.Logf("%s", out) 498 } 499 if err != nil { 500 t.Fatal(err) 501 } 502 503 if got := bytes.Count(out, []byte("fcntl")); got >= 100 { 504 t.Errorf("found %d fcntl calls, want < 100", got) 505 } 506 }