]> Projects (at) Tadryanom (dot) Me - AdrOS.git/commitdiff
vfs: fix MS_REMOUNT, mount validation, blockdev locking, and resource leaks
authorTulio A M Mendes <[email protected]>
Tue, 26 May 2026 01:51:06 +0000 (22:51 -0300)
committerTulio A M Mendes <[email protected]>
Wed, 3 Jun 2026 04:02:35 +0000 (01:02 -0300)
High-severity fixes:
- MS_REMOUNT: pass full flags to VFS, store flags & ~MS_REMOUNT in remount branch
- Reject mount replacement with -EBUSY (except with MS_REMOUNT)
- Fix init_mount_fs leak: call kill_sb on failure after fst->mount
- Add MS_RDONLY check in SYSCALL_FTRUNCATE via f->mount_root

Medium-severity fixes:
- Centralize read-only check in vfs_link with vfs_require_writable_path
- Validate mountpoint in init_mount_fs and kconsole mount (must exist and be directory)
- Add spinlock to g_blockdevs with irqsave/irqrestore protection
- Remove const from blockdev_claim/release API (block_device_t* instead of const*)

Files modified:
- src/kernel/syscall.c: MS_REMOUNT fix, ftruncate readonly check
- src/kernel/fs.c: mount replacement rejection, vfs_link readonly check, bdev const removal
- src/kernel/init.c: mountpoint validation, init_mount_fs leak fix, blockdev_init_lock call
- src/kernel/kconsole.c: mountpoint validation
- include/blockdev.h: spinlock include, const removal from API
- src/kernel/blockdev.c: spinlock implementation, const removal from functions
- include/fs.h: const removal from bdev, mount function signatures
- include/ext2.h: const removal from bdev, mount signature
- include/fat.h: const removal from bdev, mount signature
- src/kernel/ext2.c: const removal from mount signature
- src/kernel/fat.c: const removal from mount signature
- src/drivers/virtio_blk.c: const removal from ops functions
- include/kernel/init.h: const removal from init_mount_fs signature

Test results:
- Smoke test: 119/119 PASS
- cppcheck: style-level warnings only (no errors)

13 files changed:
include/blockdev.h
include/ext2.h
include/fat.h
include/fs.h
include/kernel/init.h
src/drivers/virtio_blk.c
src/kernel/blockdev.c
src/kernel/ext2.c
src/kernel/fat.c
src/kernel/fs.c
src/kernel/init.c
src/kernel/kconsole.c
src/kernel/syscall.c

index f5fdd86e9f7abdc321df6d6a7a5f4312f1549f3a..edf38f5e98c3d01a2ee9597ae1cfdd0c29e94103 100644 (file)
@@ -13,6 +13,7 @@
 #include <stdint.h>
 #include <stddef.h>
 #include "errno.h"
+#include "spinlock.h"
 
 /* Generic block device interface — abstracts ATA/virtio-blk/etc.
  * Filesystems (fat, ext2) use this instead of calling ATA directly,
@@ -23,8 +24,8 @@
 struct block_device;
 
 struct block_device_ops {
-    int (*read)(const struct block_device* dev, uint32_t lba, void* buf);
-    int (*write)(const struct block_device* dev, uint32_t lba, const void* buf);
+    int (*read)(struct block_device* dev, uint32_t lba, void* buf);
+    int (*write)(struct block_device* dev, uint32_t lba, const void* buf);
 };
 
 typedef struct block_device {
@@ -40,25 +41,25 @@ typedef struct block_device {
 int blockdev_register(const block_device_t* dev);
 
 /* Look up a block device by name (e.g. "hda"). Returns pointer or NULL. */
-const block_device_t* blockdev_find(const char* name);
+block_device_t* blockdev_find(const char* name);
 
 /* Look up a block device by drive_id. Returns pointer or NULL. */
-const block_device_t* blockdev_by_id(int drive_id);
+block_device_t* blockdev_by_id(int drive_id);
 
 /* Increment block device refcount (called when filesystem mounts). */
-void blockdev_claim(const block_device_t* dev);
+void blockdev_claim(block_device_t* dev);
 
 /* Decrement block device refcount (called when filesystem unmounts). */
-void blockdev_release(const block_device_t* dev);
+void blockdev_release(block_device_t* dev);
 
 /* Convenience: read one sector from a block device. */
