github.com/tetratelabs/wazero@v1.7.3-0.20240513003603-48f702e154b5/internal/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/internal/platform"
     9  	"github.com/tetratelabs/wazero/sys"
    10  )
    11  
    12  func DirFS(dir string) experimentalsys.FS {
    13  	return &dirFS{
    14  		dir:        dir,
    15  		cleanedDir: ensureTrailingPathSeparator(dir),
    16  	}
    17  }
    18  
    19  func ensureTrailingPathSeparator(dir string) string {
    20  	if !os.IsPathSeparator(dir[len(dir)-1]) {
    21  		return dir + string(os.PathSeparator)
    22  	}
    23  	return dir
    24  }
    25  
    26  // dirFS is not exported because the input fields must be maintained together.
    27  // This is likely why os.DirFS doesn't, either!
    28  type dirFS struct {
    29  	experimentalsys.UnimplementedFS
    30  
    31  	dir string
    32  	// cleanedDir is for easier OS-specific concatenation, as it always has
    33  	// a trailing path separator.
    34  	cleanedDir string
    35  }
    36  
    37  // String implements fmt.Stringer
    38  func (d *dirFS) String() string {
    39  	return d.dir
    40  }
    41  
    42  // OpenFile implements the same method as documented on sys.FS
    43  func (d *dirFS) OpenFile(path string, flag experimentalsys.Oflag, perm fs.FileMode) (experimentalsys.File, experimentalsys.Errno) {
    44  	return OpenOSFile(d.join(path), flag, perm)
    45  }
    46  
    47  // Lstat implements the same method as documented on sys.FS
    48  func (d *dirFS) Lstat(path string) (sys.Stat_t, experimentalsys.Errno) {
    49  	return lstat(d.join(path))
    50  }
    51  
    52  // Stat implements the same method as documented on sys.FS
    53  func (d *dirFS) Stat(path string) (sys.Stat_t, experimentalsys.Errno) {
    54  	return stat(d.join(path))
    55  }
    56  
    57  // Mkdir implements the same method as documented on sys.FS
    58  func (d *dirFS) Mkdir(path string, perm fs.FileMode) (errno experimentalsys.Errno) {
    59  	err := os.Mkdir(d.join(path), perm)
    60  	if errno = experimentalsys.UnwrapOSError(err); errno == experimentalsys.ENOTDIR {
    61  		errno = experimentalsys.ENOENT
    62  	}
    63  	return
    64  }
    65  
    66  // Readlink implements the same method as documented on sys.FS
    67  func (d *dirFS) Readlink(path string) (string, experimentalsys.Errno) {
    68  	// Note: do not use syscall.Readlink as that causes race on Windows.
    69  	// In any case, syscall.Readlink does almost the same logic as os.Readlink.
    70  	dst, err := os.Readlink(d.join(path))
    71  	if err != nil {
    72  		return "", experimentalsys.UnwrapOSError(err)
    73  	}
    74  	return platform.ToPosixPath(dst), 0
    75  }
    76  
    77  // Rmdir implements the same method as documented on sys.FS
    78  func (d *dirFS) Rmdir(path string) experimentalsys.Errno {
    79  	return rmdir(d.join(path))
    80  }
    81  
    82  // Utimens implements the same method as documented on sys.FS
    83  func (d *dirFS) Utimens(path string, atim, mtim int64) experimentalsys.Errno {
    84  	return utimens(d.join(path), atim, mtim)
    85  }
    86  
    87  func (d *dirFS) join(path string) string {
    88  	switch path {
    89  	case "", ".", "/":
    90  		if d.cleanedDir == "/" {
    91  			return "/"
    92  		}
    93  		// cleanedDir includes an unnecessary delimiter for the root path.
    94  		return d.cleanedDir[:len(d.cleanedDir)-1]
    95  	}
    96  	// TODO: Enforce similar to safefilepath.FromFS(path), but be careful as
    97  	// relative path inputs are allowed. e.g. dir or path == ../
    98  	return d.cleanedDir + path
    99  }