Skip to content

Implement VFS mount point support #421

Description

@pbalduino

Goal

Implement VFS mount point infrastructure to support mounting multiple filesystems at different paths in the directory tree (e.g., FAT32 at /boot, ext2 at /).

Context

Currently, meniOS VFS supports only a single root filesystem. To support the dual-partition layout (#228) and future multi-filesystem scenarios, we need:

  • Mount point data structures
  • Path lookup across mount boundaries
  • Mount/unmount operations
  • /proc/mounts support

This is a prerequisite for #228 (dual partition mount).

Dependencies

Required (Blocking)

Blocks (Downstream)

Priority

Medium - Required for dual-partition boot, enables proper Linux-style mounts

Justification

Implementation Scope

Phase 1: Mount Point Data Structures (2-3 days)

// Mount point structure
struct mount {
    char *path;                    // Mount path (e.g., "/boot")
    struct superblock *sb;         // Mounted filesystem superblock
    struct dentry *mount_point;    // Dentry where mounted
    struct dentry *root;           // Root dentry of mounted fs
    struct mount *parent;          // Parent mount (for umount ordering)
    struct list_head children;     // Child mounts
    struct list_head list;         // Global mount list
    uint32_t flags;                // Mount flags (ro, noexec, etc.)
};

// Global mount table
struct list_head mount_table;
struct spinlock mount_lock;
struct mount *root_mount;  // Root filesystem mount

Phase 2: Mount Operations (3-4 days)

// Mount API
int vfs_mount(struct superblock *sb, const char *path, uint32_t flags);
int vfs_umount(const char *path);
struct mount *vfs_find_mount(const char *path);
bool vfs_is_mount_point(struct dentry *dentry);

// Mount flags
#define MS_RDONLY      0x0001  // Read-only
#define MS_NOSUID      0x0002  // Ignore suid/sgid
#define MS_NODEV       0x0004  // Disallow device access
#define MS_NOEXEC      0x0008  // Disallow program execution
#define MS_SYNCHRONOUS 0x0010  // Sync writes
#define MS_REMOUNT     0x0020  // Remount existing mount
#define MS_BIND        0x1000  // Bind mount

Phase 3: Path Lookup with Mounts (4-5 days)

// Enhanced path lookup
struct dentry *vfs_path_lookup_with_mounts(const char *path) {
    struct dentry *current = root_dentry;
    struct mount *current_mount = root_mount;
    
    // Parse path components
    const char *next = path;
    while (*next) {
        // Skip leading slashes
        while (*next == '/') next++;
        
        // Get next component
        const char *component = next;
        while (*next && *next != '/') next++;
        size_t len = next - component;
        
        if (len == 0) break;
        
        // Lookup component in current directory
        struct dentry *child = dentry_lookup(current, component, len);
        if (!child) {
            return ERR_PTR(-ENOENT);
        }
        
        // Check if child is a mount point
        struct mount *mount = vfs_find_mount_at_dentry(child);
        if (mount) {
            // Cross mount boundary
            current = mount->root;
            current_mount = mount;
        } else {
            current = child;
        }
    }
    
    return current;
}

Phase 4: Mount Syscalls (2-3 days)

// System calls
long sys_mount(const char *source, const char *target,
               const char *filesystemtype, unsigned long mountflags,
               const void *data);

long sys_umount(const char *target);
long sys_umount2(const char *target, int flags);

// umount2 flags
#define MNT_FORCE      0x0001  // Force unmount even if busy
#define MNT_DETACH     0x0002  // Lazy unmount
#define MNT_EXPIRE     0x0004  // Mark for expiration

Phase 5: /proc/mounts (1-2 days)

// Expose mount table via /proc/mounts
// Format: device mountpoint fstype options
// Example:
// /dev/sda1 /boot fat32 rw 0 0
// /dev/sda2 / ext2 rw 0 0

int proc_mounts_show(struct seq_file *m, void *v) {
    struct mount *mount;
    
    list_for_each_entry(mount, &mount_table, list) {
        seq_printf(m, "%s %s %s %s 0 0\n",
                   mount->sb->s_dev_name,
                   mount->path,
                   mount->sb->s_type->name,
                   mount_flags_to_string(mount->flags));
    }
    
    return 0;
}

Definition of Done

  • Mount point data structures implemented
  • Mount/umount operations working
  • Path lookup crosses mount boundaries correctly
  • Multiple filesystems can be mounted simultaneously
  • Mount syscalls implemented
  • /proc/mounts shows mounted filesystems
  • Proper locking and synchronization
  • Unit tests for mount operations
  • Integration tests with FAT32 and ext2
  • Documentation

Implementation Details

Mount Point Detection

// Check if dentry is a mount point
bool vfs_is_mount_point(struct dentry *dentry) {
    struct mount *mount;
    
    spin_lock(&mount_lock);
    list_for_each_entry(mount, &mount_table, list) {
        if (mount->mount_point == dentry) {
            spin_unlock(&mount_lock);
            return true;
        }
    }
    spin_unlock(&mount_lock);
    return false;
}

Mount Operation

int vfs_mount(struct superblock *sb, const char *path, uint32_t flags) {
    // Lookup mount point
    struct dentry *mount_point = vfs_path_lookup(path);
    if (IS_ERR(mount_point)) {
        return PTR_ERR(mount_point);
    }
    
    // Verify mount point is a directory
    if (!S_ISDIR(mount_point->d_inode->i_mode)) {
        return -ENOTDIR;
    }
    
    // Check if already mounted
    if (vfs_is_mount_point(mount_point)) {
        return -EBUSY;
    }
    
    // Create mount structure
    struct mount *mount = kmalloc(sizeof(struct mount));
    mount->path = kstrdup(path);
    mount->sb = sb;
    mount->mount_point = mount_point;
    mount->root = sb->s_root;
    mount->flags = flags;
    
    // Add to mount table
    spin_lock(&mount_lock);
    list_add(&mount->list, &mount_table);
    mount_point->d_flags |= DCACHE_MOUNTED;
    spin_unlock(&mount_lock);
    
    kprintf("Mounted %s at %s\n", sb->s_type->name, path);
    return 0;
}

Unmount Operation

int vfs_umount(const char *path) {
    struct mount *mount;
    
    // Find mount by path
    spin_lock(&mount_lock);
    list_for_each_entry(mount, &mount_table, list) {
        if (strcmp(mount->path, path) == 0) {
            // Found it
            goto found;
        }
    }
    spin_unlock(&mount_lock);
    return -EINVAL;
    
found:
    // Check if busy (has children mounted)
    if (!list_empty(&mount->children)) {
        spin_unlock(&mount_lock);
        return -EBUSY;
    }
    
    // Remove from mount table
    list_del(&mount->list);
    mount->mount_point->d_flags &= ~DCACHE_MOUNTED;
    spin_unlock(&mount_lock);
    
    // Sync and release filesystem
    sb_sync(mount->sb);
    sb_put(mount->sb);
    
    kfree(mount->path);
    kfree(mount);
    
    kprintf("Unmounted %s\n", path);
    return 0;
}

Testing Strategy

Unit Tests

  • Mount filesystem at /mnt
  • Mount filesystem at /boot
  • Mount multiple filesystems
  • Unmount filesystem
  • Verify mount point detection
  • Test mount over existing mount (should fail)
  • Test umount of busy mount (should fail)

Integration Tests

// Test dual mount
void test_dual_mount(void) {
    // Mount FAT32 at /boot
    struct superblock *fat32_sb = fat32_mount(boot_device);
    assert(vfs_mount(fat32_sb, "/boot", MS_RDONLY) == 0);
    
    // Mount ext2 at /
    struct superblock *ext2_sb = ext2_mount(root_device);
    assert(vfs_mount(ext2_sb, "/", 0) == 0);
    
    // Verify path lookup crosses mounts
    struct dentry *boot_file = vfs_path_lookup("/boot/kernel.elf");
    assert(boot_file != NULL);
    assert(boot_file->d_sb == fat32_sb);
    
    struct dentry *root_file = vfs_path_lookup("/bin/mosh");
    assert(root_file != NULL);
    assert(root_file->d_sb == ext2_sb);
    
    // Unmount
    assert(vfs_umount("/boot") == 0);
    assert(vfs_umount("/") == 0);
}

Use Cases

Dual Partition Boot

void init_filesystems(void) {
    // Mount boot partition (FAT32)
    struct superblock *boot_sb = fat32_mount(boot_partition);
    vfs_mount(boot_sb, "/boot", MS_RDONLY);
    
    // Mount root partition (ext2)
    struct superblock *root_sb = ext2_mount(root_partition);
    vfs_mount(root_sb, "/", 0);
    
    // Mount special filesystems
    vfs_mount(devfs_mount(), "/dev", 0);
    vfs_mount(tmpfs_mount(), "/tmp", 0);
    vfs_mount(procfs_mount(), "/proc", MS_RDONLY);
}

Checking Mounts

$ cat /proc/mounts
/dev/sda1 /boot fat32 ro 0 0
/dev/sda2 / ext2 rw 0 0
devfs /dev devfs rw 0 0
tmpfs /tmp tmpfs rw 0 0
procfs /proc procfs ro 0 0

Files to Create/Modify

  • src/kernel/fs/mount.c - Mount point management
  • src/kernel/fs/vfs.c - Update path lookup for mounts
  • src/kernel/syscalls/mount.c - Mount/umount syscalls
  • src/kernel/proc/mounts.c - /proc/mounts implementation
  • include/kernel/fs/mount.h - Mount API
  • test/test_mount.c - Mount tests

Deliverables

  • Mount point infrastructure
  • Mount/umount operations
  • Enhanced path lookup
  • Mount syscalls
  • /proc/mounts support
  • Test suite
  • Documentation

Performance Goals

  • Mount/umount operations < 1ms
  • Path lookup overhead minimal (1-2 extra checks)
  • Minimal memory per mount point (~100 bytes)

Error Handling

  • Prevent mount over non-directory
  • Prevent mount over existing mount
  • Prevent umount of busy filesystem
  • Proper cleanup on mount failure
  • Handle umount during active access

Security Considerations

  • Check permissions on mount point
  • Validate mount flags
  • Prevent privilege escalation via mount
  • Proper ownership of mounted files

Related Issues

Current Status

Ready to Start - VFS layer complete, can begin implementation

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

enhancementNew feature or requestkernelKernel-level implementation

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions