]> Projects (at) Tadryanom (dot) Me - AdrOS.git/commitdiff
vfs: complete Fase 5 - /proc/mounts improvements and mountpoint validation
authorTulio A M Mendes <[email protected]>
Mon, 25 May 2026 17:22:42 +0000 (14:22 -0300)
committerTulio A M Mendes <[email protected]>
Mon, 25 May 2026 17:22:42 +0000 (14:22 -0300)
- Refactor vfs_mounts_read to support offset-based reading like a file
- Add escape_mount_string helper to escape special characters (space, tab, newline, backslash)
- Replace fixed 2048-byte buffer with dynamic allocation based on actual content size
- Escape spaces as \040, tabs as \011, newlines as \012, backslashes as \\
- Update proc_mounts_read to use new vfs_mounts_read signature with offset parameter
- Remove auto-mkdirp from sys_mount (vfs_mkdirp calls removed)
- Add mountpoint existence check before mount (vfs_lookup + directory check)
- Return -ENOENT if mountpoint does not exist, -ENOTDIR if not a directory
- Add heap.h include for kmalloc/kfree in fs.c
- Test: smoke test 119/119 PASS

include/fs.h
src/kernel/fs.c
src/kernel/procfs.c
src/kernel/syscall.c

index 80291687f4dd9694712e27868d555144126b444a..f4768a5345bd93c688926db5faf778d7abf1f857 100644 (file)
@@ -144,7 +144,7 @@ int vfs_mount_nolock_full(const char* mountpoint, fs_node_t* root,
 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);
+uint32_t vfs_mounts_read(uint8_t* buffer, uint32_t size, uint32_t offset);
 
 /* Look up mount flags for the filesystem containing the given path. */
 unsigned long vfs_mount_flags(const char* path);
index 81665c174bb1ac08fe965694d0c2782f3b9085dd..3ded2ef6dd31ca414f4de6ee82a70b9ea6bca43f 100644 (file)
@@ -15,6 +15,7 @@
 #include "errno.h"
 #include "spinlock.h"
 #include "console.h"
+#include "heap.h"
 
 #include <string.h>
 
@@ -210,24 +211,77 @@ int vfs_umount(const char* mountpoint) {
     return ret;
 }
 
+/* Helper to escape special characters for /proc/mounts */
+static void escape_mount_string(char* dst, const char* src, uint32_t dst_size) {
+    uint32_t di = 0;
+    for (uint32_t si = 0; src[si] && di < dst_size - 1; si++) {
+        switch (src[si]) {
+            case ' ':
+                if (di + 4 < dst_size) { dst[di++] = '\\'; dst[di++] = '0'; dst[di++] = '4'; dst[di++] = '0'; }
+                break;
+            case '\t':
+                if (di + 4 < dst_size) { dst[di++] = '\\'; dst[di++] = '0'; dst[di++] = '1'; dst[di++] = '1'; }
+                break;
+            case '\n':
+                if (di + 4 < dst_size) { dst[di++] = '\\'; dst[di++] = '0'; dst[di++] = '1'; dst[di++] = '2'; }
+                break;
+            case '\\':
+                if (di + 2 < dst_size) { dst[di++] = '\\'; dst[di++] = '\\'; }
+                break;
+            default:
+                dst[di++] = src[si];
+                break;
+        }
+    }
+    dst[di] = '\0';
+}
+
 /* Read the mount table into a user buffer for /proc/mounts.
  * Format per line: <source> <mountpoint> <fstype> <options>\n
+ * Supports offset-based reading like a file.
  * Returns number of bytes written. */
