Skip to content

Commit e30699f

Browse files
authored
WasmFS JS API: Implement stat, lstat (#19458)
1 parent 8a9325a commit e30699f

File tree

5 files changed

+108
-1
lines changed

5 files changed

+108
-1
lines changed

emcc.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2325,6 +2325,8 @@ def phase_linker_setup(options, state, newargs):
23252325
'_wasmfs_read',
23262326
'_wasmfs_pread',
23272327
'_wasmfs_symlink',
2328+
'_wasmfs_stat',
2329+
'_wasmfs_lstat',
23282330
'_wasmfs_chmod',
23292331
'_wasmfs_fchmod',
23302332
'_wasmfs_lchmod',

src/library_wasmfs.js

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ FS.createPreloadedFile = FS_createPreloadedFile;
1818
'$stringToUTF8OnStack',
1919
'$withStackSave',
2020
'$readI53FromI64',
21+
'$readI53FromU64',
2122
'$FS_createPreloadedFile',
2223
'$FS_getMode',
2324
// For FS.readFile
@@ -196,8 +197,46 @@ FS.createPreloadedFile = FS_createPreloadedFile;
196197
});
197198
},
198199
// TODO: readlink
200+
statBufToObject : (statBuf) => {
201+
// i53/u53 are enough for times and ino in practice.
202+
return {
203+
dev: {{{ makeGetValue('statBuf', C_STRUCTS.stat.st_dev, "u32") }}},
204+
mode: {{{ makeGetValue('statBuf', C_STRUCTS.stat.st_mode, "u32") }}},
205+
nlink: {{{ makeGetValue('statBuf', C_STRUCTS.stat.st_nlink, "u32") }}},
206+
uid: {{{ makeGetValue('statBuf', C_STRUCTS.stat.st_uid, "u32") }}},
207+
gid: {{{ makeGetValue('statBuf', C_STRUCTS.stat.st_gid, "u32") }}},
208+
rdev: {{{ makeGetValue('statBuf', C_STRUCTS.stat.st_rdev, "u32") }}},
209+
size: {{{ makeGetValue('statBuf', C_STRUCTS.stat.st_size, "i53") }}},
210+
blksize: {{{ makeGetValue('statBuf', C_STRUCTS.stat.st_blksize, "u32") }}},
211+
blocks: {{{ makeGetValue('statBuf', C_STRUCTS.stat.st_blocks, "u32") }}},
212+
atime: {{{ makeGetValue('statBuf', C_STRUCTS.stat.st_atim.tv_sec, "i53") }}},
213+
mtime: {{{ makeGetValue('statBuf', C_STRUCTS.stat.st_mtim.tv_sec, "i53") }}},
214+
ctime: {{{ makeGetValue('statBuf', C_STRUCTS.stat.st_ctim.tv_sec, "i53") }}},
215+
ino: {{{ makeGetValue('statBuf', C_STRUCTS.stat.st_ino, "u53") }}}
216+
}
217+
},
199218
// TODO: stat
219+
stat: (path) => {
220+
var statBuf = _malloc({{{ C_STRUCTS.stat.__size__ }}});
221+
FS.handleError(withStackSave(() => {
222+
return __wasmfs_stat(stringToUTF8OnStack(path), statBuf);
223+
}));
224+
var stats = FS.statBufToObject(statBuf);
225+
_free(statBuf);
226+
227+
return stats;
228+
},
200229
// TODO: lstat
230+
lstat: (path) => {
231+
var statBuf = _malloc({{{ C_STRUCTS.stat.__size__ }}});
232+
FS.handleError(withStackSave(() => {
233+
return __wasmfs_lstat(stringToUTF8OnStack(path), statBuf);
234+
}));
235+
var stats = FS.statBufToObject(statBuf);
236+
_free(statBuf);
237+
238+
return stats;
239+
},
201240
chmod: (path, mode) => {
202241
return FS.handleError(withStackSave(() => {
203242
var buffer = stringToUTF8OnStack(path);

system/lib/wasmfs/js_api.cpp

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,22 @@ int _wasmfs_close(int fd) {
175175
return __wasi_fd_close(fd);
176176
}
177177

178+
int _wasmfs_stat(char* path, struct stat* statBuf) {
179+
int err = __syscall_stat64((intptr_t)path, (intptr_t)statBuf);
180+
if (err == -1) {
181+
return errno;
182+
}
183+
return err;
184+
}
185+
186+
int _wasmfs_lstat(char* path, struct stat* statBuf) {
187+
int err = __syscall_lstat64((intptr_t)path, (intptr_t)statBuf);
188+
if (err == -1) {
189+
return errno;
190+
}
191+
return err;
192+
}
193+
178194
// Helper method that identifies what a path is:
179195
// ENOENT - if nothing exists there
180196
// EISDIR - if it is a directory

test/stat/test_stat.c

Lines changed: 50 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#include <sys/stat.h>
2020
#include <sys/types.h>
2121
#include <sys/sysmacros.h>
22+
#include <emscripten/emscripten.h>
2223

2324
void create_file(const char *path, const char *buffer, int mode) {
2425
int fd = open(path, O_WRONLY | O_CREAT | O_EXCL, mode);
@@ -85,7 +86,6 @@ void test() {
8586
assert(s.st_blocks == 1);
8687
#endif
8788
#endif
88-
8989
// stat a file
9090
memset(&s, 0, sizeof(s));
9191
err = stat("folder/file", &s);
@@ -193,6 +193,55 @@ void test() {
193193
err = stat("folder/subdir", &s);
194194
assert(s.st_mtime != TEST_TIME);
195195

196+
chmod("folder/file", 0666);
197+
EM_ASM(
198+
var stats = FS.stat("folder/file");
199+
assert(stats.dev == 1);
200+
assert(stats.ino);
201+
assert(stats.mode == 0o100666);
202+
assert(stats.nlink);
203+
assert(stats.rdev == 0);
204+
assert(stats.size == 6);
205+
assert(stats.atime);
206+
assert(stats.mtime);
207+
assert(stats.ctime);
208+
);
209+
210+
symlink("folder/file", "folder/symlinkfile");
211+
212+
EM_ASM(
213+
var linkStats = FS.lstat("folder/symlinkfile");
214+
assert(linkStats.dev == 1);
215+
assert(linkStats.ino);
216+
#if WASMFS
217+
assert(linkStats.mode == 0o120000);
218+
#else
219+
assert(linkStats.mode == 0o120777);
220+
#endif
221+
assert(linkStats.nlink);
222+
assert(linkStats.rdev == 0);
223+
assert(linkStats.size == 11);
224+
assert(linkStats.atime);
225+
assert(linkStats.mtime);
226+
assert(linkStats.ctime);
227+
228+
var ex;
229+
try {
230+
FS.stat("nonexistent");
231+
} catch (err) {
232+
ex = err;
233+
}
234+
assert(ex.name === "ErrnoError" && ex.errno === 44 /* ENOENT */);
235+
236+
try {
237+
FS.lstat("nonexistent");
238+
} catch (err) {
239+
ex = err;
240+
}
241+
assert(ex.name === "ErrnoError" && ex.errno === 44 /* ENOENT */);
242+
);
243+
chmod("folder/file", 0777);
244+
196245
puts("success");
197246
}
198247

test/test_core.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5814,6 +5814,7 @@ def test_readdir_empty(self):
58145814
self.do_run_in_out_file_test('dirent/test_readdir_empty.c')
58155815

58165816
def test_stat(self):
5817+
self.set_setting("FORCE_FILESYSTEM")
58175818
self.do_runf(test_file('stat/test_stat.c'), 'success')
58185819
self.verify_in_strict_mode('test_stat.js')
58195820

0 commit comments

Comments
 (0)