From 09ad31a2662a4dcb36dbb00d0f9202ee0ad08c79 Mon Sep 17 00:00:00 2001 From: Tulio A M Mendes Date: Wed, 20 May 2026 00:36:16 -0300 Subject: [PATCH] =?utf8?q?vfs:=20mount/umount/df=20overhaul=20=E2=80=94=20?= =?utf8?q?fstype/source=20metadata,=20/proc/mounts,=20mount=20flags,=20rec?= =?utf8?q?ursive=20mkdir-p,=20busy-check?= MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit Kernel changes: - Extended vfs_mount struct with fstype[32], source[64], flags fields - Increased mount table capacity from 16 to 32 entries - Added vfs_mount_full/vfs_mount_nolock_full for metadata-aware mounts - Added vfs_mounts_read() exposing mount table for /proc/mounts - Added vfs_mkdirp() for recursive mkdir -p (auto-create mountpoints) - Added busy-check in vfs_umount_nolock: reject if child mounts exist - Fixed MS_REMOUNT: allow NULL root (update flags only on existing mount) - Updated kernel boot-time mounts to pass fstype/source metadata - Updated SYSCALL_MOUNT: parse flags, MS_REMOUNT support, vfs_mkdirp - Updated SYSCALL_UMOUNT2: read flags arg (MNT_FORCE/MNT_DETACH) - Updated pivot_root to use vfs_mount_nolock_full - Added /proc/mounts procfs entry with proc_mounts_read handler Userland changes: - Rewrote statvfs: parses /proc/mounts with longest-prefix match, fstype-aware block count estimation (tmpfs/procfs/devfs/overlayfs) - mount: added -o flag parsing (ro, rw, remount) - umount: added -f (MNT_FORCE) and -l (MNT_DETACH) via umount2 - init: added is_mounted() check to avoid duplicate virtual FS mounts Test results: 120/120 smoke, 152/152 battery, 111/111 host PASS --- include/fs.h | 10 +++ src/kernel/fs.c | 154 ++++++++++++++++++++++++++++--- src/kernel/init.c | 23 +++-- src/kernel/procfs.c | 35 +++++++- src/kernel/syscall.c | 29 ++++-- user/cmds/init/init.c | 53 +++++++++-- user/cmds/mount/mount.c | 34 +++++-- user/cmds/umount/umount.c | 23 ++++- user/ulibc/src/statvfs.c | 184 +++++++++++++++++++++++++++----------- 9 files changed, 453 insertions(+), 92 deletions(-) diff --git a/include/fs.h b/include/fs.h index ded10d7a..db0e5738 100644 --- a/include/fs.h +++ b/include/fs.h @@ -93,6 +93,7 @@ fs_node_t* vfs_lookup_parent(const char* path, char* name_out, size_t name_sz); // Directory mutation wrappers — route through mount points transparently int vfs_create(const char* path, uint32_t flags, fs_node_t** out); int vfs_mkdir(const char* path); +int vfs_mkdirp(const char* path); int vfs_unlink(const char* path); int vfs_rmdir(const char* path); int vfs_rename(const char* old_path, const char* new_path); @@ -100,14 +101,23 @@ int vfs_truncate(const char* path, uint32_t length); int vfs_link(const char* old_path, const char* new_path); int vfs_mount(const char* mountpoint, fs_node_t* root); +int vfs_mount_full(const char* mountpoint, fs_node_t* root, + const char* fstype, const char* source, + unsigned long flags); int vfs_umount(const char* mountpoint); /* _nolock variants — caller must already hold g_vfs_lock. * Used by compound operations like pivot_root that need atomicity * across multiple mount-table modifications. */ int vfs_mount_nolock(const char* mountpoint, fs_node_t* root); +int vfs_mount_nolock_full(const char* mountpoint, fs_node_t* root, + const char* fstype, const char* source, + unsigned long flags); int vfs_umount_nolock(const char* mountpoint); +/* Read mount table for /proc/mounts. Returns bytes written. */ +uint32_t vfs_mounts_read(uint8_t* buffer, uint32_t size); + /* Global VFS spinlock — protects fs_root, g_mounts[], g_mount_count. * Acquire via spin_lock_irqsave() for any compound VFS mutation. */ extern spinlock_t g_vfs_lock; diff --git a/src/kernel/fs.c b/src/kernel/fs.c index 8aa7545b..7286ad7e 100644 --- a/src/kernel/fs.c +++ b/src/kernel/fs.c @@ -12,6 +12,9 @@ #include "utils.h" #include "errno.h" #include "spinlock.h" +#include "console.h" + +#include fs_node_t* fs_root = NULL; static fs_node_t* g_initrd_root = NULL; @@ -23,10 +26,13 @@ spinlock_t g_vfs_lock; struct vfs_mount { char mountpoint[128]; + char fstype[32]; /* e.g. "overlayfs", "tmpfs", "devfs", "procfs", "diskfs", "fat", "ext2" */ + char source[64]; /* e.g. "/dev/hda", "none", "initrd" */ + unsigned long flags; /* MS_RDONLY, MS_NOSUID, etc. */ fs_node_t* root; }; -static struct vfs_mount g_mounts[16]; +static struct vfs_mount g_mounts[32]; static int g_mount_count = 0; static int path_is_mountpoint_prefix(const char* mp, const char* path) { @@ -65,8 +71,9 @@ static void normalize_mountpoint(const char* in, char* out, size_t out_sz) { } } -int vfs_mount_nolock(const char* mountpoint, fs_node_t* root) { - if (!root) return -EINVAL; +int vfs_mount_nolock_full(const char* mountpoint, fs_node_t* root, + const char* fstype, const char* source, + unsigned long flags) { if (g_mount_count >= (int)(sizeof(g_mounts) / sizeof(g_mounts[0]))) return -ENOSPC; char mp[128]; @@ -74,39 +81,80 @@ int vfs_mount_nolock(const char* mountpoint, fs_node_t* root) { for (int i = 0; i < g_mount_count; i++) { if (strcmp(g_mounts[i].mountpoint, mp) == 0) { - g_mounts[i].root = root; + /* Remount: update provided fields only */ + if (root) g_mounts[i].root = root; + if (fstype) strncpy(g_mounts[i].fstype, fstype, sizeof(g_mounts[i].fstype) - 1); + if (source) strncpy(g_mounts[i].source, source, sizeof(g_mounts[i].source) - 1); + /* Always update flags on remount (even if 0, caller explicitly set them) */ + g_mounts[i].flags = flags; return 0; } } + /* New mount entry — root is required */ + if (!root) return -EINVAL; + strcpy(g_mounts[g_mount_count].mountpoint, mp); g_mounts[g_mount_count].root = root; + if (fstype) strncpy(g_mounts[g_mount_count].fstype, fstype, sizeof(g_mounts[g_mount_count].fstype) - 1); + else g_mounts[g_mount_count].fstype[0] = '\0'; + if (source) strncpy(g_mounts[g_mount_count].source, source, sizeof(g_mounts[g_mount_count].source) - 1); + else g_mounts[g_mount_count].source[0] = '\0'; + g_mounts[g_mount_count].flags = flags; g_mount_count++; return 0; } -int vfs_mount(const char* mountpoint, fs_node_t* root) { +int vfs_mount_nolock(const char* mountpoint, fs_node_t* root) { + return vfs_mount_nolock_full(mountpoint, root, NULL, NULL, 0); +} + +int vfs_mount_full(const char* mountpoint, fs_node_t* root, + const char* fstype, const char* source, + unsigned long flags) { uintptr_t fl = spin_lock_irqsave(&g_vfs_lock); - int ret = vfs_mount_nolock(mountpoint, root); + int ret = vfs_mount_nolock_full(mountpoint, root, fstype, source, flags); spin_unlock_irqrestore(&g_vfs_lock, fl); return ret; } +int vfs_mount(const char* mountpoint, fs_node_t* root) { + return vfs_mount_full(mountpoint, root, NULL, NULL, 0); +} + int vfs_umount_nolock(const char* mountpoint) { char mp[128]; normalize_mountpoint(mountpoint, mp, sizeof(mp)); if (strcmp(mp, "/") == 0) return -EBUSY; + /* Find the mount entry */ + int idx = -1; for (int i = 0; i < g_mount_count; i++) { if (strcmp(g_mounts[i].mountpoint, mp) == 0) { - for (int j = i; j < g_mount_count - 1; j++) - g_mounts[j] = g_mounts[j + 1]; - g_mount_count--; - return 0; + idx = i; + break; + } + } + if (idx < 0) return -EINVAL; + + /* Busy check: reject if any other mount is a child of this one */ + size_t mplen = strlen(mp); + for (int i = 0; i < g_mount_count; i++) { + if (i == idx) continue; + const char* other = g_mounts[i].mountpoint; + size_t olen = strlen(other); + if (olen > mplen && + strncmp(other, mp, mplen) == 0 && + other[mplen] == '/') { + return -EBUSY; /* child mount still active */ } } - return -EINVAL; + + for (int j = idx; j < g_mount_count - 1; j++) + g_mounts[j] = g_mounts[j + 1]; + g_mount_count--; + return 0; } int vfs_umount(const char* mountpoint) { @@ -116,6 +164,46 @@ int vfs_umount(const char* mountpoint) { return ret; } +/* Read the mount table into a user buffer for /proc/mounts. + * Format per line: \n + * Returns number of bytes written. */ +uint32_t vfs_mounts_read(uint8_t* buffer, uint32_t size) { + uintptr_t fl = spin_lock_irqsave(&g_vfs_lock); + + char tmp[2048]; + uint32_t len = 0; + + for (int i = 0; i < g_mount_count && len < sizeof(tmp) - 80; i++) { + const char* src = g_mounts[i].source[0] ? g_mounts[i].source : "none"; + const char* fst = g_mounts[i].fstype[0] ? g_mounts[i].fstype : "unknown"; + + /* Build options string from flags */ + char opts[64]; + uint32_t olen = 0; + + /* rw/ro */ + if (g_mounts[i].flags & 1 /* MS_RDONLY */) { + opts[olen++] = 'r'; opts[olen++] = 'o'; + } else { + opts[olen++] = 'r'; opts[olen++] = 'w'; + } + if (g_mounts[i].flags & 2) { opts[olen++] = ','; const char* s = "nosuid"; while (*s) opts[olen++] = *s++; } + if (g_mounts[i].flags & 4) { opts[olen++] = ','; const char* s = "nodev"; while (*s) opts[olen++] = *s++; } + if (g_mounts[i].flags & 8) { opts[olen++] = ','; const char* s = "noexec"; while (*s) opts[olen++] = *s++; } + opts[olen] = '\0'; + + /* source mountpoint fstype options */ + len += (uint32_t)ksnprintf(tmp + len, sizeof(tmp) - len, + "%s %s %s %s 0 0\n", src, g_mounts[i].mountpoint, fst, opts); + } + + spin_unlock_irqrestore(&g_vfs_lock, fl); + + if (len > size) len = size; + memcpy(buffer, tmp, len); + return len; +} + uint32_t vfs_read(fs_node_t* node, uint32_t offset, uint32_t size, uint8_t* buffer) { if (node->f_ops && node->f_ops->read) return node->f_ops->read(node, offset, size, buffer); @@ -301,6 +389,50 @@ int vfs_mkdir(const char* path) { return -ENOSYS; } +/* Recursive mkdir -p: create intermediate directories as needed. + * Returns 0 on success, -EEXIST if the directory already exists, + * or a negative errno on failure. */ +int vfs_mkdirp(const char* path) { + if (!path || path[0] != '/') return -EINVAL; + + /* Check if it already exists */ + fs_node_t* node = vfs_lookup(path); + if (node) { + if (node->flags & FS_DIRECTORY) return -EEXIST; + return -ENOTDIR; + } + + /* Walk the path creating each missing component */ + char tmp[128]; + strncpy(tmp, path, sizeof(tmp) - 1); + tmp[sizeof(tmp) - 1] = '\0'; + + /* Skip leading '/' */ + char* p = tmp + 1; + char* slash; + + while ((slash = p) && *p) { + /* Find next '/' */ + while (*p && *p != '/') p++; + if (*p == '/') { *p = '\0'; p++; } + + /* Check if this component exists */ + fs_node_t* check = vfs_lookup(tmp); + if (!check) { + /* Create it */ + int rc = vfs_mkdir(tmp); + if (rc < 0 && rc != -EEXIST) return rc; + } else if (!(check->flags & FS_DIRECTORY)) { + return -ENOTDIR; + } + + /* Restore slash for next iteration */ + if (*p) p[-1] = '/'; + } + + return 0; +} + int vfs_unlink(const char* path) { if (!path) return -EINVAL; char name[128]; diff --git a/src/kernel/init.c b/src/kernel/init.c index 8b2e59b9..de289e9a 100644 --- a/src/kernel/init.c +++ b/src/kernel/init.c @@ -40,6 +40,7 @@ #include "kernel/cmdline.h" #include +#include /* ---- Mount helper: used by fstab parser and kconsole 'mount' command ---- */ @@ -66,7 +67,19 @@ int init_mount_fs(const char* fstype, int drive, uint32_t lba, const char* mount return -1; } - if (vfs_mount(mountpoint, root) < 0) { + /* Build device name for mount table metadata */ + char devname[32] = "none"; + const char* dname = ata_drive_to_name(drive); + if (dname) { + strcpy(devname, "/dev/"); + /* Append drive name after /dev/ */ + char* dp = devname + 5; + while (*dname && (dp - devname) < (int)sizeof(devname) - 2) + *dp++ = *dname++; + *dp = '\0'; + } + + if (vfs_mount_full(mountpoint, root, fstype, devname, 0) < 0) { kprintf("[MOUNT] Failed to register mount at %s\n", mountpoint); return -1; } @@ -208,7 +221,7 @@ int init_start(const struct boot_info* bi) { if (upper) { fs_node_t* ovl = overlayfs_create_root(fs_root, upper); if (ovl) { - (void)vfs_mount("/", ovl); + (void)vfs_mount_full("/", ovl, "overlayfs", "initrd", 0); vfs_set_initrd_root(ovl); } } @@ -234,7 +247,7 @@ int init_start(const struct boot_info* bi) { if (tmp) { static const uint8_t hello[] = "hello from tmpfs\n"; (void)tmpfs_add_file(tmp, "hello.txt", hello, (uint32_t)(sizeof(hello) - 1)); - (void)vfs_mount("/tmp", tmp); + (void)vfs_mount_full("/tmp", tmp, "tmpfs", "none", 0); } /* Register hardware drivers with HAL and init in priority order */ @@ -258,7 +271,7 @@ int init_start(const struct boot_info* bi) { * existing entry so this is a harmless overlap. */ fs_node_t* dev = devfs_create_root(); if (dev) { - (void)vfs_mount("/dev", dev); + (void)vfs_mount_full("/dev", dev, "devfs", "none", 0); } vbe_register_devfs(); @@ -266,7 +279,7 @@ int init_start(const struct boot_info* bi) { fs_node_t* proc = procfs_create_root(); if (proc) { - (void)vfs_mount("/proc", proc); + (void)vfs_mount_full("/proc", proc, "procfs", "none", 0); } /* Initialize ATA subsystem — probe all 4 drives diff --git a/src/kernel/procfs.c b/src/kernel/procfs.c index eea7a96b..2e68a0d2 100644 --- a/src/kernel/procfs.c +++ b/src/kernel/procfs.c @@ -16,6 +16,7 @@ #include "pmm.h" #include "timer.h" #include "kernel/cmdline.h" +#include "fs.h" #include @@ -26,6 +27,7 @@ static fs_node_t g_proc_uptime; static fs_node_t g_proc_meminfo; static fs_node_t g_proc_cmdline; static fs_node_t g_proc_dmesg; +static fs_node_t g_proc_mounts; #define PID_NODE_POOL 8 static fs_node_t g_pid_dir[PID_NODE_POOL]; @@ -359,6 +361,25 @@ static fs_node_t* proc_get_pid_dir(uint32_t pid) { return &g_pid_dir[slot]; } +/* --- /proc/mounts read --- */ + +static uint32_t proc_mounts_read(fs_node_t* node, uint32_t offset, uint32_t size, uint8_t* buffer) { + (void)node; + /* Allocate from heap — mount table can be large */ + char* tmp = kmalloc(4096); + if (!tmp) return 0; + uint32_t len = vfs_mounts_read((uint8_t*)tmp, 4096); + uint32_t ret = 0; + if (offset < len) { + uint32_t avail = len - offset; + if (size > avail) size = avail; + memcpy(buffer, tmp + offset, size); + ret = size; + } + kfree(tmp); + return ret; +} + /* --- /proc/self --- */ static fs_node_t* proc_self_finddir(fs_node_t* node, const char* name) { @@ -409,6 +430,7 @@ static fs_node_t* proc_root_finddir(fs_node_t* node, const char* name) { if (strcmp(name, "meminfo") == 0) return &g_proc_meminfo; if (strcmp(name, "cmdline") == 0) return &g_proc_cmdline; if (strcmp(name, "dmesg") == 0) return &g_proc_dmesg; + if (strcmp(name, "mounts") == 0) return &g_proc_mounts; if (is_numeric(name)) return proc_get_pid_dir(parse_uint(name)); return NULL; } @@ -421,8 +443,8 @@ static int proc_root_readdir(fs_node_t* node, uint32_t* inout_index, void* buf, uint32_t idx = *inout_index; struct vfs_dirent* d = (struct vfs_dirent*)buf; - static const char* fixed[] = { "self", "uptime", "meminfo", "cmdline", "dmesg" }; - if (idx < 5) { + static const char* fixed[] = { "self", "uptime", "meminfo", "cmdline", "dmesg", "mounts" }; + if (idx < 6) { d->d_ino = 200 + idx; d->d_type = (idx == 0) ? FS_DIRECTORY : FS_FILE; d->d_reclen = sizeof(struct vfs_dirent); @@ -503,6 +525,10 @@ static const struct file_operations procfs_dmesg_fops = { .read = proc_dmesg_read, }; +static const struct file_operations procfs_mounts_fops = { + .read = proc_mounts_read, +}; + static const struct file_operations procfs_pid_dir_fops = {0}; static const struct inode_operations procfs_pid_dir_iops = { @@ -560,5 +586,10 @@ fs_node_t* procfs_create_root(void) { g_proc_dmesg.flags = FS_FILE; g_proc_dmesg.f_ops = &procfs_dmesg_fops; + memset(&g_proc_mounts, 0, sizeof(g_proc_mounts)); + strcpy(g_proc_mounts.name, "mounts"); + g_proc_mounts.flags = FS_FILE; + g_proc_mounts.f_ops = &procfs_mounts_fops; + return &g_proc_root; } diff --git a/src/kernel/syscall.c b/src/kernel/syscall.c index 6b48af3b..6b199892 100644 --- a/src/kernel/syscall.c +++ b/src/kernel/syscall.c @@ -4884,9 +4884,9 @@ static void socket_syscall_dispatch(struct registers* regs, uint32_t syscall_no) uintptr_t vfs_fl = spin_lock_irqsave(&g_vfs_lock); fs_node_t* old_root = fs_root; fs_root = new_root; - (void)vfs_mount_nolock("/", new_root); + (void)vfs_mount_nolock_full("/", new_root, NULL, NULL, 0); if (old_root) { - (void)vfs_mount_nolock(kput, old_root); + (void)vfs_mount_nolock_full(kput, old_root, NULL, NULL, 0); } spin_unlock_irqrestore(&g_vfs_lock, vfs_fl); sc_ret(regs) = 0; @@ -4901,6 +4901,7 @@ static void socket_syscall_dispatch(struct registers* regs, uint32_t syscall_no) const char* user_dev = (const char*)sc_arg0(regs); const char* user_mp = (const char*)sc_arg1(regs); const char* user_type = (const char*)sc_arg2(regs); + unsigned long mount_flags = (unsigned long)sc_arg3(regs); char kdev[64], kmp[128], ktype[32]; if (copy_from_user(kdev, user_dev, sizeof(kdev)) < 0 || path_resolve_user(user_mp, kmp, sizeof(kmp)) < 0 || @@ -4911,28 +4912,34 @@ static void socket_syscall_dispatch(struct registers* regs, uint32_t syscall_no) kdev[sizeof(kdev)-1] = '\0'; ktype[sizeof(ktype)-1] = '\0'; + /* MS_REMOUNT (0x20): update flags on existing mount */ + if (mount_flags & 0x20 /* MS_REMOUNT */) { + sc_ret(regs) = (uint32_t)vfs_mount_full(kmp, NULL, NULL, NULL, mount_flags & ~0x20); + return; + } + /* Virtual filesystems — no device argument needed */ if (strcmp(ktype, "tmpfs") == 0) { fs_node_t* tmp = tmpfs_create_root(); if (!tmp) { sc_ret(regs) = (uint32_t)-ENOMEM; return; } - (void)vfs_mkdir(kmp); /* auto-create mountpoint */ - sc_ret(regs) = (uint32_t)vfs_mount(kmp, tmp); + (void)vfs_mkdirp(kmp); /* auto-create mountpoint (recursive) */ + sc_ret(regs) = (uint32_t)vfs_mount_full(kmp, tmp, "tmpfs", kdev, mount_flags); return; } if (strcmp(ktype, "devfs") == 0) { extern fs_node_t* devfs_create_root(void); fs_node_t* dev = devfs_create_root(); if (!dev) { sc_ret(regs) = (uint32_t)-ENOMEM; return; } - (void)vfs_mkdir(kmp); - sc_ret(regs) = (uint32_t)vfs_mount(kmp, dev); + (void)vfs_mkdirp(kmp); + sc_ret(regs) = (uint32_t)vfs_mount_full(kmp, dev, "devfs", kdev, mount_flags); return; } if (strcmp(ktype, "procfs") == 0) { extern fs_node_t* procfs_create_root(void); fs_node_t* proc = procfs_create_root(); if (!proc) { sc_ret(regs) = (uint32_t)-ENOMEM; return; } - (void)vfs_mkdir(kmp); - sc_ret(regs) = (uint32_t)vfs_mount(kmp, proc); + (void)vfs_mkdirp(kmp); + sc_ret(regs) = (uint32_t)vfs_mount_full(kmp, proc, "procfs", kdev, mount_flags); return; } @@ -4944,7 +4951,7 @@ static void socket_syscall_dispatch(struct registers* regs, uint32_t syscall_no) if (drive < 0) { sc_ret(regs) = (uint32_t)-ENODEV; return; } extern int init_mount_fs(const char* fstype, int drive, uint32_t lba, const char* mountpoint); - (void)vfs_mkdir(kmp); /* auto-create mountpoint */ + (void)vfs_mkdirp(kmp); /* auto-create mountpoint (recursive) */ int rc = init_mount_fs(ktype, drive, 0, kmp); sc_ret(regs) = (uint32_t)(rc < 0 ? -EIO : 0); return; @@ -5077,11 +5084,15 @@ static void socket_syscall_dispatch(struct registers* regs, uint32_t syscall_no) return; } const char* user_target = (const char*)sc_arg0(regs); + unsigned long umount_flags = (unsigned long)sc_arg1(regs); char ktarget[128]; if (path_resolve_user(user_target, ktarget, sizeof(ktarget)) < 0) { sc_ret(regs) = (uint32_t)-EFAULT; return; } + /* MNT_FORCE (1) — for now, same as regular umount (no lazy unmount) */ + /* MNT_DETACH (2) — lazy unmount; currently equivalent to immediate */ + (void)umount_flags; sc_ret(regs) = (uint32_t)vfs_umount(ktarget); return; } diff --git a/user/cmds/init/init.c b/user/cmds/init/init.c index 44f356ce..b8bc4f58 100644 --- a/user/cmds/init/init.c +++ b/user/cmds/init/init.c @@ -243,13 +243,54 @@ static void check_respawn(void) { /* Mount virtual filesystems (migrated from kernel-space). * These must be done before spawning the shell since /dev/console * is needed for terminal I/O. */ +static int is_mounted(const char* mountpoint) { + /* Check /proc/mounts for an existing entry */ + int fd = open("/proc/mounts", 0); + if (fd < 0) return 0; + char buf[2048]; + int n = read(fd, buf, sizeof(buf) - 1); + close(fd); + if (n <= 0) return 0; + buf[n] = '\0'; + + int mplen = 0; + const char* p = mountpoint; + while (*p++) mplen++; + + /* Scan lines: source mountpoint fstype options ... */ + char* line = buf; + while (line && *line) { + char* nl = line; + while (*nl && *nl != '\n') nl++; + int eol = (*nl == '\n'); + if (eol) *nl = '\0'; + /* Skip source */ + char* mp = line; + while (*mp && *mp != ' ') mp++; + if (*mp) mp++; + /* Compare mountpoint */ + const char* lmp = mp; + const char* pmp = mountpoint; + while (*lmp != ' ' && *lmp != '\0' && *pmp && *lmp == *pmp) { lmp++; pmp++; } + if (*lmp == ' ' && *pmp == '\0') return 1; + line = eol ? nl + 1 : nl; + } + return 0; +} + static void mount_virtual_fs(void) { - if (mount("none", "/dev", "devfs", 0, NULL) < 0) - fprintf(stderr, "init: mount devfs on /dev failed\n"); - if (mount("none", "/proc", "procfs", 0, NULL) < 0) - fprintf(stderr, "init: mount procfs on /proc failed\n"); - if (mount("none", "/tmp", "tmpfs", 0, NULL) < 0) - fprintf(stderr, "init: mount tmpfs on /tmp failed\n"); + /* Only mount if not already mounted by kernel init. + * vfs_mount replaces existing entries, but re-creating root nodes + * (e.g. tmpfs) leaks the old instance. */ + if (!is_mounted("/dev")) + if (mount("none", "/dev", "devfs", 0, NULL) < 0) + fprintf(stderr, "init: mount devfs on /dev failed\n"); + if (!is_mounted("/proc")) + if (mount("none", "/proc", "procfs", 0, NULL) < 0) + fprintf(stderr, "init: mount procfs on /proc failed\n"); + if (!is_mounted("/tmp")) + if (mount("none", "/tmp", "tmpfs", 0, NULL) < 0) + fprintf(stderr, "init: mount tmpfs on /tmp failed\n"); } static void default_init(void) { diff --git a/user/cmds/mount/mount.c b/user/cmds/mount/mount.c index 0b7ed397..5b0d4d0a 100644 --- a/user/cmds/mount/mount.c +++ b/user/cmds/mount/mount.c @@ -18,15 +18,13 @@ static void show_mounts(void) { int fd = open("/proc/mounts", O_RDONLY); if (fd >= 0) { - char buf[1024]; + char buf[2048]; int n; while ((n = read(fd, buf, sizeof(buf))) > 0) write(STDOUT_FILENO, buf, (size_t)n); close(fd); } else { - printf("tmpfs on / type overlayfs (rw)\n"); - printf("devfs on /dev type devfs (rw)\n"); - printf("procfs on /proc type procfs (ro)\n"); + fprintf(stderr, "mount: /proc/mounts not available\n"); } } @@ -39,12 +37,32 @@ int main(int argc, char** argv) { const char* fstype = "diskfs"; const char* device = NULL; const char* mountpoint = NULL; + unsigned long mountflags = 0; /* Parse options first, then collect positional args */ int i; for (i = 1; i < argc; i++) { if (strcmp(argv[i], "-t") == 0 && i + 1 < argc) { fstype = argv[++i]; + } else if (strcmp(argv[i], "-o") == 0 && i + 1 < argc) { + i++; + /* Parse comma-separated options */ + const char* o = argv[i]; + while (*o) { + if (strncmp(o, "ro", 2) == 0 && (o[2] == ',' || o[2] == '\0')) { + mountflags |= MS_RDONLY; + o += 2; + } else if (strncmp(o, "remount", 7) == 0 && (o[7] == ',' || o[7] == '\0')) { + mountflags |= MS_REMOUNT; + o += 7; + } else if (strncmp(o, "rw", 2) == 0 && (o[2] == ',' || o[2] == '\0')) { + o += 2; + } else { + /* Skip unknown option */ + while (*o && *o != ',') o++; + } + if (*o == ',') o++; + } } else if (!device) { device = argv[i]; } else if (!mountpoint) { @@ -52,12 +70,14 @@ int main(int argc, char** argv) { } } - if (!device || !mountpoint) { - fprintf(stderr, "usage: mount [-t fstype] device mountpoint\n"); + if (!mountpoint) { + fprintf(stderr, "usage: mount [-t fstype] [-o options] device mountpoint\n"); return 1; } - int rc = mount(device, mountpoint, fstype, 0, NULL); + if (!device) device = "none"; + + int rc = mount(device, mountpoint, fstype, mountflags, NULL); if (rc < 0) { fprintf(stderr, "mount: mounting %s on %s failed: %s\n", device, mountpoint, strerror(errno)); return 1; diff --git a/user/cmds/umount/umount.c b/user/cmds/umount/umount.c index 3d649c43..a6e9dd00 100644 --- a/user/cmds/umount/umount.c +++ b/user/cmds/umount/umount.c @@ -18,8 +18,27 @@ int main(int argc, char** argv) { fprintf(stderr, "umount: missing operand\n"); return 1; } - if (umount(argv[1]) < 0) { - fprintf(stderr, "umount: %s: %s\n", argv[1], strerror(errno)); + + int flags = 0; + const char* target = NULL; + + for (int i = 1; i < argc; i++) { + if (strcmp(argv[i], "-f") == 0) { + flags |= MNT_FORCE; + } else if (strcmp(argv[i], "-l") == 0) { + flags |= MNT_DETACH; + } else { + target = argv[i]; + } + } + + if (!target) { + fprintf(stderr, "umount: missing operand\n"); + return 1; + } + + if (umount2(target, flags) < 0) { + fprintf(stderr, "umount: %s: %s\n", target, strerror(errno)); return 1; } return 0; diff --git a/user/ulibc/src/statvfs.c b/user/ulibc/src/statvfs.c index 4630c339..330f413d 100644 --- a/user/ulibc/src/statvfs.c +++ b/user/ulibc/src/statvfs.c @@ -12,69 +12,153 @@ #include "syscall.h" #include "errno.h" #include "unistd.h" +#include "string.h" + +/* Find the best-matching mount entry in /proc/mounts for a given path. + * Returns 1 if found, 0 otherwise. Fills fstype_out and flags_out. */ +static int find_mount_for_path(const char* path, + char* fstype_out, size_t fstype_sz, + char* source_out, size_t source_sz, + unsigned long* flags_out) { + int fd = open("/proc/mounts", 0); + if (fd < 0) return 0; + + char mbuf[2048]; + int n = read(fd, mbuf, sizeof(mbuf) - 1); + close(fd); + if (n <= 0) return 0; + mbuf[n] = '\0'; + + /* Find longest-prefix match among mount points */ + int best_len = -1; + char best_fstype[32] = ""; + char best_source[64] = ""; + unsigned long best_flags = 0; + + char* line = mbuf; + while (line && *line) { + char* nl = line; + while (*nl && *nl != '\n') nl++; + int eol = (*nl == '\n'); + if (eol) *nl = '\0'; + + /* Parse: source mountpoint fstype options ... */ + char* src = line; + char* mp = src; + while (*mp && *mp != ' ') mp++; + if (*mp) { *mp = '\0'; mp++; } + + char* fst = mp; + while (*fst && *fst != ' ') fst++; + if (*fst) { *fst = '\0'; fst++; } + + char* opts = fst; + while (*opts && *opts != ' ') opts++; + if (*opts) { *opts = '\0'; opts++; } + + /* Check if path starts with mountpoint (prefix match) */ + int mplen = 0; + const char* p = mp; + while (*p++) mplen++; + + int match = 0; + if (mplen == 1 && mp[0] == '/') { + match = 1; /* root mount matches everything */ + } else { + const char* pp = path; + const char* mmp = mp; + while (*mmp && *pp && *mmp == *pp) { mmp++; pp++; } + if (*mmp == '\0' && (*pp == '\0' || *pp == '/')) + match = 1; + } + + if (match && mplen > best_len) { + best_len = mplen; + strncpy(best_fstype, fst, sizeof(best_fstype) - 1); + best_fstype[sizeof(best_fstype) - 1] = '\0'; + strncpy(best_source, src, sizeof(best_source) - 1); + best_source[sizeof(best_source) - 1] = '\0'; + /* Parse options for ro/rw */ + best_flags = 0; + if (opts) { + /* Check for "ro" option */ + const char* o = opts; + while (*o) { + if ((o == opts || *(o-1) == ',') && + o[0] == 'r' && o[1] == 'o' && + (o[2] == ',' || o[2] == ' ' || o[2] == '\0')) { + best_flags |= 1; /* MS_RDONLY */ + break; + } + o++; + } + } + } + + line = eol ? nl + 1 : nl; + } + + if (best_len < 0) return 0; + + if (fstype_out) { strncpy(fstype_out, best_fstype, fstype_sz - 1); fstype_out[fstype_sz - 1] = '\0'; } + if (source_out) { strncpy(source_out, best_source, source_sz - 1); source_out[source_sz - 1] = '\0'; } + if (flags_out) *flags_out = best_flags; + return 1; +} int statvfs(const char* path, struct statvfs* buf) { struct stat st; if (stat(path, &st) < 0) return -1; - /* Build a pseudo statvfs from stat info. - * AdrOS has no f_bsize/f_blocks syscall, so we estimate from st_blksize. */ unsigned long bsize = (unsigned long)st.st_blksize; - if (bsize == 0) bsize = 512; + if (bsize == 0) bsize = 4096; + + /* Find mount info for this path */ + char fstype[32] = "unknown"; + char source[64] = "none"; + unsigned long mflags = 0; + find_mount_for_path(path, fstype, sizeof(fstype), source, sizeof(source), &mflags); buf->f_bsize = bsize; buf->f_frsize = bsize; - /* For initrd/overlayfs, report the file size as "used" and assume - * some free space. This is a best-effort approximation. */ - buf->f_blocks = (unsigned long)st.st_size / bsize + 256; - buf->f_bfree = 128; - buf->f_bavail = 128; - buf->f_files = 128; - buf->f_ffree = 64; - buf->f_favail = 64; buf->f_fsid = (unsigned long)st.st_dev; - buf->f_flag = 0; + buf->f_flag = mflags; /* MS_RDONLY etc. */ buf->f_namemax = 255; - /* Try to read actual values from /proc/mounts if available */ - int fd = open("/proc/mounts", 0 /* O_RDONLY */); - if (fd >= 0) { - char mbuf[1024]; - int n = read(fd, mbuf, sizeof(mbuf) - 1); - close(fd); - if (n > 0) { - mbuf[n] = '\0'; - /* Look for the mount point in the output */ - int plen = 0; - const char* p = path; - while (*p++) plen++; - /* Parse lines: dev dir type ... */ - char* line = mbuf; - while (line && *line) { - char* nl = line; - while (*nl && *nl != '\n') nl++; - int end = (*nl == '\n'); - if (end) *nl = '\0'; - /* Check if this line's mount point matches path */ - char* sp1 = line; - while (*sp1 && *sp1 != ' ') sp1++; - if (*sp1) sp1++; - /* sp1 now points to mount dir */ - int match = 1; - const char* mp = sp1; - const char* pp = path; - while (*mp != ' ' && *mp != '\0' && *pp) { - if (*mp != *pp) { match = 0; break; } - mp++; pp++; - } - if (match && *mp == ' ' && *pp == '\0') { - /* Found matching mount — use default estimates */ - break; - } - line = end ? nl + 1 : nl; - } - } + /* Derive block counts based on filesystem type */ + if (strcmp(fstype, "tmpfs") == 0) { + /* tmpfs: size is limited by available memory */ + buf->f_blocks = 16384; /* ~64 MB assuming 4K blocks */ + buf->f_bfree = 8192; + buf->f_bavail = 8192; + buf->f_files = 256; + buf->f_ffree = 128; + buf->f_favail = 128; + } else if (strcmp(fstype, "procfs") == 0 || strcmp(fstype, "devfs") == 0) { + /* Virtual/pseudo filesystems — zero blocks */ + buf->f_blocks = 0; + buf->f_bfree = 0; + buf->f_bavail = 0; + buf->f_files = 64; + buf->f_ffree = 32; + buf->f_favail = 32; + } else if (strcmp(fstype, "overlayfs") == 0) { + /* Overlay: report based on stat info */ + buf->f_blocks = (unsigned long)st.st_size / bsize + 512; + buf->f_bfree = 256; + buf->f_bavail = 256; + buf->f_files = 256; + buf->f_ffree = 128; + buf->f_favail = 128; + } else { + /* Disk-based (diskfs, fat, ext2, persistfs): estimate from stat */ + buf->f_blocks = (unsigned long)st.st_size / bsize + 512; + buf->f_bfree = 256; + buf->f_bavail = 256; + buf->f_files = 256; + buf->f_ffree = 128; + buf->f_favail = 128; } return 0; -- 2.43.0