diff --git a/src/library_fs.js b/src/library_fs.js index d24cdbf1c7500..985d3bbcccfea 100644 --- a/src/library_fs.js +++ b/src/library_fs.js @@ -666,6 +666,29 @@ FS.staticInit(); } return parent.node_ops.mknod(parent, name, mode, dev); }, + statfs(path) { + + // NOTE: None of the defaults here are true. We're just returning safe and + // sane values. + var rtn = { + bsize: 4096, + frsize: 4096, + blocks: 1e6, + bfree: 5e5, + bavail: 5e5, + files: FS.nextInode, + ffree: FS.nextInode - 1, + fsid: 42, + flags: 2, + namelen: 255, + }; + + var parent = FS.lookupPath(path, {follow: true}).node; + if (parent?.node_ops.statfs) { + Object.assign(rtn, parent.node_ops.statfs(parent.mount.opts.root)); + } + return rtn; + }, // helpers to create specific types of nodes create(path, mode) { mode = mode !== undefined ? mode : 438 /* 0666 */; @@ -809,7 +832,7 @@ FS.staticInit(); // do the underlying fs rename try { old_dir.node_ops.rename(old_node, new_dir, new_name); - // update old node (we do this here to avoid each backend + // update old node (we do this here to avoid each backend // needing to) old_node.parent = new_dir; } catch (e) { diff --git a/src/library_nodefs.js b/src/library_nodefs.js index d30c69815c133..a9b91f8d37a9e 100644 --- a/src/library_nodefs.js +++ b/src/library_nodefs.js @@ -214,6 +214,13 @@ addToLibrary({ var path = NODEFS.realPath(node); return NODEFS.tryFSOperation(() => fs.readlinkSync(path)); }, + statfs(path) { + var stats = NODEFS.tryFSOperation(() => fs.statfsSync(path)); + // Node.js doesn't provide frsize (fragment size). Set it to bsize (block size) + // as they're often the same in many file systems. May not be accurate for all. + stats.frsize = stats.bsize; + return stats; + } }, stream_ops: { open(stream) { diff --git a/src/library_syscall.js b/src/library_syscall.js index 14e00b3c265c4..3fde9fd54a675 100644 --- a/src/library_syscall.js +++ b/src/library_syscall.js @@ -796,22 +796,20 @@ var SyscallsLibrary = { }, __syscall_statfs64: (path, size, buf) => { - path = SYSCALLS.getStr(path); #if ASSERTIONS assert(size === {{{ C_STRUCTS.statfs.__size__ }}}); #endif - // NOTE: None of the constants here are true. We're just returning safe and - // sane values. - {{{ makeSetValue('buf', C_STRUCTS.statfs.f_bsize, '4096', 'i32') }}}; - {{{ makeSetValue('buf', C_STRUCTS.statfs.f_frsize, '4096', 'i32') }}}; - {{{ makeSetValue('buf', C_STRUCTS.statfs.f_blocks, '1000000', 'i32') }}}; - {{{ makeSetValue('buf', C_STRUCTS.statfs.f_bfree, '500000', 'i32') }}}; - {{{ makeSetValue('buf', C_STRUCTS.statfs.f_bavail, '500000', 'i32') }}}; - {{{ makeSetValue('buf', C_STRUCTS.statfs.f_files, 'FS.nextInode', 'i32') }}}; - {{{ makeSetValue('buf', C_STRUCTS.statfs.f_ffree, '1000000', 'i32') }}}; - {{{ makeSetValue('buf', C_STRUCTS.statfs.f_fsid, '42', 'i32') }}}; - {{{ makeSetValue('buf', C_STRUCTS.statfs.f_flags, '2', 'i32') }}}; // ST_NOSUID - {{{ makeSetValue('buf', C_STRUCTS.statfs.f_namelen, '255', 'i32') }}}; + var stats = FS.statfs(SYSCALLS.getStr(path)); + {{{ makeSetValue('buf', C_STRUCTS.statfs.f_bsize, 'stats.bsize', 'i32') }}}; + {{{ makeSetValue('buf', C_STRUCTS.statfs.f_frsize, 'stats.bsize', 'i32') }}}; + {{{ makeSetValue('buf', C_STRUCTS.statfs.f_blocks, 'stats.blocks', 'i32') }}}; + {{{ makeSetValue('buf', C_STRUCTS.statfs.f_bfree, 'stats.bfree', 'i32') }}}; + {{{ makeSetValue('buf', C_STRUCTS.statfs.f_bavail, 'stats.bavail', 'i32') }}}; + {{{ makeSetValue('buf', C_STRUCTS.statfs.f_files, 'stats.files', 'i32') }}}; + {{{ makeSetValue('buf', C_STRUCTS.statfs.f_ffree, 'stats.ffree', 'i32') }}}; + {{{ makeSetValue('buf', C_STRUCTS.statfs.f_fsid, 'stats.fsid', 'i32') }}}; + {{{ makeSetValue('buf', C_STRUCTS.statfs.f_flags, 'stats.flags', 'i32') }}}; // ST_NOSUID + {{{ makeSetValue('buf', C_STRUCTS.statfs.f_namelen, 'stats.namelen', 'i32') }}}; return 0; }, __syscall_fstatfs64__deps: ['__syscall_statfs64'], diff --git a/test/core/test_statvfs.c b/test/core/test_statvfs.c index 2de11485b03ff..7b1baddd8aa90 100644 --- a/test/core/test_statvfs.c +++ b/test/core/test_statvfs.c @@ -8,10 +8,13 @@ #include #include #include +#include +#include int main() { struct statvfs s; + mkdir("/test", S_IRWXU | S_IRWXG | S_IRWXO); printf("result: %d\n", statvfs("/test", &s)); printf("errno: %d\n", errno); @@ -21,8 +24,8 @@ int main() { printf("f_bfree: %u\n", s.f_bfree); printf("f_bavail: %u\n", s.f_bavail); printf("f_files: %d\n", s.f_files > 5); - printf("f_ffree: %u\n", s.f_ffree); - printf("f_favail: %u\n", s.f_favail); + printf("f_ffree: %u\n", s.f_ffree <= s.f_files && s.f_ffree > 0); + printf("f_favail: %u\n", s.f_favail <= s.f_files && s.f_favail > 0); printf("f_fsid: %lu\n", s.f_fsid); printf("f_flag: %lu\n", s.f_flag); printf("f_namemax: %lu\n", s.f_namemax); diff --git a/test/core/test_statvfs.out b/test/core/test_statvfs.out index 8c414d0949dd2..169df0a92be7d 100644 --- a/test/core/test_statvfs.out +++ b/test/core/test_statvfs.out @@ -6,8 +6,8 @@ f_blocks: 1000000 f_bfree: 500000 f_bavail: 500000 f_files: 1 -f_ffree: 1000000 -f_favail: 1000000 +f_ffree: 1 +f_favail: 1 f_fsid: 42 f_flag: 2 f_namemax: 255 diff --git a/test/fs/test_nodefs_statvfs.c b/test/fs/test_nodefs_statvfs.c new file mode 100644 index 0000000000000..b455affb6749a --- /dev/null +++ b/test/fs/test_nodefs_statvfs.c @@ -0,0 +1,35 @@ +#include +#include +#include +#include + +void test_statvfs(const char *path) { + printf("Testing statfs for path: %s\n", path); + struct statvfs st; + int result = statvfs(path, &st); + + assert(result == 0 && "statvfs should succeed"); + + // Basic sanity checks + assert(st.f_bsize > 0 && "Block size should be positive"); + assert(st.f_blocks > 0 && "Total blocks should be positive"); + assert(st.f_bfree <= st.f_blocks && "Free blocks should not exceed total blocks"); + assert(st.f_bavail <= st.f_bfree && "Available blocks should not exceed free blocks"); + assert(st.f_files >= 0 && "Total inodes should be 0 or positive"); + assert(st.f_ffree <= st.f_files && "Free inodes should not exceed total inodes"); +} + +void setup() { + EM_ASM( + FS.mkdir('/working'); + FS.mount(NODEFS, { root: '.' }, '/working'); + ); +} + +int main() { + setup(); + // Test the root filesystem (which should be MEMFS by default) + test_statvfs("/"); + test_statvfs("/working"); + puts("success"); +} diff --git a/test/test_core.py b/test/test_core.py index b9a8470b5fa7b..c8989758453a3 100644 --- a/test/test_core.py +++ b/test/test_core.py @@ -5762,10 +5762,20 @@ def test_fs_nodefs_readdir(self): # externally setup an existing folder structure: existing/a if self.get_setting('WASMFS'): self.set_setting('FORCE_FILESYSTEM') - os.makedirs(os.path.join(self.working_dir, 'existing', 'a')) + os.makedirs('existing/a') self.emcc_args += ['-lnodefs.js'] self.do_runf('fs/test_nodefs_readdir.c', 'success') + @requires_node + @crossplatform + def test_fs_nodefs_statvfs(self): + # externally setup an existing folder structure: existing/a + if self.get_setting('WASMFS'): + self.set_setting('FORCE_FILESYSTEM') + os.makedirs('existing/a') + self.emcc_args += ['-lnodefs.js'] + self.do_runf('fs/test_nodefs_statvfs.c', 'success') + @no_windows('no symlink support on windows') @requires_node def test_fs_noderawfs_nofollow(self):