Source file
src/os/root_openat.go
1
2
3
4
5
6
7 package os
8
9 import (
10 "runtime"
11 "slices"
12 "sync"
13 "syscall"
14 "time"
15 )
16
17
18
19 type root struct {
20 name string
21
22
23
24
25 mu sync.Mutex
26 fd sysfdType
27 refs int
28 closed bool
29 }
30
31 func (r *root) Close() error {
32 r.mu.Lock()
33 defer r.mu.Unlock()
34 if !r.closed && r.refs == 0 {
35 syscall.Close(r.fd)
36 }
37 r.closed = true
38 runtime.SetFinalizer(r, nil)
39 return nil
40 }
41
42 func (r *root) incref() error {
43 r.mu.Lock()
44 defer r.mu.Unlock()
45 if r.closed {
46 return ErrClosed
47 }
48 r.refs++
49 return nil
50 }
51
52 func (r *root) decref() {
53 r.mu.Lock()
54 defer r.mu.Unlock()
55 if r.refs <= 0 {
56 panic("bad Root refcount")
57 }
58 r.refs--
59 if r.closed && r.refs == 0 {
60 syscall.Close(r.fd)
61 }
62 }
63
64 func (r *root) Name() string {
65 return r.name
66 }
67
68 func rootChmod(r *Root, name string, mode FileMode) error {
69 _, err := doInRoot(r, name, nil, func(parent sysfdType, name string) (struct{}, error) {
70 return struct{}{}, chmodat(parent, name, mode)
71 })
72 if err != nil {
73 return &PathError{Op: "chmodat", Path: name, Err: err}
74 }
75 return nil
76 }
77
78 func rootChown(r *Root, name string, uid, gid int) error {
79 _, err := doInRoot(r, name, nil, func(parent sysfdType, name string) (struct{}, error) {
80 return struct{}{}, chownat(parent, name, uid, gid)
81 })
82 if err != nil {
83 return &PathError{Op: "chownat", Path: name, Err: err}
84 }
85 return nil
86 }
87
88 func rootLchown(r *Root, name string, uid, gid int) error {
89 _, err := doInRoot(r, name, nil, func(parent sysfdType, name string) (struct{}, error) {
90 return struct{}{}, lchownat(parent, name, uid, gid)
91 })
92 if err != nil {
93 return &PathError{Op: "lchownat", Path: name, Err: err}
94 }
95 return err
96 }
97
98 func rootChtimes(r *Root, name string, atime time.Time, mtime time.Time) error {
99 _, err := doInRoot(r, name, nil, func(parent sysfdType, name string) (struct{}, error) {
100 return struct{}{}, chtimesat(parent, name, atime, mtime)
101 })
102 if err != nil {
103 return &PathError{Op: "chtimesat", Path: name, Err: err}
104 }
105 return err
106 }
107
108 func rootMkdir(r *Root, name string, perm FileMode) error {
109 _, err := doInRoot(r, name, nil, func(parent sysfdType, name string) (struct{}, error) {
110 return struct{}{}, mkdirat(parent, name, perm)
111 })
112 if err != nil {
113 return &PathError{Op: "mkdirat", Path: name, Err: err}
114 }
115 return nil
116 }
117
118 func rootMkdirAll(r *Root, fullname string, perm FileMode) error {
119
120
121
122
123
124 openDirFunc := func(parent sysfdType, name string) (sysfdType, error) {
125 for try := range 2 {
126 fd, err := rootOpenDir(parent, name)
127 switch err.(type) {
128 case nil, errSymlink:
129 return fd, err
130 }
131 if try > 0 || !IsNotExist(err) {
132 return 0, &PathError{Op: "openat", Err: err}
133 }
134
135
136 if err := mkdirat(parent, name, perm); err != nil && err != syscall.EEXIST {
137 return 0, &PathError{Op: "mkdirat", Err: err}
138 }
139 }
140 panic("unreachable")
141 }
142
143 openLastComponentFunc := func(parent sysfdType, name string) (struct{}, error) {
144 err := mkdirat(parent, name, perm)
145 if err == syscall.EEXIST {
146 mode, e := modeAt(parent, name)
147 if e == nil {
148 if mode.IsDir() {
149
150 err = nil
151 } else if mode&ModeSymlink != 0 {
152
153
154
155
156
157 fi, e := r.Stat(fullname)
158 if e == nil && fi.Mode().IsDir() {
159 err = nil
160 }
161 }
162 }
163 }
164 switch err.(type) {
165 case nil, errSymlink:
166 return struct{}{}, err
167 }
168 return struct{}{}, &PathError{Op: "mkdirat", Err: err}
169 }
170 _, err := doInRoot(r, fullname, openDirFunc, openLastComponentFunc)
171 if err != nil {
172 if _, ok := err.(*PathError); !ok {
173 err = &PathError{Op: "mkdirat", Path: fullname, Err: err}
174 }
175 }
176 return err
177 }
178
179 func rootReadlink(r *Root, name string) (string, error) {
180 target, err := doInRoot(r, name, nil, func(parent sysfdType, name string) (string, error) {
181 return readlinkat(parent, name)
182 })
183 if err != nil {
184 return "", &PathError{Op: "readlinkat", Path: name, Err: err}
185 }
186 return target, nil
187 }
188
189 func rootRemove(r *Root, name string) error {
190 _, err := doInRoot(r, name, nil, func(parent sysfdType, name string) (struct{}, error) {
191 return struct{}{}, removeat(parent, name)
192 })
193 if err != nil {
194 return &PathError{Op: "removeat", Path: name, Err: err}
195 }
196 return nil
197 }
198
199 func rootRemoveAll(r *Root, name string) error {
200
201
202 for len(name) > 0 && IsPathSeparator(name[len(name)-1]) {
203 name = name[:len(name)-1]
204 }
205 if endsWithDot(name) {
206
207 return &PathError{Op: "RemoveAll", Path: name, Err: syscall.EINVAL}
208 }
209 _, err := doInRoot(r, name, nil, func(parent sysfdType, name string) (struct{}, error) {
210 return struct{}{}, removeAllFrom(parent, name)
211 })
212 if IsNotExist(err) {
213 return nil
214 }
215 if err != nil {
216 return &PathError{Op: "RemoveAll", Path: name, Err: underlyingError(err)}
217 }
218 return err
219 }
220
221 func rootRename(r *Root, oldname, newname string) error {
222 _, err := doInRoot(r, oldname, nil, func(oldparent sysfdType, oldname string) (struct{}, error) {
223 _, err := doInRoot(r, newname, nil, func(newparent sysfdType, newname string) (struct{}, error) {
224 return struct{}{}, renameat(oldparent, oldname, newparent, newname)
225 })
226 return struct{}{}, err
227 })
228 if err != nil {
229 return &LinkError{"renameat", oldname, newname, err}
230 }
231 return err
232 }
233
234 func rootLink(r *Root, oldname, newname string) error {
235 _, err := doInRoot(r, oldname, nil, func(oldparent sysfdType, oldname string) (struct{}, error) {
236 _, err := doInRoot(r, newname, nil, func(newparent sysfdType, newname string) (struct{}, error) {
237 return struct{}{}, linkat(oldparent, oldname, newparent, newname)
238 })
239 return struct{}{}, err
240 })
241 if err != nil {
242 return &LinkError{"linkat", oldname, newname, err}
243 }
244 return err
245 }
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265 func doInRoot[T any](r *Root, name string, openDirFunc func(parent sysfdType, name string) (sysfdType, error), f func(parent sysfdType, name string) (T, error)) (ret T, err error) {
266 if err := r.root.incref(); err != nil {
267 return ret, err
268 }
269 defer r.root.decref()
270
271 parts, suffixSep, err := splitPathInRoot(name, nil, nil)
272 if err != nil {
273 return ret, err
274 }
275 if openDirFunc == nil {
276 openDirFunc = rootOpenDir
277 }
278
279 rootfd := r.root.fd
280 dirfd := rootfd
281 defer func() {
282 if dirfd != rootfd {
283 syscall.Close(dirfd)
284 }
285 }()
286
287
288
289
290
291
292
293 const maxSteps = 255
294 const maxRestarts = 8
295
296 i := 0
297 steps := 0
298 restarts := 0
299 symlinks := 0
300 Loop:
301 for {
302 steps++
303 if steps > maxSteps && restarts > maxRestarts {
304 return ret, syscall.ENAMETOOLONG
305 }
306
307 if parts[i] == ".." {
308
309
310
311
312
313 restarts++
314 end := i + 1
315 for end < len(parts) && parts[end] == ".." {
316 end++
317 }
318 count := end - i
319 if count > i {
320 return ret, errPathEscapes
321 }
322 parts = slices.Delete(parts, i-count, end)
323 if len(parts) == 0 {
324 parts = []string{"."}
325 }
326 i = 0
327 if dirfd != rootfd {
328 syscall.Close(dirfd)
329 }
330 dirfd = rootfd
331 continue
332 }
333
334 if i == len(parts)-1 {
335
336
337
338
339
340
341 ret, err = f(dirfd, parts[i]+suffixSep)
342 if err == nil {
343 return
344 }
345 } else {
346 var fd sysfdType
347 fd, err = openDirFunc(dirfd, parts[i])
348 if err == nil {
349 if dirfd != rootfd {
350 syscall.Close(dirfd)
351 }
352 dirfd = fd
353 }
354 }
355
356 switch e := err.(type) {
357 case nil:
358 case errSymlink:
359 symlinks++
360 if symlinks > rootMaxSymlinks {
361 return ret, syscall.ELOOP
362 }
363 newparts, newSuffixSep, err := splitPathInRoot(string(e), parts[:i], parts[i+1:])
364 if err != nil {
365 return ret, err
366 }
367 if i == len(parts)-1 {
368
369
370
371
372
373
374 suffixSep = newSuffixSep
375 }
376 if len(newparts) < i || !slices.Equal(parts[:i], newparts[:i]) {
377
378
379 i = 0
380 if dirfd != rootfd {
381 syscall.Close(dirfd)
382 }
383 dirfd = rootfd
384 }
385 parts = newparts
386 continue Loop
387 case *PathError:
388
389 e.Path = parts[0]
390 for _, part := range parts[1 : i+1] {
391 e.Path += string(PathSeparator) + part
392 }
393 return ret, e
394 default:
395 return ret, err
396 }
397
398 i++
399 }
400 }
401
402
403
404 type errSymlink string
405
406 func (errSymlink) Error() string { panic("errSymlink is not user-visible") }
407
View as plain text