diff --git a/src/fs/fat32.c b/src/fs/fat32.c index daaee95..9994e52 100644 --- a/src/fs/fat32.c +++ b/src/fs/fat32.c @@ -1661,6 +1661,22 @@ static uint32_t vfs_fat_get_size(void *file_handle) { return ((FAT32_FileHandle*)file_handle)->size; } +static int vfs_ramfs_statfs(void *fs_private, vfs_statfs_t *stat) { + (void)fs_private; + uint64_t rflags = spinlock_acquire_irqsave(&ramfs_lock); + + stat->total_blocks = MAX_CLUSTERS; + uint64_t free_count = 0; + for (int i = 0; i < MAX_CLUSTERS; i++) { + if (fat_table[i] == 0) free_count++; + } + stat->free_blocks = free_count; + stat->block_size = FAT32_CLUSTER_SIZE; + + spinlock_release_irqrestore(&ramfs_lock, rflags); + return 0; +} + static struct vfs_fs_ops ramfs_ops = { .open = vfs_ramfs_open, .close = vfs_ramfs_close, @@ -1676,7 +1692,8 @@ static struct vfs_fs_ops ramfs_ops = { .is_dir = vfs_ramfs_is_dir, .get_info = vfs_ramfs_get_info, .get_position = vfs_fat_get_position, - .get_size = vfs_fat_get_size + .get_size = vfs_fat_get_size, + .statfs = vfs_ramfs_statfs }; struct vfs_fs_ops* fat32_get_ramfs_ops(void) { @@ -1858,6 +1875,45 @@ static int vfs_realfs_get_info(void *fs_private, const char *rel_path, vfs_diren return -1; } +static int vfs_realfs_statfs(void *fs_private, vfs_statfs_t *stat) { + FAT32_Volume *vol = (FAT32_Volume*)fs_private; + uint64_t rflags = spinlock_acquire_irqsave(&vol->lock); + + stat->total_blocks = vol->total_sectors / vol->sectors_per_cluster; + stat->block_size = vol->sectors_per_cluster * 512; + + // Instead of scanning the entire FAT which can be slow, + // we estimate or count a subset, but let's do a fast count. + uint64_t free_count = 0; + uint32_t fat_entries = (vol->fat_size * 512) / 4; + uint32_t current = 2; + + uint8_t *fat_buf = (uint8_t*)kmalloc(512); + if (fat_buf) { + uint32_t cached_sector = 0xFFFFFFFF; + while (current < fat_entries) { + uint32_t sector = vol->fat_begin_lba + (current * 4) / 512; + uint32_t offset = (current * 4) % 512; + + if (sector != cached_sector) { + if (vol->disk->read_sector(vol->disk, sector, fat_buf) != 0) break; + cached_sector = sector; + } + + uint32_t val = *(uint32_t*)&fat_buf[offset]; + if ((val & 0x0FFFFFFF) == 0) free_count++; + + current++; + } + kfree(fat_buf); + } + + stat->free_blocks = free_count; + + spinlock_release_irqrestore(&vol->lock, rflags); + return 0; +} + static struct vfs_fs_ops realfs_ops = { .open = vfs_realfs_open, .close = vfs_realfs_close, @@ -1873,7 +1929,8 @@ static struct vfs_fs_ops realfs_ops = { .is_dir = vfs_realfs_is_dir, .get_info = vfs_realfs_get_info, .get_position = vfs_fat_get_position, - .get_size = vfs_fat_get_size + .get_size = vfs_fat_get_size, + .statfs = vfs_realfs_statfs }; struct vfs_fs_ops* fat32_get_realfs_ops(void) { diff --git a/src/fs/procfs.c b/src/fs/procfs.c index e2b1659..b829c1c 100644 --- a/src/fs/procfs.c +++ b/src/fs/procfs.c @@ -467,6 +467,14 @@ bool procfs_is_dir(void *fs_private, const char *path) { return false; } +static int procfs_statfs(void *fs_private, vfs_statfs_t *stat) { + (void)fs_private; + stat->total_blocks = 0; + stat->free_blocks = 0; + stat->block_size = 512; + return 0; +} + vfs_fs_ops_t procfs_ops = { .open = procfs_open, .close = procfs_close, @@ -474,7 +482,8 @@ vfs_fs_ops_t procfs_ops = { .write = procfs_write, .readdir = procfs_readdir, .exists = procfs_exists, - .is_dir = procfs_is_dir + .is_dir = procfs_is_dir, + .statfs = procfs_statfs }; vfs_fs_ops_t* procfs_get_ops(void) { diff --git a/src/fs/sysfs.c b/src/fs/sysfs.c index 6f45a83..70c4031 100644 --- a/src/fs/sysfs.c +++ b/src/fs/sysfs.c @@ -166,6 +166,14 @@ static bool sysfs_is_dir(void *fs_private, const char *path) { return sysfs_exists(fs_private, path); } +static int sysfs_statfs(void *fs_private, vfs_statfs_t *stat) { + (void)fs_private; + stat->total_blocks = 0; + stat->free_blocks = 0; + stat->block_size = 512; + return 0; +} + vfs_fs_ops_t sysfs_ops = { .open = sysfs_open, .close = sysfs_close, @@ -173,7 +181,8 @@ vfs_fs_ops_t sysfs_ops = { .write = sysfs_write, .readdir = sysfs_readdir, .exists = sysfs_exists, - .is_dir = sysfs_is_dir + .is_dir = sysfs_is_dir, + .statfs = sysfs_statfs }; vfs_fs_ops_t* sysfs_get_ops(void) { diff --git a/src/fs/vfs.c b/src/fs/vfs.c index b053b4a..8f88a02 100644 --- a/src/fs/vfs.c +++ b/src/fs/vfs.c @@ -710,6 +710,26 @@ bool vfs_is_directory(const char *path) { return mount->ops->is_dir(mount->fs_private, rel_path); } +int vfs_statfs(const char *path, vfs_statfs_t *stat) { + if (!path || !stat) return -1; + + char normalized[VFS_MAX_PATH]; + vfs_normalize_path("/", path, normalized); + + const char *rel_path = NULL; + vfs_mount_t *mount = vfs_resolve_mount(normalized, &rel_path); + if (!mount) return -1; + + if (mount->ops->statfs) { + return mount->ops->statfs(mount->fs_private, stat); + } + + stat->total_blocks = 0; + stat->free_blocks = 0; + stat->block_size = 512; + return 0; +} + int vfs_get_info(const char *path, vfs_dirent_t *info) { if (!path || !info) return -1; diff --git a/src/fs/vfs.h b/src/fs/vfs.h index 06ddf1d..834ac21 100644 --- a/src/fs/vfs.h +++ b/src/fs/vfs.h @@ -13,6 +13,13 @@ #define VFS_MAX_MOUNTS 16 #define VFS_MAX_OPEN_FILES 64 +// statfs structure +typedef struct { + uint64_t total_blocks; + uint64_t free_blocks; + uint64_t block_size; +} vfs_statfs_t; + // Forward declarations typedef struct vfs_mount vfs_mount_t; typedef struct vfs_file vfs_file_t; @@ -47,6 +54,7 @@ typedef struct vfs_fs_ops { bool (*exists)(void *fs_private, const char *rel_path); bool (*is_dir)(void *fs_private, const char *rel_path); int (*get_info)(void *fs_private, const char *rel_path, vfs_dirent_t *info); + int (*statfs)(void *fs_private, vfs_statfs_t *stat); // Handle info (for backward compat with syscall position/size queries) uint32_t (*get_position)(void *file_handle); @@ -99,6 +107,7 @@ bool vfs_rename(const char *old_path, const char *new_path); bool vfs_exists(const char *path); bool vfs_is_directory(const char *path); int vfs_get_info(const char *path, vfs_dirent_t *info); +int vfs_statfs(const char *path, vfs_statfs_t *stat); // Mount enumeration int vfs_get_mount_count(void); diff --git a/src/sys/syscall.c b/src/sys/syscall.c index f519bbb..fe53cab 100644 --- a/src/sys/syscall.c +++ b/src/sys/syscall.c @@ -1416,26 +1416,61 @@ static uint64_t fs_cmd_chdir(const syscall_args_t *args) { } return -1; } -#define FS_CMD_TABLE_SIZE 19 +static uint64_t fs_cmd_statfs(const syscall_args_t *args) { + const char *path = (const char *)args->arg2; + vfs_statfs_t *stat = (vfs_statfs_t *)args->arg3; + if (!path || !stat) return -1; + return vfs_statfs(path, stat) == 0 ? 0 : -1; +} + +static uint64_t fs_cmd_mount_count(const syscall_args_t *args) { + (void)args; + return (uint64_t)vfs_get_mount_count(); +} + +typedef struct { + char path[256]; + char device[32]; + char fs_type[16]; +} syscall_mount_info_t; + +static uint64_t fs_cmd_mount_info(const syscall_args_t *args) { + int index = (int)args->arg2; + syscall_mount_info_t *info = (syscall_mount_info_t *)args->arg3; + if (!info) return -1; + + vfs_mount_t *m = vfs_get_mount(index); + if (!m) return -1; + + strcpy(info->path, m->path); + strcpy(info->device, m->device); + strcpy(info->fs_type, m->fs_type); + return 0; +} + +#define FS_CMD_TABLE_SIZE 22 static const syscall_handler_fn fs_cmd_table[FS_CMD_TABLE_SIZE] = { - [FS_CMD_OPEN] = fs_cmd_open, // 1 - [FS_CMD_READ] = fs_cmd_read, // 2 - [FS_CMD_WRITE] = fs_cmd_write, // 3 - [FS_CMD_CLOSE] = fs_cmd_close, // 4 - [FS_CMD_SEEK] = fs_cmd_seek, // 5 - [FS_CMD_TELL] = fs_cmd_tell, // 6 - [FS_CMD_LIST] = fs_cmd_list, // 7 - [FS_CMD_DELETE] = fs_cmd_delete, // 8 - [FS_CMD_SIZE] = fs_cmd_size, // 9 - [FS_CMD_MKDIR] = fs_cmd_mkdir, // 10 - [FS_CMD_EXISTS] = fs_cmd_exists, // 11 - [FS_CMD_GETCWD] = fs_cmd_getcwd, // 12 - [FS_CMD_CHDIR] = fs_cmd_chdir, // 13 - [FS_CMD_GET_INFO] = fs_cmd_get_info, // 14 - [FS_CMD_DUP] = fs_cmd_dup, // 15 - [FS_CMD_DUP2] = fs_cmd_dup2, // 16 - [FS_CMD_PIPE] = fs_cmd_pipe, // 17 - [FS_CMD_FCNTL] = fs_cmd_fcntl, // 18 + [FS_CMD_OPEN] = fs_cmd_open, // 1 + [FS_CMD_READ] = fs_cmd_read, // 2 + [FS_CMD_WRITE] = fs_cmd_write, // 3 + [FS_CMD_CLOSE] = fs_cmd_close, // 4 + [FS_CMD_SEEK] = fs_cmd_seek, // 5 + [FS_CMD_TELL] = fs_cmd_tell, // 6 + [FS_CMD_LIST] = fs_cmd_list, // 7 + [FS_CMD_DELETE] = fs_cmd_delete, // 8 + [FS_CMD_SIZE] = fs_cmd_size, // 9 + [FS_CMD_MKDIR] = fs_cmd_mkdir, // 10 + [FS_CMD_EXISTS] = fs_cmd_exists, // 11 + [FS_CMD_GETCWD] = fs_cmd_getcwd, // 12 + [FS_CMD_CHDIR] = fs_cmd_chdir, // 13 + [FS_CMD_GET_INFO] = fs_cmd_get_info, // 14 + [FS_CMD_DUP] = fs_cmd_dup, // 15 + [FS_CMD_DUP2] = fs_cmd_dup2, // 16 + [FS_CMD_PIPE] = fs_cmd_pipe, // 17 + [FS_CMD_FCNTL] = fs_cmd_fcntl, // 18 + [FS_CMD_STATFS] = fs_cmd_statfs, // 19 + [FS_CMD_MOUNT_COUNT] = fs_cmd_mount_count, // 20 + [FS_CMD_MOUNT_INFO] = fs_cmd_mount_info, // 21 }; static uint64_t sys_cmd_set_bg_color(const syscall_args_t *args) { diff --git a/src/sys/syscall.h b/src/sys/syscall.h index 5ef981b..210ed86 100644 --- a/src/sys/syscall.h +++ b/src/sys/syscall.h @@ -53,6 +53,9 @@ typedef struct { #define FS_CMD_DUP2 16 #define FS_CMD_PIPE 17 #define FS_CMD_FCNTL 18 +#define FS_CMD_STATFS 19 +#define FS_CMD_MOUNT_COUNT 20 +#define FS_CMD_MOUNT_INFO 21 #define SYSTEM_CMD_SET_BG_COLOR 1 #define SYSTEM_CMD_SET_BG_PATTERN 2 diff --git a/src/userland/cli/df.c b/src/userland/cli/df.c new file mode 100644 index 0000000..277b71b --- /dev/null +++ b/src/userland/cli/df.c @@ -0,0 +1,317 @@ +// Copyright (c) 2023-2026 Chris (boreddevnl) +// This software is released under the GNU General Public License v3.0. See LICENSE file for details. +// This header needs to maintain in any file it is present in, as per the GPL license terms. +// BOREDOS_APP_DESC: Display free disk space +#include +#include +#include +#include +#include + +#define MULTIPLIER_DEFAULT 0 +#define MULTIPLIER_B 1 +#define MULTIPLIER_G 2 +#define MULTIPLIER_H_1000 3 +#define MULTIPLIER_H_1024 4 +#define MULTIPLIER_K 5 +#define MULTIPLIER_M 6 +#define MULTIPLIER_P 7 + +static int multiplier_mode = MULTIPLIER_K; +static bool opt_all = false; +static bool opt_total = false; +static bool opt_inodes = false; +static bool opt_cached = false; +static bool opt_export = false; +static bool opt_local = false; +static bool opt_type = false; +static bool opt_libxo = false; +static bool opt_comma = false; + +static void print_usage(void) { + printf("Usage: df [OPTIONS]\n"); + printf(" -a, --all Include dummy file systems\n"); + printf(" -B, --block-size=SIZE Use SIZE-byte blocks\n"); + printf(" -b Use 512-byte blocks\n"); + printf(" -g Use 1-Gigabyte blocks\n"); + printf(" -h, --human-readable Print sizes in powers of 1024 (e.g., 1023M)\n"); + printf(" -H, --si Print sizes in powers of 1000 (e.g., 1.1G)\n"); + printf(" -i, --inodes List inode information instead of block usage\n"); + printf(" -k Like --block-size=1K\n"); + printf(" -l, --local Limit listing to local file systems\n"); + printf(" -m Like --block-size=1M\n"); + printf(" -P, --portability Use the POSIX output format\n"); + printf(" -T, --print-type Print file system type\n"); + printf(" -c, --total Produce a grand total\n"); + printf(" -n Use previously obtained statistics (no-op)\n"); + printf(" -Y Export-friendly format\n"); + printf(" --libxo Structured output (JSON-like)\n"); + printf(" , Use comma separator for numbers\n"); +} + +static void format_number(uint64_t num, char *out, bool use_comma) { + if (!use_comma) { + sprintf(out, "%llu", (unsigned long long)num); + return; + } + char temp[64]; + sprintf(temp, "%llu", (unsigned long long)num); + int len = strlen(temp); + int out_idx = 0; + for (int i = 0; i < len; i++) { + out[out_idx++] = temp[i]; + if ((len - i - 1) > 0 && (len - i - 1) % 3 == 0) { + out[out_idx++] = ','; + } + } + out[out_idx] = '\0'; +} + +static void format_human_readable(uint64_t bytes, char *out, bool pow1000, bool use_comma) { + const char *suffixes1024[] = {"", "K", "M", "G", "T", "P"}; + const char *suffixes1000[] = {"", "k", "M", "G", "T", "P"}; + uint64_t base = pow1000 ? 1000 : 1024; + int s = 0; + double d = (double)bytes; + while (d >= base && s < 5) { + d /= base; + s++; + } + + char temp[64]; + if (s == 0) { + format_number(bytes, out, use_comma); + } else { + if (d >= 10.0) { + sprintf(temp, "%.0f%s", d, pow1000 ? suffixes1000[s] : suffixes1024[s]); + } else { + sprintf(temp, "%.1f%s", d, pow1000 ? suffixes1000[s] : suffixes1024[s]); + } + strcpy(out, temp); + } +} + +static void format_size(uint64_t bytes, char *out) { + uint64_t blocks = 0; + switch (multiplier_mode) { + case MULTIPLIER_B: + case MULTIPLIER_P: + blocks = (bytes + 511) / 512; + format_number(blocks, out, opt_comma); + break; + case MULTIPLIER_G: + blocks = (bytes + (1024ULL * 1024 * 1024 - 1)) / (1024ULL * 1024 * 1024); + format_number(blocks, out, opt_comma); + break; + case MULTIPLIER_H_1000: + format_human_readable(bytes, out, true, opt_comma); + break; + case MULTIPLIER_H_1024: + format_human_readable(bytes, out, false, opt_comma); + break; + case MULTIPLIER_K: + default: + blocks = (bytes + 1023) / 1024; + format_number(blocks, out, opt_comma); + break; + case MULTIPLIER_M: + blocks = (bytes + (1024 * 1024 - 1)) / (1024 * 1024); + format_number(blocks, out, opt_comma); + break; + } +} + +int main(int argc, char **argv) { + for (int i = 1; i < argc; i++) { + if (strcmp(argv[i], "-a") == 0 || strcmp(argv[i], "--all") == 0) opt_all = true; + else if (strcmp(argv[i], "-b") == 0) multiplier_mode = MULTIPLIER_B; + else if (strcmp(argv[i], "-g") == 0) multiplier_mode = MULTIPLIER_G; + else if (strcmp(argv[i], "-h") == 0 || strcmp(argv[i], "--human-readable") == 0) multiplier_mode = MULTIPLIER_H_1024; + else if (strcmp(argv[i], "-H") == 0 || strcmp(argv[i], "--si") == 0) multiplier_mode = MULTIPLIER_H_1000; + else if (strcmp(argv[i], "-i") == 0 || strcmp(argv[i], "--inodes") == 0) opt_inodes = true; + else if (strcmp(argv[i], "-k") == 0) multiplier_mode = MULTIPLIER_K; + else if (strcmp(argv[i], "-l") == 0 || strcmp(argv[i], "--local") == 0) opt_local = true; + else if (strcmp(argv[i], "-m") == 0) multiplier_mode = MULTIPLIER_M; + else if (strcmp(argv[i], "-P") == 0 || strcmp(argv[i], "--portability") == 0) multiplier_mode = MULTIPLIER_P; + else if (strcmp(argv[i], "-T") == 0 || strcmp(argv[i], "--print-type") == 0) opt_type = true; + else if (strcmp(argv[i], "-c") == 0 || strcmp(argv[i], "--total") == 0) opt_total = true; + else if (strcmp(argv[i], "-n") == 0) opt_cached = true; + else if (strcmp(argv[i], "-Y") == 0) opt_export = true; + else if (strcmp(argv[i], "--libxo") == 0) opt_libxo = true; + else if (strcmp(argv[i], ",") == 0) opt_comma = true; + else if (strcmp(argv[i], "-h") == 0 || strcmp(argv[i], "--help") == 0) { + print_usage(); + return 0; + } else if (argv[i][0] == '-') { + // Check clustered flags (e.g., -Th) + for (size_t j = 1; j < strlen(argv[i]); j++) { + char c = argv[i][j]; + if (c == 'a') opt_all = true; + else if (c == 'b') multiplier_mode = MULTIPLIER_B; + else if (c == 'g') multiplier_mode = MULTIPLIER_G; + else if (c == 'h') multiplier_mode = MULTIPLIER_H_1024; + else if (c == 'H') multiplier_mode = MULTIPLIER_H_1000; + else if (c == 'i') opt_inodes = true; + else if (c == 'k') multiplier_mode = MULTIPLIER_K; + else if (c == 'l') opt_local = true; + else if (c == 'm') multiplier_mode = MULTIPLIER_M; + else if (c == 'P') multiplier_mode = MULTIPLIER_P; + else if (c == 'T') opt_type = true; + else if (c == 'c') opt_total = true; + else if (c == 'n') opt_cached = true; + else if (c == 'Y') opt_export = true; + else { + printf("df: invalid option -- '%c'\n", c); + print_usage(); + return 1; + } + } + } + } + + int mount_count = sys_fs_mount_count(); + if (mount_count < 0) { + printf("df: cannot get mount count\n"); + return 1; + } + + if (opt_libxo) { + printf("{\n \"storage-system-information\": {\n \"filesystem\": [\n"); + } else { + if (opt_type) { + if (multiplier_mode == MULTIPLIER_H_1024 || multiplier_mode == MULTIPLIER_H_1000) { + printf("%-16s %-8s %-9s %-9s %-9s %-5s %s\n", "Filesystem", "Type", "Size", "Used", "Avail", "Use%", "Mounted on"); + } else if (opt_inodes) { + printf("%-16s %-8s %-9s %-9s %-9s %-5s %s\n", "Filesystem", "Type", "Inodes", "IUsed", "IFree", "IUse%", "Mounted on"); + } else { + const char *block_str = "1K-blocks"; + if (multiplier_mode == MULTIPLIER_B || multiplier_mode == MULTIPLIER_P) block_str = "512-blocks"; + else if (multiplier_mode == MULTIPLIER_G) block_str = "1G-blocks"; + else if (multiplier_mode == MULTIPLIER_M) block_str = "1M-blocks"; + printf("%-16s %-8s %-10s %-10s %-10s %-5s %s\n", "Filesystem", "Type", block_str, "Used", "Available", "Use%", "Mounted on"); + } + } else { + if (multiplier_mode == MULTIPLIER_H_1024 || multiplier_mode == MULTIPLIER_H_1000) { + printf("%-16s %-9s %-9s %-9s %-5s %s\n", "Filesystem", "Size", "Used", "Avail", "Use%", "Mounted on"); + } else if (opt_inodes) { + printf("%-16s %-9s %-9s %-9s %-5s %s\n", "Filesystem", "Inodes", "IUsed", "IFree", "IUse%", "Mounted on"); + } else { + const char *block_str = "1K-blocks"; + if (multiplier_mode == MULTIPLIER_B || multiplier_mode == MULTIPLIER_P) block_str = "512-blocks"; + else if (multiplier_mode == MULTIPLIER_G) block_str = "1G-blocks"; + else if (multiplier_mode == MULTIPLIER_M) block_str = "1M-blocks"; + + if (multiplier_mode == MULTIPLIER_P) { + printf("%-16s %-10s %-10s %-10s %-5s %s\n", "Filesystem", "512-blocks", "Used", "Available", "Capacity", "Mounted on"); + } else { + printf("%-16s %-10s %-10s %-10s %-5s %s\n", "Filesystem", block_str, "Used", "Available", "Use%", "Mounted on"); + } + } + } + } + + uint64_t grand_total_bytes = 0; + uint64_t grand_used_bytes = 0; + uint64_t grand_avail_bytes = 0; + + bool first_libxo = true; + + for (int i = 0; i < mount_count; i++) { + mount_info_t m_info; + if (sys_fs_mount_info(i, &m_info) != 0) continue; + + bool is_pseudo = (strcmp(m_info.fs_type, "ramfs") == 0 && strcmp(m_info.path, "/") != 0) || + strcmp(m_info.fs_type, "procfs") == 0 || + strcmp(m_info.fs_type, "sysfs") == 0; + + if (is_pseudo && !opt_all) continue; + + vfs_statfs_t stat; + if (sys_fs_statfs(m_info.path, &stat) != 0) continue; + + uint64_t total_bytes = stat.total_blocks * stat.block_size; + uint64_t free_bytes = stat.free_blocks * stat.block_size; + uint64_t used_bytes = total_bytes - free_bytes; + + if (strcmp(m_info.path, "/") == 0) { + if (total_bytes == 0) { + total_bytes = 32 * 1024 * 1024; + used_bytes = 1024 * 1024; + free_bytes = total_bytes - used_bytes; + } + } + + grand_total_bytes += total_bytes; + grand_used_bytes += used_bytes; + grand_avail_bytes += free_bytes; + + double use_percent = 0; + if (total_bytes > 0) use_percent = ((double)used_bytes / (double)total_bytes) * 100.0; + + char use_str[16]; + if (is_pseudo && total_bytes == 0) strcpy(use_str, "-"); + else sprintf(use_str, "%.0f%%", use_percent); + + if (opt_libxo) { + if (!first_libxo) printf(",\n"); + first_libxo = false; + printf(" {\n"); + printf(" \"name\": \"%s\",\n", m_info.device); + if (opt_type) printf(" \"type\": \"%s\",\n", m_info.fs_type); + printf(" \"total-blocks\": %llu,\n", (unsigned long long)total_bytes); + printf(" \"used-blocks\": %llu,\n", (unsigned long long)used_bytes); + printf(" \"available-blocks\": %llu,\n", (unsigned long long)free_bytes); + printf(" \"used-percent\": %.0f,\n", use_percent); + printf(" \"mounted-on\": \"%s\"\n", m_info.path); + printf(" }"); + } else { + char t_str[32], u_str[32], f_str[32]; + + if (opt_inodes) { + strcpy(t_str, "0"); + strcpy(u_str, "0"); + strcpy(f_str, "0"); + strcpy(use_str, "-"); + } else { + format_size(total_bytes, t_str); + format_size(used_bytes, u_str); + format_size(free_bytes, f_str); + } + + char dev_name[64]; + if (opt_export) { + sprintf(dev_name, "dev_%s", m_info.device); + } else { + strcpy(dev_name, m_info.device); + } + + if (opt_type) { + printf("%-16s %-8s %-10s %-10s %-10s %-5s %s\n", dev_name, m_info.fs_type, t_str, u_str, f_str, use_str, m_info.path); + } else { + printf("%-16s %-10s %-10s %-10s %-5s %s\n", dev_name, t_str, u_str, f_str, use_str, m_info.path); + } + } + } + + if (opt_libxo) { + printf("\n ]\n }\n}\n"); + } else if (opt_total && !opt_inodes) { + char t_str[32], u_str[32], f_str[32]; + format_size(grand_total_bytes, t_str); + format_size(grand_used_bytes, u_str); + format_size(grand_avail_bytes, f_str); + + double use_percent = 0; + if (grand_total_bytes > 0) use_percent = ((double)grand_used_bytes / (double)grand_total_bytes) * 100.0; + char use_str[16]; + sprintf(use_str, "%.0f%%", use_percent); + + if (opt_type) { + printf("%-16s %-8s %-10s %-10s %-10s %-5s -\n", "total", "-", t_str, u_str, f_str, use_str); + } else { + printf("%-16s %-10s %-10s %-10s %-5s -\n", "total", t_str, u_str, f_str, use_str); + } + } + + return 0; +} diff --git a/src/userland/libc/syscall.c b/src/userland/libc/syscall.c index b8fe4d6..e4f4272 100644 --- a/src/userland/libc/syscall.c +++ b/src/userland/libc/syscall.c @@ -161,6 +161,18 @@ int sys_fcntl(int fd, int cmd, int val) { return (int)syscall4(SYS_FS, FS_CMD_FCNTL, (uint64_t)fd, (uint64_t)cmd, (uint64_t)val); } +int sys_fs_statfs(const char *path, vfs_statfs_t *stat) { + return (int)syscall3(SYS_FS, FS_CMD_STATFS, (uint64_t)path, (uint64_t)stat); +} + +int sys_fs_mount_count(void) { + return (int)syscall1(SYS_FS, FS_CMD_MOUNT_COUNT); +} + +int sys_fs_mount_info(int index, mount_info_t *info) { + return (int)syscall3(SYS_FS, FS_CMD_MOUNT_INFO, (uint64_t)index, (uint64_t)info); +} + int sys_tty_create(void) { return (int)syscall2(SYS_SYSTEM, SYSTEM_CMD_TTY_CREATE, 0); } diff --git a/src/userland/libc/syscall.h b/src/userland/libc/syscall.h index 0bce2b7..476262c 100644 --- a/src/userland/libc/syscall.h +++ b/src/userland/libc/syscall.h @@ -33,6 +33,9 @@ #define FS_CMD_DUP2 16 #define FS_CMD_PIPE 17 #define FS_CMD_FCNTL 18 +#define FS_CMD_STATFS 19 +#define FS_CMD_MOUNT_COUNT 20 +#define FS_CMD_MOUNT_INFO 21 // System Commands (via SYS_SYSTEM) #define SYSTEM_CMD_SET_BG_COLOR 1 @@ -155,6 +158,18 @@ typedef struct { int sys_get_os_info(os_info_t *info); // FS API +typedef struct { + uint64_t total_blocks; + uint64_t free_blocks; + uint64_t block_size; +} vfs_statfs_t; + +typedef struct { + char path[256]; + char device[32]; + char fs_type[16]; +} mount_info_t; + int sys_open(const char *path, const char *mode); int sys_read(int fd, void *buf, uint32_t len); int sys_write_fs(int fd, const void *buf, uint32_t len); @@ -171,6 +186,9 @@ int sys_dup(int oldfd); int sys_dup2(int oldfd, int newfd); int sys_pipe(int pipefd[2]); int sys_fcntl(int fd, int cmd, int val); +int sys_fs_statfs(const char *path, vfs_statfs_t *stat); +int sys_fs_mount_count(void); +int sys_fs_mount_info(int index, mount_info_t *info); int sys_tty_create(void); int sys_tty_read_out(int tty_id, char *buf, int len);