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, suffixSep 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 partEnd := j
219 for j < len(s) && IsPathSeparator(s[j]) {
220 j++
221 }
222 if j == len(s) {
223
224
225 suffixSep = s[partEnd:]
226 break
227 }
228 if parts[len(parts)-1] == "." {
229
230 parts = parts[:len(parts)-1]
231 }
232 i = j
233 }
234 if len(suffix) > 0 && len(parts) > 0 && parts[len(parts)-1] == "." {
235
236 parts = parts[:len(parts)-1]
237 }
238 parts = append(parts, suffix...)
239 return parts, suffixSep, nil
240 }
241
242
243
244
245
246 func (r *Root) FS() fs.FS {
247 return (*rootFS)(r)
248 }
249
250 type rootFS Root
251
252 func (rfs *rootFS) Open(name string) (fs.File, error) {
253 r := (*Root)(rfs)
254 if !isValidRootFSPath(name) {
255 return nil, &PathError{Op: "open", Path: name, Err: ErrInvalid}
256 }
257 f, err := r.Open(name)
258 if err != nil {
259 return nil, err
260 }
261 return f, nil
262 }
263
264 func (rfs *rootFS) ReadDir(name string) ([]DirEntry, error) {
265 r := (*Root)(rfs)
266 if !isValidRootFSPath(name) {
267 return nil, &PathError{Op: "readdir", Path: name, Err: ErrInvalid}
268 }
269
270
271
272
273
274
275
276 f, err := r.Open(name)
277 if err != nil {
278 return nil, err
279 }
280 defer f.Close()
281 dirs, err := f.ReadDir(-1)
282 slices.SortFunc(dirs, func(a, b DirEntry) int {
283 return bytealg.CompareString(a.Name(), b.Name())
284 })
285 return dirs, err
286 }
287
288 func (rfs *rootFS) ReadFile(name string) ([]byte, error) {
289 r := (*Root)(rfs)
290 if !isValidRootFSPath(name) {
291 return nil, &PathError{Op: "readfile", Path: name, Err: ErrInvalid}
292 }
293 f, err := r.Open(name)
294 if err != nil {
295 return nil, err
296 }
297 defer f.Close()
298 return readFileContents(f)
299 }
300
301 func (rfs *rootFS) Stat(name string) (FileInfo, error) {
302 r := (*Root)(rfs)
303 if !isValidRootFSPath(name) {
304 return nil, &PathError{Op: "stat", Path: name, Err: ErrInvalid}
305 }
306 return r.Stat(name)
307 }
308
309
310 func isValidRootFSPath(name string) bool {
311 if !fs.ValidPath(name) {
312 return false
313 }
314 if runtime.GOOS == "windows" {
315
316
317
318
319 if stringslite.IndexByte(name, '\\') >= 0 {
320 return false
321 }
322 }
323 return true
324 }
325
View as plain text