]> Projects (at) Tadryanom (dot) Me - AdrOS.git/commitdiff
vfs: mount/umount/df overhaul — fstype/source metadata, /proc/mounts, mount flags...
authorTulio A M Mendes <[email protected]>
Wed, 20 May 2026 03:36:16 +0000 (00:36 -0300)
committerTulio A M Mendes <[email protected]>
Wed, 20 May 2026 03:36:16 +0000 (00:36 -0300)
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
src/kernel/fs.c
src/kernel/init.c
src/kernel/procfs.c
src/kernel/syscall.c
user/cmds/init/init.c
user/cmds/mount/mount.c
user/cmds/umount/umount.c
user/ulibc/src/statvfs.c

index ded10d7a5994c29aa68ed791206258a90281dc0f..db0e573844447c1a242fe3a6487b109c9fcc7f29 100644 (file)
@@ -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;
index 8aa7545b3b6a0dbe2ca0db834557ba439a652438..7286ad7ef6242d1ee9acf8bf79dc91e52cf90c35 100644 (file)
@@ -12,6 +12,9 @@
 #include "utils.h"
 #include "errno.h"
 #include "spinlock.h"
+#include "console.h"
+
+#include <string.h>
 
 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: <source> <mountpoint> <fstype> <options>\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];
index 8b2e59b969ee6c28f39f259e7172053ea690a953..de289e9a2dcd2aae01101162f8c048c5737bcbf9 100644 (file)
@@ -40,6 +40,7 @@
 #include "kernel/cmdline.h"
 
 #include <stddef.h>
+#include <string.h>
 
 /* ---- 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
index eea7a96bd04d2af3a8399f27c7581198c604ac5e..2e68a0d286d25f21eb8798c3a42f648629850708 100644 (file)
@@ -16,6 +16,7 @@
 #include "pmm.h"
 #include "timer.h"
 #include "kernel/cmdline.h"
+#include "fs.h"
 
 #include <stddef.h>
 
@@ -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;
 }
index 6b48af3bf4b3287810498cc0f883e266f483ae5b..6b199892bb78f6f56d8f88bdbc0ee477112bb3fe 100644 (file)
@@ -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;
     }
index 44f356ceb02a2235eb7dc873d842c0d0f3ef3512..b8bc4f585bf794ebf92b829fec1805b1e7fe8625 100644 (file)
@@ -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) {
index 0b7ed397bd89e6dcc66ed86b4283b83403de97d9..5b0d4d0ae999d01bafb22c47bcbd7cfc1c2c2ad6 100644 (file)
 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;
index 3d649c43e8236780eb928eaa6fbf011f6928f62f..a6e9dd0095a955d35a787085794241d2e27ffe5b 100644 (file)
@@ -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;
index 4630c33976bfcb0593063445ed86b9d79a31cb54..330f413d0d42ddcb5253ad89a284f2f4a15dc97d 100644 (file)
 #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;