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, suffixSep string, err error) {
302 if len(s) == 0 {
303 return nil, "", errors.New("empty path")
304 }
305 if IsPathSeparator(s[0]) {
306 return nil, "", errPathEscapes
307 }
308
309 if runtime.GOOS == "windows" {
310
311 s, err = rootCleanPath(s, prefix, suffix)
312 if err != nil {
313 return nil, "", 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 suffixSep = s[partEnd:]
337 break
338 }
339 if parts[len(parts)-1] == "." {
340
341 parts = parts[:len(parts)-1]
342 }
343 i = j
344 }
345 if len(suffix) > 0 && len(parts) > 0 && parts[len(parts)-1] == "." {
346
347 parts = parts[:len(parts)-1]
348 }
349 parts = append(parts, suffix...)
350 return parts, suffixSep, nil
351 }
352
353
354
355
356
357 func (r *Root) FS() fs.FS {
358 return (*rootFS)(r)
359 }
360
361 type rootFS Root
362
363 func (rfs *rootFS) Open(name string) (fs.File, error) {
364 r := (*Root)(rfs)
365 if !isValidRootFSPath(name) {
366 return nil, &PathError{Op: "open", Path: name, Err: ErrInvalid}
367 }
368 f, err := r.Open(name)
369 if err != nil {
370 return nil, err
371 }
372 return f, nil
373 }
374
375 func (rfs *rootFS) ReadDir(name string) ([]DirEntry, error) {
376 r := (*Root)(rfs)
377 if !isValidRootFSPath(name) {
378 return nil, &PathError{Op: "readdir", Path: name, Err: ErrInvalid}
379 }
380
381
382
383
384
385
386
387 f, err := r.Open(name)
388 if err != nil {
389 return nil, err
390 }
391 defer f.Close()
392 dirs, err := f.ReadDir(-1)
393 slices.SortFunc(dirs, func(a, b DirEntry) int {
394 return bytealg.CompareString(a.Name(), b.Name())
395 })
396 return dirs, err
397 }
398
399 func (rfs *rootFS) ReadFile(name string) ([]byte, error) {
400 r := (*Root)(rfs)
401 if !isValidRootFSPath(name) {
402 return nil, &PathError{Op: "readfile", Path: name, Err: ErrInvalid}
403 }
404 f, err := r.Open(name)
405 if err != nil {
406 return nil, err
407 }
408 defer f.Close()
409 return readFileContents(statOrZero(f), f.Read)
410 }
411
412 func (rfs *rootFS) ReadLink(name string) (string, error) {
413 r := (*Root)(rfs)
414 if !isValidRootFSPath(name) {
415 return "", &PathError{Op: "readlink", Path: name, Err: ErrInvalid}
416 }
417 return r.Readlink(name)
418 }
419
420 func (rfs *rootFS) Stat(name string) (FileInfo, error) {
421 r := (*Root)(rfs)
422 if !isValidRootFSPath(name) {
423 return nil, &PathError{Op: "stat", Path: name, Err: ErrInvalid}
424 }
425 return r.Stat(name)
426 }
427
428 func (rfs *rootFS) Lstat(name string) (FileInfo, error) {
429 r := (*Root)(rfs)
430 if !isValidRootFSPath(name) {
431 return nil, &PathError{Op: "lstat", Path: name, Err: ErrInvalid}
432 }
433 return r.Lstat(name)
434 }
435
436
437 func isValidRootFSPath(name string) bool {
438 if !fs.ValidPath(name) {
439 return false
440 }
441 if runtime.GOOS == "windows" {
442
443
444
445
446 if stringslite.IndexByte(name, '\\') >= 0 {
447 return false
448 }
449 }
450 return true
451 }
452
View as plain text