1 package sysfs
2
3 import (
4 "io"
5 "io/fs"
6 "os"
7 "time"
8
9 experimentalsys "github.com/tetratelabs/wazero/experimental/sys"
10 "github.com/tetratelabs/wazero/internal/fsapi"
11 "github.com/tetratelabs/wazero/sys"
12 )
13
14 func NewStdioFile(stdin bool, f fs.File) (fsapi.File, error) {
15
16
17
18 var mode fs.FileMode
19 if st, err := f.Stat(); err != nil {
20 return nil, err
21 } else {
22 mode = st.Mode()
23 }
24 var flag experimentalsys.Oflag
25 if stdin {
26 flag = experimentalsys.O_RDONLY
27 } else {
28 flag = experimentalsys.O_WRONLY
29 }
30 var file fsapi.File
31 if of, ok := f.(*os.File); ok {
32
33 file = newOsFile("", flag, 0, of)
34 } else {
35 file = &fsFile{file: f}
36 }
37 return &stdioFile{File: file, st: sys.Stat_t{Mode: mode, Nlink: 1}}, nil
38 }
39
40 func OpenFile(path string, flag experimentalsys.Oflag, perm fs.FileMode) (*os.File, experimentalsys.Errno) {
41 if flag&experimentalsys.O_DIRECTORY != 0 && flag&(experimentalsys.O_WRONLY|experimentalsys.O_RDWR) != 0 {
42 return nil, experimentalsys.EISDIR
43 }
44 return openFile(path, flag, perm)
45 }
46
47 func OpenOSFile(path string, flag experimentalsys.Oflag, perm fs.FileMode) (experimentalsys.File, experimentalsys.Errno) {
48 f, errno := OpenFile(path, flag, perm)
49 if errno != 0 {
50 return nil, errno
51 }
52 return newOsFile(path, flag, perm, f), 0
53 }
54
55 func OpenFSFile(fs fs.FS, path string, flag experimentalsys.Oflag, perm fs.FileMode) (experimentalsys.File, experimentalsys.Errno) {
56 if flag&experimentalsys.O_DIRECTORY != 0 && flag&(experimentalsys.O_WRONLY|experimentalsys.O_RDWR) != 0 {
57 return nil, experimentalsys.EISDIR
58 }
59 f, err := fs.Open(path)
60 if errno := experimentalsys.UnwrapOSError(err); errno != 0 {
61 return nil, errno
62 }
63
64
65 return &fsFile{fs: fs, name: path, file: f}, 0
66 }
67
68 type stdioFile struct {
69 fsapi.File
70 st sys.Stat_t
71 }
72
73
74 func (f *stdioFile) SetAppend(bool) experimentalsys.Errno {
75
76 return 0
77 }
78
79
80 func (f *stdioFile) IsAppend() bool {
81 return true
82 }
83
84
85 func (f *stdioFile) Stat() (sys.Stat_t, experimentalsys.Errno) {
86 return f.st, 0
87 }
88
89
90 func (f *stdioFile) Close() experimentalsys.Errno {
91 return 0
92 }
93
94
95
96
97 type fsFile struct {
98 experimentalsys.UnimplementedFile
99
100
101
102 fs fs.FS
103
104
105 name string
106
107
108 file fs.File
109
110
111
112
113
114 reopenDir bool
115
116
117 closed bool
118
119
120 cachedSt *cachedStat
121 }
122
123 type cachedStat struct {
124
125 dev uint64
126
127
128 ino sys.Inode
129
130
131 isDir bool
132 }
133
134
135
136 func (f *fsFile) cachedStat() (dev uint64, ino sys.Inode, isDir bool, errno experimentalsys.Errno) {
137 if f.cachedSt == nil {
138 if _, errno = f.Stat(); errno != 0 {
139 return
140 }
141 }
142 return f.cachedSt.dev, f.cachedSt.ino, f.cachedSt.isDir, 0
143 }
144
145
146 func (f *fsFile) Dev() (uint64, experimentalsys.Errno) {
147 dev, _, _, errno := f.cachedStat()
148 return dev, errno
149 }
150
151
152 func (f *fsFile) Ino() (sys.Inode, experimentalsys.Errno) {
153 _, ino, _, errno := f.cachedStat()
154 return ino, errno
155 }
156
157
158 func (f *fsFile) IsDir() (bool, experimentalsys.Errno) {
159 _, _, isDir, errno := f.cachedStat()
160 return isDir, errno
161 }
162
163
164 func (f *fsFile) IsAppend() bool {
165 return false
166 }
167
168
169 func (f *fsFile) SetAppend(bool) (errno experimentalsys.Errno) {
170 return fileError(f, f.closed, experimentalsys.ENOSYS)
171 }
172
173
174 func (f *fsFile) Stat() (sys.Stat_t, experimentalsys.Errno) {
175 if f.closed {
176 return sys.Stat_t{}, experimentalsys.EBADF
177 }
178
179 st, errno := statFile(f.file)
180 switch errno {
181 case 0:
182 f.cachedSt = &cachedStat{dev: st.Dev, ino: st.Ino, isDir: st.Mode&fs.ModeDir == fs.ModeDir}
183 case experimentalsys.EIO:
184 errno = experimentalsys.EBADF
185 }
186 return st, errno
187 }
188
189
190 func (f *fsFile) Read(buf []byte) (n int, errno experimentalsys.Errno) {
191 if n, errno = read(f.file, buf); errno != 0 {
192
193 errno = fileError(f, f.closed, errno)
194 }
195 return
196 }
197
198
199 func (f *fsFile) Pread(buf []byte, off int64) (n int, errno experimentalsys.Errno) {
200 if ra, ok := f.file.(io.ReaderAt); ok {
201 if n, errno = pread(ra, buf, off); errno != 0 {
202
203 errno = fileError(f, f.closed, errno)
204 }
205 return
206 }
207
208
209 if rs, ok := f.file.(io.ReadSeeker); ok {
210
211 currentOffset, err := rs.Seek(0, io.SeekCurrent)
212 if err != nil {
213 return 0, fileError(f, f.closed, experimentalsys.UnwrapOSError(err))
214 }
215
216
217 defer func() { _, _ = rs.Seek(currentOffset, io.SeekStart) }()
218
219
220 if off != currentOffset {
221 if _, err = rs.Seek(off, io.SeekStart); err != nil {
222 return 0, fileError(f, f.closed, experimentalsys.UnwrapOSError(err))
223 }
224 }
225
226 n, err = rs.Read(buf)
227 if errno = experimentalsys.UnwrapOSError(err); errno != 0 {
228
229 errno = fileError(f, f.closed, errno)
230 }
231 } else {
232 errno = experimentalsys.ENOSYS
233 }
234 return
235 }
236
237
238 func (f *fsFile) Seek(offset int64, whence int) (newOffset int64, errno experimentalsys.Errno) {
239
240
241 var isDir bool
242 if offset == 0 && whence == io.SeekStart {
243 if isDir, errno = f.IsDir(); errno == 0 && isDir {
244 f.reopenDir = true
245 return
246 }
247 }
248
249 if s, ok := f.file.(io.Seeker); ok {
250 if newOffset, errno = seek(s, offset, whence); errno != 0 {
251
252 errno = fileError(f, f.closed, errno)
253 }
254 } else {
255 errno = experimentalsys.ENOSYS
256 }
257 return
258 }
259
260
261
262
263
264 func (f *fsFile) Readdir(n int) (dirents []experimentalsys.Dirent, errno experimentalsys.Errno) {
265
266
267
268 if f.closed {
269 errno = experimentalsys.EBADF
270 return
271 }
272
273 if f.reopenDir {
274 f.reopenDir = false
275 if errno = adjustReaddirErr(f, f.closed, f.reopen()); errno != 0 {
276 return
277 }
278 }
279
280 if of, ok := f.file.(readdirFile); ok {
281
282
283
284 if dirents, errno = readdir(of, "", n); errno != 0 {
285 errno = adjustReaddirErr(f, f.closed, errno)
286 }
287 return
288 }
289
290
291
292 if rdf, ok := f.file.(fs.ReadDirFile); ok {
293 entries, e := rdf.ReadDir(n)
294 if errno = adjustReaddirErr(f, f.closed, e); errno != 0 {
295 return
296 }
297 dirents = make([]experimentalsys.Dirent, 0, len(entries))
298 for _, e := range entries {
299
300 dirents = append(dirents, experimentalsys.Dirent{Name: e.Name(), Type: e.Type()})
301 }
302 } else {
303 errno = experimentalsys.EBADF
304 }
305 return
306 }
307
308
309 func (f *fsFile) Write(buf []byte) (n int, errno experimentalsys.Errno) {
310 if w, ok := f.file.(io.Writer); ok {
311 if n, errno = write(w, buf); errno != 0 {
312
313 errno = fileError(f, f.closed, errno)
314 }
315 } else {
316 errno = experimentalsys.ENOSYS
317 }
318 return
319 }
320
321
322 func (f *fsFile) Pwrite(buf []byte, off int64) (n int, errno experimentalsys.Errno) {
323 if wa, ok := f.file.(io.WriterAt); ok {
324 if n, errno = pwrite(wa, buf, off); errno != 0 {
325
326 errno = fileError(f, f.closed, errno)
327 }
328 } else {
329 errno = experimentalsys.ENOSYS
330 }
331 return
332 }
333
334
335 func (f *fsFile) Close() experimentalsys.Errno {
336 if f.closed {
337 return 0
338 }
339 f.closed = true
340 return f.close()
341 }
342
343 func (f *fsFile) close() experimentalsys.Errno {
344 return experimentalsys.UnwrapOSError(f.file.Close())
345 }
346
347
348 func (f *fsFile) IsNonblock() bool {
349 return false
350 }
351
352
353 func (f *fsFile) SetNonblock(bool) experimentalsys.Errno {
354 return experimentalsys.ENOSYS
355 }
356
357
358 func (f *fsFile) Poll(fsapi.Pflag, int32) (ready bool, errno experimentalsys.Errno) {
359 return false, experimentalsys.ENOSYS
360 }
361
362
363 func dirError(f experimentalsys.File, isClosed bool, errno experimentalsys.Errno) experimentalsys.Errno {
364 if vErrno := validate(f, isClosed, false, true); vErrno != 0 {
365 return vErrno
366 }
367 return errno
368 }
369
370
371 func fileError(f experimentalsys.File, isClosed bool, errno experimentalsys.Errno) experimentalsys.Errno {
372 if vErrno := validate(f, isClosed, true, false); vErrno != 0 {
373 return vErrno
374 }
375 return errno
376 }
377
378
379 func validate(f experimentalsys.File, isClosed, wantFile, wantDir bool) experimentalsys.Errno {
380 if isClosed {
381 return experimentalsys.EBADF
382 }
383
384 isDir, errno := f.IsDir()
385 if errno != 0 {
386 return errno
387 }
388
389 if wantFile && isDir {
390 return experimentalsys.EISDIR
391 } else if wantDir && !isDir {
392 return experimentalsys.ENOTDIR
393 }
394 return 0
395 }
396
397 func read(r io.Reader, buf []byte) (n int, errno experimentalsys.Errno) {
398 if len(buf) == 0 {
399 return 0, 0
400 }
401
402 n, err := r.Read(buf)
403 return n, experimentalsys.UnwrapOSError(err)
404 }
405
406 func pread(ra io.ReaderAt, buf []byte, off int64) (n int, errno experimentalsys.Errno) {
407 if len(buf) == 0 {
408 return 0, 0
409 }
410
411 n, err := ra.ReadAt(buf, off)
412 return n, experimentalsys.UnwrapOSError(err)
413 }
414
415 func seek(s io.Seeker, offset int64, whence int) (int64, experimentalsys.Errno) {
416 if uint(whence) > io.SeekEnd {
417 return 0, experimentalsys.EINVAL
418 }
419
420 newOffset, err := s.Seek(offset, whence)
421 return newOffset, experimentalsys.UnwrapOSError(err)
422 }
423
424
425
426 type reopenFile func() experimentalsys.Errno
427
428
429 var _ reopenFile = (*fsFile)(nil).reopen
430
431
432 func (f *fsFile) reopen() experimentalsys.Errno {
433 _ = f.close()
434 var err error
435 f.file, err = f.fs.Open(f.name)
436 return experimentalsys.UnwrapOSError(err)
437 }
438
439
440 type readdirFile interface {
441 Readdir(n int) ([]fs.FileInfo, error)
442 }
443
444
445 func readdir(f readdirFile, path string, n int) (dirents []experimentalsys.Dirent, errno experimentalsys.Errno) {
446 fis, e := f.Readdir(n)
447 if errno = experimentalsys.UnwrapOSError(e); errno != 0 {
448 return
449 }
450
451 dirents = make([]experimentalsys.Dirent, 0, len(fis))
452
453
454 var ino sys.Inode
455 for fi := range fis {
456 t := fis[fi]
457
458
459 if ino, errno = inoFromFileInfo(path, t); errno != 0 {
460 return
461 }
462 dirents = append(dirents, experimentalsys.Dirent{Name: t.Name(), Ino: ino, Type: t.Mode().Type()})
463 }
464 return
465 }
466
467 func write(w io.Writer, buf []byte) (n int, errno experimentalsys.Errno) {
468 if len(buf) == 0 {
469 return 0, 0
470 }
471
472 n, err := w.Write(buf)
473 return n, experimentalsys.UnwrapOSError(err)
474 }
475
476 func pwrite(w io.WriterAt, buf []byte, off int64) (n int, errno experimentalsys.Errno) {
477 if len(buf) == 0 {
478 return 0, 0
479 }
480
481 n, err := w.WriteAt(buf, off)
482 return n, experimentalsys.UnwrapOSError(err)
483 }
484
485 func chtimes(path string, atim, mtim int64) (errno experimentalsys.Errno) {
486
487 if atim == experimentalsys.UTIME_OMIT && mtim == experimentalsys.UTIME_OMIT {
488 return
489 }
490
491
492
493
494
495 var st sys.Stat_t
496 if atim == experimentalsys.UTIME_OMIT || mtim == experimentalsys.UTIME_OMIT {
497 if st, errno = stat(path); errno != 0 {
498 return
499 }
500 }
501
502 var atime, mtime time.Time
503 if atim == experimentalsys.UTIME_OMIT {
504 atime = epochNanosToTime(st.Atim)
505 mtime = epochNanosToTime(mtim)
506 } else if mtim == experimentalsys.UTIME_OMIT {
507 atime = epochNanosToTime(atim)
508 mtime = epochNanosToTime(st.Mtim)
509 } else {
510 atime = epochNanosToTime(atim)
511 mtime = epochNanosToTime(mtim)
512 }
513 return experimentalsys.UnwrapOSError(os.Chtimes(path, atime, mtime))
514 }
515
516 func epochNanosToTime(epochNanos int64) time.Time {
517 seconds := epochNanos / 1e9
518 nanos := epochNanos % 1e9
519 return time.Unix(seconds, nanos)
520 }
521
View as plain text