| // Copyright 2018 The Go Authors. All rights reserved. | |
| // Use of this source code is governed by a BSD-style | |
| // license that can be found in the LICENSE file. | |
| // +build !aix,!darwin,!dragonfly,!freebsd,!linux,!netbsd,!openbsd,!solaris | |
| package os | |
| import ( | |
| "io" | |
| "runtime" | |
| "syscall" | |
| ) | |
| func removeAll(path string) error { | |
| if path == "" { | |
| // fail silently to retain compatibility with previous behavior | |
| // of RemoveAll. See issue 28830. | |
| return nil | |
| } | |
| // The rmdir system call permits removing "." on Plan 9, | |
| // so we don't permit it to remain consistent with the | |
| // "at" implementation of RemoveAll. | |
| if endsWithDot(path) { | |
| return &PathError{Op: "RemoveAll", Path: path, Err: syscall.EINVAL} | |
| } | |
| // Simple case: if Remove works, we're done. | |
| err := Remove(path) | |
| if err == nil || IsNotExist(err) { | |
| return nil | |
| } | |
| // Otherwise, is this a directory we need to recurse into? | |
| dir, serr := Lstat(path) | |
| if serr != nil { | |
| if serr, ok := serr.(*PathError); ok && (IsNotExist(serr.Err) || serr.Err == syscall.ENOTDIR) { | |
| return nil | |
| } | |
| return serr | |
| } | |
| if !dir.IsDir() { | |
| // Not a directory; return the error from Remove. | |
| return err | |
| } | |
| // Remove contents & return first error. | |
| err = nil | |
| for { | |
| fd, err := Open(path) | |
| if err != nil { | |
| if IsNotExist(err) { | |
| // Already deleted by someone else. | |
| return nil | |
| } | |
| return err | |
| } | |
| const reqSize = 1024 | |
| var names []string | |
| var readErr error | |
| for { | |
| numErr := 0 | |
| names, readErr = fd.Readdirnames(reqSize) | |
| for _, name := range names { | |
| err1 := RemoveAll(path + string(PathSeparator) + name) | |
| if err == nil { | |
| err = err1 | |
| } | |
| if err1 != nil { | |
| numErr++ | |
| } | |
| } | |
| // If we can delete any entry, break to start new iteration. | |
| // Otherwise, we discard current names, get next entries and try deleting them. | |
| if numErr != reqSize { | |
| break | |
| } | |
| } | |
| // 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() | |
| if readErr == io.EOF { | |
| break | |
| } | |
| // If Readdirnames returned an error, use it. | |
| if err == nil { | |
| err = readErr | |
| } | |
| if len(names) == 0 { | |
| break | |
| } | |
| // 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) < reqSize { | |
| 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) | |
| if err1 == nil || IsNotExist(err1) { | |
| return nil | |
| } | |
| if runtime.GOOS == "windows" && IsPermission(err1) { | |
| if fs, err := Stat(path); err == nil { | |
| if err = Chmod(path, FileMode(0200|int(fs.Mode()))); err == nil { | |
| err1 = Remove(path) | |
| } | |
| } | |
| } | |
| if err == nil { | |
| err = err1 | |
| } | |
| return err | |
| } |