// 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 unixpackage osimport ()func removeAll( string) error {if == "" {// fail silently to retain compatibility with previous behavior // of RemoveAll. See issue 28830.returnnil }// The rmdir system call does not permit removing ".", // so we don't permit it either.ifendsWithDot() {return &PathError{Op: "RemoveAll", Path: , Err: syscall.EINVAL} }// Simple case: if Remove works, we're done. := Remove()if == nil || IsNotExist() {returnnil }// RemoveAll recurses by deleting the path base from // its parent directory , := splitPath() , := Open()ifIsNotExist() {// If parent does not exist, base cannot exist. Fail silentlyreturnnil }if != nil {return }defer .Close()if := removeAllFrom(, ); != nil {if , := .(*PathError); { .Path = + string(PathSeparator) + .Path = }return }returnnil}func removeAllFrom( *File, string) error { := int(.Fd())// Simple case: if Unlink (aka remove) works, we're done. := ignoringEINTR(func() error {returnunix.Unlinkat(, , 0) })if == nil || IsNotExist() {returnnil }// EISDIR means that we have a directory, and we need to // remove its contents. // EPERM or EACCES means that we don't have write permission on // the parent directory, but this entry might still be a directory // whose contents need to be removed. // Otherwise just return the error.if != syscall.EISDIR && != syscall.EPERM && != syscall.EACCES {return &PathError{Op: "unlinkat", Path: , Err: } } := // Remove the directory's entries.varerrorfor {const = 1024varint// Open the directory to recurse into , := openDirAt(, )if != nil {ifIsNotExist() {returnnil }if == syscall.ENOTDIR || == unix.NoFollowErrno {// Not a directory; return the error from the unix.Unlinkat.return &PathError{Op: "unlinkat", Path: , Err: } } = &PathError{Op: "openfdat", Path: , Err: }break }for { := 0 , := .Readdirnames()// Errors other than EOF should stop us from continuing.if != nil && != io.EOF { .Close()ifIsNotExist() {returnnil }return &PathError{Op: "readdirnames", Path: , Err: } } = len()for , := range { := (, )if != nil {if , := .(*PathError); { .Path = + string(PathSeparator) + .Path } ++if == nil { = } } }// If we can delete any entry, break to start new iteration. // Otherwise, we discard current names, get next entries and try deleting them.if != {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. .Close()// Finish when the end of the directory is reachedif < {break } }// Remove the directory itself. := ignoringEINTR(func() error {returnunix.Unlinkat(, , unix.AT_REMOVEDIR) })if == nil || IsNotExist() {returnnil }if != nil {return }return &PathError{Op: "unlinkat", Path: , Err: }}// openDirAt opens a directory name relative to the directory referred to by// the file descriptor dirfd. If name is anything but a directory (this// includes a symlink to one), it should return an error. Other than that this// should act like openFileNolog.//// This acts like openFileNolog rather than OpenFile because// we are going to (try to) remove the file.// The contents of this file are not relevant for test caching.func openDirAt( int, string) (*File, error) {varintfor {varerror , = unix.Openat(, , O_RDONLY|syscall.O_CLOEXEC|syscall.O_DIRECTORY|syscall.O_NOFOLLOW, 0)if == nil {break }// See comment in openFileNolog.if == syscall.EINTR {continue }returnnil, }if !supportsCloseOnExec {syscall.CloseOnExec() }// We use kindNoPoll because we know that this is a directory.returnnewFile(, , kindNoPoll, false), nil}
The pages are generated with Goldsv0.7.0-preview. (GOOS=linux GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu.
PR and bug reports are welcome and can be submitted to the issue list.
Please follow @zigo_101 (reachable from the left QR code) to get the latest news of Golds.