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
78
79 var statInfo syscall.Stat_t
80 statErr := ignoringEINTR(func() error {
81 return unix.Fstatat(parentFd, base, &statInfo, unix.AT_SYMLINK_NOFOLLOW)
82 })
83 if statErr != nil {
84 if IsNotExist(statErr) {
85 return nil
86 }
87 return &PathError{Op: "fstatat", Path: base, Err: statErr}
88 }
89 if statInfo.Mode&syscall.S_IFMT != syscall.S_IFDIR {
90
91 return &PathError{Op: "unlinkat", Path: base, Err: err}
92 }
93
94
95 var recurseErr error
96 for {
97 const reqSize = 1024
98 var respSize int
99
100
101 file, err := openFdAt(parentFd, base)
102 if err != nil {
103 if IsNotExist(err) {
104 return nil
105 }
106 recurseErr = &PathError{Op: "openfdat", Path: base, Err: err}
107 break
108 }
109
110 for {
111 numErr := 0
112
113 names, readErr := file.Readdirnames(reqSize)
114
115 if readErr != nil && readErr != io.EOF {
116 file.Close()
117 if IsNotExist(readErr) {
118 return nil
119 }
120 return &PathError{Op: "readdirnames", Path: base, Err: readErr}
121 }
122
123 respSize = len(names)
124 for _, name := range names {
125 err := removeAllFrom(file, name)
126 if err != nil {
127 if pathErr, ok := err.(*PathError); ok {
128 pathErr.Path = base + string(PathSeparator) + pathErr.Path
129 }
130 numErr++
131 if recurseErr == nil {
132 recurseErr = err
133 }
134 }
135 }
136
137
138
139 if numErr != reqSize {
140 break
141 }
142 }
143
144
145
146
147
148
149 file.Close()
150
151
152 if respSize < reqSize {
153 break
154 }
155 }
156
157
158 unlinkError := ignoringEINTR(func() error {
159 return unix.Unlinkat(parentFd, base, unix.AT_REMOVEDIR)
160 })
161 if unlinkError == nil || IsNotExist(unlinkError) {
162 return nil
163 }
164
165 if recurseErr != nil {
166 return recurseErr
167 }
168 return &PathError{Op: "unlinkat", Path: base, Err: unlinkError}
169 }
170
171
172
173
174
175
176 func openFdAt(dirfd int, name string) (*File, error) {
177 var r int
178 for {
179 var e error
180 r, e = unix.Openat(dirfd, name, O_RDONLY|syscall.O_CLOEXEC, 0)
181 if e == nil {
182 break
183 }
184
185
186 if e == syscall.EINTR {
187 continue
188 }
189
190 return nil, e
191 }
192
193 if !supportsCloseOnExec {
194 syscall.CloseOnExec(r)
195 }
196
197
198 return newFile(r, name, kindNoPoll), nil
199 }
200
View as plain text