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