1 package sysfs
2
3 import (
4 "os"
5 "path"
6 "runtime"
7 "testing"
8 "time"
9
10 experimentalsys "github.com/tetratelabs/wazero/experimental/sys"
11 "github.com/tetratelabs/wazero/internal/testing/require"
12 "github.com/tetratelabs/wazero/sys"
13 )
14
15 func TestStat(t *testing.T) {
16 tmpDir := t.TempDir()
17
18 _, errno := stat(path.Join(tmpDir, "cat"))
19 require.EqualErrno(t, experimentalsys.ENOENT, errno)
20 _, errno = stat(path.Join(tmpDir, "sub/cat"))
21 require.EqualErrno(t, experimentalsys.ENOENT, errno)
22
23 var st sys.Stat_t
24
25 t.Run("empty dir", func(t *testing.T) {
26 st, errno = stat(tmpDir)
27 require.EqualErrno(t, 0, errno)
28
29 require.True(t, st.Mode.IsDir())
30 require.NotEqual(t, uint64(0), st.Ino)
31
32
33 expectedNlink := uint64(1)
34 if dirNlinkIncludesDot {
35 expectedNlink++
36 }
37 require.Equal(t, expectedNlink, st.Nlink, runtime.GOOS)
38 })
39
40 subdir := path.Join(tmpDir, "sub")
41 var stSubdir sys.Stat_t
42 t.Run("subdir", func(t *testing.T) {
43 require.NoError(t, os.Mkdir(subdir, 0o500))
44
45 stSubdir, errno = stat(subdir)
46 require.EqualErrno(t, 0, errno)
47
48 require.True(t, stSubdir.Mode.IsDir())
49 require.NotEqual(t, uint64(0), st.Ino)
50 })
51
52 t.Run("not empty dir", func(t *testing.T) {
53 st, errno = stat(tmpDir)
54 require.EqualErrno(t, 0, errno)
55
56
57 expectedNlink := uint64(2)
58 if dirNlinkIncludesDot {
59 expectedNlink++
60 } else if runtime.GOOS == "windows" {
61 expectedNlink = 1
62 }
63 require.Equal(t, expectedNlink, st.Nlink, runtime.GOOS)
64 })
65
66
67
68
69 file := path.Join(tmpDir, "file")
70 var stFile sys.Stat_t
71
72 t.Run("file", func(t *testing.T) {
73 require.NoError(t, os.WriteFile(file, nil, 0o400))
74
75 stFile, errno = stat(file)
76 require.EqualErrno(t, 0, errno)
77
78 require.False(t, stFile.Mode.IsDir())
79 require.NotEqual(t, uint64(0), st.Ino)
80 })
81
82 t.Run("link to file", func(t *testing.T) {
83 link := path.Join(tmpDir, "file-link")
84 require.NoError(t, os.Symlink(file, link))
85
86 stLink, errno := stat(link)
87 require.EqualErrno(t, 0, errno)
88
89 require.Equal(t, stFile, stLink)
90 })
91
92 t.Run("link to dir", func(t *testing.T) {
93 link := path.Join(tmpDir, "dir-link")
94 require.NoError(t, os.Symlink(subdir, link))
95
96 stLink, errno := stat(link)
97 require.EqualErrno(t, 0, errno)
98
99 require.Equal(t, stSubdir, stLink)
100 })
101 }
102
103 func TestStatFile(t *testing.T) {
104 tmpDir := t.TempDir()
105
106 tmpDirF := requireOpenFile(t, tmpDir, experimentalsys.O_RDONLY, 0)
107 defer tmpDirF.Close()
108
109 t.Run("dir", func(t *testing.T) {
110 st, errno := tmpDirF.Stat()
111 require.EqualErrno(t, 0, errno)
112 requireDir(t, tmpDirF, st)
113 requireDevIno(t, tmpDirF, st)
114 })
115
116
117
118 if runtime.GOOS != "windows" {
119 t.Run("closed dir", func(t *testing.T) {
120 require.EqualErrno(t, 0, tmpDirF.Close())
121 _, errno := tmpDirF.Stat()
122 require.EqualErrno(t, experimentalsys.EBADF, errno)
123 })
124 }
125
126 file := path.Join(tmpDir, "file")
127 require.NoError(t, os.WriteFile(file, nil, 0o400))
128 fileF := requireOpenFile(t, file, experimentalsys.O_RDONLY, 0)
129 defer fileF.Close()
130
131 t.Run("file", func(t *testing.T) {
132 st, errno := fileF.Stat()
133 require.EqualErrno(t, 0, errno)
134
135 require.False(t, st.Mode.IsDir())
136 require.NotEqual(t, uint64(0), st.Ino)
137 })
138
139 t.Run("closed fsFile", func(t *testing.T) {
140 require.EqualErrno(t, 0, fileF.Close())
141 _, errno := fileF.Stat()
142 require.EqualErrno(t, experimentalsys.EBADF, errno)
143 })
144
145 subdir := path.Join(tmpDir, "sub")
146 require.NoError(t, os.Mkdir(subdir, 0o500))
147 subdirF := requireOpenFile(t, subdir, experimentalsys.O_RDONLY, 0)
148 defer subdirF.Close()
149
150 t.Run("subdir", func(t *testing.T) {
151 st, errno := subdirF.Stat()
152 require.EqualErrno(t, 0, errno)
153 requireDir(t, subdirF, st)
154 requireDevIno(t, subdirF, st)
155 })
156
157 if runtime.GOOS != "windows" {
158 t.Run("closed subdir", func(t *testing.T) {
159 require.EqualErrno(t, 0, subdirF.Close())
160 _, errno := subdirF.Stat()
161 require.EqualErrno(t, experimentalsys.EBADF, errno)
162 })
163 }
164 }
165
166 func Test_StatFile_times(t *testing.T) {
167 tmpDir := t.TempDir()
168
169 file := path.Join(tmpDir, "file")
170 err := os.WriteFile(file, []byte{}, 0o700)
171 require.NoError(t, err)
172
173 type test struct {
174 name string
175 atimeNsec, mtimeNsec int64
176 }
177
178
179 tests := []test{
180 {
181 name: "positive",
182 atimeNsec: time.Unix(123, 4*1e3).UnixNano(),
183 mtimeNsec: time.Unix(567, 8*1e3).UnixNano(),
184 },
185 {name: "zero"},
186 }
187
188
189 if runtime.GOOS == "windows" || runtime.GOOS == "darwin" {
190 tests = append(tests,
191 test{
192 name: "negative",
193 atimeNsec: time.Unix(-123, -4*1e3).UnixNano(),
194 mtimeNsec: time.Unix(-567, -8*1e3).UnixNano(),
195 },
196 )
197 }
198
199 for _, tt := range tests {
200 tc := tt
201 t.Run(tc.name, func(t *testing.T) {
202 err := os.Chtimes(file, time.UnixMicro(tc.atimeNsec/1e3), time.UnixMicro(tc.mtimeNsec/1e3))
203 require.NoError(t, err)
204
205 f := requireOpenFile(t, file, experimentalsys.O_RDONLY, 0)
206 defer f.Close()
207
208 st, errno := f.Stat()
209 require.EqualErrno(t, 0, errno)
210
211 require.Equal(t, st.Atim, tc.atimeNsec)
212 require.Equal(t, st.Mtim, tc.mtimeNsec)
213 })
214 }
215 }
216
217 func TestStatFile_dev_inode(t *testing.T) {
218 tmpDir := t.TempDir()
219 d := requireOpenFile(t, tmpDir, experimentalsys.O_RDONLY, 0)
220 defer d.Close()
221
222 path1 := path.Join(tmpDir, "1")
223 f1 := requireOpenFile(t, path1, experimentalsys.O_CREAT, 0o666)
224 defer f1.Close()
225
226 path2 := path.Join(tmpDir, "2")
227 f2 := requireOpenFile(t, path2, experimentalsys.O_CREAT, 0o666)
228 defer f2.Close()
229
230 pathLink2 := path.Join(tmpDir, "link2")
231 err := os.Symlink(path2, pathLink2)
232 require.NoError(t, err)
233 l2 := requireOpenFile(t, pathLink2, experimentalsys.O_RDONLY, 0)
234 defer l2.Close()
235
236
237 st1, errno := d.Stat()
238 require.EqualErrno(t, 0, errno)
239 requireDir(t, d, st1)
240 requireDevIno(t, d, st1)
241
242
243 st1, errno = f1.Stat()
244 require.EqualErrno(t, 0, errno)
245 requireNotDir(t, f1, st1)
246 requireDevIno(t, f1, st1)
247
248 st2, errno := f2.Stat()
249 require.EqualErrno(t, 0, errno)
250 requireNotDir(t, f2, st2)
251 requireDevIno(t, f2, st2)
252
253 st3, errno := l2.Stat()
254 require.EqualErrno(t, 0, errno)
255 requireNotDir(t, l2, st3)
256 requireDevIno(t, l2, st3)
257
258
259 require.Equal(t, st1.Dev, st2.Dev)
260 require.NotEqual(t, st1.Ino, st2.Ino)
261 require.Equal(t, st2, st3)
262
263
264 st1Again, errno := f1.Stat()
265 require.EqualErrno(t, 0, errno)
266 require.Equal(t, st1.Dev, st1Again.Dev)
267
268
269
270 require.EqualErrno(t, 0, f1.Close())
271 require.EqualErrno(t, 0, f2.Close())
272 require.EqualErrno(t, 0, l2.Close())
273
274
275 require.EqualErrno(t, 0, rename(path1, path2))
276 f1 = requireOpenFile(t, path2, experimentalsys.O_RDONLY, 0)
277 defer f1.Close()
278
279 st1Again, errno = f1.Stat()
280 require.EqualErrno(t, 0, errno)
281 require.Equal(t, st1.Dev, st1Again.Dev)
282 require.Equal(t, st1.Ino, st1Again.Ino)
283 }
284
285 func requireNotDir(t *testing.T, d experimentalsys.File, st sys.Stat_t) {
286
287 isDir, errno := d.IsDir()
288 require.EqualErrno(t, 0, errno)
289 require.False(t, isDir)
290 require.False(t, st.Mode.IsDir())
291 }
292
293 func requireDir(t *testing.T, d experimentalsys.File, st sys.Stat_t) {
294
295 isDir, errno := d.IsDir()
296 require.EqualErrno(t, 0, errno)
297 require.True(t, isDir)
298 require.True(t, st.Mode.IsDir())
299 }
300
301 func requireDevIno(t *testing.T, f experimentalsys.File, st sys.Stat_t) {
302
303 if statSetsIno() {
304 require.NotEqual(t, uint64(0), st.Dev)
305 require.NotEqual(t, uint64(0), st.Ino)
306 }
307
308
309
310 dev, errno := f.Dev()
311 require.EqualErrno(t, 0, errno)
312 require.Equal(t, st.Dev, dev)
313 ino, errno := f.Ino()
314 require.EqualErrno(t, 0, errno)
315 require.Equal(t, st.Ino, ino)
316 }
317
View as plain text