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.
5 //go:build aix || darwin || dragonfly || freebsd || hurd || linux || netbsd || openbsd || solaris
10 "internal/syscall/unix"
15 func removeAll(path
string) error
{
17 // fail silently to retain compatibility with previous behavior
18 // of RemoveAll. See issue 28830.
22 // The rmdir system call does not permit removing ".",
23 // so we don't permit it either.
24 if endsWithDot(path
) {
25 return &PathError
{Op
: "RemoveAll", Path
: path
, Err
: syscall
.EINVAL
}
28 // Simple case: if Remove works, we're done.
30 if err
== nil ||
IsNotExist(err
) {
34 // RemoveAll recurses by deleting the path base from
35 // its parent directory
36 parentDir
, base
:= splitPath(path
)
38 parent
, err
:= Open(parentDir
)
40 // If parent does not exist, base cannot exist. Fail silently
48 if err
:= removeAllFrom(parent
, base
); err
!= nil {
49 if pathErr
, ok
:= err
.(*PathError
); ok
{
50 pathErr
.Path
= parentDir
+ string(PathSeparator
) + pathErr
.Path
58 func removeAllFrom(parent
*File
, base
string) error
{
59 parentFd
:= int(parent
.Fd())
60 // Simple case: if Unlink (aka remove) works, we're done.
61 err
:= unix
.Unlinkat(parentFd
, base
, 0)
62 if err
== nil ||
IsNotExist(err
) {
66 // EISDIR means that we have a directory, and we need to
67 // remove its contents.
68 // EPERM or EACCES means that we don't have write permission on
69 // the parent directory, but this entry might still be a directory
70 // whose contents need to be removed.
71 // Otherwise just return the error.
72 if err
!= syscall
.EISDIR
&& err
!= syscall
.EPERM
&& err
!= syscall
.EACCES
{
73 return &PathError
{Op
: "unlinkat", Path
: base
, Err
: err
}
76 // Is this a directory we need to recurse into?
77 var statInfo syscall
.Stat_t
78 statErr
:= unix
.Fstatat(parentFd
, base
, &statInfo
, unix
.AT_SYMLINK_NOFOLLOW
)
80 if IsNotExist(statErr
) {
83 return &PathError
{Op
: "fstatat", Path
: base
, Err
: statErr
}
85 if statInfo
.Mode
&syscall
.S_IFMT
!= syscall
.S_IFDIR
{
86 // Not a directory; return the error from the unix.Unlinkat.
87 return &PathError
{Op
: "unlinkat", Path
: base
, Err
: err
}
90 // Remove the directory's entries.
96 // Open the directory to recurse into
97 file
, err
:= openFdAt(parentFd
, base
)
102 recurseErr
= &PathError
{Op
: "openfdat", Path
: base
, Err
: err
}
109 names
, readErr
:= file
.Readdirnames(reqSize
)
110 // Errors other than EOF should stop us from continuing.
111 if readErr
!= nil && readErr
!= io
.EOF
{
113 if IsNotExist(readErr
) {
116 return &PathError
{Op
: "readdirnames", Path
: base
, Err
: readErr
}
119 respSize
= len(names
)
120 for _
, name
:= range names
{
121 err
:= removeAllFrom(file
, name
)
123 if pathErr
, ok
:= err
.(*PathError
); ok
{
124 pathErr
.Path
= base
+ string(PathSeparator
) + pathErr
.Path
127 if recurseErr
== nil {
133 // If we can delete any entry, break to start new iteration.
134 // Otherwise, we discard current names, get next entries and try deleting them.
135 if numErr
!= reqSize
{
140 // Removing files from the directory may have caused
141 // the OS to reshuffle it. Simply calling Readdirnames
142 // again may skip some entries. The only reliable way
143 // to avoid this is to close and re-open the
144 // directory. See issue 20841.
147 // Finish when the end of the directory is reached
148 if respSize
< reqSize
{
153 // Remove the directory itself.
154 unlinkError
:= unix
.Unlinkat(parentFd
, base
, unix
.AT_REMOVEDIR
)
155 if unlinkError
== nil ||
IsNotExist(unlinkError
) {
159 if recurseErr
!= nil {
162 return &PathError
{Op
: "unlinkat", Path
: base
, Err
: unlinkError
}
165 // openFdAt opens path relative to the directory in fd.
166 // Other than that this should act like openFileNolog.
167 // This acts like openFileNolog rather than OpenFile because
168 // we are going to (try to) remove the file.
169 // The contents of this file are not relevant for test caching.
170 func openFdAt(dirfd
int, name
string) (*File
, error
) {
174 r
, e
= unix
.Openat(dirfd
, name
, O_RDONLY|syscall
.O_CLOEXEC
, 0)
179 // See comment in openFileNolog.
180 if e
== syscall
.EINTR
{
187 if !supportsCloseOnExec
{
188 syscall
.CloseOnExec(r
)
191 return newFile(uintptr(r
), name
, kindOpenFile
), nil