]> Projects (at) Tadryanom (dot) Me - AdrOS.git/commitdiff
vfs: Fase 2 - block device abstraction and refcount
authorTulio A M Mendes <[email protected]>
Mon, 25 May 2026 16:24:15 +0000 (13:24 -0300)
committerTulio A M Mendes <[email protected]>
Wed, 3 Jun 2026 04:02:35 +0000 (01:02 -0300)
- Modified init_mount_fs to accept block_device_t* instead of int drive
- Updated sys_mount, init.c, kconsole.c to use blockdev_find instead of ata_name_to_drive
- Registered virtio-blk as block_device_t (/dev/vda) with blockdev ops wrappers
- Updated fat_mount and ext2_mount to accept block_device_t*
- Added refcount field to block_device_t
- Implemented blockdev_claim and blockdev_release
- Added bdev field to vfs_mount struct
- Integrated blockdev_claim/release in init_mount_fs and vfs_umount_nolock
- Updated vfs_mount_full and vfs_mount_nolock_full signatures to accept bdev
- Added blockdev.h include to fs.h

18 files changed:
include/blockdev.h
include/ext2.h
include/fat.h
include/fat16.h [deleted file]
include/fs.h
include/kernel/init.h
include/virtio_blk.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
tests/smoke_test.exp
tests/test_battery.exp
user/cmds/fulltest/fulltest.c

index 759ddfbae4781b5a7881b4f45eb57d09ecb398ea..f5fdd86e9f7abdc321df6d6a7a5f4312f1549f3a 100644 (file)
@@ -32,6 +32,7 @@ typedef struct block_device {
     uint32_t sector_size;       /* typically 512 */
     uint32_t sector_count;      /* total sectors (0 if unknown) */
     int drive_id;               /* opaque identifier passed to ops */
+    int refcount;               /* number of filesystems using this device */
     const struct block_device_ops* ops;
 } block_device_t;
 
@@ -44,6 +45,12 @@ const 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);
 
+/* Increment block device refcount (called when filesystem mounts). */
+void blockdev_claim(const block_device_t* dev);
+
+/* Decrement block device refcount (called when filesystem unmounts). */
+void blockdev_release(const 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) {
     if (!dev || !dev->ops || !dev->ops->read) return -ENODEV;
index 8cf7be436db574edba9098a4b59435d48ce864fc..4a3ce543658cb586bf8929d34b1edb920ff80642 100644 (file)
 #define EXT2_H
 
 #include "fs.h"
+#include "blockdev.h"
 #include <stdint.h>
 
 /* Mount an ext2 filesystem on the given ATA drive starting at the given
  * LBA offset.  Returns a VFS root node or NULL on failure. */
-fs_node_t* ext2_mount(int drive, uint32_t partition_lba);
+fs_node_t* ext2_mount(const block_device_t* bdev, uint32_t partition_lba);
 
 #endif
index 5190f4eaa85e4fb490db6a6b1eb5debef7dd9991..68ffa4492dfbc9cb4ecaf83ac9bb2353761faee7 100644 (file)
 #define FAT_H
 
 #include "fs.h"
+#include "blockdev.h"
 #include <stdint.h>
 
 /* Mount a FAT12/16/32 filesystem on the given ATA drive starting at
  * the given LBA offset.  Auto-detects FAT type from BPB.
  * Returns a VFS root node or NULL on failure. */
-fs_node_t* fat_mount(int drive, uint32_t partition_lba);
+fs_node_t* fat_mount(const block_device_t* bdev, uint32_t partition_lba);
 
 #endif
diff --git a/include/fat16.h b/include/fat16.h
deleted file mode 100644 (file)
index 9ecaf42..0000000
+++ /dev/null
@@ -1,21 +0,0 @@
-// SPDX-License-Identifier: BSD-3-Clause
-/*
- * Copyright (c) 2018, Tulio A M Mendes <[email protected]>
- * All rights reserved.
- * See LICENSE for details.
- *
- * Source: https://github.com/tadryanom/AdrOS
- */
-
-#ifndef FAT16_H
-#define FAT16_H
-
-/* Legacy header — redirects to unified FAT driver */
-#include "fat.h"
-
-/* Backward compatibility: fat16_mount() is now fat_mount() */
-static inline fs_node_t* fat16_mount(uint32_t partition_lba) {
-    return fat_mount(partition_lba);
-}
-
-#endif
index cb660fce94f1f0a878682e04b1015bad58c90f0b..766664a736dfae0380f2f49b7ac4a9fadb6bc7e2 100644 (file)
@@ -13,6 +13,7 @@
 #include <stdint.h>
 #include <stddef.h>
 #include "spinlock.h"
+#include "blockdev.h"
 
 #define FS_FILE        0x01
 #define FS_DIRECTORY   0x02
@@ -111,7 +112,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);
+                    unsigned long flags, const block_device_t* bdev);
 int vfs_umount(const char* mountpoint);
 
 /* _nolock variants — caller must already hold g_vfs_lock.
@@ -120,7 +121,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);
+                            unsigned long flags, const block_device_t* bdev);
 int vfs_umount_nolock(const char* mountpoint);
 
 /* Read mount table for /proc/mounts. Returns bytes written. */
