github.com/zxy12/go_duplicate_112_new@v0.0.0-20200807091221-747231827200/src/os/removeall_at.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 // +build aix darwin dragonfly freebsd linux netbsd openbsd solaris 6 7 package os 8 9 import ( 10 "internal/syscall/unix" 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 does not permit removing ".", 24 // so we don't permit it either. 25 if endsWithDot(path) { 26 return &PathError{"RemoveAll", path, syscall.EINVAL} 27 } 28 29 // Simple case: if Remove works, we're done. 30 err := Remove(path) 31 if err == nil || IsNotExist(err) { 32 return nil 33 } 34 35 // RemoveAll recurses by deleting the path base from 36 // its parent directory 37 parentDir, base := splitPath(path) 38 39 parent, err := Open(parentDir) 40 if IsNotExist(err) { 41 // If parent does not exist, base cannot exist. Fail silently 42 return nil 43 } 44 if err != nil { 45 return err 46 } 47 defer parent.Close() 48 49 if err := removeAllFrom(parent, base); err != nil { 50 if pathErr, ok := err.(*PathError); ok { 51 pathErr.Path = parentDir + string(PathSeparator) + pathErr.Path 52 err = pathErr 53 } 54 return err 55 } 56 return nil 57 } 58 59 func removeAllFrom(parent *File, base string) error { 60 parentFd := int(parent.Fd()) 61 // Simple case: if Unlink (aka remove) works, we're done. 62 err := unix.Unlinkat(parentFd, base, 0) 63 if err == nil || IsNotExist(err) { 64 return nil 65 } 66 67 // EISDIR means that we have a directory, and we need to 68 // remove its contents. 69 // EPERM or EACCES means that we don't have write permission on 70 // the parent directory, but this entry might still be a directory 71 // whose contents need to be removed. 72 // Otherwise just return the error. 73 if err != syscall.EISDIR && err != syscall.EPERM && err != syscall.EACCES { 74 return &PathError{"unlinkat", base, err} 75 } 76 77 // Is this a directory we need to recurse into? 78 var statInfo syscall.Stat_t 79 statErr := unix.Fstatat(parentFd, base, &statInfo, unix.AT_SYMLINK_NOFOLLOW) 80 if statErr != nil { 81 if IsNotExist(statErr) { 82 return nil 83 } 84 return &PathError{"fstatat", base, statErr} 85 } 86 if statInfo.Mode&syscall.S_IFMT != syscall.S_IFDIR { 87 // Not a directory; return the error from the unix.Unlinkat. 88 return &PathError{"unlinkat", base, err} 89 } 90 91 // Remove the directory's entries. 92 var recurseErr error 93 for { 94 const request = 1024 95 96 // Open the directory to recurse into 97 file, err := openFdAt(parentFd, base) 98 if err != nil { 99 if IsNotExist(err) { 100 return nil 101 } 102 recurseErr = &PathError{"openfdat", base, err} 103 break 104 } 105 106 names, readErr := file.Readdirnames(request) 107 // Errors other than EOF should stop us from continuing. 108 if readErr != nil && readErr != io.EOF { 109 file.Close() 110 if IsNotExist(readErr) { 111 return nil 112 } 113 return &PathError{"readdirnames", base, readErr} 114 } 115 116 for _, name := range names { 117 err := removeAllFrom(file, name) 118 if err != nil { 119 if pathErr, ok := err.(*PathError); ok { 120 pathErr.Path = base + string(PathSeparator) + pathErr.Path 121 } 122 recurseErr = err 123 } 124 } 125 126 // Removing files from the directory may have caused 127 // the OS to reshuffle it. Simply calling Readdirnames 128 // again may skip some entries. The only reliable way 129 // to avoid this is to close and re-open the 130 // directory. See issue 20841. 131 file.Close() 132 133 // Finish when the end of the directory is reached 134 if len(names) < request { 135 break 136 } 137 } 138 139 // Remove the directory itself. 140 unlinkError := unix.Unlinkat(parentFd, base, unix.AT_REMOVEDIR) 141 if unlinkError == nil || IsNotExist(unlinkError) { 142 return nil 143 } 144 145 if recurseErr != nil { 146 return recurseErr 147 } 148 return &PathError{"unlinkat", base, unlinkError} 149 } 150 151 // openFdAt opens path relative to the directory in fd. 152 // Other than that this should act like openFileNolog. 153 // This acts like openFileNolog rather than OpenFile because 154 // we are going to (try to) remove the file. 155 // The contents of this file are not relevant for test caching. 156 func openFdAt(dirfd int, name string) (*File, error) { 157 var r int 158 for { 159 var e error 160 r, e = unix.Openat(dirfd, name, O_RDONLY, 0) 161 if e == nil { 162 break 163 } 164 165 // See comment in openFileNolog. 166 if runtime.GOOS == "darwin" && e == syscall.EINTR { 167 continue 168 } 169 170 return nil, e 171 } 172 173 if !supportsCloseOnExec { 174 syscall.CloseOnExec(r) 175 } 176 177 return newFile(uintptr(r), name, kindOpenFile), nil 178 }