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