github.com/code-reading/golang@v0.0.0-20220303082512-ba5bc0e589a3/go/src/os/removeall_noat.go (about) 1 // Copyright 2018 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 //go:build !aix && !darwin && !dragonfly && !freebsd && !linux && !netbsd && !openbsd && !solaris 6 // +build !aix,!darwin,!dragonfly,!freebsd,!linux,!netbsd,!openbsd,!solaris 7 8 package os 9 10 import ( 11 "io" 12 "runtime" 13 "syscall" 14 ) 15 16 func removeAll(path string) error { 17 if path == "" { 18 // fail silently to retain compatibility with previous behavior 19 // of RemoveAll. See issue 28830. 20 return nil 21 } 22 23 // The rmdir system call permits removing "." on Plan 9, 24 // so we don't permit it to remain consistent with the 25 // "at" implementation of RemoveAll. 26 if endsWithDot(path) { 27 return &PathError{Op: "RemoveAll", Path: path, Err: syscall.EINVAL} 28 } 29 30 // Simple case: if Remove works, we're done. 31 err := Remove(path) 32 if err == nil || IsNotExist(err) { 33 return nil 34 } 35 36 // Otherwise, is this a directory we need to recurse into? 37 dir, serr := Lstat(path) 38 if serr != nil { 39 if serr, ok := serr.(*PathError); ok && (IsNotExist(serr.Err) || serr.Err == syscall.ENOTDIR) { 40 return nil 41 } 42 return serr 43 } 44 if !dir.IsDir() { 45 // Not a directory; return the error from Remove. 46 return err 47 } 48 49 // Remove contents & return first error. 50 err = nil 51 for { 52 fd, err := Open(path) 53 if err != nil { 54 if IsNotExist(err) { 55 // Already deleted by someone else. 56 return nil 57 } 58 return err 59 } 60 61 const reqSize = 1024 62 var names []string 63 var readErr error 64 65 for { 66 numErr := 0 67 names, readErr = fd.Readdirnames(reqSize) 68 69 for _, name := range names { 70 err1 := RemoveAll(path + string(PathSeparator) + name) 71 if err == nil { 72 err = err1 73 } 74 if err1 != nil { 75 numErr++ 76 } 77 } 78 79 // If we can delete any entry, break to start new iteration. 80 // Otherwise, we discard current names, get next entries and try deleting them. 81 if numErr != reqSize { 82 break 83 } 84 } 85 86 // Removing files from the directory may have caused 87 // the OS to reshuffle it. Simply calling Readdirnames 88 // again may skip some entries. The only reliable way 89 // to avoid this is to close and re-open the 90 // directory. See issue 20841. 91 fd.Close() 92 93 if readErr == io.EOF { 94 break 95 } 96 // If Readdirnames returned an error, use it. 97 if err == nil { 98 err = readErr 99 } 100 if len(names) == 0 { 101 break 102 } 103 104 // We don't want to re-open unnecessarily, so if we 105 // got fewer than request names from Readdirnames, try 106 // simply removing the directory now. If that 107 // succeeds, we are done. 108 if len(names) < reqSize { 109 err1 := Remove(path) 110 if err1 == nil || IsNotExist(err1) { 111 return nil 112 } 113 114 if err != nil { 115 // We got some error removing the 116 // directory contents, and since we 117 // read fewer names than we requested 118 // there probably aren't more files to 119 // remove. Don't loop around to read 120 // the directory again. We'll probably 121 // just get the same error. 122 return err 123 } 124 } 125 } 126 127 // Remove directory. 128 err1 := Remove(path) 129 if err1 == nil || IsNotExist(err1) { 130 return nil 131 } 132 if runtime.GOOS == "windows" && IsPermission(err1) { 133 if fs, err := Stat(path); err == nil { 134 if err = Chmod(path, FileMode(0200|int(fs.Mode()))); err == nil { 135 err1 = Remove(path) 136 } 137 } 138 } 139 if err == nil { 140 err = err1 141 } 142 return err 143 }