-
Notifications
You must be signed in to change notification settings - Fork 274
/
Copy pathdirfs.go
106 lines (91 loc) · 3.12 KB
/
dirfs.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
package sysfs
import (
"io/fs"
"os"
"path"
"strings"
experimentalsys "github.com/tetratelabs/wazero/experimental/sys"
"github.com/tetratelabs/wazero/internal/platform"
"github.com/tetratelabs/wazero/sys"
)
func DirFS(dir string) experimentalsys.FS {
return &dirFS{
dir: dir,
cleanedDir: strings.TrimSuffix(dir, "/"),
}
}
func ensureTrailingPathSeparator(dir string) string {
if !os.IsPathSeparator(dir[len(dir)-1]) {
return dir + string(os.PathSeparator)
}
return dir
}
// dirFS is not exported because the input fields must be maintained together.
// This is likely why os.DirFS doesn't, either!
type dirFS struct {
experimentalsys.UnimplementedFS
dir string
// cleanedDir is for easier OS-specific concatenation, as it always has
// a trailing path separator.
cleanedDir string
}
// String implements fmt.Stringer
func (d *dirFS) String() string {
return d.dir
}
// OpenFile implements the same method as documented on sys.FS
func (d *dirFS) OpenFile(path string, flag experimentalsys.Oflag, perm fs.FileMode) (experimentalsys.File, experimentalsys.Errno) {
return OpenOSFile(d.join(path), flag, perm)
}
// Lstat implements the same method as documented on sys.FS
func (d *dirFS) Lstat(path string) (sys.Stat_t, experimentalsys.Errno) {
return lstat(d.join(path))
}
// Stat implements the same method as documented on sys.FS
func (d *dirFS) Stat(path string) (sys.Stat_t, experimentalsys.Errno) {
return stat(d.join(path))
}
// Mkdir implements the same method as documented on sys.FS
func (d *dirFS) Mkdir(path string, perm fs.FileMode) (errno experimentalsys.Errno) {
err := os.Mkdir(d.join(path), perm)
if errno = experimentalsys.UnwrapOSError(err); errno == experimentalsys.ENOTDIR {
errno = experimentalsys.ENOENT
}
return
}
// Readlink implements the same method as documented on sys.FS
func (d *dirFS) Readlink(path string) (string, experimentalsys.Errno) {
// Note: do not use syscall.Readlink as that causes race on Windows.
// In any case, syscall.Readlink does almost the same logic as os.Readlink.
dst, err := os.Readlink(d.join(path))
if err != nil {
return "", experimentalsys.UnwrapOSError(err)
}
return platform.ToPosixPath(dst), 0
}
// Rmdir implements the same method as documented on sys.FS
func (d *dirFS) Rmdir(path string) experimentalsys.Errno {
return rmdir(d.join(path))
}
// Utimens implements the same method as documented on sys.FS
func (d *dirFS) Utimens(path string, atim, mtim int64) experimentalsys.Errno {
return utimens(d.join(path), atim, mtim)
}
func (d *dirFS) join(name string) string {
// Lexically clean `name` to enforce that it always stays within the root
// hierarchy. This is necessary to avoid trivially escaping the root.
// See: https://github.com/tetratelabs/wazero/issues/2321
// TODO: this violates POSIX pathname resolution semantics.
// https://pubs.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap04.html#tag_04_11
cleanedName := path.Clean("/" + name)
if cleanedName == "/" {
if d.cleanedDir != "" {
return d.cleanedDir
}
return "/"
}
if strings.HasSuffix(name, "/") {
return d.cleanedDir + cleanedName + "/"
}
return d.cleanedDir + cleanedName
}