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, 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, 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, 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, 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, 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 rootReadlink(r *Root, name string) (string, error) {
122 target, err := doInRoot(r, name, func(parent sysfdType, name string) (string, error) {
123 return readlinkat(parent, name)
124 })
125 if err != nil {
126 return "", &PathError{Op: "readlinkat", Path: name, Err: err}
127 }
128 return target, nil
129 }
130
131 func rootRemove(r *Root, name string) error {
132 _, err := doInRoot(r, name, func(parent sysfdType, name string) (struct{}, error) {
133 return struct{}{}, removeat(parent, name)
134 })
135 if err != nil {
136 return &PathError{Op: "removeat", Path: name, Err: err}
137 }
138 return nil
139 }
140
141 func rootRename(r *Root, oldname, newname string) error {
142 _, err := doInRoot(r, oldname, func(oldparent sysfdType, oldname string) (struct{}, error) {
143 _, err := doInRoot(r, newname, func(newparent sysfdType, newname string) (struct{}, error) {
144 return struct{}{}, renameat(oldparent, oldname, newparent, newname)
145 })
146 return struct{}{}, err
147 })
148 if err != nil {
149 return &LinkError{"renameat", oldname, newname, err}
150 }
151 return err
152 }
153
154 func rootLink(r *Root, oldname, newname string) error {
155 _, err := doInRoot(r, oldname, func(oldparent sysfdType, oldname string) (struct{}, error) {
156 _, err := doInRoot(r, newname, func(newparent sysfdType, newname string) (struct{}, error) {
157 return struct{}{}, linkat(oldparent, oldname, newparent, newname)
158 })
159 return struct{}{}, err
160 })
161 if err != nil {
162 return &LinkError{"linkat", oldname, newname, err}
163 }
164 return err
165 }
166
167
168
169
170
171
172
173
174
175 func doInRoot[T any](r *Root, name string, f func(parent sysfdType, name string) (T, error)) (ret T, err error) {
176 if err := r.root.incref(); err != nil {
177 return ret, err
178 }
179 defer r.root.decref()
180
181 parts, err := splitPathInRoot(name, nil, nil)
182 if err != nil {
183 return ret, err
184 }
185
186 rootfd := r.root.fd
187 dirfd := rootfd
188 defer func() {
189 if dirfd != rootfd {
190 syscall.Close(dirfd)
191 }
192 }()
193
194
195
196
197
198
199
200 const maxSteps = 255
201 const maxRestarts = 8
202
203 i := 0
204 steps := 0
205 restarts := 0
206 symlinks := 0
207 for {
208 steps++
209 if steps > maxSteps && restarts > maxRestarts {
210 return ret, syscall.ENAMETOOLONG
211 }
212
213 if parts[i] == ".." {
214
215
216
217
218
219 restarts++
220 end := i + 1
221 for end < len(parts) && parts[end] == ".." {
222 end++
223 }
224 count := end - i
225 if count > i {
226 return ret, errPathEscapes
227 }
228 parts = slices.Delete(parts, i-count, end)
229 if len(parts) == 0 {
230 parts = []string{"."}
231 }
232 i = 0
233 if dirfd != rootfd {
234 syscall.Close(dirfd)
235 }
236 dirfd = rootfd
237 continue
238 }
239
240 if i == len(parts)-1 {
241
242
243
244
245 ret, err = f(dirfd, parts[i])
246 if _, ok := err.(errSymlink); !ok {
247 return ret, err
248 }
249 } else {
250 var fd sysfdType
251 fd, err = rootOpenDir(dirfd, parts[i])
252 if err == nil {
253 if dirfd != rootfd {
254 syscall.Close(dirfd)
255 }
256 dirfd = fd
257 } else if _, ok := err.(errSymlink); !ok {
258 return ret, err
259 }
260 }
261
262 if e, ok := err.(errSymlink); ok {
263 symlinks++
264 if symlinks > rootMaxSymlinks {
265 return ret, syscall.ELOOP
266 }
267 newparts, err := splitPathInRoot(string(e), parts[:i], parts[i+1:])
268 if err != nil {
269 return ret, err
270 }
271 if len(newparts) < i || !slices.Equal(parts[:i], newparts[:i]) {
272
273
274 i = 0
275 if dirfd != rootfd {
276 syscall.Close(dirfd)
277 }
278 dirfd = rootfd
279 }
280 parts = newparts
281 continue
282 }
283
284 i++
285 }
286 }
287
288
289
290 type errSymlink string
291
292 func (errSymlink) Error() string { panic("errSymlink is not user-visible") }
293
View as plain text