]> Projects (at) Tadryanom (dot) Me - AdrOS.git/commitdiff
mount/VFS: remove all auto-format paths; add mkfs; fix persistfs and flags
authorTulio A M Mendes <[email protected]>
Wed, 20 May 2026 17:06:48 +0000 (14:06 -0300)
committerTulio A M Mendes <[email protected]>
Wed, 3 Jun 2026 04:02:35 +0000 (01:02 -0300)
Patch A (CRITICAL): diskfs_create_root() no longer auto-formats on
missing superblock.  Any error from diskfs_super_load() (ENODEV,
EINVAL, EIO) now returns NULL immediately.  g_ready is only set when
the superblock is valid.  This eliminates all destructive mount paths.

Patch B: Added 'mkfs diskfs /dev/hdX' kconsole command for explicit
diskfs formatting.  Added diskfs_mkfs() public API that wraps
diskfs_format().  This is the ONLY way to create a new diskfs
filesystem.  Battery/smoke tests now pre-format disk images with a
minimal diskfs superblock (magic+version+next_free_lba+root_inode)
at LBA 2 before QEMU launch.

Patch C (CRITICAL): persistfs_create_root() now calls diskfs_probe()
before diskfs_create_root().  If the drive does not contain a valid
diskfs superblock, the diskfs initialization is skipped entirely.
This prevents persistfs from triggering diskfs_format() on a FAT/ext2
disk via the indirect path: boot detects ext2 -> mounts /disk as ext2
-> init_mount_fs(persistfs) -> persistfs_create_root() -> [was]
diskfs_create_root() -> diskfs_format() -> corrupts ext2 superblock.

Patch D: init_mount_fs() now accepts unsigned long flags parameter
and passes it to vfs_mount_full().  sys_mount passes mount_flags
through to init_mount_fs.  Boot-internal callers pass flags=0.
This allows mount -o ro,nodev,noexec to work for disk-based FS.

Patch E: mount CLI improvements — unknown -o options now cause error
exit (not just warning).  Added nosuid, nodev, noexec option parsing.
show_mounts() now returns error code when /proc/mounts is unavailable.

Patch F: Updated all test disk images to use create_diskfs_disk
helper that stamps a minimal diskfs superblock.  Updated Makefile
run/test-gdb targets similarly.  Updated fstab comment.

12 files changed:
Makefile
include/diskfs.h
include/kernel/init.h
rootfs/etc/fstab
src/kernel/diskfs.c
src/kernel/init.c
src/kernel/kconsole.c
src/kernel/persistfs.c
src/kernel/syscall.c
tests/smoke_test.exp
tests/test_battery.exp
user/cmds/mount/mount.c

index 9198ca62bbc1df431431eac34879a75b7bfee774..4739f9331fd23ebced307d5057764d9ab76a5929 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -288,7 +288,7 @@ $(INITRD_IMG): $(INITRD_DEPS)
 
 run: iso
        @rm -f serial.log qemu.log
-       @test -f disk.img || dd if=/dev/zero of=disk.img bs=1M count=4 2>/dev/null
+       @test -f disk.img || { dd if=/dev/zero of=disk.img bs=1M count=4 2>/dev/null && printf '\x31\x53\x46\x44\x03\x00\x00\x00\x04\x00\x00\x00' | dd of=disk.img bs=1 seek=1024 conv=notrunc 2>/dev/null; }
        @qemu-system-i386 -boot d -cdrom adros-$(ARCH).iso -m 128M -display none \
                -drive file=disk.img,if=ide,format=raw \
                -nic user,model=e1000 \
@@ -384,7 +384,7 @@ test-host:
 test-gdb: $(KERNEL_NAME) iso
        @echo "[TEST-GDB] Starting QEMU with GDB stub..."
        @rm -f serial.log
-       @test -f disk.img || dd if=/dev/zero of=disk.img bs=1M count=4 2>/dev/null
+       @test -f disk.img || { dd if=/dev/zero of=disk.img bs=1M count=4 2>/dev/null && printf '\x31\x53\x46\x44\x03\x00\x00\x00\x04\x00\x00\x00' | dd of=disk.img bs=1 seek=1024 conv=notrunc 2>/dev/null; }
        @qemu-system-i386 -smp 4 -boot d -cdrom adros-$(ARCH).iso -m 128M -display none \
                -drive file=disk.img,if=ide,format=raw \
                -serial file:serial.log -monitor none -no-reboot -no-shutdown \
