github.com/tetratelabs/wazero@v1.2.1/internal/sysfs/adapter.go (about) 1 package sysfs 2 3 import ( 4 "fmt" 5 "io/fs" 6 "path" 7 "syscall" 8 9 "github.com/tetratelabs/wazero/internal/fsapi" 10 ) 11 12 // Adapt adapts the input to api.FS unless it is already one. Use NewDirFS instead 13 // of os.DirFS as it handles interop issues such as windows support. 14 // 15 // Note: This performs no flag verification on OpenFile. fsapi.FS cannot read 16 // flags as there is no parameter to pass them through with. Moreover, fsapi.FS 17 // documentation does not require the file to be present. In summary, we can't 18 // enforce flag behavior. 19 func Adapt(fs fs.FS) fsapi.FS { 20 if fs == nil { 21 return fsapi.UnimplementedFS{} 22 } 23 if sys, ok := fs.(fsapi.FS); ok { 24 return sys 25 } 26 return &adapter{fs: fs} 27 } 28 29 type adapter struct { 30 fsapi.UnimplementedFS 31 fs fs.FS 32 } 33 34 // String implements fmt.Stringer 35 func (a *adapter) String() string { 36 return fmt.Sprintf("%v", a.fs) 37 } 38 39 // OpenFile implements the same method as documented on api.FS 40 func (a *adapter) OpenFile(path string, flag int, perm fs.FileMode) (fsapi.File, syscall.Errno) { 41 return OpenFSFile(a.fs, cleanPath(path), flag, perm) 42 } 43 44 // Stat implements the same method as documented on api.FS 45 func (a *adapter) Stat(path string) (fsapi.Stat_t, syscall.Errno) { 46 f, errno := a.OpenFile(path, syscall.O_RDONLY, 0) 47 if errno != 0 { 48 return fsapi.Stat_t{}, errno 49 } 50 defer f.Close() 51 return f.Stat() 52 } 53 54 // Lstat implements the same method as documented on api.FS 55 func (a *adapter) Lstat(path string) (fsapi.Stat_t, syscall.Errno) { 56 // At this time, we make the assumption that api.FS instances do not support 57 // symbolic links, therefore Lstat is the same as Stat. This is obviously 58 // not true but until api.FS has a solid story for how to handle symlinks we 59 // are better off not making a decision that would be difficult to revert 60 // later on. 61 // 62 // For further discussions on the topic, see: 63 // https://github.com/golang/go/issues/49580 64 return a.Stat(path) 65 } 66 67 func cleanPath(name string) string { 68 if len(name) == 0 { 69 return name 70 } 71 // fs.ValidFile cannot be rooted (start with '/') 72 cleaned := name 73 if name[0] == '/' { 74 cleaned = name[1:] 75 } 76 cleaned = path.Clean(cleaned) // e.g. "sub/." -> "sub" 77 return cleaned 78 }