rm.go 1.9 KB

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