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