import (
"io"
- "runtime"
"syscall"
)
return err
}
- // Directory.
- fd, err := Open(path)
- if err != nil {
- if IsNotExist(err) {
- // Race. It was deleted between the Lstat and Open.
- // Return nil per RemoveAll's docs.
- return nil
- }
- return err
- }
-
// Remove contents & return first error.
err = nil
for {
- if err == nil && (runtime.GOOS == "plan9" || runtime.GOOS == "nacl") {
- // Reset read offset after removing directory entries.
- // See golang.org/issue/22572.
- fd.Seek(0, 0)
+ fd, err := Open(path)
+ if err != nil {
+ if IsNotExist(err) {
+ // Already deleted by someone else.
+ return nil
+ }
+ return err
}
- names, err1 := fd.Readdirnames(100)
+
+ const request = 1024
+ names, err1 := fd.Readdirnames(request)
+
+ // Removing files from the directory may have caused
+ // the OS to reshuffle it. Simply calling Readdirnames
+ // again may skip some entries. The only reliable way
+ // to avoid this is to close and re-open the
+ // directory. See issue 20841.
+ fd.Close()
+
for _, name := range names {
err1 := RemoveAll(path + string(PathSeparator) + name)
if err == nil {
err = err1
}
}
+
if err1 == io.EOF {
break
}
if len(names) == 0 {
break
}
- }
- // Close directory, because windows won't remove opened directory.
- fd.Close()
+ // We don't want to re-open unnecessarily, so if we
+ // got fewer than request names from Readdirnames, try
+ // simply removing the directory now. If that
+ // succeeds, we are done.
+ if len(names) < request {
+ err1 := Remove(path)
+ if err1 == nil || IsNotExist(err1) {
+ return nil
+ }
+
+ if err != nil {
+ // We got some error removing the
+ // directory contents, and since we
+ // read fewer names than we requested
+ // there probably aren't more files to
+ // remove. Don't loop around to read
+ // the directory again. We'll probably
+ // just get the same error.
+ return err
+ }
+ }
+ }
// Remove directory.
err1 := Remove(path)