Source file
src/os/removeall_at.go
1
2
3
4
5
6
7 package os
8
9 import (
10 "internal/syscall/unix"
11 "io"
12 "syscall"
13 )
14
15 func removeAll(path string) error {
16 if path == "" {
17
18
19 return nil
20 }
21
22
23
24 if endsWithDot(path) {
25 return &PathError{Op: "RemoveAll", Path: path, Err: syscall.EINVAL}
26 }
27
28
29 err := Remove(path)
30 if err == nil || IsNotExist(err) {
31 return nil
32 }
33
34
35
36 parentDir, base := splitPath(path)
37
38 parent, err := Open(parentDir)
39 if IsNotExist(err) {
40
41 return nil
42 }
43 if err != nil {
44 return err
45 }
46 defer parent.Close()
47
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
52 }
53 return err
54 }
55 return nil
56 }
57
58 func removeAllFrom(parent *File, base string) error {
59 parentFd := int(parent.Fd())
60
61 err := ignoringEINTR(func() error {
62 return unix.Unlinkat(parentFd, base, 0)
63 })
64 if err == nil || IsNotExist(err) {
65 return nil
66 }
67
68
69
70
71
72
73
74 if err != syscall.EISDIR && err != syscall.EPERM && err != syscall.EACCES {
75 return &PathError{Op: "unlinkat", Path: base, Err: err}
76 }
77 uErr := err
78
79
80 var recurseErr error
81 for {
82 const reqSize = 1024
83 var respSize int
84
85
86 file, err := openDirAt(parentFd, base)
87 if err != nil {
88 if IsNotExist(err) {
89 return nil
90 }
91 if err == syscall.ENOTDIR || err == unix.NoFollowErrno {
92
93 return &PathError{Op: "unlinkat", Path: base, Err: uErr}
94 }
95 recurseErr = &PathError{Op: "openfdat", Path: base, Err: err}
96 break
97 }
98
99 for {
100 numErr := 0
101
102 names, readErr := file.Readdirnames(reqSize)
103
104 if readErr != nil && readErr != io.EOF {
105 file.Close()
106 if IsNotExist(readErr) {
107 return nil
108 }
109 return &PathError{Op: "readdirnames", Path: base, Err: readErr}
110 }
111
112 respSize = len(names)
113 for _, name := range names {
114 err := removeAllFrom(file, name)
115 if err != nil {
116 if pathErr, ok := err.(*PathError); ok {
117 pathErr.Path = base + string(PathSeparator) + pathErr.Path
118 }
119 numErr++
120 if recurseErr == nil {
121 recurseErr = err
122 }
123 }
124 }
125
126
127
128 if numErr != reqSize {
129 break
130 }
131 }
132
133
134
135
136
137
138 file.Close()
139
140
141 if respSize < reqSize {
142 break
143 }
144 }
145
146
147 unlinkError := ignoringEINTR(func() error {
148 return unix.Unlinkat(parentFd, base, unix.AT_REMOVEDIR)
149 })
150 if unlinkError == nil || IsNotExist(unlinkError) {
151 return nil
152 }
153
154 if recurseErr != nil {
155 return recurseErr
156 }
157 return &PathError{Op: "unlinkat", Path: base, Err: unlinkError}
158 }
159
160
161
162
163
164
165
166
167
168 func openDirAt(dirfd int, name string) (*File, error) {
169 var r int
170 for {
171 var e error
172 r, e = unix.Openat(dirfd, name, O_RDONLY|syscall.O_CLOEXEC|syscall.O_DIRECTORY|syscall.O_NOFOLLOW, 0)
173 if e == nil {
174 break
175 }
176
177
178 if e == syscall.EINTR {
179 continue
180 }
181
182 return nil, e
183 }
184
185 if !supportsCloseOnExec {
186 syscall.CloseOnExec(r)
187 }
188
189
190 return newFile(r, name, kindNoPoll, false), nil
191 }
192
View as plain text