Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add syscall mount and umount #855

Merged
merged 3 commits into from
Jun 4, 2024

Conversation

Plucky923
Copy link
Contributor

@Plucky923 Plucky923 commented May 17, 2024

This PR introduces incomplete implementations of the mount and umount system calls. The primary changes are as follows:

syscall mount

The syscall mount is a complex system call with various functionalities. A call to mount() performs one of several operations based on the bits specified in mountflags. The operation to perform is determined by testing the bits set in mountflags, in the following order:

  • Modify mount options of a file system(do_reconfigure_mnt): mountflags includes MS_REMOUNT and MS_BIND
  • Remount an existing mount(do_remount): mountflags includes MS_REMOUNT
  • Create a bind mount(do_loopback): mountflags includes MS_BIND
  • Change the propagation type of an existing mount(do_change_type): mountflags includes one of MS_SHARED, MS_PRIVATE, MS_SLAVE, or MS_UNBINDABLE
  • Move an existing mount to a new location(do_move_mount_old): mountflags includes MS_MOVE
  • Create a new mount(do_new_mount): mountflags includes none of the above flags

This PR primarily implements the do_loopback, do_move_mount_old, and do_new_mount functions.

do_loopback

This function handles the main implementation for bind mounts. A bind mount makes a file or a directory subtree visible at another point within the single directory hierarchy. By default, only the directory itself is mounted. If the MS_REC flag is specified, a recursive bind mount operation is performed, making all submounts under the source subtree (except unbindable mounts) also bind mounted at the corresponding location in the target subtree. The primary method involves copying the subtree of the directory to be bind-mounted and grafting it to another directory's subtree.

do_move_mount_old

This function moves an existing mount by detaching it from its current location in the mount tree and grafting it onto a new directory's subtree.

do_new_mount

This function mounts a new file system. Currently, I only supports mounting ext2 and exfat file systems due to limited support for file system types and device names. The implementation uses the following function to get the file system type and then perform the mount operation:

pub fn get_fs(fs_type: &str) -> Result<Arc<dyn FileSystem>> {
    match fs_type {
        "ext2" => {
            if let Ok(block_device_ext2) = start_block_device("vext2") {
                let ext2_fs = Ext2::open(block_device_ext2).unwrap();
                Ok(ext2_fs)
            } else {
                return_errno_with_message!(Errno::EINVAL, "Ext2 fs does not exist")
            }
        }
        "exfat" => {
            if let Ok(block_device_exfat) = start_block_device("vexfat") {
                let exfat_fs =
                    ExfatFS::open(block_device_exfat, ExfatMountOptions::default()).unwrap();
                Ok(exfat_fs)
            } else {
                return_errno_with_message!(Errno::EINVAL, "Exfat fs dose not exist")
            }
        }
        _ => return_errno_with_message!(Errno::EINVAL, "Invalid fs type"),
    }
}

syscall umount

The syscall umount mainly handles the unmounting of a mounted file system using the umount method defined in the impl Dentry. Further functionality requires additional features like mount namespace.

VFS Layer Changes

The VFS layer includes the following new methods:

MountNode

  • clone_mount: Clones a mount node with a root Dentry_, setting the parent and children manually.
  • copy_mount_tree: Copies a mount tree rooted at a given Dentry_, maintaining the same structure as the original tree.
  • detach_mount: Detaches a mount node from its parent.
  • attach_mount: Attaches a mount node to the mountpoint.
  • graft_mount_tree: Grafts a mount tree to the mountpoint.

Dentry_

  • is_subdir: Determines if one Dentry_ is a subdirectory of another.

Dentry

  • do_loopback: Used in bind mount implementation to copy the current directory or all submounts recursively based on the recursive flag.

Testing

Tests for syscall mount and umount are included in mount_test. Both require CAP_SYS_ADMIN capabilities, necessitating support for capabilities to run the tests. To test, run the following commands in the shell:

mount --bind dir1 dir2 # bind mount, makes dir1's contents visible in dir2
mount --move dir1 dir2 # dir1 must be a mount point, moves the ext2 file system originally at /ext2 to dir2 
mount -t ext2 vext2 dir # currently only supports ext2 and exfat file systems, devname can be vext2 or vexfat, creates an ext2 or exfat file system mounted at dir.

@Plucky923 Plucky923 changed the title Add syscall mount Add syscall mount and umount May 17, 2024
@Plucky923 Plucky923 force-pushed the plucky/Add-syscall-mount branch 2 times, most recently from 069cd60 to 584aee3 Compare May 18, 2024 03:01
Signed-off-by: Zhenchen Wang <[email protected]>
@Plucky923 Plucky923 marked this pull request as ready for review May 18, 2024 05:57
@Plucky923 Plucky923 force-pushed the plucky/Add-syscall-mount branch 2 times, most recently from afed1b5 to 0544fcd Compare May 18, 2024 07:26
Copy link
Contributor

@grief8 grief8 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM. Thank you for your contribution!

kernel/aster-nix/src/syscall/mount.rs Outdated Show resolved Hide resolved
kernel/aster-nix/src/syscall/mount.rs Show resolved Hide resolved
kernel/aster-nix/src/fs/mod.rs Outdated Show resolved Hide resolved
kernel/aster-nix/src/syscall/mount.rs Outdated Show resolved Hide resolved
@StanPlatinum
Copy link
Contributor

Testing

Tests for syscall mount and umount are included in mount_test. Both require CAP_SYS_ADMIN capabilities, necessitating support for capabilities to run the tests. To test, run the following commands in the shell:

mount --bind dir1 dir2 # bind mount, makes dir1's contents visible in dir2
mount --move dir1 dir2 # dir1 must be a mount point, moves the ext2 file system originally at /ext2 to dir2 
mount -t ext2 devname dir # currently only supports ext2 and exfat file systems, devname can be any value, creates an ext2 file system mounted at dir.

Tests will be added later after the capget/capset syscalls are supported.

Signed-off-by: Zhenchen Wang <[email protected]>
kernel/aster-nix/src/fs/path/dentry.rs Outdated Show resolved Hide resolved
@@ -635,6 +647,14 @@ impl Dentry {
self.inner.rename(old_name, &new_dir.inner, new_name)
}

pub fn do_loopback(&self, recursive: bool) -> Arc<MountNode> {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are Rust docs that explain some trivial methods of Dentry like rename and rmdir, but no Rust doc for a weird method named do_loopback having a mysterious argument named recursive? This method is more worthy of explanation than other methods of Dentry. I often find the lack of Rust doc is a symptom of poor design. When the design is poor, even the author finds it hard to explain it, thus omitting the doc.

I assume the method follows that of Linux's, but I doubt the wisdom of doing so. The original meaning of loopback in Linux is closely related to loopback devices, through which a Linux node can send network packages back to itself. But how can the original meaning of loopback be extended to file systems or mounts? You need to establish this loopback concept for file systems or mounts before using it. One proper place is the Rust doc of this method. If you can't convince yourself and others by writing the Rust doc, then you probably should describe this method in another way.

By the way, do_xxx names are usually reserved for internal methods. Think twice before using it on public methods.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I greatly appreciate the comprehensive review you've offered on the documentation and naming conventions.

The original naming of do_loopback was indeed inspired by the corresponding naming conventions in Linux. However,the Linux naming is quite perplexing. In light of your feedback and to enhance clarity, I have renamed do_loopback to do_bind_mount. This new name more accurately describes the method's role in facilitating bind mount operations.

Moreover, I have restructured the method to Dentry::bind_mount_to, which now explicitly communicates that it is intended for binding one Dentry to another. This renaming and re-encapsulation should lead to a better understanding of the method's purpose and enhance the code's readability.

Regarding the recursive parameter, it functions as follows:

do_bind_mount(  
	devname,  
	dst_dentry,  
	mount_flags.contains(MountFlags::MS_REC),  
)?;  

When the MS_REC flag is not set, the recursive parameter is false, indicating that only the current directory is bound, and any sub-mounted directories within it are not recursively bound. Conversely, when MS_REC is set, recursive becomes true, allowing for a recursive bind of the sub-mounted directories within the directory.

To illustrate with examples:

  • A non-recursive bind mount, corresponding to the user command mount --bind src dst, only binds the specified source (src) to the destination (dst) without affecting sub-mounts.
  • A recursive bind mount, corresponding to the user command mount --rbind src dst, performs a recursive operation, allowing access to the contents of sub-mounted file systems within the bound directory.

@@ -558,7 +570,7 @@ impl Dentry {

/// Make this Dentry' inner to be a mountpoint,
/// and set the mountpoint of the child mount to this Dentry's inner.
fn set_mountpoint(&self, child_mount: Arc<MountNode>) {
pub fn set_mountpoint(&self, child_mount: Arc<MountNode>) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why public?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Given the possibility that within MountNode we may need to reassign the mount point of a MountNode, MountNode could become user of set_mountpoint. To ensure that this functionality is utilized exclusively by MountNode, I believe opting for pub(super) is a choice.

kernel/aster-nix/src/fs/path/mount.rs Show resolved Hide resolved
kernel/aster-nix/src/syscall/mount.rs Outdated Show resolved Hide resolved
kernel/aster-nix/src/syscall/umount.rs Outdated Show resolved Hide resolved
kernel/aster-nix/src/syscall/mount.rs Outdated Show resolved Hide resolved
kernel/aster-nix/src/fs/path/mount.rs Outdated Show resolved Hide resolved
/// The new mount tree will replicate the structure of the original tree.
/// The new tree is a separate entity rooted at the given `Dentry_`,
/// and the original tree remains unchanged.
pub fn copy_mount_node_tree(&self, root_dentry: Arc<Dentry_>) -> Arc<Self> {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be a private method.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for your review and the suggestion to make copy_mount_node_tree a private method. However, there are use cases where clone_mount_node_tree could be utilized within Dentry, such as in the Dentry::bind_mount_to:

pub fn bind_mount_to(&self, dst_dentry: &Arc<Self>, recursive: bool) -> Result<()> {
    let src_mount = self
        .mount_node
        .clone_mount_node_tree(&self.inner, recursive);
    src_mount.graft_mount_node_tree(dst_dentry)?;
    Ok(())
}

Considering this, I believe it would be more appropriate to use pub(super) for clone_mount_node_tree to indicate that it may be used by Dentry.

Furthermore, in the context of the mount namespace, there may be operations involving clone_mount_node_tree where the process's root and current working directory are moved to the new mount node tree.

kernel/aster-nix/src/fs/path/dentry.rs Outdated Show resolved Hide resolved
@Plucky923 Plucky923 force-pushed the plucky/Add-syscall-mount branch 3 times, most recently from 6099a58 to f1a2d22 Compare June 2, 2024 07:55
…redefined the method for bind mount.

Signed-off-by: Zhenchen Wang <[email protected]>
Copy link
Contributor

@tatetian tatetian left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM. Thanks for the contribution!

@tatetian tatetian merged commit faf9cf7 into asterinas:main Jun 4, 2024
5 checks passed
@Plucky923 Plucky923 deleted the plucky/Add-syscall-mount branch June 4, 2024 08:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

5 participants