6060#include <fnmatch.h>
6161#include <limits.h>
6262#include <fcntl.h>
63+ #include <sys/statvfs.h> /* statvfs(3) */
6364
6465#if defined(__linux__ )
66+ #include <sys/statfs.h> /* statfs() for f_type */
6567#define HAS_IOCTL
6668#endif
6769
@@ -1779,6 +1781,89 @@ uc_fs_lstat(uc_vm_t *vm, size_t nargs)
17791781 return uc_fs_stat_common (vm , nargs , true);
17801782}
17811783
1784+ /**
1785+ * @typedef {Object} module:fs.StatVFSResult
1786+ * @property {number} bsize file system block size
1787+ * @property {number} frsize fragment size
1788+ * @property {number} blocks total blocks
1789+ * @property {number} bfree free blocks
1790+ * @property {number} bavail free blocks available to unprivileged users
1791+ * @property {number} files total file nodes (inodes)
1792+ * @property {number} ffree free file nodes
1793+ * @property {number} favail free nodes available to unprivileged users
1794+ * @property {number} fsid file system id
1795+ * @property {module:fs.ST_FLAGS} flag mount flags
1796+ * @property {number} namemax maximum filename length
1797+ * @property {number} freesize free space in bytes (calculated as `frsize * bfree`)
1798+ * @property {number} totalsize total size of the filesystem (calculated as `frsize * blocks`)
1799+ * @property {number} type (Linux only) magic number of the filesystem, obtained from `statfs`
1800+ */
1801+
1802+ /**
1803+ * Query filesystem statistics for a given pathname.
1804+ *
1805+ * The returned object mirrors the members of `struct statvfs`.
1806+ * Convenience properties `freesize` and `totalsize` are added,
1807+ * which are calculated as:
1808+ * - `frsize * bfree` to provide the free space in bytes and
1809+ * - `frsize * blocks` to provide the total size of the filesystem.
1810+ *
1811+ * On Linux an additional `type` field (magic number from `statfs`) is
1812+ * provided if the call succeeds.
1813+ *
1814+ * Returns `null` on failure (and sets `fs.last_error`).
1815+ *
1816+ * @function module:fs#statvfs
1817+ *
1818+ * @param {string} path
1819+ * The path to the directory or file with which to query the filesystem.
1820+ *
1821+ * @returns {?module:fs.StatVFSResult}
1822+ *
1823+ * @example
1824+ * // Get filesystem statistics for a path
1825+ * const stats = statvfs('path/to/directory');
1826+ * print(stats.bsize); // file system block size
1827+ */
1828+ static uc_value_t *
1829+ uc_fs_statvfs (uc_vm_t * vm , size_t nargs )
1830+ {
1831+ uc_value_t * path = uc_fn_arg (0 );
1832+ struct statvfs sv ;
1833+
1834+ if (ucv_type (path ) != UC_STRING )
1835+ err_return (EINVAL );
1836+
1837+ if (statvfs (ucv_string_get (path ), & sv ) == -1 )
1838+ err_return (errno );
1839+
1840+ uc_value_t * o = ucv_object_new (vm );
1841+
1842+ ucv_object_add (o , "bsize" , ucv_int64_new (sv .f_bsize ));
1843+ ucv_object_add (o , "frsize" , ucv_int64_new (sv .f_frsize ));
1844+ ucv_object_add (o , "blocks" , ucv_int64_new (sv .f_blocks ));
1845+ ucv_object_add (o , "bfree" , ucv_int64_new (sv .f_bfree ));
1846+ ucv_object_add (o , "bavail" , ucv_int64_new (sv .f_bavail ));
1847+ ucv_object_add (o , "files" , ucv_int64_new (sv .f_files ));
1848+ ucv_object_add (o , "ffree" , ucv_int64_new (sv .f_ffree ));
1849+ ucv_object_add (o , "favail" , ucv_int64_new (sv .f_favail ));
1850+ ucv_object_add (o , "fsid" , ucv_int64_new (sv .f_fsid ));
1851+ ucv_object_add (o , "flag" , ucv_int64_new (sv .f_flag ));
1852+ ucv_object_add (o , "namemax" , ucv_int64_new (sv .f_namemax ));
1853+ ucv_object_add (o , "freesize" , ucv_int64_new (sv .f_frsize * sv .f_bfree ));
1854+ ucv_object_add (o , "totalsize" ,ucv_int64_new (sv .f_frsize * sv .f_blocks ));
1855+
1856+ #ifdef __linux__
1857+ /* Call statfs to expose the magic number (`f_type`) */
1858+ struct statfs sf ;
1859+ if (statfs (ucv_string_get (path ), & sf ) == 0 ) {
1860+ ucv_object_add (o , "type" , ucv_int64_new (sf .f_type ));
1861+ }
1862+ #endif
1863+
1864+ return o ;
1865+ }
1866+
17821867/**
17831868 * Creates a new directory.
17841869 *
@@ -3024,6 +3109,7 @@ static const uc_function_list_t global_fns[] = {
30243109 { "popen" , uc_fs_popen },
30253110 { "readlink" , uc_fs_readlink },
30263111 { "stat" , uc_fs_stat },
3112+ { "statvfs" , uc_fs_statvfs },
30273113 { "lstat" , uc_fs_lstat },
30283114 { "mkdir" , uc_fs_mkdir },
30293115 { "rmdir" , uc_fs_rmdir },
@@ -3078,6 +3164,53 @@ static void close_dir(void *ud)
30783164void uc_module_init (uc_vm_t * vm , uc_value_t * scope )
30793165{
30803166 uc_function_list_register (scope , global_fns );
3167+ #define ADD_CONST (x ) ucv_object_add(scope, #x, ucv_int64_new(x))
3168+
3169+ /**
3170+ * @typedef
3171+ * @name module:fs.ST_FLAGS
3172+ * @description Bitmask flags used at volume mount time (¹ - Linux only).
3173+ * @property {number} ST_MANDLOCK - Mandatory locking.¹
3174+ * @property {number} ST_NOATIME - Do not update access times.¹
3175+ * @property {number} ST_NODEV - Do not allow device files.¹
3176+ * @property {number} ST_NODIRATIME - Do not update directory access times.¹
3177+ * @property {number} ST_NOEXEC - Do not allow execution of binaries.¹
3178+ * @property {number} ST_NOSUID - Do not allow set-user-identifier or set-group-identifier bits.
3179+ * @property {number} ST_RDONLY - Read-only filesystem.
3180+ * @property {number} ST_RELATIME - Update access times relative to modification time.¹
3181+ * @property {number} ST_SYNCHRONOUS - Synchronous writes.¹
3182+ * @property {number} ST_NOSYMFOLLOW - Do not follow symbolic links.¹
3183+ */
3184+ #ifdef ST_MANDLOCK
3185+ ADD_CONST (ST_MANDLOCK );
3186+ #endif
3187+ #ifdef ST_NOATIME
3188+ ADD_CONST (ST_NOATIME );
3189+ #endif
3190+ #ifdef ST_NODEV
3191+ ADD_CONST (ST_NODEV );
3192+ #endif
3193+ #ifdef ST_NODIRATIME
3194+ ADD_CONST (ST_NODIRATIME );
3195+ #endif
3196+ #ifdef ST_NOEXEC
3197+ ADD_CONST (ST_NOEXEC );
3198+ #endif
3199+ #ifdef ST_NOSUID
3200+ ADD_CONST (ST_NOSUID );
3201+ #endif
3202+ #ifdef ST_RDONLY
3203+ ADD_CONST (ST_RDONLY );
3204+ #endif
3205+ #ifdef ST_RELATIME
3206+ ADD_CONST (ST_RELATIME );
3207+ #endif
3208+ #ifdef ST_SYNCHRONOUS
3209+ ADD_CONST (ST_SYNCHRONOUS );
3210+ #endif
3211+ #ifdef ST_NOSYMFOLLOW
3212+ ADD_CONST (ST_NOSYMFOLLOW );
3213+ #endif
30813214
30823215 uc_type_declare (vm , "fs.proc" , proc_fns , close_proc );
30833216 uc_type_declare (vm , "fs.dir" , dir_fns , close_dir );
@@ -3089,7 +3222,6 @@ void uc_module_init(uc_vm_t *vm, uc_value_t *scope)
30893222 ucv_object_add (scope , "stderr" , uc_resource_new (file_type , stderr ));
30903223
30913224#ifdef HAS_IOCTL
3092- #define ADD_CONST (x ) ucv_object_add(scope, #x, ucv_int64_new(x))
30933225 ADD_CONST (IOC_DIR_NONE );
30943226 ADD_CONST (IOC_DIR_READ );
30953227 ADD_CONST (IOC_DIR_WRITE );
0 commit comments