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