@@ -139,6 +140,10 @@ fs_node_t* vfs_find_mount_root(const char* path);
 void vfs_mount_ref(fs_node_t* mount_root);
 void vfs_mount_unref(fs_node_t* mount_root);
 
+/* Check if the filesystem containing the given path is writable.
+ * Returns 0 if writable, -EROFS if MS_RDONLY is set. */
+int vfs_require_writable_path(const char* path);
+
 /* 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 54f008494a207baf5df81a20472f53bb5e64efa4..31416f8eb2a014d63a77d06c281a20ce1052a65f 100644 (file)
 #define KERNEL_INIT_H
 
 #include "kernel/boot_info.h"
+#include "blockdev.h"
 #include <stdint.h>
 
 int init_start(const struct boot_info* bi);
 
-/* Mount a filesystem on the given ATA drive at the given mountpoint.
+/* Mount a filesystem on the given block device at the given mountpoint.
  * fstype: "fat", "ext2"
- * drive: ATA_DEV_PRIMARY_MASTER .. ATA_DEV_SECONDARY_SLAVE
+ * bdev: block device (from blockdev_find or blockdev_by_id)
  * lba: partition start LBA (0 for whole disk)
  * 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, int drive, uint32_t lba, const char* mountpoint, unsigned long flags);
+int init_mount_fs(const char* fstype, const struct block_device* bdev, uint32_t lba, const char* mountpoint, unsigned long flags);
 
 #endif
index b73cc694c2172a5992d9bf69efd0568cfd6fb39c..f31747774ca3b57da9fcdb24c6cfb26a91728da9 100644 (file)
@@ -22,5 +22,6 @@ int  virtio_blk_write(uint64_t sector, const void* buf, uint32_t count);
 uint64_t virtio_blk_capacity(void);
 
 void virtio_blk_driver_register(void);
+void virtio_blk_register_blockdev(void);
 
 #endif
index ef49bf0ee10226c8486a7781355fbce7a86e5511..d93ead4262afef794ff3915d0d6fb8570ea29779 100644 (file)
@@ -28,6 +28,7 @@
 #include "interrupts.h"
 #include "hal/driver.h"
 #include "io.h"
+#include "blockdev.h"
 
 #include <stddef.h>
 
@@ -299,6 +300,22 @@ uint64_t virtio_blk_capacity(void) {
     return vblk_capacity_sectors;
 }
 
+/* ---- Block device operations ---- */
+static int vblk_bdev_read(const 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) {
+    (void)dev;
+    return virtio_blk_write((uint64_t)lba, buf, 1);
+}
+
+static const struct block_device_ops vblk_bdev_ops = {
+    .read  = vblk_bdev_read,
+    .write = vblk_bdev_write,
+};
+
 /* ---- HAL driver registration ---- */
 static int vblk_drv_probe(void) {
     return pci_find_device(VIRTIO_VENDOR_ID, VIRTIO_BLK_DEVICE_ID) ? 0 : -1;
@@ -318,3 +335,22 @@ static const struct hal_driver vblk_hal_driver = {
 void virtio_blk_driver_register(void) {
     hal_driver_register(&vblk_hal_driver);
 }
+
+/* Register virtio-blk as a block device after initialization */
+void virtio_blk_register_blockdev(void) {
+    if (!vblk_ready) return;
+
+    static block_device_t vblk_bdev;
+    vblk_bdev.sector_size = 512;
+    vblk_bdev.sector_count = (uint32_t)vblk_capacity_sectors;
+    vblk_bdev.drive_id = 100; /* Use a high ID to avoid conflict with ATA drives */
+    vblk_bdev.ops = &vblk_bdev_ops;
+    strcpy(vblk_bdev.name, "vda");
+
+    int rc = blockdev_register(&vblk_bdev);
+    if (rc < 0) {
+        kprintf("[VIRTIO-BLK] Failed to register as block device: %d\n", rc);
+    } else {
+        kprintf("[VIRTIO-BLK] Registered as block device: %s\n", vblk_bdev.name);
+    }
+}
index fa5153cfc9bee7c5b782378603fe08cb6025b502..bdc18709ac8a2d4b7ecbdc57bb41d53fea2191ac 100644 (file)
@@ -51,6 +51,21 @@ const block_device_t* blockdev_by_id(int drive_id) {
     return NULL;
 }
 
+void blockdev_claim(const 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++;
+}
+
+void blockdev_release(const 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--;
+}
+
 /* ---- ATA block device ops ---- */
 
 static int ata_bd_read(const block_device_t* dev, uint32_t lba, void* buf) {
index cbd7f431276c6836c6ccd62181a4ca99102e47e6..664458c39a499f7560c3fa0c5b6080f7de231e50 100644 (file)
@@ -1348,16 +1348,15 @@ static int ext2_link_impl(struct fs_node* dir, const char* name, struct fs_node*
 
 /* ---- Mount ---- */
 
-fs_node_t* ext2_mount(int drive, uint32_t partition_lba) {
-    const block_device_t* bdev = blockdev_by_id(drive);
+fs_node_t* ext2_mount(const block_device_t* bdev, uint32_t partition_lba) {
     if (!bdev) {
-        kprintf("[EXT2] No block device for drive %d\n", drive);
+        kprintf("[EXT2] No block device provided\n");
         return NULL;
     }
 
     memset(&g_ext2, 0, sizeof(g_ext2));
     g_ext2.bdev = bdev;
-    g_ext2.drive = drive;
+    g_ext2.drive = bdev->drive_id;
     g_ext2.part_lba = partition_lba;
 
     struct ext2_superblock sb;
index f03008b63433ea541db48d59a336f28cb7d4f85b..102a19db60641cac753a13e829554eddc4497310 100644 (file)
@@ -1118,17 +1118,15 @@ static int fat_truncate_impl(struct fs_node* node, uint32_t length) {
 
 /* ---- Mount ---- */
 
-fs_node_t* fat_mount(int drive, uint32_t partition_lba) {
-    /* Look up block device by drive ID */
-    const block_device_t* bdev = blockdev_by_id(drive);
+fs_node_t* fat_mount(const block_device_t* bdev, uint32_t partition_lba) {
     if (!bdev) {
-        kprintf("[FAT] No block device for drive %d\n", drive);
+        kprintf("[FAT] No block device provided\n");
         return NULL;
     }
 
     /* Store bdev early so fat_read_sector can use it */
     g_fat.bdev = bdev;
-    g_fat.drive = drive;
+    g_fat.drive = bdev->drive_id;
 
     uint8_t boot_sec[FAT_SECTOR_SIZE];
     if (fat_read_sector(partition_lba, boot_sec) < 0) {
@@ -1149,7 +1147,7 @@ fs_node_t* fat_mount(int drive, uint32_t partition_lba) {
 
     memset(&g_fat, 0, sizeof(g_fat));
     g_fat.bdev = bdev;
-    g_fat.drive = drive;
+    g_fat.drive = bdev->drive_id;
     g_fat.part_lba = partition_lba;
     g_fat.bytes_per_sector = bpb->bytes_per_sector;
     g_fat.sectors_per_cluster = bpb->sectors_per_cluster;
index e8cbac9d4e7a4ef3a38b1feeca50406a42441b35..938a5c4e6a562be55546a3eded828c0aad02bea4 100644 (file)
@@ -30,6 +30,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) */
     fs_node_t* root;
 };
 
@@ -74,7 +75,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) {
+                            unsigned long flags, const block_device_t* bdev) {
     char mp[128];
     normalize_mountpoint(mountpoint, mp, sizeof(mp));
 
@@ -92,6 +93,7 @@ int vfs_mount_nolock_full(const char* mountpoint, fs_node_t* root,
             }
             /* Always update flags on remount (even if 0, caller explicitly set them) */
             g_mounts[i].flags = flags;
+            g_mounts[i].bdev = bdev;
             return 0;
         }
     }
@@ -115,25 +117,26 @@ int vfs_mount_nolock_full(const char* mountpoint, fs_node_t* root,
         g_mounts[g_mount_count].source[0] = '\0';
     }
     g_mounts[g_mount_count].flags = flags;
+    g_mounts[g_mount_count].bdev = bdev;
     g_mount_count++;
     return 0;
 }
 
 int vfs_mount_nolock(const char* mountpoint, fs_node_t* root) {
-    return vfs_mount_nolock_full(mountpoint, root, NULL, NULL, 0);
+    return vfs_mount_nolock_full(mountpoint, root, NULL, NULL, 0, NULL);
 }
 
 int vfs_mount_full(const char* mountpoint, fs_node_t* root,
                     const char* fstype, const char* source,
-                    unsigned long flags) {
+                    unsigned long flags, const block_device_t* bdev) {
     uintptr_t fl = spin_lock_irqsave(&g_vfs_lock);
-    int ret = vfs_mount_nolock_full(mountpoint, root, fstype, source, flags);
+    int ret = vfs_mount_nolock_full(mountpoint, root, fstype, source, flags, bdev);
     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);
+    return vfs_mount_full(mountpoint, root, NULL, NULL, 0, NULL);
 }
 
 int vfs_umount_nolock(const char* mountpoint) {
@@ -168,6 +171,11 @@ int vfs_umount_nolock(const char* mountpoint) {
         }
     }
 
+    /* Release the block device */
+    if (g_mounts[idx].bdev) {
+        blockdev_release(g_mounts[idx].bdev);
+    }
+
     for (int j = idx; j < g_mount_count - 1; j++)
         g_mounts[j] = g_mounts[j + 1];
     g_mount_count--;
@@ -485,6 +493,8 @@ int vfs_rename(const char* old_path, const char* new_path) {
 
 int vfs_truncate(const char* path, uint32_t length) {
     if (!path) return -EINVAL;
+    int rc = vfs_require_writable_path(path);
+    if (rc < 0) return rc;
     fs_node_t* node = vfs_lookup(path);
     if (!node) return -ENOENT;
     if (node->flags != FS_FILE) return -EISDIR;
@@ -607,3 +617,12 @@ void vfs_mount_unref(fs_node_t* mount_root) {
     }
     spin_unlock_irqrestore(&g_vfs_lock, fl);
 }
+
+/* Check if the filesystem containing the given path is writable.
+ * Returns 0 if writable, -EROFS if MS_RDONLY is set. */
+int vfs_require_writable_path(const char* path) {
+    if (!path) return -EINVAL;
+    unsigned long mflags = vfs_mount_flags(path);
+    if (mflags & MS_RDONLY) return -EROFS;
+    return 0;
+}
index 77d9431d16438a070ca7a1e1b16104d2f7f74c9a..c79426a714cea2e8ff8eb73f3188aba66d6c4db9 100644 (file)
 
 /* ---- Mount helper: used by fstab parser and kconsole 'mount' command ---- */
 
-int init_mount_fs(const char* fstype, int drive, uint32_t lba, const char* mountpoint, unsigned long flags) {
+int init_mount_fs(const char* fstype, const block_device_t* bdev, uint32_t lba, const char* mountpoint, unsigned long flags) {
     fs_node_t* root = NULL;
 
     if (strcmp(fstype, "fat") == 0) {
-        root = fat_mount(drive, lba);
+        root = fat_mount(bdev, lba);
     } else if (strcmp(fstype, "ext2") == 0) {
-        root = ext2_mount(drive, lba);
+        root = ext2_mount(bdev, lba);
     } else {
         kprintf("[MOUNT] Unknown filesystem type: %s\n", fstype);
         return -EINVAL;
     }
 
     if (!root) {
-        kprintf("[MOUNT] Failed to mount %s on /dev/%s at %s\n",
-                fstype, ata_drive_to_name(drive) ? ata_drive_to_name(drive) : "?",
+        kprintf("[MOUNT] Failed to mount %s on %s at %s\n",
+                fstype, bdev ? bdev->name : "?",
                 mountpoint);
         return -ENODEV;
     }
 
+    /* Claim the block device */
+    if (bdev) {
+        blockdev_claim(bdev);
+    }
+
     /* Build device name for mount table metadata */
     char devname[32] = "none";
-    const char* dname = ata_drive_to_name(drive);
-    if (dname) {
+    if (bdev) {
         strcpy(devname, "/dev/");
-        /* Append drive name after /dev/ */
+        /* Append device name after /dev/ */
         char* dp = devname + 5;
+        const char* dname = bdev->name;
         while (*dname && (dp - devname) < (int)sizeof(devname) - 2)
             *dp++ = *dname++;
         *dp = '\0';
     }
 
-    int rc = vfs_mount_full(mountpoint, root, fstype, devname, flags);
+    int rc = vfs_mount_full(mountpoint, root, fstype, devname, flags, bdev);
     if (rc < 0) {
         kprintf("[MOUNT] Failed to register mount at %s (err=%d)\n", mountpoint, rc);
+        if (bdev) {
+            blockdev_release(bdev);
+        }
         return rc;
     }
 
     kprintf("[MOUNT] %s on /dev/%s -> %s\n",
-            fstype, ata_drive_to_name(drive) ? ata_drive_to_name(drive) : "?",
+            fstype, bdev ? bdev->name : "?",
             mountpoint);
     return 0;
 }
@@ -123,7 +131,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_full("/", ovl, "overlayfs", "initrd", 0);
+                (void)vfs_mount_full("/", ovl, "overlayfs", "initrd", 0, NULL);
                 vfs_set_initrd_root(ovl);
             }
         }
@@ -139,13 +147,13 @@ int init_start(const struct boot_info* bi) {
         if (rc < 0 && rc != -EEXIST) kprintf("[INIT] mkdir /dev failed: %d\n", rc);
         rc = vfs_mkdir("/proc");
         if (rc < 0 && rc != -EEXIST) kprintf("[INIT] mkdir /proc failed: %d\n", rc);
-        /* /disk and /persist directories are created by userspace init
+        /* /disk directory is created by userspace init
          * or by fstab-driven mount — no longer created by kernel */
     }
 
     fs_node_t* tmp = tmpfs_create_root();
     if (tmp) {
-        (void)vfs_mount_full("/tmp", tmp, "tmpfs", "none", 0);
+        (void)vfs_mount_full("/tmp", tmp, "tmpfs", "none", 0, NULL);
     }
 
     /* Register hardware drivers with HAL and init in priority order */
@@ -155,6 +163,10 @@ int init_start(const struct boot_info* bi) {
     virtio_blk_driver_register(); /* priority 25: virtio-blk */
     hal_drivers_init_all();
 
+    /* Register virtio-blk as a block device if initialized */
+    extern void virtio_blk_register_blockdev(void);
+    virtio_blk_register_blockdev();
+
     net_init();
     ksocket_init();
     vbe_init(bi);
@@ -168,7 +180,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_full("/dev", dev, "devfs", "none", 0);
+        (void)vfs_mount_full("/dev", dev, "devfs", "none", 0, NULL);
     }
 
     vbe_register_devfs();
@@ -176,7 +188,7 @@ int init_start(const struct boot_info* bi) {
 
     fs_node_t* proc = procfs_create_root();
     if (proc) {
-        (void)vfs_mount_full("/proc", proc, "procfs", "none", 0);
+        (void)vfs_mount_full("/proc", proc, "procfs", "none", 0, NULL);
     }
 
     /* Initialize ATA subsystem — probe all 4 drives
@@ -200,15 +212,16 @@ int init_start(const struct boot_info* bi) {
      * has disk access.  /etc/fstab parsing is now done by /sbin/init. */
     const char* root_dev = cmdline_get("root");
     if (root_dev) {
-        int drive = -1;
+        const char* devname = root_dev;
         if (strncmp(root_dev, "/dev/", 5) == 0)
-            drive = ata_name_to_drive(root_dev + 5);
-        if (drive >= 0 && ata_pio_drive_present(drive)) {
+            devname = root_dev + 5;
+        const 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 };
             int mounted = 0;
             for (int i = 0; fstypes[i]; i++) {
-                if (init_mount_fs(fstypes[i], drive, 0, "/disk", 0) == 0) {
+                if (init_mount_fs(fstypes[i], bdev, 0, "/disk", 0) == 0) {
                     kprintf("[INIT] root=%s mounted as %s on /disk\n",
                             root_dev, fstypes[i]);
                     mounted = 1;
@@ -220,13 +233,16 @@ int init_start(const struct boot_info* bi) {
         } else {
             kprintf("[INIT] root=%s: device not found\n", root_dev);
         }
-    } else if (ata_pio_drive_present(0)) {
-        /* No root= on cmdline, but primary master is present — auto-mount */
-        static const char* fstypes[] = { "ext2", "fat", NULL };
-        for (int i = 0; fstypes[i]; i++) {
-            if (init_mount_fs(fstypes[i], 0, 0, "/disk", 0) == 0) {
-                kprintf("[INIT] /dev/hda auto-mounted as %s on /disk\n", fstypes[i]);
-                break;
+    } else {
+        /* No root= on cmdline — try to auto-mount primary master if present */
+        const block_device_t* bdev = blockdev_find("hda");
+        if (bdev) {
+            static const char* fstypes[] = { "ext2", "fat", NULL };
+            for (int i = 0; fstypes[i]; i++) {
+                if (init_mount_fs(fstypes[i], bdev, 0, "/disk", 0) == 0) {
+                    kprintf("[INIT] /dev/hda auto-mounted as %s on /disk\n", fstypes[i]);
+                    break;
+                }
             }
         }
     }
index b291e52f5b11bfbbfb6e0ccbd10d85595f113a3a..8058f73c43ad5655092f865c0abde2bd33d1db31 100644 (file)
@@ -350,21 +350,18 @@ static void kconsole_mount(const char* args) {
         return;
     }
 
-    /* Resolve device to drive ID */
-    int drive = -1;
+    /* Resolve device to block device */
+    const char* devname = device;
     if (strncmp(device, "/dev/", 5) == 0) {
-        drive = ata_name_to_drive(device + 5);
+        devname = device + 5;
     }
-    if (drive < 0) {
+    const block_device_t* bdev = blockdev_find(devname);
+    if (!bdev) {
         kprintf("mount: unknown device: %s\n", device);
         return;
     }
-    if (!ata_pio_drive_present(drive)) {
-        kprintf("mount: device %s not present\n", device);
-        return;
-    }
 
-    (void)init_mount_fs(fstype, drive, 0, mountpoint, 0);
+    (void)init_mount_fs(fstype, bdev, 0, mountpoint, 0);
 }
 
 static void kconsole_exec(const char* cmd) {
index c48e193cbf32289f0ba6ff41ae3f31dae63ea82d..6555463a3cc3404d8e44ec2a4572cccbffc93d46 100644 (file)
@@ -13,6 +13,7 @@
 #include "process.h"
 #include "spinlock.h"
 #include "uaccess.h"
+#include "blockdev.h"
 
 #include "console.h"
 #include "utils.h"
@@ -2384,7 +2385,9 @@ static int syscall_open_impl(const char* user_path, uint32_t flags) {
     fs_node_t* node = vfs_lookup(path);
     if (!node && (flags & 0x40U) != 0U) {
         /* O_CREAT: create file through VFS */
-        int rc = vfs_create(path, flags, &node);
+        int rc = vfs_require_writable_path(path);
+        if (rc < 0) return rc;
+        rc = vfs_create(path, flags, &node);
         if (rc < 0) return rc;
     } else if (!node) {
         return -ENOENT;
@@ -2396,6 +2399,8 @@ static int syscall_open_impl(const char* user_path, uint32_t flags) {
         return -ENOTDIR;
     } else if ((flags & 0x200U) != 0U && node->flags == FS_FILE) {
         /* O_TRUNC on existing file */
+        int rc = vfs_require_writable_path(path);
+        if (rc < 0) return rc;
         if (node->i_ops && node->i_ops->truncate) {
             node->i_ops->truncate(node, 0);
             node->length = 0;
@@ -2419,13 +2424,14 @@ static int syscall_open_impl(const char* user_path, uint32_t flags) {
     f->offset = 0;
     f->flags = flags;
     f->refcount = 1;
-    vfs_mount_ref(f->mount_root);
 
     int fd = fd_alloc(f);
     if (fd < 0) {
         kfree(f);
         return -EMFILE;
     }
+    vfs_mount_ref(f->mount_root);
+
     if ((flags & O_CLOEXEC) && current_process) {
         current_process->fd_flags[fd] = FD_CLOEXEC;
     }
@@ -2699,6 +2705,9 @@ static int syscall_mkdir_impl(const char* user_path, uint32_t mode) {
     int prc = path_resolve_user(user_path, path, sizeof(path));
     if (prc < 0) return prc;
 
+    int rc = vfs_require_writable_path(path);
+    if (rc < 0) return rc;
+
     return vfs_mkdir(path);
 }
 
@@ -2735,6 +2744,9 @@ static int syscall_unlink_impl(const char* user_path) {
     int prc = path_resolve_user(user_path, path, sizeof(path));
     if (prc < 0) return prc;
 
+    int rc = vfs_require_writable_path(path);
+    if (rc < 0) return rc;
+
     return vfs_unlink(path);
 }
 
@@ -2751,6 +2763,9 @@ static int syscall_rmdir_impl(const char* user_path) {
     int prc = path_resolve_user(user_path, path, sizeof(path));
     if (prc < 0) return prc;
 
+    int rc = vfs_require_writable_path(path);
+    if (rc < 0) return rc;
+
     return vfs_rmdir(path);
 }
 
@@ -2764,6 +2779,11 @@ static int syscall_rename_impl(const char* user_old, const char* user_new) {
     rc = path_resolve_user(user_new, newp, sizeof(newp));
     if (rc < 0) return rc;
 
+    rc = vfs_require_writable_path(oldp);
+    if (rc < 0) return rc;
+    rc = vfs_require_writable_path(newp);
+    if (rc < 0) return rc;
+
     return vfs_rename(oldp, newp);
 }
 
@@ -3311,6 +3331,9 @@ static int syscall_chmod_impl(const char* user_path, uint32_t mode) {
     int prc = path_resolve_user(user_path, path, sizeof(path));
     if (prc < 0) return prc;
 
+    int rc = vfs_require_writable_path(path);
+    if (rc < 0) return rc;
+
     fs_node_t* node = vfs_lookup(path);
     if (!node) return -ENOENT;
 
@@ -3331,6 +3354,9 @@ static int syscall_chown_impl(const char* user_path, uint32_t uid, uint32_t gid)
     int prc = path_resolve_user(user_path, path, sizeof(path));
     if (prc < 0) return prc;
 
+    int rc = vfs_require_writable_path(path);
+    if (rc < 0) return rc;
+
     fs_node_t* node = vfs_lookup(path);
     if (!node) return -ENOENT;
 
@@ -5049,9 +5075,9 @@ static void extended_syscall_dispatch(struct registers* regs, uint32_t syscall_n
         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_full("/", new_root, NULL, NULL, 0);
+        (void)vfs_mount_nolock_full("/", new_root, NULL, NULL, 0, NULL);
         if (old_root) {
-            (void)vfs_mount_nolock_full(kput, old_root, NULL, NULL, 0);
+            (void)vfs_mount_nolock_full(kput, old_root, NULL, NULL, 0, NULL);
         }
         spin_unlock_irqrestore(&g_vfs_lock, vfs_fl);
         sc_ret(regs) = 0;
@@ -5078,7 +5104,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);
+            sc_ret(regs) = (uint32_t)vfs_mount_full(kmp, NULL, NULL, NULL, mount_flags & ~MS_REMOUNT, NULL);
             return;
         }
 
@@ -5087,7 +5113,7 @@ static void extended_syscall_dispatch(struct registers* regs, uint32_t syscall_n
             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);
+            sc_ret(regs) = (uint32_t)vfs_mount_full(kmp, tmp, "tmpfs", kdev, mount_flags, NULL);
             return;
         }
         if (strcmp(ktype, "devfs") == 0) {
@@ -5095,7 +5121,7 @@ static void extended_syscall_dispatch(struct registers* regs, uint32_t syscall_n
             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);
+            sc_ret(regs) = (uint32_t)vfs_mount_full(kmp, dev, "devfs", kdev, mount_flags, NULL);
             return;
         }
         if (strcmp(ktype, "procfs") == 0) {
@@ -5103,20 +5129,20 @@ static void extended_syscall_dispatch(struct registers* regs, uint32_t syscall_n
             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);
+            sc_ret(regs) = (uint32_t)vfs_mount_full(kmp, proc, "procfs", kdev, mount_flags, NULL);
             return;
         }
 
-        /* Disk-based: parse /dev/hdX -> drive number */
+        /* Disk-based: parse /dev/hdX -> block device */
         const char* devname = kdev;
         if (strncmp(devname, "/dev/", 5) == 0) devname += 5;
-        extern int ata_name_to_drive(const char* name);
-        int drive = ata_name_to_drive(devname);
-        if (drive < 0) { sc_ret(regs) = (uint32_t)-ENODEV; return; }
+        extern const block_device_t* blockdev_find(const char* name);
+        const block_device_t* bdev = blockdev_find(devname);
+        if (!bdev) { sc_ret(regs) = (uint32_t)-ENODEV; return; }
 
-        extern int init_mount_fs(const char* fstype, int drive, uint32_t lba, const char* mountpoint, unsigned long flags);
+        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, drive, 0, kmp, mount_flags);
+        int rc = init_mount_fs(ktype, bdev, 0, kmp, mount_flags);
         sc_ret(regs) = (uint32_t)(rc < 0 ? rc : 0);
         return;
     }
index 270fc6318650f6f0067d1de3dc52f70ad119ce1b..b0a71ce28bfab9807fe361f280b296f6eb6a77c7 100755 (executable)
@@ -153,6 +153,9 @@ set tests {
     {"named semaphore"     "\\[test\\] named semaphore OK"}
     {"getrusage"           "\\[test\\] getrusage OK"}
     {"mount/umount2"       "\\[test\\] mount/umount2 OK"}
+    {"MS_RDONLY enforcement" "\\[test\\] MS_RDONLY enforcement OK"}
+    {"MS_NODEV enforcement"  "\\[test\\] MS_NODEV enforcement OK"}
+    {"busy umount"          "\\[test\\] busy umount OK"}
     {"sigqueue"            "\\[test\\] sigqueue OK"}
     {"clone"               "\\[test\\] clone OK"}
     {"inotify_init1"       "\\[test\\] inotify_init1 OK"}
index 7eab463112199fff5e0b6fdad9771af3a9691af1..104a7ac1b8a9561df317dc55273769a49e913875 100644 (file)
@@ -261,6 +261,9 @@ set patterns {
     {"named semaphore"     "\\[test\\] named semaphore OK"}
     {"getrusage"           "\\[test\\] getrusage OK"}
     {"mount/umount2"       "\\[test\\] mount/umount2 OK"}
+    {"MS_RDONLY enforcement" "\\[test\\] MS_RDONLY enforcement OK"}
+    {"MS_NODEV enforcement"  "\\[test\\] MS_NODEV enforcement OK"}
+    {"busy umount"          "\\[test\\] busy umount OK"}
     {"sigqueue"            "\\[test\\] sigqueue OK"}
     {"clone"               "\\[test\\] clone OK"}
     {"inotify_init1"       "\\[test\\] inotify_init1 OK"}
index 4e777ab0ff64582d54bc3069ad8128c4510d005a..2190171f576f74f7e1b374d2d274de76e4421141 100644 (file)
@@ -267,6 +267,7 @@ enum {
 };
 
 enum {
+    O_RDONLY  = 0x00,
     O_WRONLY  = 0x01,
     O_RDWR    = 0x02,
     O_CREAT   = 0x40,
@@ -1628,6 +1629,15 @@ static int sys_execveat(int dirfd, const char* path, const char* const* argv, co
     return __syscall_fix(ret);
 }
 
+/* mount flags — must match kernel include/fs.h */
+enum {
+    MS_RDONLY  = 1,
+    MS_NOSUID  = 2,
+    MS_NODEV   = 4,
+    MS_NOEXEC  = 8,
+    MS_REMOUNT = 32,
+};
+
 /* clone flags — must match kernel include/process.h */
 enum {
     CLONE_VM            = 0x00000100,
@@ -5008,6 +5018,120 @@ void _start(void) {
                   (uint32_t)(sizeof("[test] mount/umount2 OK\n") - 1));
     }
 
+    // I13: MS_RDONLY enforcement — write operations should fail
+    {
+        (void)sys_mkdir("/tmp/mnt_ro");
+        if (sys_mount("none", "/tmp/mnt_ro", "tmpfs", MS_RDONLY) < 0) {
+            sys_write(1, "[test] mount ro failed\n",
+                      (uint32_t)(sizeof("[test] mount ro failed\n") - 1));
+            sys_exit(1);
+        }
+        /* O_CREAT should fail on read-only mount */
+        int fd = sys_openat(AT_FDCWD, "/tmp/mnt_ro/test.txt", O_CREAT | O_RDWR, 0644);
+        if (fd >= 0) {
+            sys_write(1, "[test] ro: O_CREAT should fail\n",
+                      (uint32_t)(sizeof("[test] ro: O_CREAT should fail\n") - 1));
+            sys_exit(1);
+        }
+        /* mkdir should fail */
+        if (sys_mkdir("/tmp/mnt_ro/subdir") >= 0) {
+            sys_write(1, "[test] ro: mkdir should fail\n",
+                      (uint32_t)(sizeof("[test] ro: mkdir should fail\n") - 1));
+            sys_exit(1);
+        }
+        /* unlink should fail */
+        if (sys_unlinkat(AT_FDCWD, "/tmp/mnt_ro/test.txt", 0) >= 0) {
+            sys_write(1, "[test] ro: unlink should fail\n",
+                      (uint32_t)(sizeof("[test] ro: unlink should fail\n") - 1));
+            sys_exit(1);
+        }
+        /* rmdir should fail */
+        if (sys_rmdir("/tmp/mnt_ro/subdir") >= 0) {
+            sys_write(1, "[test] ro: rmdir should fail\n",
+                      (uint32_t)(sizeof("[test] ro: rmdir should fail\n") - 1));
+            sys_exit(1);
+        }
+        /* rename should fail */
+        if (sys_rename("/tmp/mnt_ro/a", "/tmp/mnt_ro/b") >= 0) {
+            sys_write(1, "[test] ro: rename should fail\n",
+                      (uint32_t)(sizeof("[test] ro: rename should fail\n") - 1));
+            sys_exit(1);
+        }
+        /* chmod should fail */
+        if (sys_chmod("/tmp/mnt_ro/test.txt", 0755) >= 0) {
+            sys_write(1, "[test] ro: chmod should fail\n",
+                      (uint32_t)(sizeof("[test] ro: chmod should fail\n") - 1));
+            sys_exit(1);
+        }
+        /* chown should fail */
+        if (sys_chown("/tmp/mnt_ro/test.txt", 0, 0) >= 0) {
+            sys_write(1, "[test] ro: chown should fail\n",
+                      (uint32_t)(sizeof("[test] ro: chown should fail\n") - 1));
+            sys_exit(1);
+        }
+        if (sys_umount2("/tmp/mnt_ro") < 0) {
+            sys_write(1, "[test] umount ro failed\n",
+                      (uint32_t)(sizeof("[test] umount ro failed\n") - 1));
+            sys_exit(1);
+        }
+        sys_write(1, "[test] MS_RDONLY enforcement OK\n",
+                  (uint32_t)(sizeof("[test] MS_RDONLY enforcement OK\n") - 1));
+    }
+
+    // I14: MS_NODEV enforcement — device nodes should be inaccessible
+    {
+        (void)sys_mkdir("/tmp/mnt_nodev");
+        if (sys_mount("none", "/tmp/mnt_nodev", "devfs", MS_NODEV) < 0) {
+            sys_write(1, "[test] mount nodev failed\n",
+                      (uint32_t)(sizeof("[test] mount nodev failed\n") - 1));
+            sys_exit(1);
+        }
+        /* Opening a device node on nodev mount should fail */
+        int fd = sys_openat(AT_FDCWD, "/tmp/mnt_nodev/tty0", O_RDONLY, 0);
+        if (fd >= 0) {
+            sys_write(1, "[test] nodev: device open should fail\n",
+                      (uint32_t)(sizeof("[test] nodev: device open should fail\n") - 1));
+            sys_exit(1);
+        }
+        if (sys_umount2("/tmp/mnt_nodev") < 0) {
+            sys_write(1, "[test] umount nodev failed\n",
+                      (uint32_t)(sizeof("[test] umount nodev failed\n") - 1));
+            sys_exit(1);
+        }
+        sys_write(1, "[test] MS_NODEV enforcement OK\n",
+                  (uint32_t)(sizeof("[test] MS_NODEV enforcement OK\n") - 1));
+    }
+
+    // I15: Busy umount — umount should fail with open file
+    {
+        (void)sys_mkdir("/tmp/mnt_busy");
+        if (sys_mount("none", "/tmp/mnt_busy", "tmpfs", 0) < 0) {
+            sys_write(1, "[test] mount busy failed\n",
+                      (uint32_t)(sizeof("[test] mount busy failed\n") - 1));
+            sys_exit(1);
+        }
+        int fd = sys_openat(AT_FDCWD, "/tmp/mnt_busy/test.txt", O_CREAT | O_RDWR, 0644);
+        if (fd < 0) {
+            sys_write(1, "[test] busy: file create failed\n",
+                      (uint32_t)(sizeof("[test] busy: file create failed\n") - 1));
+            sys_exit(1);
+        }
+        /* umount should fail because file is open */
+        if (sys_umount2("/tmp/mnt_busy") >= 0) {
+            sys_write(1, "[test] busy: umount should fail with open file\n",
+                      (uint32_t)(sizeof("[test] busy: umount should fail with open file\n") - 1));
+            sys_exit(1);
+        }
+        (void)sys_close(fd);
+        if (sys_umount2("/tmp/mnt_busy") < 0) {
+            sys_write(1, "[test] umount busy failed\n",
+                      (uint32_t)(sizeof("[test] umount busy failed\n") - 1));
+            sys_exit(1);
+        }
+        sys_write(1, "[test] busy umount OK\n",
+                  (uint32_t)(sizeof("[test] busy umount OK\n") - 1));
+    }
+
     // I13: clone — create a thread sharing address space
     {
         /* We use a simple clone with CLONE_VM|CLONE_FS|CLONE_FILES|CLONE_SIGHAND