mirror of
https://github.com/moby/moby.git
synced 2022-11-09 12:21:53 -05:00
2502db66d0
The tricks performed by EnsureRemoveAll only make sense for Linux and other Unices, so separate it out, and make EnsureRemoveAll for Windows just an alias of os.RemoveAll. This makes sure RecursiveUnmount is not called on Windows. Signed-off-by: Kir Kolyshkin <kolyshkin@gmail.com>
78 lines
1.9 KiB
Go
78 lines
1.9 KiB
Go
// +build !windows
|
|
|
|
package system // import "github.com/docker/docker/pkg/system"
|
|
|
|
import (
|
|
"os"
|
|
"syscall"
|
|
"time"
|
|
|
|
"github.com/moby/sys/mount"
|
|
"github.com/pkg/errors"
|
|
)
|
|
|
|
// EnsureRemoveAll wraps `os.RemoveAll` to check for specific errors that can
|
|
// often be remedied.
|
|
// Only use `EnsureRemoveAll` if you really want to make every effort to remove
|
|
// a directory.
|
|
//
|
|
// Because of the way `os.Remove` (and by extension `os.RemoveAll`) works, there
|
|
// can be a race between reading directory entries and then actually attempting
|
|
// to remove everything in the directory.
|
|
// These types of errors do not need to be returned since it's ok for the dir to
|
|
// be gone we can just retry the remove operation.
|
|
//
|
|
// This should not return a `os.ErrNotExist` kind of error under any circumstances
|
|
func EnsureRemoveAll(dir string) error {
|
|
notExistErr := make(map[string]bool)
|
|
|
|
// track retries
|
|
exitOnErr := make(map[string]int)
|
|
maxRetry := 50
|
|
|
|
// Attempt to unmount anything beneath this dir first
|
|
mount.RecursiveUnmount(dir)
|
|
|
|
for {
|
|
err := os.RemoveAll(dir)
|
|
if err == nil {
|
|
return nil
|
|
}
|
|
|
|
pe, ok := err.(*os.PathError)
|
|
if !ok {
|
|
return err
|
|
}
|
|
|
|
if os.IsNotExist(err) {
|
|
if notExistErr[pe.Path] {
|
|
return err
|
|
}
|
|
notExistErr[pe.Path] = true
|
|
|
|
// There is a race where some subdir can be removed but after the parent
|
|
// dir entries have been read.
|
|
// So the path could be from `os.Remove(subdir)`
|
|
// If the reported non-existent path is not the passed in `dir` we
|
|
// should just retry, but otherwise return with no error.
|
|
if pe.Path == dir {
|
|
return nil
|
|
}
|
|
continue
|
|
}
|
|
|
|
if pe.Err != syscall.EBUSY {
|
|
return err
|
|
}
|
|
|
|
if e := mount.Unmount(pe.Path); e != nil {
|
|
return errors.Wrapf(e, "error while removing %s", dir)
|
|
}
|
|
|
|
if exitOnErr[pe.Path] == maxRetry {
|
|
return err
|
|
}
|
|
exitOnErr[pe.Path]++
|
|
time.Sleep(100 * time.Millisecond)
|
|
}
|
|
}
|