rm.go 1.9 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879
  1. //go:build !darwin && !windows
  2. // +build !darwin,!windows
  3. package containerfs // import "github.com/docker/docker/pkg/containerfs"
  4. import (
  5. "os"
  6. "syscall"
  7. "time"
  8. "github.com/moby/sys/mount"
  9. "github.com/pkg/errors"
  10. )
  11. // EnsureRemoveAll wraps `os.RemoveAll` to check for specific errors that can
  12. // often be remedied.
  13. // Only use `EnsureRemoveAll` if you really want to make every effort to remove
  14. // a directory.
  15. //
  16. // Because of the way `os.Remove` (and by extension `os.RemoveAll`) works, there
  17. // can be a race between reading directory entries and then actually attempting
  18. // to remove everything in the directory.
  19. // These types of errors do not need to be returned since it's ok for the dir to
  20. // be gone we can just retry the remove operation.
  21. //
  22. // This should not return a `os.ErrNotExist` kind of error under any circumstances
  23. func EnsureRemoveAll(dir string) error {
  24. notExistErr := make(map[string]bool)
  25. // track retries
  26. exitOnErr := make(map[string]int)
  27. maxRetry := 50
  28. // Attempt to unmount anything beneath this dir first
  29. mount.RecursiveUnmount(dir)
  30. for {
  31. err := os.RemoveAll(dir)
  32. if err == nil {
  33. return nil
  34. }
  35. pe, ok := err.(*os.PathError)
  36. if !ok {
  37. return err
  38. }
  39. if os.IsNotExist(err) {
  40. if notExistErr[pe.Path] {
  41. return err
  42. }
  43. notExistErr[pe.Path] = true
  44. // There is a race where some subdir can be removed but after the parent
  45. // dir entries have been read.
  46. // So the path could be from `os.Remove(subdir)`
  47. // If the reported non-existent path is not the passed in `dir` we
  48. // should just retry, but otherwise return with no error.
  49. if pe.Path == dir {
  50. return nil
  51. }
  52. continue
  53. }
  54. if pe.Err != syscall.EBUSY {
  55. return err
  56. }
  57. if e := mount.Unmount(pe.Path); e != nil {
  58. return errors.Wrapf(e, "error while removing %s", dir)
  59. }
  60. if exitOnErr[pe.Path] == maxRetry {
  61. return err
  62. }
  63. exitOnErr[pe.Path]++
  64. time.Sleep(100 * time.Millisecond)
  65. }
  66. }