From 62c8f59bc18272d5eb7106813f95dcaeeb4b53f6 Mon Sep 17 00:00:00 2001 From: Tulio A M Mendes Date: Mon, 25 May 2026 13:24:15 -0300 Subject: [PATCH] vfs: Fase 2 - block device abstraction and refcount - 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 --- include/blockdev.h | 7 ++ include/ext2.h | 3 +- include/fat.h | 3 +- include/fat16.h | 21 ------ include/fs.h | 9 ++- include/kernel/init.h | 7 +- include/virtio_blk.h | 1 + src/drivers/virtio_blk.c | 36 ++++++++++ src/kernel/blockdev.c | 15 ++++ src/kernel/ext2.c | 7 +- src/kernel/fat.c | 10 ++- src/kernel/fs.c | 29 ++++++-- src/kernel/init.c | 68 ++++++++++++------- src/kernel/kconsole.c | 15 ++-- src/kernel/syscall.c | 54 +++++++++++---- tests/smoke_test.exp | 3 + tests/test_battery.exp | 3 + user/cmds/fulltest/fulltest.c | 124 ++++++++++++++++++++++++++++++++++ 18 files changed, 323 insertions(+), 92 deletions(-) delete mode 100644 include/fat16.h diff --git a/include/blockdev.h b/include/blockdev.h index 759ddfba..f5fdd86e 100644 --- a/include/blockdev.h +++ b/include/blockdev.h @@ -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; diff --git a/include/ext2.h b/include/ext2.h index 8cf7be43..4a3ce543 100644 --- a/include/ext2.h +++ b/include/ext2.h @@ -11,10 +11,11 @@ #define EXT2_H #include "fs.h" +#include "blockdev.h" #include /* 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 diff --git a/include/fat.h b/include/fat.h index 5190f4ea..68ffa449 100644 --- a/include/fat.h +++ b/include/fat.h @@ -11,11 +11,12 @@ #define FAT_H #include "fs.h" +#include "blockdev.h" #include /* 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 index 9ecaf42b..00000000 --- a/include/fat16.h +++ /dev/null @@ -1,21 +0,0 @@ -// SPDX-License-Identifier: BSD-3-Clause -/* - * Copyright (c) 2018, Tulio A M Mendes - * 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 diff --git a/include/fs.h b/include/fs.h index cb660fce..766664a7 100644 --- a/include/fs.h +++ b/include/fs.h @@ -13,6 +13,7 @@ #include #include #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; diff --git a/include/kernel/init.h b/include/kernel/init.h index 54f00849..31416f8e 100644 --- a/include/kernel/init.h +++ b/include/kernel/init.h @@ -11,17 +11,18 @@ #define KERNEL_INIT_H #include "kernel/boot_info.h" +#include "blockdev.h" #include 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 diff --git a/include/virtio_blk.h b/include/virtio_blk.h index b73cc694..f3174777 100644 --- a/include/virtio_blk.h +++ b/include/virtio_blk.h @@ -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 diff --git a/src/drivers/virtio_blk.c b/src/drivers/virtio_blk.c index ef49bf0e..d93ead42 100644 --- a/src/drivers/virtio_blk.c +++ b/src/drivers/virtio_blk.c @@ -28,6 +28,7 @@ #include "interrupts.h" #include "hal/driver.h" #include "io.h" +#include "blockdev.h" #include @@ -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); + } +} diff --git a/src/kernel/blockdev.c b/src/kernel/blockdev.c index fa5153cf..bdc18709 100644 --- a/src/kernel/blockdev.c +++ b/src/kernel/blockdev.c @@ -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) { diff --git a/src/kernel/ext2.c b/src/kernel/ext2.c index cbd7f431..664458c3 100644 --- a/src/kernel/ext2.c +++ b/src/kernel/ext2.c @@ -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; diff --git a/src/kernel/fat.c b/src/kernel/fat.c index f03008b6..102a19db 100644 --- a/src/kernel/fat.c +++ b/src/kernel/fat.c @@ -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; diff --git a/src/kernel/fs.c b/src/kernel/fs.c index e8cbac9d..938a5c4e 100644 --- a/src/kernel/fs.c +++ b/src/kernel/fs.c @@ -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; +} diff --git a/src/kernel/init.c b/src/kernel/init.c index 77d9431d..c79426a7 100644 --- a/src/kernel/init.c +++ b/src/kernel/init.c @@ -44,45 +44,53 @@ /* ---- 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; + } } } } diff --git a/src/kernel/kconsole.c b/src/kernel/kconsole.c index b291e52f..8058f73c 100644 --- a/src/kernel/kconsole.c +++ b/src/kernel/kconsole.c @@ -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) { diff --git a/src/kernel/syscall.c b/src/kernel/syscall.c index c48e193c..6555463a 100644 --- a/src/kernel/syscall.c +++ b/src/kernel/syscall.c @@ -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; } diff --git a/tests/smoke_test.exp b/tests/smoke_test.exp index 270fc631..b0a71ce2 100755 --- a/tests/smoke_test.exp +++ b/tests/smoke_test.exp @@ -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"} diff --git a/tests/test_battery.exp b/tests/test_battery.exp index 7eab4631..104a7ac1 100644 --- a/tests/test_battery.exp +++ b/tests/test_battery.exp @@ -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"} diff --git a/user/cmds/fulltest/fulltest.c b/user/cmds/fulltest/fulltest.c index 4e777ab0..2190171f 100644 --- a/user/cmds/fulltest/fulltest.c +++ b/user/cmds/fulltest/fulltest.c @@ -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 -- 2.43.0