-uint32_t vfs_mounts_read(uint8_t* buffer, uint32_t size) {
+uint32_t vfs_mounts_read(uint8_t* buffer, uint32_t size, uint32_t offset) {
     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++) {
+    /* First pass: calculate total size needed */
+    uint32_t total_size = 0;
+    for (int i = 0; i < g_mount_count; 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;
+        if (g_mounts[i].flags & MS_RDONLY) {
+            opts[olen++] = 'r'; opts[olen++] = 'o';
+        } else {
+            opts[olen++] = 'r'; opts[olen++] = 'w';
+        }
+        if (g_mounts[i].flags & MS_NOSUID) { opts[olen++] = ','; const char* s = "nosuid"; while (*s) opts[olen++] = *s++; }
+        if (g_mounts[i].flags & MS_NODEV)  { opts[olen++] = ','; const char* s = "nodev"; while (*s) opts[olen++] = *s++; }
+        if (g_mounts[i].flags & MS_NOEXEC) { opts[olen++] = ','; const char* s = "noexec"; while (*s) opts[olen++] = *s++; }
+        opts[olen] = '\0';
 
-        /* rw/ro */
+        /* Estimate size (escaped strings can be up to 4x longer) */
+        total_size += strlen(src) * 4 + strlen(g_mounts[i].mountpoint) * 4 + strlen(fst) * 4 + strlen(opts) * 4 + 20;
+    }
+
+    /* Allocate buffer */
+    char* tmp = (char*)kmalloc(total_size + 1);
+    if (!tmp) {
+        spin_unlock_irqrestore(&g_vfs_lock, fl);
+        return 0;
+    }
+
+    /* Second pass: write escaped content */
+    uint32_t len = 0;
+    for (int i = 0; i < g_mount_count; 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;
         if (g_mounts[i].flags & MS_RDONLY) {
             opts[olen++] = 'r'; opts[olen++] = 'o';
         } else {
@@ -238,16 +292,29 @@ uint32_t vfs_mounts_read(uint8_t* buffer, uint32_t size) {
         if (g_mounts[i].flags & MS_NOEXEC) { 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);
+        /* Escape each field */
+        char esc_src[256], esc_mp[256], esc_fst[64], esc_opts[128];
+        escape_mount_string(esc_src, src, sizeof(esc_src));
+        escape_mount_string(esc_mp, g_mounts[i].mountpoint, sizeof(esc_mp));
+        escape_mount_string(esc_fst, fst, sizeof(esc_fst));
+        escape_mount_string(esc_opts, opts, sizeof(esc_opts));
+
+        len += (uint32_t)ksnprintf(tmp + len, total_size - len,
+                    "%s %s %s %s 0 0\n", esc_src, esc_mp, esc_fst, esc_opts);
     }
 
     spin_unlock_irqrestore(&g_vfs_lock, fl);
 
-    if (len > size) len = size;
-    memcpy(buffer, tmp, len);
-    return len;
+    /* Copy requested portion based on offset */
+    uint32_t copy_len = 0;
+    if (offset < len) {
+        uint32_t available = len - offset;
+        copy_len = (available < size) ? available : size;
+        memcpy(buffer, tmp + offset, copy_len);
+    }
+
+    kfree(tmp);
+    return copy_len;
 }
 
 uint32_t vfs_read(fs_node_t* node, uint32_t offset, uint32_t size, uint8_t* buffer) {
index 2e68a0d286d25f21eb8798c3a42f648629850708..c46b894ca116637f323883526a1db1d034b9bb8a 100644 (file)
@@ -365,19 +365,7 @@ static fs_node_t* proc_get_pid_dir(uint32_t pid) {
 
 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;
+    return vfs_mounts_read(buffer, size, offset);
 }
 
 /* --- /proc/self --- */
index 1b46ea066c4a09dddc9393bafc4237e88496d67e..d4df50412f7a01aae195f57ce407d96173d5c2f1 100644 (file)
@@ -5108,11 +5108,15 @@ static void extended_syscall_dispatch(struct registers* regs, uint32_t syscall_n
             return;
         }
 
+        /* Check if mountpoint exists */
+        fs_node_t* mp_node = vfs_lookup(kmp);
+        if (!mp_node) { sc_ret(regs) = (uint32_t)-ENOENT; return; }
+        if (!(mp_node->flags & FS_DIRECTORY)) { sc_ret(regs) = (uint32_t)-ENOTDIR; 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_mkdirp(kmp);  /* auto-create mountpoint (recursive) */
             sc_ret(regs) = (uint32_t)vfs_mount_full(kmp, tmp, "tmpfs", kdev, mount_flags, NULL);
             return;
         }
@@ -5120,7 +5124,6 @@ static void extended_syscall_dispatch(struct registers* regs, uint32_t syscall_n
             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_mkdirp(kmp);
             sc_ret(regs) = (uint32_t)vfs_mount_full(kmp, dev, "devfs", kdev, mount_flags, NULL);
             return;
         }
@@ -5128,7 +5131,6 @@ static void extended_syscall_dispatch(struct registers* regs, uint32_t syscall_n
             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_mkdirp(kmp);
             sc_ret(regs) = (uint32_t)vfs_mount_full(kmp, proc, "procfs", kdev, mount_flags, NULL);
             return;
         }
@@ -5141,7 +5143,6 @@ static void extended_syscall_dispatch(struct registers* regs, uint32_t syscall_n
         if (!bdev) { sc_ret(regs) = (uint32_t)-ENODEV; return; }
 
         extern int init_mount_fs(const char* fstype, const block_device_t* bdev, uint32_t lba, const char* mountpoint, unsigned long flags);
-        (void)vfs_mkdirp(kmp);  /* auto-create mountpoint (recursive) */
         int rc = init_mount_fs(ktype, bdev, 0, kmp, mount_flags);
         sc_ret(regs) = (uint32_t)(rc < 0 ? rc : 0);
         return;