Source file
src/os/root.go
1
2
3
4
5 package os
6
7 import (
8 "errors"
9 "internal/bytealg"
10 "internal/stringslite"
11 "internal/testlog"
12 "io/fs"
13 "runtime"
14 "slices"
15 "time"
16 )
17
18
19
20
21
22
23
24
25 func OpenInRoot(dir, name string) (*File, error) {
26 r, err := OpenRoot(dir)
27 if err != nil {
28 return nil, err
29 }
30 defer r.Close()
31 return r.Open(name)
32 }
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68 type Root struct {
69 root *root
70 }
71
72 const (
73
74
75
76 rootMaxSymlinks = 8
77 )
78
79
80
81
82 func OpenRoot(name string) (*Root, error) {
83 testlog.Open(name)
84 return openRootNolog(name)
85 }
86
87
88
89
90 func (r *Root) Name() string {
91 return r.root.Name()
92 }
93
94
95
96 func (r *Root) Close() error {
97 return r.root.Close()
98 }
99
100
101
102 func (r *Root) Open(name string) (*File, error) {
103 return r.OpenFile(name, O_RDONLY, 0)
104 }
105
106
107
108 func (r *Root) Create(name string) (*File, error) {
109 return r.OpenFile(name, O_RDWR|O_CREATE|O_TRUNC, 0666)
110 }
111
112
113
114
115
116
117 func (r *Root) OpenFile(name string, flag int, perm FileMode) (*File, error) {
118 if perm&0o777 != perm {
119 return nil, &PathError{Op: "openat", Path: name, Err: errors.New("unsupported file mode")}
120 }
121 r.logOpen(name)
122 rf, err := rootOpenFileNolog(r, name, flag, perm)
123 if err != nil {
124 return nil, err
125 }
126 rf.appendMode = flag&O_APPEND != 0
127 return rf, nil
128 }
129
130
131
132 func (r *Root) OpenRoot(name string) (*Root, error) {
133 r.logOpen(name)
134 return openRootInRoot(r, name)
135 }
136
137
138
139 func (r *Root) Chmod(name string, mode FileMode) error {
140 return rootChmod(r, name, mode)
141 }
142
143
144
145
146
147
148
149 func (r *Root) Mkdir(name string, perm FileMode) error {
150 if perm&0o777 != perm {
151 return &PathError{Op: "mkdirat", Path: name, Err: errors.New("unsupported file mode")}
152 }
153 return rootMkdir(r, name, perm)
154 }
155
156
157
158
159
160
161 func (r *Root) MkdirAll(name string, perm FileMode) error {
162 if perm&0o777 != perm {
163 return &PathError{Op: "mkdirat", Path: name, Err: errors.New("unsupported file mode")}
164 }
165 return rootMkdirAll(r, name, perm)
166 }
167
168
169
170 func (r *Root) Chown(name string, uid, gid int) error {
171 return rootChown(r, name, uid, gid)
172 }
173
174
175
176 func (r *Root) Lchown(name string, uid, gid int) error {
177 return rootLchown(r, name, uid, gid)
178 }
179
180
181
182 func (r *Root) Chtimes(name string, atime time.Time, mtime time.Time) error {
183 return rootChtimes(r, name, atime, mtime)
184 }
185
186
187
188 func (r *Root) Remove(name string) error {
189 return rootRemove(r, name)
190 }
191
192
193
194 func (r *Root) RemoveAll(name string) error {
195 return rootRemoveAll(r, name)
196 }
197
198
199
200 func (r *Root) Stat(name string) (FileInfo, error) {
201 r.logStat(name)
202 return rootStat(r, name, false)
203 }
204
205
206
207
208
209 func (r *Root) Lstat(name string) (FileInfo, error) {
210 r.logStat(name)
211 return rootStat(r, name, true)
212 }
213
214
215
216 func (r *Root) Readlink(name string) (string, error) {
217 return rootReadlink(r, name)
218 }
219
220
221
222
223 func (r *Root) Rename(oldname, newname string) error {
224 return rootRename(r, oldname, newname)
225 }
226
227
228
229
230
231
232
233
234
235 func (r *Root) Link(oldname, newname string) error {
236 return rootLink(r, oldname, newname)
237 }
238
239
240
241
242
243
244
245
246
247 func (r *Root) Symlink(oldname, newname string) error {
248 return rootSymlink(r, oldname, newname)
249 }
250
251
252
253 func (r *Root) ReadFile(name string) ([]byte, error) {
254 f, err := r.Open(name)
255 if err != nil {
256 return nil, err
257 }
258 defer f.Close()
259 return readFileContents(statOrZero(f), f.Read)
260 }
261
262
263
264 func (r *Root) WriteFile(name string, data []byte, perm FileMode) error {
265 f, err := r.OpenFile(name, O_WRONLY|O_CREATE|O_TRUNC, perm)
266 if err != nil {
267 return err
268 }
269 _, err = f.Write(data)
270 if err1 := f.Close(); err == nil {
271 err = err1
272 }
273 return err
274 }
275
276 func (r *Root) logOpen(name string) {
277 if log := testlog.Logger(); log != nil {
278
279
280 log.Open(joinPath(r.Name(), name))
281 }
282 }
283
284 func (r *Root) logStat(name string) {
285 if log := testlog.Logger(); log != nil {
286
287
288 log.Stat(joinPath(r.Name(), name))
289 }
290 }
291
292
293
294
295
296
297
298
299
300
301 func splitPathInRoot(s string, prefix, suffix []string) (_ []string, endsInSlash bool, err error) {
302 if len(s) == 0 {
303 return nil, false, errors.New("empty path")
304 }
305 if IsPathSeparator(s[0]) {
306 return nil, false, errPathEscapes
307 }
308
309 if runtime.GOOS == "windows" {
310
311 s, err = rootCleanPath(s, prefix, suffix)
312 if err != nil {
313 return nil, false, err
314 }
315 prefix = nil
316 suffix = nil
317 }
318
319 parts := slices.Clone(prefix)
320 i, j := 0, 1
321 for {
322 if j < len(s) && !IsPathSeparator(s[j]) {
323
324 j++
325 continue
326 }
327 parts = append(parts, s[i:j])
328
329 partEnd := j
330 for j < len(s) && IsPathSeparator(s[j]) {
331 j++
332 }
333 if j == len(s) {
334
335
336 if len(s[partEnd:]) > 0 {
337 endsInSlash = true
338 }
339 break
340 }
341 if parts[len(parts)-1] == "." {
342
343 parts = parts[:len(parts)-1]
344 }
345 i = j
346 }
347 if len(suffix) > 0 && len(parts) > 0 && parts[len(parts)-1] == "." {
348
349 parts = parts[:len(parts)-1]
350 }
351 parts = append(parts, suffix...)
352 return parts, endsInSlash, nil
353 }
354
355
356
357
358
359 func (r *Root) FS() fs.FS {
360 return (*rootFS)(r)
361 }
362
363 type rootFS Root
364
365 func (rfs *rootFS) Open(name string) (fs.File, error) {
366 r := (*Root)(rfs)
367 if !isValidRootFSPath(name) {
368 return nil, &PathError{Op: "open", Path: name, Err: ErrInvalid}
369 }
370 f, err := r.Open(name)
371 if err != nil {
372 return nil, err
373 }
374 return f, nil
375 }
376
377 func (rfs *rootFS) ReadDir(name string) ([]DirEntry, error) {
378 r := (*Root)(rfs)
379 if !isValidRootFSPath(name) {
380 return nil, &PathError{Op: "readdir", Path: name, Err: ErrInvalid}
381 }
382
383
384
385
386
387
388
389 f, err := r.Open(name)
390 if err != nil {
391 return nil, err
392 }
393 defer f.Close()
394 dirs, err := f.ReadDir(-1)
395 slices.SortFunc(dirs, func(a, b DirEntry) int {
396 return bytealg.CompareString(a.Name(), b.Name())
397 })
398 return dirs, err
399 }
400
401 func (rfs *rootFS) ReadFile(name string) ([]byte, error) {
402 r := (*Root)(rfs)
403 if !isValidRootFSPath(name) {
404 return nil, &PathError{Op: "readfile", Path: name, Err: ErrInvalid}
405 }
406 f, err := r.Open(name)
407 if err != nil {
408 return nil, err
409 }
410 defer f.Close()
411 return readFileContents(statOrZero(f), f.Read)
412 }
413
414 func (rfs *rootFS) ReadLink(name string) (string, error) {
415 r := (*Root)(rfs)
416 if !isValidRootFSPath(name) {
417 return "", &PathError{Op: "readlink", Path: name, Err: ErrInvalid}
418 }
419 return r.Readlink(name)
420 }
421
422 func (rfs *rootFS) Stat(name string) (FileInfo, error) {
423 r := (*Root)(rfs)
424 if !isValidRootFSPath(name) {
425 return nil, &PathError{Op: "stat", Path: name, Err: ErrInvalid}
426 }
427 return r.Stat(name)
428 }
429
430 func (rfs *rootFS) Lstat(name string) (FileInfo, error) {
431 r := (*Root)(rfs)
432 if !isValidRootFSPath(name) {
433 return nil, &PathError{Op: "lstat", Path: name, Err: ErrInvalid}
434 }
435 return r.Lstat(name)
436 }
437
438
439 func isValidRootFSPath(name string) bool {
440 if !fs.ValidPath(name) {
441 return false
442 }
443 if runtime.GOOS == "windows" {
444
445
446
447
448 if stringslite.IndexByte(name, '\\') >= 0 {
449 return false
450 }
451 }
452 return true
453 }
454
View as plain text