rm_unix.go 1.9 KB

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