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  }