index fadc51057b34e8a9f737464b3a3399d7b531ee46..5b4bec6e7c57ab0755742a3f386f18e60c738097 100644 (file)
@@ -19,6 +19,10 @@ fs_node_t* diskfs_create_root(int drive);
 // -ENODEV if not diskfs, or other negative errno on I/O error.
 int diskfs_probe(int drive);
 
+// Explicit formatting: write a fresh diskfs superblock to the drive.
+// Returns 0 on success, negative errno on error.
+int diskfs_mkfs(int drive);
+
 // Open (and optionally create) a diskfs file at the root (flat namespace).
 // rel_path must not contain '/'.
 // flags: supports O_CREAT (0x40) and O_TRUNC (0x200) semantics (minimal).
index 4602bfd0b4690cf6223c3442d41d88ec859aa1ed..f232837896b19af42359a429f558d3a10218e2f4 100644 (file)
@@ -20,7 +20,8 @@ int init_start(const struct boot_info* bi);
  * drive: ATA_DEV_PRIMARY_MASTER .. ATA_DEV_SECONDARY_SLAVE
  * lba: partition start LBA (0 for whole disk)
  * mountpoint: e.g. "/disk", "/fat", "/ext2"
- * Returns 0 on success, -1 on failure. */
-int init_mount_fs(const char* fstype, int drive, uint32_t lba, const char* mountpoint);
+ * 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);
 
 #endif
index c7cd6587ce19215dba84c2ad1dba73396888a961..6ed54a3bccc9d7667fea11175039b4f53051fdb1 100644 (file)
@@ -1,4 +1,5 @@
 # /etc/fstab — AdrOS filesystem table
 # <device>      <mountpoint>  <fstype>     <options>
+# Note: diskfs requires a pre-formatted disk (use 'mkfs diskfs /dev/hdX').
 /dev/hda        /disk         diskfs       defaults
 /dev/hda        /persist      persistfs    defaults
