// 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. //go:build !unix 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 }