Skip to content

Implement VFS mount point support #421

@pbalduino

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

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