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 )
16
17
18
19
20
21
22
23
24 func OpenInRoot(dir, name string) (*File, error) {
25 r, err := OpenRoot(dir)
26 if err != nil {
27 return nil, err
28 }
29 defer r.Close()
30 return r.Open(name)
31 }
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 type Root struct {
63 root root
64 }
65
66 const (
67
68
69
70 rootMaxSymlinks = 8
71 )
72
73
74
75 func OpenRoot(name string) (*Root, error) {
76 testlog.Open(name)
77 return openRootNolog(name)
78 }
79
80
81
82
83 func (r *Root) Name() string {
84 return r.root.Name()
85 }
86
87
88
89 func (r *Root) Close() error {
90 return r.root.Close()
91 }
92
93
94
95 func (r *Root) Open(name string) (*File, error) {
96 return r.OpenFile(name, O_RDONLY, 0)
97 }
98
99
100
101 func (r *Root) Create(name string) (*File, error) {
102 return r.OpenFile(name, O_RDWR|O_CREATE|O_TRUNC, 0666)
103 }
104
105
106
107
108
109
110 func (r *Root) OpenFile(name string, flag int, perm FileMode) (*File, error) {
111 if perm&0o777 != perm {
112 return nil, &PathError{Op: "openat", Path: name, Err: errors.New("unsupported file mode")}
113 }
114 r.logOpen(name)
115 rf, err := rootOpenFileNolog(r, name, flag, perm)
116 if err != nil {
117 return nil, err
118 }
119 rf.appendMode = flag&O_APPEND != 0
120 return rf, nil
121 }
122
123
124
125 func (r *Root) OpenRoot(name string) (*Root, error) {
126 r.logOpen(name)
127 return openRootInRoot(r, name)
128 }
129
130
131
132
133
134
135
136 func (r *Root) Mkdir(name string, perm FileMode) error {
137 if perm&0o777 != perm {
138 return &PathError{Op: "mkdirat", Path: name, Err: errors.New("unsupported file mode")}
139 }
140 return rootMkdir(r, name, perm)
141 }
142
143
144
145 func (r *Root) Remove(name string) error {
146 return rootRemove(r, name)
147 }
148
149
150
151 func (r *Root) Stat(name string) (FileInfo, error) {
152 r.logStat(name)
153 return rootStat(r, name, false)
154 }
155
156
157
158
159
160 func (r *Root) Lstat(name string) (FileInfo, error) {
161 r.logStat(name)
162 return rootStat(r, name, true)
163 }
164
165 func (r *Root) logOpen(name string) {
166 if log := testlog.Logger(); log != nil {
167
168
169 log.Open(joinPath(r.Name(), name))
170 }
171 }
172
173 func (r *Root) logStat(name string) {
174 if log := testlog.Logger(); log != nil {
175
176
177 log.Stat(joinPath(r.Name(), name))
178 }
179 }
180
181
182
183
184
185
186
187
188
189
190 func splitPathInRoot(s string, prefix, suffix []string) (_ []string, err error) {
191 if len(s) == 0 {
192 return nil, errors.New("empty path")
193 }
194 if IsPathSeparator(s[0]) {
195 return nil, errPathEscapes
196 }
197
198 if runtime.GOOS == "windows" {
199
200 s, err = rootCleanPath(s, prefix, suffix)
201 if err != nil {
202 return nil, err
203 }
204 prefix = nil
205 suffix = nil
206 }
207
208 parts := append([]string{}, prefix...)
209 i, j := 0, 1
210 for {
211 if j < len(s) && !IsPathSeparator(s[j]) {
212
213 j++
214 continue
215 }
216 parts = append(parts, s[i:j])
217
218 for j < len(s) && IsPathSeparator(s[j]) {
219 j++
220 }
221 if j == len(s) {
222
223
224 parts[len(parts)-1] = s[i:]
225 break
226 }
227 if parts[len(parts)-1] == "." {
228
229 parts = parts[:len(parts)-1]
230 }
231 i = j
232 }
233 if len(suffix) > 0 && len(parts) > 0 && parts[len(parts)-1] == "." {
234
235 parts = parts[:len(parts)-1]
236 }
237 parts = append(parts, suffix...)
238 return parts, nil
239 }
240
241
242
243
244
245 func (r *Root) FS() fs.FS {
246 return (*rootFS)(r)
247 }
248
249 type rootFS Root
250
251 func (rfs *rootFS) Open(name string) (fs.File, error) {
252 r := (*Root)(rfs)
253 if !isValidRootFSPath(name) {
254 return nil, &PathError{Op: "open", Path: name, Err: ErrInvalid}
255 }
256 f, err := r.Open(name)
257 if err != nil {
258 return nil, err
259 }
260 return f, nil
261 }
262
263 func (rfs *rootFS) ReadDir(name string) ([]DirEntry, error) {
264 r := (*Root)(rfs)
265 if !isValidRootFSPath(name) {
266 return nil, &PathError{Op: "readdir", Path: name, Err: ErrInvalid}
267 }
268
269
270
271
272
273
274
275 f, err := r.Open(name)
276 if err != nil {
277 return nil, err
278 }
279 defer f.Close()
280 dirs, err := f.ReadDir(-1)
281 slices.SortFunc(dirs, func(a, b DirEntry) int {
282 return bytealg.CompareString(a.Name(), b.Name())
283 })
284 return dirs, err
285 }
286
287 func (rfs *rootFS) ReadFile(name string) ([]byte, error) {
288 r := (*Root)(rfs)
289 if !isValidRootFSPath(name) {
290 return nil, &PathError{Op: "readfile", Path: name, Err: ErrInvalid}
291 }
292 f, err := r.Open(name)
293 if err != nil {
294 return nil, err
295 }
296 defer f.Close()
297 return readFileContents(f)
298 }
299
300 func (rfs *rootFS) Stat(name string) (FileInfo, error) {
301 r := (*Root)(rfs)
302 if !isValidRootFSPath(name) {
303 return nil, &PathError{Op: "stat", Path: name, Err: ErrInvalid}
304 }
305 return r.Stat(name)
306 }
307
308
309 func isValidRootFSPath(name string) bool {
310 if !fs.ValidPath(name) {
311 return false
312 }
313 if runtime.GOOS == "windows" {
314
315
316
317
318 if stringslite.IndexByte(name, '\\') >= 0 {
319 return false
320 }
321 }
322 return true
323 }
324
View as plain text