1 package sysfs
2
3 import (
4 "errors"
5 "fmt"
6 "io"
7 "io/fs"
8 "os"
9 "path/filepath"
10 "runtime"
11 "syscall"
12 "testing"
13
14 experimentalsys "github.com/tetratelabs/wazero/experimental/sys"
15 "github.com/tetratelabs/wazero/internal/fstest"
16 "github.com/tetratelabs/wazero/internal/testing/require"
17 "github.com/tetratelabs/wazero/sys"
18 )
19
20 func TestAdaptFS_MkDir(t *testing.T) {
21 testFS := &AdaptFS{FS: os.DirFS(t.TempDir())}
22
23 err := testFS.Mkdir("mkdir", fs.ModeDir)
24 require.EqualErrno(t, experimentalsys.ENOSYS, err)
25 }
26
27 func TestAdaptFS_Chmod(t *testing.T) {
28 testFS := &AdaptFS{FS: os.DirFS(t.TempDir())}
29
30 err := testFS.Chmod("chmod", fs.ModeDir)
31 require.EqualErrno(t, experimentalsys.ENOSYS, err)
32 }
33
34 func TestAdaptFS_Rename(t *testing.T) {
35 tmpDir := t.TempDir()
36 testFS := &AdaptFS{FS: os.DirFS(tmpDir)}
37
38 file1 := "file1"
39 file1Path := joinPath(tmpDir, file1)
40 file1Contents := []byte{1}
41 err := os.WriteFile(file1Path, file1Contents, 0o600)
42 require.NoError(t, err)
43
44 file2 := "file2"
45 file2Path := joinPath(tmpDir, file2)
46 file2Contents := []byte{2}
47 err = os.WriteFile(file2Path, file2Contents, 0o600)
48 require.NoError(t, err)
49
50 err = testFS.Rename(file1, file2)
51 require.EqualErrno(t, experimentalsys.ENOSYS, err)
52 }
53
54 func TestAdaptFS_Rmdir(t *testing.T) {
55 tmpDir := t.TempDir()
56 testFS := &AdaptFS{FS: os.DirFS(tmpDir)}
57
58 path := "rmdir"
59 realPath := joinPath(tmpDir, path)
60 require.NoError(t, os.Mkdir(realPath, 0o700))
61
62 err := testFS.Rmdir(path)
63 require.EqualErrno(t, experimentalsys.ENOSYS, err)
64 }
65
66 func TestAdaptFS_Unlink(t *testing.T) {
67 tmpDir := t.TempDir()
68 testFS := &AdaptFS{FS: os.DirFS(tmpDir)}
69
70 path := "unlink"
71 realPath := joinPath(tmpDir, path)
72 require.NoError(t, os.WriteFile(realPath, []byte{}, 0o600))
73
74 err := testFS.Unlink(path)
75 require.EqualErrno(t, experimentalsys.ENOSYS, err)
76 }
77
78 func TestAdaptFS_UtimesNano(t *testing.T) {
79 tmpDir := t.TempDir()
80 testFS := &AdaptFS{FS: os.DirFS(tmpDir)}
81
82 path := "utimes"
83 realPath := joinPath(tmpDir, path)
84 require.NoError(t, os.WriteFile(realPath, []byte{}, 0o600))
85
86 err := testFS.Utimens(path, experimentalsys.UTIME_OMIT, experimentalsys.UTIME_OMIT)
87 require.EqualErrno(t, experimentalsys.ENOSYS, err)
88 }
89
90 func TestAdaptFS_Open_Read(t *testing.T) {
91 tmpDir := t.TempDir()
92
93 tmpDir = joinPath(tmpDir, t.Name())
94 require.NoError(t, os.Mkdir(tmpDir, 0o700))
95 require.NoError(t, fstest.WriteTestFiles(tmpDir))
96 testFS := &AdaptFS{FS: os.DirFS(tmpDir)}
97
98
99
100 testOpen_Read(t, testFS, statSetsIno(), runtime.GOOS != "windows")
101
102 t.Run("path outside root invalid", func(t *testing.T) {
103 _, err := testFS.OpenFile("../foo", experimentalsys.O_RDONLY, 0)
104
105
106 require.EqualErrno(t, experimentalsys.EINVAL, err)
107 })
108 }
109
110 func TestAdaptFS_Lstat(t *testing.T) {
111 tmpDir := t.TempDir()
112 testFS := &AdaptFS{FS: os.DirFS(tmpDir)}
113 require.NoError(t, fstest.WriteTestFiles(tmpDir))
114
115 for _, path := range []string{"animals.txt", "sub", "sub-link"} {
116 fullPath := joinPath(tmpDir, path)
117 linkPath := joinPath(tmpDir, path+"-link")
118 require.NoError(t, os.Symlink(fullPath, linkPath))
119
120 _, errno := testFS.Lstat(filepath.Base(linkPath))
121 require.EqualErrno(t, 0, errno)
122 }
123 }
124
125 func TestAdaptFS_Stat(t *testing.T) {
126 tmpDir := t.TempDir()
127 testFS := &AdaptFS{FS: os.DirFS(tmpDir)}
128 require.NoError(t, fstest.WriteTestFiles(tmpDir))
129
130 testStat(t, testFS)
131 }
132
133
134
135
136
137 type hackFS string
138
139 func (dir hackFS) Open(name string) (fs.File, error) {
140 path := ensureTrailingPathSeparator(string(dir)) + name
141
142 if f, err := os.OpenFile(path, os.O_RDWR, 0); err == nil {
143 return f, nil
144 } else if errors.Is(err, syscall.EISDIR) {
145 return os.OpenFile(path, os.O_RDONLY, 0)
146 } else if errors.Is(err, syscall.ENOENT) {
147 return os.OpenFile(path, os.O_RDONLY|os.O_CREATE, 0o444)
148 } else {
149 return nil, err
150 }
151 }
152
153
154
155 func TestAdaptFS_HackedWrites(t *testing.T) {
156 tmpDir := t.TempDir()
157 testFS := &AdaptFS{FS: hackFS(tmpDir)}
158
159 testOpen_O_RDWR(t, tmpDir, testFS)
160 }
161
162
163
164 type MaskOsFS struct {
165 Fs fs.FS
166
167
168 ZeroIno bool
169 }
170
171
172 func (fs *MaskOsFS) Open(name string) (fs.File, error) {
173 if f, err := fs.Fs.Open(name); err != nil {
174 return nil, err
175 } else if osF, ok := f.(*os.File); !ok {
176 return nil, fmt.Errorf("input not an os.File %v", osF)
177 } else if fs.ZeroIno {
178 return &zeroInoOsFile{osF}, nil
179 } else {
180 return struct{ methodsUsedByFsAdapter }{osF}, nil
181 }
182 }
183
184
185
186 type zeroInoOsFile struct{ *os.File }
187
188
189 func (f *zeroInoOsFile) Readdir(n int) ([]fs.FileInfo, error) {
190 infos, err := f.File.Readdir(n)
191 if err != nil {
192 return nil, err
193 }
194 for i := range infos {
195 infos[i] = withZeroIno(infos[i])
196 }
197 return infos, nil
198 }
199
200
201 func (f *zeroInoOsFile) Stat() (fs.FileInfo, error) {
202 info, err := f.File.Stat()
203 if err != nil {
204 return nil, err
205 }
206 return withZeroIno(info), nil
207 }
208
209
210
211
212 func withZeroIno(info fs.FileInfo) fs.FileInfo {
213 st := sys.NewStat_t(info)
214 st.Ino = 0
215 return &sysFileInfo{info, &st}
216 }
217
218
219 type sysFileInfo struct {
220 fs.FileInfo
221 sys *sys.Stat_t
222 }
223
224
225 func (i *sysFileInfo) Sys() any {
226 return i.sys
227 }
228
229
230
231
232
233
234
235
236
237
238
239 type methodsUsedByFsAdapter interface {
240
241 fs.File
242
243
244
245 Fd() uintptr
246
247
248 io.ReaderAt
249
250
251
252 io.Seeker
253
254
255
256
257 Readdir(n int) ([]fs.FileInfo, error)
258
259
260 fs.ReadDirFile
261
262
263 io.Writer
264
265
266 io.WriterAt
267 }
268
View as plain text