-static inline int blockdev_read(const block_device_t* dev, uint32_t lba, void* buf) {
+static inline int blockdev_read(block_device_t* dev, uint32_t lba, void* buf) {
     if (!dev || !dev->ops || !dev->ops->read) return -ENODEV;
     return dev->ops->read(dev, lba, buf);
 }
 
 /* Convenience: write one sector to a block device. */
-static inline int blockdev_write(const block_device_t* dev, uint32_t lba, const void* buf) {
+static inline int blockdev_write(block_device_t* dev, uint32_t lba, const void* buf) {
     if (!dev || !dev->ops || !dev->ops->write) return -ENODEV;
     return dev->ops->write(dev, lba, buf);
 }
@@ -66,4 +67,7 @@ static inline int blockdev_write(const block_device_t* dev, uint32_t lba, const
 /* Register all detected ATA drives as block devices. */
 void blockdev_register_ata(void);
 
+/* Initialize the blockdev lock (must be called before any blockdev operations). */
+void blockdev_init_lock(void);
+
 #endif
index 592a1e13640e86dfa6028de29074403f3831b83a..04098a997d147b29705a5e07f1a1b91ce55bc591 100644 (file)
@@ -18,7 +18,7 @@ struct ext2_group_desc;
 
 /* Per-mount filesystem state */
 struct ext2_mount {
-    const block_device_t* bdev;
+    block_device_t* bdev;
     uint32_t part_lba;        /* partition start LBA */
     uint32_t block_size;      /* bytes per block (1024, 2048, or 4096) */
     uint32_t sectors_per_block;
@@ -35,7 +35,7 @@ struct ext2_mount {
 
 /* Mount an ext2 filesystem on the given block device starting at the given
  * LBA offset.  Returns a mount result with root node and superblock, or {NULL, NULL} on failure. */
-vfs_mount_result_t ext2_mount(const block_device_t* bdev, uint32_t partition_lba);
+vfs_mount_result_t ext2_mount(block_device_t* bdev, uint32_t partition_lba);
 
 /* Unmount an ext2 filesystem and free its resources */
 void ext2_umount(struct ext2_mount* em);
index ca774a008e0c9778c69da9ea23726b79d2868748..5465a13fbf8eed7bb2e5bd4cfc2c2018737a2e6d 100644 (file)
@@ -22,7 +22,7 @@ enum fat_type {
 
 /* Per-mount filesystem state */
 struct fat_mount {
-    const block_device_t* bdev;
+    block_device_t* bdev;
     uint32_t part_lba;
     uint16_t bytes_per_sector;
     uint8_t  sectors_per_cluster;
@@ -42,7 +42,7 @@ struct fat_mount {
 /* Mount a FAT12/16/32 filesystem on the given block device starting at
  * the given LBA offset.  Auto-detects FAT type from BPB.
  * Returns a mount result with root node and superblock, or {NULL, NULL} on failure. */
-vfs_mount_result_t fat_mount(const block_device_t* bdev, uint32_t partition_lba);
+vfs_mount_result_t fat_mount(block_device_t* bdev, uint32_t partition_lba);
 
 /* Unmount a FAT filesystem and free its resources */
 void fat_umount(struct fat_mount* fm);
index d4238b1bb89e0492105fe043826990ca2cbd8242..d8bc90d38922cff2712c245486ef8c47c4c26589 100644 (file)
@@ -39,7 +39,7 @@ struct vfs_fs_type; /* forward declaration */
 /* VFS superblock — per-mount filesystem metadata */
 typedef struct vfs_superblock {
     const struct vfs_fs_type* fstype;    /* Filesystem type */
-    const block_device_t* bdev;          /* Block device (NULL for virtual FS) */
+    block_device_t* bdev;          /* Block device (NULL for virtual FS) */
     uint32_t lba;                        /* Partition start LBA (0 for whole disk) */
     void* private_data;                  /* Filesystem-specific data (e.g. fat_mount, ext2_mount) */
     struct fs_node* root;                /* VFS root node (for cleanup on umount) */
@@ -55,7 +55,7 @@ typedef struct vfs_mount_result {
 typedef struct vfs_fs_type {
     const char* name;                     /* e.g. "fat", "ext2" */
     uint32_t flags;                       /* FS_NEEDS_BDEV, etc. */
-    vfs_mount_result_t (*mount)(const block_device_t* bdev, uint32_t lba);  /* Mount function */
+    vfs_mount_result_t (*mount)(block_device_t* bdev, uint32_t lba);  /* Mount function */
     void (*kill_sb)(vfs_superblock_t* sb); /* Unmount/cleanup function */
 } vfs_fs_type_t;
 
@@ -145,7 +145,7 @@ 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, const block_device_t* bdev,
+                    unsigned long flags, block_device_t* bdev,
                     vfs_superblock_t* sb);
 int vfs_umount(const char* mountpoint);
 
@@ -155,7 +155,7 @@ int vfs_umount(const char* mountpoint);
 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, const block_device_t* bdev,
+                            unsigned long flags, block_device_t* bdev,
                             vfs_superblock_t* sb);
 int vfs_umount_nolock(const char* mountpoint);
 
index 31416f8eb2a014d63a77d06c281a20ce1052a65f..bbc1bcaaca79459db62cb47f30f883ff53053fe3 100644 (file)
@@ -23,6 +23,6 @@ int init_start(const struct boot_info* bi);
  * mountpoint: e.g. "/disk", "/fat", "/ext2"
  * flags: mount flags (MS_RDONLY, etc.) — stored in VFS mount table
  * Returns 0 on success, negative errno on failure. */
-int init_mount_fs(const char* fstype, const struct block_device* bdev, uint32_t lba, const char* mountpoint, unsigned long flags);
+int init_mount_fs(const char* fstype, struct block_device* bdev, uint32_t lba, const char* mountpoint, unsigned long flags);
 
 #endif
index d93ead4262afef794ff3915d0d6fb8570ea29779..06757c54528c76146128738b1de624888763584a 100644 (file)
@@ -301,12 +301,12 @@ uint64_t virtio_blk_capacity(void) {
 }
 
 /* ---- Block device operations ---- */
-static int vblk_bdev_read(const block_device_t* dev, uint32_t lba, void* buf) {
+static int vblk_bdev_read(block_device_t* dev, uint32_t lba, void* buf) {
     (void)dev;
     return virtio_blk_read((uint64_t)lba, buf, 1);
 }
 
-static int vblk_bdev_write(const block_device_t* dev, uint32_t lba, const void* buf) {
+static int vblk_bdev_write(block_device_t* dev, uint32_t lba, const void* buf) {
     (void)dev;
     return virtio_blk_write((uint64_t)lba, buf, 1);
 }
index bdc18709ac8a2d4b7ecbdc57bb41d53fea2191ac..5eae1a51b4fcde373d80ed95e893ccdfb837dc40 100644 (file)
 
 static block_device_t g_blockdevs[BLOCKDEV_MAX];
 static int g_blockdev_count = 0;
+static spinlock_t g_blockdev_lock;
+
+void blockdev_init_lock(void) {
+    spinlock_init(&g_blockdev_lock);
+}
 
 int blockdev_register(const block_device_t* dev) {
     if (!dev) return -EINVAL;
-    if (g_blockdev_count >= BLOCKDEV_MAX) return -ENOSPC;
+
+    uintptr_t flags = spin_lock_irqsave(&g_blockdev_lock);
+
+    if (g_blockdev_count >= BLOCKDEV_MAX) {
+        spin_unlock_irqrestore(&g_blockdev_lock, flags);
+        return -ENOSPC;
+    }
 
     /* Check for duplicate name */
     for (int i = 0; i < g_blockdev_count; i++) {
         if (strcmp(g_blockdevs[i].name, dev->name) == 0) {
             /* Update existing entry */
             g_blockdevs[i] = *dev;
+            spin_unlock_irqrestore(&g_blockdev_lock, flags);
             return 0;
         }
     }
 
     g_blockdevs[g_blockdev_count++] = *dev;
+    spin_unlock_irqrestore(&g_blockdev_lock, flags);
     return 0;
 }
 
-const block_device_t* blockdev_find(const char* name) {
+block_device_t* blockdev_find(const char* name) {
     if (!name) return NULL;
+
+    uintptr_t flags = spin_lock_irqsave(&g_blockdev_lock);
+    block_device_t* result = NULL;
     for (int i = 0; i < g_blockdev_count; i++) {
-        if (strcmp(g_blockdevs[i].name, name) == 0)
-            return &g_blockdevs[i];
+        if (strcmp(g_blockdevs[i].name, name) == 0) {
+            result = &g_blockdevs[i];
+            break;
+        }
     }
-    return NULL;
+    spin_unlock_irqrestore(&g_blockdev_lock, flags);
+    return result;
 }
 
-const block_device_t* blockdev_by_id(int drive_id) {
+block_device_t* blockdev_by_id(int drive_id) {
+    uintptr_t flags = spin_lock_irqsave(&g_blockdev_lock);
+    block_device_t* result = NULL;
     for (int i = 0; i < g_blockdev_count; i++) {
-        if (g_blockdevs[i].drive_id == drive_id)
-            return &g_blockdevs[i];
+        if (g_blockdevs[i].drive_id == drive_id) {
+            result = &g_blockdevs[i];
+            break;
+        }
     }
-    return NULL;
+    spin_unlock_irqrestore(&g_blockdev_lock, flags);
+    return result;
 }
 
-void blockdev_claim(const block_device_t* dev) {
+void blockdev_claim(block_device_t* dev) {
     if (!dev) return;
-    /* Cast away const since we need to modify refcount */
-    block_device_t* mutable_dev = (block_device_t*)dev;
-    mutable_dev->refcount++;
+    uintptr_t flags = spin_lock_irqsave(&g_blockdev_lock);
+    dev->refcount++;
+    spin_unlock_irqrestore(&g_blockdev_lock, flags);
 }
 
-void blockdev_release(const block_device_t* dev) {
+void blockdev_release(block_device_t* dev) {
     if (!dev) return;
-    /* Cast away const since we need to modify refcount */
-    block_device_t* mutable_dev = (block_device_t*)dev;
-    if (mutable_dev->refcount > 0)
-        mutable_dev->refcount--;
+    uintptr_t flags = spin_lock_irqsave(&g_blockdev_lock);
+    if (dev->refcount > 0)
+        dev->refcount--;
+    spin_unlock_irqrestore(&g_blockdev_lock, flags);
 }
 
 /* ---- ATA block device ops ---- */
 
-static int ata_bd_read(const block_device_t* dev, uint32_t lba, void* buf) {
+static int ata_bd_read(block_device_t* dev, uint32_t lba, void* buf) {
     return ata_pio_read28(dev->drive_id, lba, (uint8_t*)buf);
 }
 
-static int ata_bd_write(const block_device_t* dev, uint32_t lba, const void* buf) {
+static int ata_bd_write(block_device_t* dev, uint32_t lba, const void* buf) {
     return ata_pio_write28(dev->drive_id, lba, (const uint8_t*)buf);
 }
 
index 05b2a54a0dedac26c318ee59125eb40c58b4cee6..677ce5d54c11c74fdb709250bcf18e4aa1563253 100644 (file)
@@ -1427,7 +1427,7 @@ static int ext2_link_impl(struct fs_node* dir, const char* name, struct fs_node*
 
 /* ---- Mount ---- */
 
-vfs_mount_result_t ext2_mount(const block_device_t* bdev, uint32_t partition_lba) {
+vfs_mount_result_t ext2_mount(block_device_t* bdev, uint32_t partition_lba) {
     vfs_mount_result_t result = {NULL, NULL};
 
     if (!bdev) {
index ebf7afbad3ae5aa0e0c8a77668766e8529bb6ff0..8e5991c7f4419780b6ae758b125698eceae74ddd 100644 (file)
@@ -1133,7 +1133,7 @@ static int fat_truncate_impl(struct fs_node* node, uint32_t length) {
 
 /* ---- Mount ---- */
 
-vfs_mount_result_t fat_mount(const block_device_t* bdev, uint32_t partition_lba) {
+vfs_mount_result_t fat_mount(block_device_t* bdev, uint32_t partition_lba) {
     vfs_mount_result_t result = {NULL, NULL};
 
     if (!bdev) {
index 9268659ce7def16ab70fa39acc7f7936654c97d6..e75db37a5099edb16104c6f75872b0ac9386ad72 100644 (file)
@@ -34,7 +34,7 @@ struct vfs_mount {
     char source[64];     /* e.g. "/dev/hda", "none", "initrd" */
     unsigned long flags; /* MS_RDONLY, MS_NOSUID, etc. */
     int refcount;        /* number of open files on this mount */
-    const block_device_t* bdev; /* block device (NULL for virtual FS) */
+    block_device_t* bdev; /* block device (NULL for virtual FS) */
     vfs_superblock_t* sb; /* superblock (NULL for virtual FS) */
     fs_node_t* root;
 };
@@ -85,7 +85,7 @@ static void normalize_mountpoint(const char* in, char* out, size_t out_sz) {
 
 int vfs_mount_nolock_full(const char* mountpoint, fs_node_t* root,
                             const char* fstype, const char* source,
-                            unsigned long flags, const block_device_t* bdev,
+                            unsigned long flags, block_device_t* bdev,
                             vfs_superblock_t* sb) {
     char mp[128];
     normalize_mountpoint(mountpoint, mp, sizeof(mp));
@@ -95,28 +95,11 @@ int vfs_mount_nolock_full(const char* mountpoint, fs_node_t* root,
             /* Mount already exists at this mountpoint */
             if (flags & MS_REMOUNT) {
                 /* Remount: update flags only (bdev, sb, root, source, fstype preserved) */
-                g_mounts[i].flags = flags;
+                g_mounts[i].flags = flags & ~MS_REMOUNT;
                 return 0;
             } else {
-                /* New mount attempt on existing mountpoint - reject if active */
-                if (g_mounts[i].refcount > 0) {
-                    return -EBUSY;
-                }
-                /* Allow replacement if mount is idle (refcount == 0) */
-                /* This is the old behavior for backward compatibility */
-                if (root) g_mounts[i].root = root;
-                if (fstype) {
-                    strncpy(g_mounts[i].fstype, fstype, sizeof(g_mounts[i].fstype) - 1);
-                    g_mounts[i].fstype[sizeof(g_mounts[i].fstype) - 1] = '\0';
-                }
-                if (source) {
-                    strncpy(g_mounts[i].source, source, sizeof(g_mounts[i].source) - 1);
-                    g_mounts[i].source[sizeof(g_mounts[i].source) - 1] = '\0';
-                }
-                g_mounts[i].flags = flags;
-                g_mounts[i].bdev = bdev;
-                g_mounts[i].sb = sb;
-                return 0;
+                /* Reject new mount on existing mountpoint - use MS_REMOUNT to change flags */
+                return -EBUSY;
             }
         }
     }
@@ -153,7 +136,7 @@ int vfs_mount_nolock(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, const block_device_t* bdev,
+                    unsigned long flags, block_device_t* bdev,
                     vfs_superblock_t* sb) {
     uintptr_t fl = spin_lock_irqsave(&g_vfs_lock);
     int ret = vfs_mount_nolock_full(mountpoint, root, fstype, source, flags, bdev, sb);
@@ -702,6 +685,11 @@ int vfs_check_permission(fs_node_t* node, int want) {
 
 int vfs_link(const char* old_path, const char* new_path) {
     if (!old_path || !new_path) return -EINVAL;
+    
+    /* Check if new_path is on a read-only mount */
+    int rc = vfs_require_writable_path(new_path);
+    if (rc < 0) return rc;
+    
     fs_node_t* target = vfs_lookup(old_path);
     if (!target) return -ENOENT;
     if (target->flags != FS_FILE) return -EPERM;
index 9195de0161bdd5af9a66ac1136ca442ecd4b5216..57448bbf01a806115285d489642c65a1aac83834 100644 (file)
 
 /* ---- Mount helper: used by fstab parser and kconsole 'mount' command ---- */
 
-int init_mount_fs(const char* fstype, const block_device_t* bdev, uint32_t lba, const char* mountpoint, unsigned long flags) {
+int init_mount_fs(const char* fstype, block_device_t* bdev, uint32_t lba, const char* mountpoint, unsigned long flags) {
+    /* Validate mountpoint exists and is a directory */
+    fs_node_t* mp_node = vfs_lookup(mountpoint);
+    if (!mp_node) {
+        kprintf("[MOUNT] Mountpoint does not exist: %s\n", mountpoint);
+        return -ENOENT;
+    }
+    if (!(mp_node->flags & FS_DIRECTORY)) {
+        kprintf("[MOUNT] Mountpoint is not a directory: %s\n", mountpoint);
+        return -ENOTDIR;
+    }
+
     const vfs_fs_type_t* fst = vfs_fs_type_find(fstype);
     if (!fst) {
         kprintf("[MOUNT] Unknown filesystem type: %s\n", fstype);
@@ -86,6 +97,12 @@ int init_mount_fs(const char* fstype, const block_device_t* bdev, uint32_t lba,
         if (bdev) {
             blockdev_release(bdev);
         }
+        /* Cleanup filesystem state if mount registration failed */
+        if (mres.sb && mres.sb->fstype && mres.sb->fstype->kill_sb) {
+            mres.sb->fstype->kill_sb(mres.sb);
+        } else if (mres.root) {
+            kfree(mres.root);
+        }
         return rc;
     }
 
@@ -245,6 +262,7 @@ int init_start(const struct boot_info* bi) {
     ata_register_devfs();
 
     /* Register ATA drives as generic block devices (used by fat/ext2) */
+    blockdev_init_lock();
     blockdev_register_ata();
 
     /* If root= is specified on the kernel command line, mount that device
@@ -260,7 +278,7 @@ int init_start(const struct boot_info* bi) {
         const char* devname = root_dev;
         if (strncmp(root_dev, "/dev/", 5) == 0)
             devname = root_dev + 5;
-        const block_device_t* bdev = blockdev_find(devname);
+        block_device_t* bdev = blockdev_find(devname);
         if (bdev) {
             /* Auto-detect: try ext2, then fat (non-destructive probes). */
             static const char* fstypes[] = { "ext2", "fat", NULL };
@@ -280,7 +298,7 @@ int init_start(const struct boot_info* bi) {
         }
     } else {
         /* No root= on cmdline — try to auto-mount primary master if present */
-        const block_device_t* bdev = blockdev_find("hda");
+        block_device_t* bdev = blockdev_find("hda");
         if (bdev) {
             static const char* fstypes[] = { "ext2", "fat", NULL };
             for (int i = 0; fstypes[i]; i++) {
index 8058f73c43ad5655092f865c0abde2bd33d1db31..01af235707ecf17f8da16a5180f4a284a1893bf8 100644 (file)
@@ -350,12 +350,24 @@ static void kconsole_mount(const char* args) {
         return;
     }
 
+    /* Validate mountpoint exists and is a directory */
+    extern fs_node_t* vfs_lookup(const char* path);
+    fs_node_t* mp_node = vfs_lookup(mountpoint);
+    if (!mp_node) {
+        kprintf("mount: mountpoint does not exist: %s\n", mountpoint);
+        return;
+    }
+    if (!(mp_node->flags & FS_DIRECTORY)) {
+        kprintf("mount: mountpoint is not a directory: %s\n", mountpoint);
+        return;
+    }
+
     /* Resolve device to block device */
     const char* devname = device;
     if (strncmp(device, "/dev/", 5) == 0) {
         devname = device + 5;
     }
-    const block_device_t* bdev = blockdev_find(devname);
+    block_device_t* bdev = blockdev_find(devname);
     if (!bdev) {
         kprintf("mount: unknown device: %s\n", device);
         return;
index dd789aab23d78e17014ede9c2f75e3e6dbc34bb0..953f402ec3b7226b226c0fab2fd884323c9d5833 100644 (file)
@@ -4445,6 +4445,11 @@ static void posix_ext_syscall_dispatch(struct registers* regs, uint32_t syscall_
         if (!f || !f->node) { sc_ret(regs) = (uint32_t)-EBADF; return; }
         if ((f->flags & 3U) == 0U) { sc_ret(regs) = (uint32_t)-EBADF; return; }  /* O_RDONLY */
         if (!(f->node->flags & FS_FILE)) { sc_ret(regs) = (uint32_t)-EINVAL; return; }
+        /* Check if mount is read-only */
+        if (f->mount_root && (vfs_node_mount_flags(f->mount_root) & MS_RDONLY)) {
+            sc_ret(regs) = (uint32_t)-EROFS;
+            return;
+        }
         /* A14: use vfs_truncate_node to call backend truncate when available */
         int rc = vfs_truncate_node(f->node, length);
         sc_ret(regs) = (uint32_t)rc;
@@ -5174,7 +5179,7 @@ static void extended_syscall_dispatch(struct registers* regs, uint32_t syscall_n
 
         /* MS_REMOUNT: update flags on existing mount */
         if (mount_flags & MS_REMOUNT) {
-            sc_ret(regs) = (uint32_t)vfs_mount_full(kmp, NULL, NULL, NULL, mount_flags & ~MS_REMOUNT, NULL, NULL);
+            sc_ret(regs) = (uint32_t)vfs_mount_full(kmp, NULL, NULL, NULL, mount_flags, NULL, NULL);
             return;
         }
 
@@ -5208,8 +5213,8 @@ static void extended_syscall_dispatch(struct registers* regs, uint32_t syscall_n
         /* Disk-based: parse /dev/hdX -> block device */
         const char* devname = kdev;
         if (strncmp(devname, "/dev/", 5) == 0) devname += 5;
-        extern const block_device_t* blockdev_find(const char* name);
-        const block_device_t* bdev = blockdev_find(devname);
+        extern block_device_t* blockdev_find(const char* name);
+        block_device_t* bdev = blockdev_find(devname);
         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);