github.com/tetratelabs/wazero@v1.2.1/internal/sysfs/dirfs.go (about) 1 package sysfs 2 3 import ( 4 "io/fs" 5 "os" 6 "syscall" 7 8 "github.com/tetratelabs/wazero/internal/fsapi" 9 "github.com/tetratelabs/wazero/internal/platform" 10 ) 11 12 func NewDirFS(dir string) fsapi.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 type dirFS struct { 27 fsapi.UnimplementedFS 28 dir string 29 // cleanedDir is for easier OS-specific concatenation, as it always has 30 // a trailing path separator. 31 cleanedDir string 32 } 33 34 // String implements fmt.Stringer 35 func (d *dirFS) String() string { 36 return d.dir 37 } 38 39 // OpenFile implements the same method as documented on api.FS 40 func (d *dirFS) OpenFile(path string, flag int, perm fs.FileMode) (fsapi.File, syscall.Errno) { 41 return OpenOSFile(d.join(path), flag, perm) 42 } 43 44 // Lstat implements the same method as documented on api.FS 45 func (d *dirFS) Lstat(path string) (fsapi.Stat_t, syscall.Errno) { 46 return lstat(d.join(path)) 47 } 48 49 // Stat implements the same method as documented on api.FS 50 func (d *dirFS) Stat(path string) (fsapi.Stat_t, syscall.Errno) { 51 return stat(d.join(path)) 52 } 53 54 // Mkdir implements the same method as documented on api.FS 55 func (d *dirFS) Mkdir(path string, perm fs.FileMode) (errno syscall.Errno) { 56 err := os.Mkdir(d.join(path), perm) 57 if errno = platform.UnwrapOSError(err); errno == syscall.ENOTDIR { 58 errno = syscall.ENOENT 59 } 60 return 61 } 62 63 // Chmod implements the same method as documented on api.FS 64 func (d *dirFS) Chmod(path string, perm fs.FileMode) syscall.Errno { 65 err := os.Chmod(d.join(path), perm) 66 return platform.UnwrapOSError(err) 67 } 68 69 // Chown implements the same method as documented on api.FS 70 func (d *dirFS) Chown(path string, uid, gid int) syscall.Errno { 71 return Chown(d.join(path), uid, gid) 72 } 73 74 // Lchown implements the same method as documented on api.FS 75 func (d *dirFS) Lchown(path string, uid, gid int) syscall.Errno { 76 return Lchown(d.join(path), uid, gid) 77 } 78 79 // Rename implements the same method as documented on api.FS 80 func (d *dirFS) Rename(from, to string) syscall.Errno { 81 from, to = d.join(from), d.join(to) 82 return Rename(from, to) 83 } 84 85 // Readlink implements the same method as documented on api.FS 86 func (d *dirFS) Readlink(path string) (string, syscall.Errno) { 87 // Note: do not use syscall.Readlink as that causes race on Windows. 88 // In any case, syscall.Readlink does almost the same logic as os.Readlink. 89 dst, err := os.Readlink(d.join(path)) 90 if err != nil { 91 return "", platform.UnwrapOSError(err) 92 } 93 return platform.ToPosixPath(dst), 0 94 } 95 96 // Link implements the same method as documented on api.FS 97 func (d *dirFS) Link(oldName, newName string) syscall.Errno { 98 err := os.Link(d.join(oldName), d.join(newName)) 99 return platform.UnwrapOSError(err) 100 } 101 102 // Rmdir implements the same method as documented on api.FS 103 func (d *dirFS) Rmdir(path string) syscall.Errno { 104 err := syscall.Rmdir(d.join(path)) 105 return platform.UnwrapOSError(err) 106 } 107 108 // Unlink implements the same method as documented on api.FS 109 func (d *dirFS) Unlink(path string) (err syscall.Errno) { 110 return Unlink(d.join(path)) 111 } 112 113 // Symlink implements the same method as documented on api.FS 114 func (d *dirFS) Symlink(oldName, link string) syscall.Errno { 115 // Note: do not resolve `oldName` relative to this dirFS. The link result is always resolved 116 // when dereference the `link` on its usage (e.g. readlink, read, etc). 117 // https://github.com/bytecodealliance/cap-std/blob/v1.0.4/cap-std/src/fs/dir.rs#L404-L409 118 err := os.Symlink(oldName, d.join(link)) 119 return platform.UnwrapOSError(err) 120 } 121 122 // Utimens implements the same method as documented on api.FS 123 func (d *dirFS) Utimens(path string, times *[2]syscall.Timespec, symlinkFollow bool) syscall.Errno { 124 return Utimens(d.join(path), times, symlinkFollow) 125 } 126 127 // Truncate implements the same method as documented on api.FS 128 func (d *dirFS) Truncate(path string, size int64) syscall.Errno { 129 // Use os.Truncate as syscall.Truncate doesn't exist on Windows. 130 err := os.Truncate(d.join(path), size) 131 return platform.UnwrapOSError(err) 132 } 133 134 func (d *dirFS) join(path string) string { 135 switch path { 136 case "", ".", "/": 137 if d.cleanedDir == "/" { 138 return "/" 139 } 140 // cleanedDir includes an unnecessary delimiter for the root path. 141 return d.cleanedDir[:len(d.cleanedDir)-1] 142 } 143 // TODO: Enforce similar to safefilepath.FromFS(path), but be careful as 144 // relative path inputs are allowed. e.g. dir or path == ../ 145 return d.cleanedDir + path 146 }