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