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  }