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;
/* 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;
#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
#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
+++ /dev/null
-// SPDX-License-Identifier: BSD-3-Clause
-/*
- * 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
#include <stdint.h>
#include <stddef.h>
#include "spinlock.h"
+#include "blockdev.h"
#define FS_FILE 0x01
#define FS_DIRECTORY 0x02
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.
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. */
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;
#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
uint64_t virtio_blk_capacity(void);
void virtio_blk_driver_register(void);
+void virtio_blk_register_blockdev(void);
#endif
#include "interrupts.h"
#include "hal/driver.h"
#include "io.h"
+#include "blockdev.h"
#include <stddef.h>
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;
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);
+ }
+}
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) {
/* ---- 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;
/* ---- 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) {
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;
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;
};
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));
}
/* Always update flags on remount (even if 0, caller explicitly set them) */
g_mounts[i].flags = flags;
+ g_mounts[i].bdev = bdev;
return 0;
}
}
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) {
}
}
+ /* 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--;
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;
}
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;
+}
/* ---- 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;
}
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);
}
}
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 */
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);
* 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();
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
* 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;
} 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;
+ }
}
}
}
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) {
#include "process.h"
#include "spinlock.h"
#include "uaccess.h"
+#include "blockdev.h"
#include "console.h"
#include "utils.h"
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;
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;
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;
}
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);
}
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);
}
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);
}
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);
}
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;
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;
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;
/* 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;
}
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) {
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) {
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;
}
{"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"}
{"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"}
};
enum {
+ O_RDONLY = 0x00,
O_WRONLY = 0x01,
O_RDWR = 0x02,
O_CREAT = 0x40,
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,
(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