From 2a649e1b96e4d8a616aa41194684f03d56478e64 Mon Sep 17 00:00:00 2001 From: Tulio A M Mendes Date: Mon, 25 May 2026 14:22:42 -0300 Subject: [PATCH] vfs: complete Fase 5 - /proc/mounts improvements and mountpoint validation - 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 | 2 +- src/kernel/fs.c | 91 ++++++++++++++++++++++++++++++++++++++------ src/kernel/procfs.c | 14 +------ src/kernel/syscall.c | 9 +++-- 4 files changed, 86 insertions(+), 30 deletions(-) diff --git a/include/fs.h b/include/fs.h index 80291687..f4768a53 100644 --- a/include/fs.h +++ b/include/fs.h @@ -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); diff --git a/src/kernel/fs.c b/src/kernel/fs.c index 81665c17..3ded2ef6 100644 --- a/src/kernel/fs.c +++ b/src/kernel/fs.c @@ -15,6 +15,7 @@ #include "errno.h" #include "spinlock.h" #include "console.h" +#include "heap.h" #include @@ -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: \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) { diff --git a/src/kernel/procfs.c b/src/kernel/procfs.c index 2e68a0d2..c46b894c 100644 --- a/src/kernel/procfs.c +++ b/src/kernel/procfs.c @@ -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 --- */ diff --git a/src/kernel/syscall.c b/src/kernel/syscall.c index 1b46ea06..d4df5041 100644 --- a/src/kernel/syscall.c +++ b/src/kernel/syscall.c @@ -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; -- 2.43.0