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 := unix.Unlinkat(parentFd, base, 0)
62 if err == nil || IsNotExist(err) {
63 return nil
64 }
65
66
67
68
69
70
71
72 if err != syscall.EISDIR && err != syscall.EPERM && err != syscall.EACCES {
73 return &PathError{Op: "unlinkat", Path: base, Err: err}
74 }
75
76
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
82 }
83 return &PathError{Op: "fstatat", Path: base, Err: statErr}
84 }
85 if statInfo.Mode&syscall.S_IFMT != syscall.S_IFDIR {
86
87 return &PathError{Op: "unlinkat", Path: base, Err: err}
88 }
89
90
91 var recurseErr error
92 for {
93 const reqSize = 1024
94 var respSize int
95
96
97 file, err := openFdAt(parentFd, base)
98 if err != nil {
99 if IsNotExist(err) {
100 return nil
101 }
102 recurseErr = &PathError{Op: "openfdat", Path: base, Err: err}
103 break
104 }
105
106 for {
107 numErr := 0
108
109 names, readErr := file.Readdirnames(reqSize)
110
111 if readErr != nil && readErr != io.EOF {
112 file.Close()
113 if IsNotExist(readErr) {
114 return nil
115 }
116 return &PathError{Op: "readdirnames", Path: base, Err: readErr}
117 }
118
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
125 }
126 numErr++
127 if recurseErr == nil {
128 recurseErr = err
129 }
130 }
131 }
132
133
134
135 if numErr != reqSize {
136 break
137 }
138 }
139
140
141
142
143
144
145 file.Close()
146
147
148 if respSize < reqSize {
149 break
150 }
151 }
152
153
154 unlinkError := unix.Unlinkat(parentFd, base, unix.AT_REMOVEDIR)
155 if unlinkError == nil || IsNotExist(unlinkError) {
156 return nil
157 }
158
159 if recurseErr != nil {
160 return recurseErr
161 }
162 return &PathError{Op: "unlinkat", Path: base, Err: unlinkError}
163 }
164
165
166
167
168
169
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
177 }
178
179
180 if e == syscall.EINTR {
181 continue
182 }
183
184 return nil, e
185 }
186
187 if !supportsCloseOnExec {
188 syscall.CloseOnExec(r)
189 }
190
191 return newFile(uintptr(r), name, kindOpenFile), nil
192 }
193
View as plain text