Add __builtion_unreachable to vector::size(), vector::capacity()
[official-gcc.git] / libgo / go / os / removeall_at.go
blobbc5376f1cbcfa6f01b9012c5d3ce6ffcd958154a
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
7 package os
9 import (
10 "internal/syscall/unix"
11 "io"
12 "syscall"
15 func removeAll(path string) error {
16 if path == "" {
17 // fail silently to retain compatibility with previous behavior
18 // of RemoveAll. See issue 28830.
19 return nil
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.
29 err := Remove(path)
30 if err == nil || IsNotExist(err) {
31 return nil
34 // RemoveAll recurses by deleting the path base from
35 // its parent directory
36 parentDir, base := splitPath(path)
38 parent, err := Open(parentDir)
39 if IsNotExist(err) {
40 // If parent does not exist, base cannot exist. Fail silently
41 return nil
43 if err != nil {
44 return err
46 defer parent.Close()
48 if err := removeAllFrom(parent, base); err != nil {
49 if pathErr, ok := err.(*PathError); ok {
50 pathErr.Path = parentDir + string(PathSeparator) + pathErr.Path
51 err = pathErr
53 return err
55 return nil
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) {
63 return nil
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)
79 if statErr != nil {
80 if IsNotExist(statErr) {
81 return nil
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.
91 var recurseErr error
92 for {
93 const reqSize = 1024
94 var respSize int
96 // Open the directory to recurse into
97 file, err := openFdAt(parentFd, base)
98 if err != nil {
99 if IsNotExist(err) {
100 return nil
102 recurseErr = &PathError{Op: "openfdat", Path: base, Err: err}
103 break
106 for {
107 numErr := 0
109 names, readErr := file.Readdirnames(reqSize)
110 // Errors other than EOF should stop us from continuing.
111 if readErr != nil && readErr != io.EOF {
112 file.Close()
113 if IsNotExist(readErr) {
114 return nil
116 return &PathError{Op: "readdirnames", Path: base, Err: readErr}
119 respSize = len(names)
120 for _, name := range names {
121 err := removeAllFrom(file, name)
122 if err != nil {
123 if pathErr, ok := err.(*PathError); ok {
124 pathErr.Path = base + string(PathSeparator) + pathErr.Path
126 numErr++
127 if recurseErr == nil {
128 recurseErr = err
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 {
136 break
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.
145 file.Close()
147 // Finish when the end of the directory is reached
148 if respSize < reqSize {
149 break
153 // Remove the directory itself.
154 unlinkError := unix.Unlinkat(parentFd, base, unix.AT_REMOVEDIR)
155 if unlinkError == nil || IsNotExist(unlinkError) {
156 return nil
159 if recurseErr != nil {
160 return recurseErr
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) {
171 var r int
172 for {
173 var e error
174 r, e = unix.Openat(dirfd, name, O_RDONLY|syscall.O_CLOEXEC, 0)
175 if e == nil {
176 break
179 // See comment in openFileNolog.
180 if e == syscall.EINTR {
181 continue
184 return nil, e
187 if !supportsCloseOnExec {
188 syscall.CloseOnExec(r)
191 return newFile(uintptr(r), name, kindOpenFile), nil