github.com/gaukas/wazerofs@v0.1.0/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/sys" 9 ) 10 11 func DirFS(dir string) experimentalsys.FS { 12 return &dirFS{ 13 dir: dir, 14 cleanedDir: ensureTrailingPathSeparator(dir), 15 } 16 } 17 18 func ensureTrailingPathSeparator(dir string) string { 19 if !os.IsPathSeparator(dir[len(dir)-1]) { 20 return dir + string(os.PathSeparator) 21 } 22 return dir 23 } 24 25 // dirFS is not exported because the input fields must be maintained together. 26 // This is likely why os.DirFS doesn't, either! 27 type dirFS struct { 28 experimentalsys.UnimplementedFS 29 30 dir string 31 // cleanedDir is for easier OS-specific concatenation, as it always has 32 // a trailing path separator. 33 cleanedDir string 34 } 35 36 // String implements fmt.Stringer 37 func (d *dirFS) String() string { 38 return d.dir 39 } 40 41 // OpenFile implements the same method as documented on sys.FS 42 func (d *dirFS) OpenFile(path string, flag experimentalsys.Oflag, perm fs.FileMode) (experimentalsys.File, experimentalsys.Errno) { 43 return OpenOSFile(d.join(path), flag, perm) 44 } 45 46 // Lstat implements the same method as documented on sys.FS 47 func (d *dirFS) Lstat(path string) (sys.Stat_t, experimentalsys.Errno) { 48 return lstat(d.join(path)) 49 } 50 51 // Stat implements the same method as documented on sys.FS 52 func (d *dirFS) Stat(path string) (sys.Stat_t, experimentalsys.Errno) { 53 return stat(d.join(path)) 54 } 55 56 // Mkdir implements the same method as documented on sys.FS 57 func (d *dirFS) Mkdir(path string, perm fs.FileMode) (errno experimentalsys.Errno) { 58 err := os.Mkdir(d.join(path), perm) 59 if errno = experimentalsys.UnwrapOSError(err); errno == experimentalsys.ENOTDIR { 60 errno = experimentalsys.ENOENT 61 } 62 return 63 } 64 65 // Chmod implements the same method as documented on sys.FS 66 func (d *dirFS) Chmod(path string, perm fs.FileMode) experimentalsys.Errno { 67 err := os.Chmod(d.join(path), perm) 68 return experimentalsys.UnwrapOSError(err) 69 } 70 71 // Rename implements the same method as documented on sys.FS 72 func (d *dirFS) Rename(from, to string) experimentalsys.Errno { 73 from, to = d.join(from), d.join(to) 74 return rename(from, to) 75 } 76 77 // Readlink implements the same method as documented on sys.FS 78 func (d *dirFS) Readlink(path string) (string, experimentalsys.Errno) { 79 // Note: do not use syscall.Readlink as that causes race on Windows. 80 // In any case, syscall.Readlink does almost the same logic as os.Readlink. 81 dst, err := os.Readlink(d.join(path)) 82 if err != nil { 83 return "", experimentalsys.UnwrapOSError(err) 84 } 85 return toPosixPath(dst), 0 86 } 87 88 // Link implements the same method as documented on sys.FS 89 func (d *dirFS) Link(oldName, newName string) experimentalsys.Errno { 90 err := os.Link(d.join(oldName), d.join(newName)) 91 return experimentalsys.UnwrapOSError(err) 92 } 93 94 // Rmdir implements the same method as documented on sys.FS 95 func (d *dirFS) Rmdir(path string) experimentalsys.Errno { 96 return rmdir(d.join(path)) 97 } 98 99 // Unlink implements the same method as documented on sys.FS 100 func (d *dirFS) Unlink(path string) (err experimentalsys.Errno) { 101 return unlink(d.join(path)) 102 } 103 104 // Symlink implements the same method as documented on sys.FS 105 func (d *dirFS) Symlink(oldName, link string) experimentalsys.Errno { 106 // Note: do not resolve `oldName` relative to this dirFS. The link result is always resolved 107 // when dereference the `link` on its usage (e.g. readlink, read, etc). 108 // https://github.com/bytecodealliance/cap-std/blob/v1.0.4/cap-std/src/fs/dir.rs#L404-L409 109 err := os.Symlink(oldName, d.join(link)) 110 return experimentalsys.UnwrapOSError(err) 111 } 112 113 // Utimens implements the same method as documented on sys.FS 114 func (d *dirFS) Utimens(path string, atim, mtim int64) experimentalsys.Errno { 115 return utimens(d.join(path), atim, mtim) 116 } 117 118 func (d *dirFS) join(path string) string { 119 switch path { 120 case "", ".", "/": 121 if d.cleanedDir == "/" { 122 return "/" 123 } 124 // cleanedDir includes an unnecessary delimiter for the root path. 125 return d.cleanedDir[:len(d.cleanedDir)-1] 126 } 127 // TODO: Enforce similar to safefilepath.FromFS(path), but be careful as 128 // relative path inputs are allowed. e.g. dir or path == ../ 129 return d.cleanedDir + path 130 }