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 if err := mkdirat(parent, name, perm); err != nil {
135 return 0, &PathError{Op: "mkdirat", Err: err}
136 }
137 }
138 panic("unreachable")
139 }
140
141 openLastComponentFunc := func(parent sysfdType, name string) (struct{}, error) {
142 err := mkdirat(parent, name, perm)
143 if err == syscall.EEXIST {
144 mode, e := modeAt(parent, name)
145 if e == nil {
146 if mode.IsDir() {
147
148 err = nil
149 } else if mode&ModeSymlink != 0 {
150
151
152
153
154
155 fi, e := r.Stat(fullname)
156 if e == nil && fi.Mode().IsDir() {
157 err = nil
158 }
159 }
160 }
161 }
162 switch err.(type) {
163 case nil, errSymlink:
164 return struct{}{}, err
165 }
166 return struct{}{}, &PathError{Op: "mkdirat", Err: err}
167 }
168 _, err := doInRoot(r, fullname, openDirFunc, openLastComponentFunc)
169 if err != nil {
170 if _, ok := err.(*PathError); !ok {
171 err = &PathError{Op: "mkdirat", Path: fullname, Err: err}
172 }
173 }
174 return err
175 }
176
177 func rootReadlink(r *Root, name string) (string, error) {
178 target, err := doInRoot(r, name, nil, func(parent sysfdType, name string) (string, error) {
179 return readlinkat(parent, name)
180 })
181 if err != nil {
182 return "", &PathError{Op: "readlinkat", Path: name, Err: err}
183 }
184 return target, nil
185 }
186
187 func rootRemove(r *Root, name string) error {
188 _, err := doInRoot(r, name, nil, func(parent sysfdType, name string) (struct{}, error) {
189 return struct{}{}, removeat(parent, name)
190 })
191 if err != nil {
192 return &PathError{Op: "removeat", Path: name, Err: err}
193 }
194 return nil
195 }
196
197 func rootRemoveAll(r *Root, name string) error {
198
199
200 for len(name) > 0 && IsPathSeparator(name[len(name)-1]) {
201 name = name[:len(name)-1]
202 }
203 if endsWithDot(name) {
204
205 return &PathError{Op: "RemoveAll", Path: name, Err: syscall.EINVAL}
206 }
207 _, err := doInRoot(r, name, nil, func(parent sysfdType, name string) (struct{}, error) {
208 return struct{}{}, removeAllFrom(parent, name)
209 })
210 if IsNotExist(err) {
211 return nil
212 }
213 if err != nil {
214 return &PathError{Op: "RemoveAll", Path: name, Err: underlyingError(err)}
215 }
216 return err
217 }
218
219 func rootRename(r *Root, oldname, newname string) error {
220 _, err := doInRoot(r, oldname, nil, func(oldparent sysfdType, oldname string) (struct{}, error) {
221 _, err := doInRoot(r, newname, nil, func(newparent sysfdType, newname string) (struct{}, error) {
222 return struct{}{}, renameat(oldparent, oldname, newparent, newname)
223 })
224 return struct{}{}, err
225 })
226 if err != nil {
227 return &LinkError{"renameat", oldname, newname, err}
228 }
229 return err
230 }
231
232 func rootLink(r *Root, oldname, newname string) error {
233 _, err := doInRoot(r, oldname, nil, func(oldparent sysfdType, oldname string) (struct{}, error) {
234 _, err := doInRoot(r, newname, nil, func(newparent sysfdType, newname string) (struct{}, error) {
235 return struct{}{}, linkat(oldparent, oldname, newparent, newname)
236 })
237 return struct{}{}, err
238 })
239 if err != nil {
240 return &LinkError{"linkat", oldname, newname, err}
241 }
242 return err
243 }
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263 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) {
264 if err := r.root.incref(); err != nil {
265 return ret, err
266 }
267 defer r.root.decref()
268
269 parts, suffixSep, err := splitPathInRoot(name, nil, nil)
270 if err != nil {
271 return ret, err
272 }
273 if openDirFunc == nil {
274 openDirFunc = rootOpenDir
275 }
276
277 rootfd := r.root.fd
278 dirfd := rootfd
279 defer func() {
280 if dirfd != rootfd {
281 syscall.Close(dirfd)
282 }
283 }()
284
285
286
287
288
289
290
291 const maxSteps = 255
292 const maxRestarts = 8
293
294 i := 0
295 steps := 0
296 restarts := 0
297 symlinks := 0
298 Loop:
299 for {
300 steps++
301 if steps > maxSteps && restarts > maxRestarts {
302 return ret, syscall.ENAMETOOLONG
303 }
304
305 if parts[i] == ".." {
306
307
308
309
310
311 restarts++
312 end := i + 1
313 for end < len(parts) && parts[end] == ".." {
314 end++
315 }
316 count := end - i
317 if count > i {
318 return ret, errPathEscapes
319 }
320 parts = slices.Delete(parts, i-count, end)
321 if len(parts) == 0 {
322 parts = []string{"."}
323 }
324 i = 0
325 if dirfd != rootfd {
326 syscall.Close(dirfd)
327 }
328 dirfd = rootfd
329 continue
330 }
331
332 if i == len(parts)-1 {
333
334
335
336
337
338
339 ret, err = f(dirfd, parts[i]+suffixSep)
340 if err == nil {
341 return
342 }
343 } else {
344 var fd sysfdType
345 fd, err = openDirFunc(dirfd, parts[i])
346 if err == nil {
347 if dirfd != rootfd {
348 syscall.Close(dirfd)
349 }
350 dirfd = fd
351 }
352 }
353
354 switch e := err.(type) {
355 case nil:
356 case errSymlink:
357 symlinks++
358 if symlinks > rootMaxSymlinks {
359 return ret, syscall.ELOOP
360 }
361 newparts, newSuffixSep, err := splitPathInRoot(string(e), parts[:i], parts[i+1:])
362 if err != nil {
363 return ret, err
364 }
365 if i == len(parts)-1 {
366
367
368
369
370
371
372 suffixSep = newSuffixSep
373 }
374 if len(newparts) < i || !slices.Equal(parts[:i], newparts[:i]) {
375
376
377 i = 0
378 if dirfd != rootfd {
379 syscall.Close(dirfd)
380 }
381 dirfd = rootfd
382 }
383 parts = newparts
384 continue Loop
385 case *PathError:
386
387 e.Path = parts[0]
388 for _, part := range parts[1 : i+1] {
389 e.Path += string(PathSeparator) + part
390 }
391 return ret, e
392 default:
393 return ret, err
394 }
395
396 i++
397 }
398 }
399
400
401
402 type errSymlink string
403
404 func (errSymlink) Error() string { panic("errSymlink is not user-visible") }
405
View as plain text