index c22f8e649ffd236c345614f41c8e279eaddd1608..76ed0c3463a6b8d41aabb670d0bcc629e660231a 100644 (file)
@@ -1168,11 +1168,8 @@ static int diskfs_vfs_link(struct fs_node* dir, const char* name, struct fs_node
 fs_node_t* diskfs_create_root(int drive) {
     if (!g_ready) {
         g_diskfs_drive = drive;
-        if (ata_pio_drive_present(drive)) {
-            g_ready = 1;
-        } else {
-            g_ready = 0;
-        }
+        if (!ata_pio_drive_present(drive))
+            return NULL;
 
         memset(&g_root, 0, sizeof(g_root));
         strcpy(g_root.vfs.name, "disk");
@@ -1183,22 +1180,18 @@ fs_node_t* diskfs_create_root(int drive) {
         g_root.vfs.i_ops = &diskfs_dir_iops;
         g_root.ino = 0;
 
-        if (g_ready) {
-            struct diskfs_super sb;
-            int rc = diskfs_super_load(&sb);
-            if (rc == -ENODEV) {
-                /* No diskfs superblock found — format a fresh one.
-                 * This preserves the auto-format behavior for boot-time
-                 * mounting of blank disks (e.g. battery test root_hda.img).
-                 * Autodetect probing will check diskfs_probe() first and
-                 * skip diskfs if it returns -ENODEV, so this path is only
-                 * reached when the caller explicitly requests diskfs. */
-                (void)diskfs_format(&sb);
-            }
+        struct diskfs_super sb;
+        int rc = diskfs_super_load(&sb);
+        if (rc < 0) {
+            /* No valid diskfs superblock — do NOT auto-format.
+             * Use 'mkfs diskfs /dev/hdX' from kconsole to format. */
+            return NULL;
         }
+
+        g_ready = 1;
     }
 
-    return g_ready ? &g_root.vfs : NULL;
+    return &g_root.vfs;
 }
 
 int diskfs_probe(int drive) {
@@ -1214,3 +1207,23 @@ int diskfs_probe(int drive) {
     if (magic != DISKFS_MAGIC) return -ENODEV;
     return 0;
 }
+
+int diskfs_mkfs(int drive) {
+    /* Explicit formatting: write a fresh diskfs superblock to the drive.
+     * This is the ONLY way to create a new diskfs filesystem.
+     * Returns 0 on success, negative errno on error. */
+    if (!ata_pio_drive_present(drive)) return -ENODEV;
+
+    /* If diskfs is already initialized on this drive, reject */
+    if (g_ready && g_diskfs_drive == drive) return -EBUSY;
+
+    /* Temporarily set the drive so diskfs_format can write */
+    int saved_drive = g_diskfs_drive;
+    g_diskfs_drive = drive;
+
+    struct diskfs_super sb;
+    int rc = diskfs_format(&sb);
+
+    g_diskfs_drive = saved_drive;
+    return rc;
+}
index 3faf40bddbfb6287400ba208f8292288702bd500..8774aa014522aa5c442d1e1b4762a841191f7517 100644 (file)
@@ -44,7 +44,7 @@
 
 /* ---- 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) {
+int init_mount_fs(const char* fstype, int drive, uint32_t lba, const char* mountpoint, unsigned long flags) {
     fs_node_t* root = NULL;
 
     if (strcmp(fstype, "diskfs") == 0) {
@@ -79,7 +79,7 @@ int init_mount_fs(const char* fstype, int drive, uint32_t lba, const char* mount
         *dp = '\0';
     }
 
-    int rc = vfs_mount_full(mountpoint, root, fstype, devname, 0);
+    int rc = vfs_mount_full(mountpoint, root, fstype, devname, flags);
     if (rc < 0) {
         kprintf("[MOUNT] Failed to register mount at %s (err=%d)\n", mountpoint, rc);
         return rc;
@@ -209,8 +209,8 @@ int init_start(const struct boot_info* bi) {
         if (drive >= 0 && ata_pio_drive_present(drive)) {
             /* Auto-detect: try ext2, fat first (non-destructive), then diskfs.
              * diskfs_probe() is non-destructive — it only checks the magic
-             * without formatting.  diskfs_create_root() will auto-format
-             * blank disks, so it is only called when probe confirms diskfs. */
+             * without formatting.  diskfs_create_root() no longer auto-formats;
+             * use 'mkfs diskfs /dev/hdX' from kconsole to create a new fs. */
             static const char* fstypes[] = { "ext2", "fat", "diskfs", NULL };
             int mounted = 0;
             for (int i = 0; fstypes[i]; i++) {
@@ -219,7 +219,7 @@ int init_start(const struct boot_info* bi) {
                     extern int diskfs_probe(int drive);
                     if (diskfs_probe(drive) != 0) continue;
                 }
-                if (init_mount_fs(fstypes[i], drive, 0, "/disk") == 0) {
+                if (init_mount_fs(fstypes[i], drive, 0, "/disk", 0) == 0) {
                     kprintf("[INIT] root=%s mounted as %s on /disk\n",
                             root_dev, fstypes[i]);
                     mounted = 1;
@@ -239,13 +239,13 @@ int init_start(const struct boot_info* bi) {
                 extern int diskfs_probe(int drive);
                 if (diskfs_probe(0) != 0) continue;
             }
-            if (init_mount_fs(fstypes[i], 0, 0, "/disk") == 0) {
+            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;
             }
         }
         /* Also mount persistfs on /persist (was previously in /etc/fstab) */
-        if (init_mount_fs("persistfs", 0, 0, "/persist") == 0) {
+        if (init_mount_fs("persistfs", 0, 0, "/persist", 0) == 0) {
             kprintf("[INIT] /dev/hda auto-mounted as persistfs on /persist\n");
         }
     }
index 5ea18bdb671c348f37a6e9504e964f25bcc0aa34..1769eb988b4cbfbbd6be9b95c5c8090d573023a9 100644 (file)
@@ -21,6 +21,7 @@
 #include "hal/cpu.h"
 #include "kernel/init.h"
 #include "ata_pio.h"
+#include "errno.h"
 
 #define KCMD_MAX 128
 
@@ -258,6 +259,7 @@ static void kconsole_help(void) {
     kc_puts("  dmesg                             - Show kernel log buffer\n");
     kc_puts("  lsblk                             - List detected ATA drives\n");
     kc_puts("  mount -t <type> /dev/<hd> <mnt>   - Mount filesystem\n");
+    kc_puts("  mkfs diskfs /dev/<hd>              - Format diskfs filesystem\n");
     kc_puts("  reboot                            - Restart system\n");
     kc_puts("  halt                              - Halt the CPU\n");
 }
@@ -363,7 +365,61 @@ static void kconsole_mount(const char* args) {
         return;
     }
 
-    (void)init_mount_fs(fstype, drive, 0, mountpoint);
+    (void)init_mount_fs(fstype, drive, 0, mountpoint, 0);
+}
+
+/* mkfs diskfs /dev/<hdX> */
+static void kconsole_mkfs(const char* args) {
+    const char* p = args;
+    while (*p == ' ') p++;
+
+    /* Extract fstype */
+    const char* fs_start = p;
+    while (*p && *p != ' ') p++;
+    char fstype[16];
+    size_t fs_len = (size_t)(p - fs_start);
+    if (fs_len >= sizeof(fstype)) fs_len = sizeof(fstype) - 1;
+    memcpy(fstype, fs_start, fs_len);
+    fstype[fs_len] = '\0';
+
+    if (strcmp(fstype, "diskfs") != 0) {
+        kprintf("mkfs: unsupported filesystem type: %s (only 'diskfs')\n", fstype);
+        return;
+    }
+
+    while (*p == ' ') p++;
+
+    /* Extract device */
+    const char* dev_start = p;
+    while (*p && *p != ' ') p++;
+    char device[32];
+    size_t dev_len = (size_t)(p - dev_start);
+    if (dev_len >= sizeof(device)) dev_len = sizeof(device) - 1;
+    memcpy(device, dev_start, dev_len);
+    device[dev_len] = '\0';
+
+    int drive = -1;
+    if (strncmp(device, "/dev/", 5) == 0) {
+        drive = ata_name_to_drive(device + 5);
+    }
+    if (drive < 0) {
+        kprintf("mkfs: unknown device: %s\n", device);
+        return;
+    }
+    if (!ata_pio_drive_present(drive)) {
+        kprintf("mkfs: device %s not present\n", device);
+        return;
+    }
+
+    extern int diskfs_mkfs(int drive);
+    int rc = diskfs_mkfs(drive);
+    if (rc == 0) {
+        kprintf("mkfs: diskfs filesystem created on %s\n", device);
+    } else if (rc == -EBUSY) {
+        kprintf("mkfs: %s already has an active diskfs mount\n", device);
+    } else {
+        kprintf("mkfs: failed to format %s (err=%d)\n", device, rc);
+    }
 }
 
 static void kconsole_exec(const char* cmd) {
@@ -438,6 +494,9 @@ static void kconsole_exec(const char* cmd) {
     else if (strncmp(cmd, "mount ", 6) == 0) {
         kconsole_mount(cmd + 6);
     }
+    else if (strncmp(cmd, "mkfs ", 5) == 0) {
+        kconsole_mkfs(cmd + 5);
+    }
     else if (cmd[0] != '\0') {
         kprintf("unknown command: %s\n", cmd);
     }
index efeab7aafde088f3dc54d80c084a8e3b5712fa0d..20198f522128598c677fe32c755e705b5725e0e2 100644 (file)
@@ -10,6 +10,7 @@
 #include "persistfs.h"
 
 #include "ata_pio.h"
+#include "console.h"
 #include "diskfs.h"
 #include "errno.h"
 #include "heap.h"
@@ -100,21 +101,31 @@ fs_node_t* persistfs_create_root(int drive) {
         }
 
         if (g_ready) {
-            // Ensure diskfs is initialized even if /disk mount happens later.
-            (void)diskfs_create_root(drive);
-
-            // One-time migration from legacy LBA1 counter storage.
-            uint8_t sec[512];
-            if (ata_pio_read28(drive, PERSISTFS_LBA_COUNTER, sec) == 0) {
-                fs_node_t* b = persistfs_backing_open(PERSIST_O_CREAT);
-                if (b) {
-                    uint8_t cur4[4];
-                    uint32_t rd = vfs_read(b, 0, 4, cur4);
-                    if (rd == 0) {
-                        (void)vfs_write(b, 0, 4, sec);
+            /* Only initialize diskfs backing if the drive actually contains
+             * a diskfs filesystem.  Without this check, diskfs_create_root()
+             * would auto-format a non-diskfs disk (FAT/ext2), corrupting
+             * data.  Now that diskfs_create_root() no longer auto-formats,
+             * the call would simply fail — but we skip it entirely when the
+             * probe fails to avoid unnecessary I/O and confusing log msgs. */
+            extern int diskfs_probe(int drive);
+            if (diskfs_probe(drive) == 0) {
+                (void)diskfs_create_root(drive);
+
+                // One-time migration from legacy LBA1 counter storage.
+                uint8_t sec[512];
+                if (ata_pio_read28(drive, PERSISTFS_LBA_COUNTER, sec) == 0) {
+                    fs_node_t* b = persistfs_backing_open(PERSIST_O_CREAT);
+                    if (b) {
+                        uint8_t cur4[4];
+                        uint32_t rd = vfs_read(b, 0, 4, cur4);
+                        if (rd == 0) {
+                            (void)vfs_write(b, 0, 4, sec);
+                        }
+                        persistfs_backing_close(b);
                     }
-                    persistfs_backing_close(b);
                 }
+            } else {
+                kprintf("[PERSISTFS] No diskfs backing on drive %d — skipping\n", drive);
             }
         }
 
index 428ff52e156eff36ccd32317183d5308e8f3ed6d..4d0251fdc4b55d3bdbde4c30c798207368f43994 100644 (file)
@@ -4976,9 +4976,9 @@ static void socket_syscall_dispatch(struct registers* regs, uint32_t syscall_no)
         int drive = ata_name_to_drive(devname);
         if (drive < 0) { sc_ret(regs) = (uint32_t)-ENODEV; return; }
 
-        extern int init_mount_fs(const char* fstype, int drive, uint32_t lba, const char* mountpoint);
+        extern int init_mount_fs(const char* fstype, int drive, 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);
+        int rc = init_mount_fs(ktype, drive, 0, kmp, mount_flags);
         sc_ret(regs) = (uint32_t)(rc < 0 ? rc : 0);
         return;
     }
index 43f1c7dbd7e072ef72525aa61f5762732845d7f7..a26e4a79d6447ff8632d47f9a7cba1492add15bc 100755 (executable)
@@ -21,9 +21,17 @@ set iso "adros-x86.iso"
 set disk "disk.img"
 set serial_log "serial.log"
 
-# Ensure disk image exists
+# Ensure disk image exists with diskfs superblock pre-formatted
+# (diskfs no longer auto-formats on mount)
 if {![file exists $disk]} {
     exec dd if=/dev/zero of=$disk bs=1M count=4 2>/dev/null
+    set fd [open $disk r+]
+    fconfigure $fd -translation binary
+    seek $fd 1024
+    puts -nonewline $fd [binary format iiii 0x44465331 3 4 0]
+    seek $fd [expr {1024 + 12}]
+    puts -nonewline $fd [binary format ii 1 0]
+    close $fd
 }
 
 # Remove old serial log
index 6c1e1d3ce14bc24b0c8dc1a840ec18364b6f1572..7372e6a9970071cc58544ce1aa8e3b2d20f419bf 100644 (file)
@@ -26,6 +26,28 @@ proc create_disk {path mb} {
     }
 }
 
+# Create a disk image with a minimal diskfs superblock pre-formatted.
+# diskfs no longer auto-formats on mount, so test disks need this.
+proc create_diskfs_disk {path mb} {
+    if {![file exists $path]} {
+        exec dd if=/dev/zero of=$path bs=1M count=$mb 2>/dev/null
+        # Write diskfs superblock at LBA 2 (byte offset 1024):
+        #   magic=0x44465331 version=3 next_free_lba=4
+        #   root inode: type=1 (DIR) parent=0
+        # Using printf + dd to stamp the 12-byte header + root inode
+        set fd [open $path r+]
+        fconfigure $fd -translation binary
+        seek $fd 1024
+        # magic (4 bytes LE) + version (4 bytes LE) + next_free_lba (4 bytes LE)
+        puts -nonewline $fd [binary format iiii 0x44465331 3 4 0]
+        # Root inode at offset 1024+12: type=1(DIR), parent=0
+        # struct diskfs_inode: type(4) parent(4) name(24) start_lba(4) nsectors(4) nlink(4) cap_sectors(4)
+        seek $fd [expr {1024 + 12}]
+        puts -nonewline $fd [binary format ii 1 0]
+        close $fd
+    }
+}
+
 proc run_qemu {iso smp serial_log timeout_sec drive_args} {
     file delete -force $serial_log
     set cmd [list qemu-system-i386 \
@@ -142,7 +164,7 @@ puts "========================================="
 puts "  AdrOS Test Battery"
 puts "========================================="
 
-create_disk "disk.img" 4
+create_diskfs_disk "disk.img" 4
 
 set pid [run_qemu $iso $smp $serial_log $timeout_sec \
     {{-drive file=disk.img,if=ide,format=raw}}]
@@ -306,10 +328,10 @@ kill_qemu $iso
 report_section "Multi-disk ATA (3 drives)" [lindex $res 0] [lindex $res 1]
 
 # ================================================================
-# TEST 3: VFS mount root=/dev/hda (diskfs auto-format)
+# TEST 3: VFS mount root=/dev/hda (diskfs pre-formatted)
 # ================================================================
 
-create_disk "root_hda.img" 4
+create_diskfs_disk "root_hda.img" 4
 
 set pid [run_qemu $iso $smp $serial_log $timeout_sec \
     {{-drive file=root_hda.img,if=ide,index=0,format=raw}}]
@@ -366,7 +388,7 @@ report_section "ATA /dev/hdd detection" [lindex $res 0] [lindex $res 1]
 # TEST 6: SMP=1 boot (single-CPU regression)
 # ================================================================
 
-create_disk "smp1_disk.img" 4
+create_diskfs_disk "smp1_disk.img" 4
 
 set pid [run_qemu $iso 1 $serial_log $timeout_sec \
     {{-drive file=smp1_disk.img,if=ide,format=raw}}]
@@ -394,7 +416,7 @@ report_section "SMP=1 boot regression" [lindex $res 0] [lindex $res 1]
 # TEST 7: SMP=2 boot (dual-CPU)
 # ================================================================
 
-create_disk "smp2_disk.img" 4
+create_diskfs_disk "smp2_disk.img" 4
 
 set pid [run_qemu $iso 2 $serial_log $timeout_sec \
     {{-drive file=smp2_disk.img,if=ide,format=raw}}]
index c64a676106c6fd6379e1bc983a9ba28b69b2e523..9df7cea8629cea38df1ebfa58ae2a16905849e6e 100644 (file)
@@ -15,7 +15,7 @@
 #include <sys/mount.h>
 #include <errno.h>
 
-static void show_mounts(void) {
+static int show_mounts(void) {
     int fd = open("/proc/mounts", O_RDONLY);
     if (fd >= 0) {
         char buf[2048];
@@ -23,15 +23,16 @@ static void show_mounts(void) {
         while ((n = read(fd, buf, sizeof(buf))) > 0)
             write(STDOUT_FILENO, buf, (size_t)n);
         close(fd);
+        return 0;
     } else {
         fprintf(stderr, "mount: /proc/mounts not available\n");
+        return 1;
     }
 }
 
 int main(int argc, char** argv) {
     if (argc < 2) {
-        show_mounts();
-        return 0;
+        return show_mounts();
     }
 
     const char* fstype = NULL;
@@ -57,11 +58,21 @@ int main(int argc, char** argv) {
                     o += 7;
                 } else if (strncmp(o, "rw", 2) == 0 && (o[2] == ',' || o[2] == '\0')) {
                     o += 2;
+                } else if (strncmp(o, "nosuid", 6) == 0 && (o[6] == ',' || o[6] == '\0')) {
+                    mountflags |= MS_NOSUID;
+                    o += 6;
+                } else if (strncmp(o, "nodev", 5) == 0 && (o[5] == ',' || o[5] == '\0')) {
+                    mountflags |= MS_NODEV;
+                    o += 5;
+                } else if (strncmp(o, "noexec", 6) == 0 && (o[6] == ',' || o[6] == '\0')) {
+                    mountflags |= MS_NOEXEC;
+                    o += 6;
                 } else {
-                    /* Warn on unknown option */
+                    /* Fail on unknown option */
                     const char* start = o;
                     while (*o && *o != ',') o++;
                     fprintf(stderr, "mount: unknown option: %.*s\n", (int)(o - start), start);
+                    return 1;
                 }
                 if (*o == ',') o++;
             }