github.com/gaukas/wazerofs@v0.1.0/memfs/fs.go (about) 1 // memfs implements a simple fake memory FS for Wazero. 2 // 3 // The actual implementation of the FS is from github.com/blang/vfs/memfs, 4 // this package is just wrapping that for Wazero. 5 // 6 // It implements only very small number of functions, because only those were needed 7 // for my purposes (that is, running ghostscript with WASI), as ghostscript only calls these. 8 // 9 // Feel free to make a PR if you need to implement some other functions. 10 package memfs 11 12 import ( 13 "errors" 14 "io/fs" 15 "strings" 16 17 wasys "github.com/tetratelabs/wazero/sys" 18 19 "os" 20 21 "github.com/tetratelabs/wazero/experimental/sys" 22 23 "github.com/blang/vfs/memfs" 24 ) 25 26 // New creates a new memory filesystem 27 func New() *MemFS { 28 mfs := memfs.Create() 29 mmfs := &MemFS{fs: mfs} 30 return mmfs 31 } 32 33 // WriteFile is a helper function that writes a content to a file. 34 // Errors have the same semantics as wazero errors 35 func (m *MemFS) WriteFile(path string, content []byte) sys.Errno { 36 f, err := m.OpenFile(path, sys.O_WRONLY|sys.O_CREAT, 0) 37 if err != 0 { 38 return err 39 } 40 41 _, err = f.Write(content) 42 return err 43 } 44 45 // ReadFile is a helper function that returns a content of a file. 46 // Errors have the same semantics as wazero errors 47 func (m *MemFS) ReadFile(path string) ([]byte, sys.Errno) { 48 f, err := m.OpenFile(path, sys.O_RDONLY, 0) 49 if err != 0 { 50 return nil, err 51 } 52 53 st, errno := f.Stat() 54 if errno != 0 { 55 return nil, errno 56 } 57 58 buf := make([]byte, st.Size) 59 _, errno = f.Read(buf) 60 return buf, errno 61 } 62 63 // MemFS is a memory-only wazero filesystem, implementing just some basic functions. 64 type MemFS struct { 65 fs *memfs.MemFS 66 67 sys.UnimplementedFS 68 } 69 70 // toOsOpenFlag is copied from wazero codebase 71 func toOsOpenFlag(oflag sys.Oflag) (flag int) { 72 // First flags are exclusive 73 switch oflag & (sys.O_RDONLY | sys.O_RDWR | sys.O_WRONLY) { 74 case sys.O_RDONLY: 75 flag |= os.O_RDONLY 76 case sys.O_RDWR: 77 flag |= os.O_RDWR 78 case sys.O_WRONLY: 79 flag |= os.O_WRONLY 80 } 81 82 // Run down the flags defined in the os package 83 if oflag&sys.O_APPEND != 0 { 84 flag |= os.O_APPEND 85 } 86 if oflag&sys.O_CREAT != 0 { 87 flag |= os.O_CREATE 88 } 89 if oflag&sys.O_EXCL != 0 { 90 flag |= os.O_EXCL 91 } 92 if oflag&sys.O_SYNC != 0 { 93 flag |= os.O_SYNC 94 } 95 if oflag&sys.O_TRUNC != 0 { 96 flag |= os.O_TRUNC 97 } 98 return flag 99 } 100 101 // OpenFile opens a file as defined in sys.File 102 func (m *MemFS) OpenFile(path string, flag sys.Oflag, perm fs.FileMode) (sys.File, sys.Errno) { 103 f, err := m.fs.OpenFile(path, toOsOpenFlag(flag), perm) 104 if err != nil { 105 if errors.Is(err, memfs.ErrIsDirectory) { 106 if flag&sys.O_WRONLY == 1 || flag&sys.O_RDWR == 1 { 107 return nil, sys.EISDIR 108 } 109 // return directory as a different type 110 dir := &memoryFSDir{fs: m.fs, path: path} 111 return dir, 0 112 } 113 if errors.Is(err, os.ErrNotExist) { 114 return nil, sys.ENOENT 115 } 116 if errors.Is(err, os.ErrExist) { 117 return nil, sys.EEXIST 118 } 119 return nil, sys.EINVAL // just general IO error, not that important 120 } 121 fl := &memoryFSFile{fl: f, path: path, fs: m.fs} 122 return fl, 0 123 } 124 125 func (m *MemFS) Mkdir(path string, perm fs.FileMode) sys.Errno { 126 err := m.fs.Mkdir(path, perm) 127 // note - this is not 100% correct, but good enough 128 // note - we canno "just" call stat here, as mkdir should be atomic; the file maybe doesn't exist anymore 129 // just return EEXIST I guess... 130 if err != nil { 131 if strings.Contains(err.Error(), "already exists") { 132 return sys.EEXIST 133 } 134 return sys.EINVAL 135 } 136 return 0 137 } 138 139 func (m *MemFS) Unlink(path string) sys.Errno { 140 err := m.fs.Remove(path) 141 if err != nil { 142 if errors.Is(err, os.ErrNotExist) { 143 return sys.ENOENT 144 } 145 return sys.EINVAL 146 } 147 return 0 148 } 149 150 func stat(mfs *memfs.MemFS, path string) (wasys.Stat_t, sys.Errno) { 151 fst, err := mfs.Stat(path) 152 if err != nil { 153 if errors.Is(err, os.ErrNotExist) { 154 return wasys.Stat_t{}, sys.ENOENT 155 156 } 157 return wasys.Stat_t{}, sys.EIO // this should "never happen" 158 } 159 return wasys.NewStat_t(fst), 0 160 } 161 162 // Stat returns file stat as defined in sys.File 163 func (m *MemFS) Stat(path string) (wasys.Stat_t, sys.Errno) { 164 return stat(m.fs, path) 165 }