github.com/tetratelabs/wazero@v1.7.3-0.20240513003603-48f702e154b5/internal/sysfs/dirfs.go (about) 1 package sysfs 2 3 import ( 4 "io/fs" 5 "os" 6 7 experimentalsys "github.com/tetratelabs/wazero/experimental/sys" 8 "github.com/tetratelabs/wazero/internal/platform" 9 "github.com/tetratelabs/wazero/sys" 10 ) 11 12 func DirFS(dir string) experimentalsys.FS { 13 return &dirFS{ 14 dir: dir, 15 cleanedDir: ensureTrailingPathSeparator(dir), 16 } 17 } 18 19 func ensureTrailingPathSeparator(dir string) string { 20 if !os.IsPathSeparator(dir[len(dir)-1]) { 21 return dir + string(os.PathSeparator) 22 } 23 return dir 24 } 25 26 // dirFS is not exported because the input fields must be maintained together. 27 // This is likely why os.DirFS doesn't, either! 28 type dirFS struct { 29 experimentalsys.UnimplementedFS 30 31 dir string 32 // cleanedDir is for easier OS-specific concatenation, as it always has 33 // a trailing path separator. 34 cleanedDir string 35 } 36 37 // String implements fmt.Stringer 38 func (d *dirFS) String() string { 39 return d.dir 40 } 41 42 // OpenFile implements the same method as documented on sys.FS 43 func (d *dirFS) OpenFile(path string, flag experimentalsys.Oflag, perm fs.FileMode) (experimentalsys.File, experimentalsys.Errno) { 44 return OpenOSFile(d.join(path), flag, perm) 45 } 46 47 // Lstat implements the same method as documented on sys.FS 48 func (d *dirFS) Lstat(path string) (sys.Stat_t, experimentalsys.Errno) { 49 return lstat(d.join(path)) 50 } 51 52 // Stat implements the same method as documented on sys.FS 53 func (d *dirFS) Stat(path string) (sys.Stat_t, experimentalsys.Errno) { 54 return stat(d.join(path)) 55 } 56 57 // Mkdir implements the same method as documented on sys.FS 58 func (d *dirFS) Mkdir(path string, perm fs.FileMode) (errno experimentalsys.Errno) { 59 err := os.Mkdir(d.join(path), perm) 60 if errno = experimentalsys.UnwrapOSError(err); errno == experimentalsys.ENOTDIR { 61 errno = experimentalsys.ENOENT 62 } 63 return 64 } 65 66 // Readlink implements the same method as documented on sys.FS 67 func (d *dirFS) Readlink(path string) (string, experimentalsys.Errno) { 68 // Note: do not use syscall.Readlink as that causes race on Windows. 69 // In any case, syscall.Readlink does almost the same logic as os.Readlink. 70 dst, err := os.Readlink(d.join(path)) 71 if err != nil { 72 return "", experimentalsys.UnwrapOSError(err) 73 } 74 return platform.ToPosixPath(dst), 0 75 } 76 77 // Rmdir implements the same method as documented on sys.FS 78 func (d *dirFS) Rmdir(path string) experimentalsys.Errno { 79 return rmdir(d.join(path)) 80 } 81 82 // Utimens implements the same method as documented on sys.FS 83 func (d *dirFS) Utimens(path string, atim, mtim int64) experimentalsys.Errno { 84 return utimens(d.join(path), atim, mtim) 85 } 86 87 func (d *dirFS) join(path string) string { 88 switch path { 89 case "", ".", "/": 90 if d.cleanedDir == "/" { 91 return "/" 92 } 93 // cleanedDir includes an unnecessary delimiter for the root path. 94 return d.cleanedDir[:len(d.cleanedDir)-1] 95 } 96 // TODO: Enforce similar to safefilepath.FromFS(path), but be careful as 97 // relative path inputs are allowed. e.g. dir or path == ../ 98 return d.cleanedDir + path 99 }