diff --git a/docs/src/kernel/linux-compatibility.md b/docs/src/kernel/linux-compatibility.md index 465f5ff27..0b6b77a58 100644 --- a/docs/src/kernel/linux-compatibility.md +++ b/docs/src/kernel/linux-compatibility.md @@ -185,8 +185,8 @@ provided by Linux on x86-64 architecture. | 162 | sync | ✅ | | 163 | acct | ❌ | | 164 | settimeofday | ❌ | -| 165 | mount | ❌ | -| 166 | umount2 | ❌ | +| 165 | mount | ✅ | +| 166 | umount2 | ✅ | | 167 | swapon | ❌ | | 168 | swapoff | ❌ | | 169 | reboot | ❌ | diff --git a/kernel/aster-nix/src/fs/mod.rs b/kernel/aster-nix/src/fs/mod.rs index 9c6cefcc4..32114b732 100644 --- a/kernel/aster-nix/src/fs/mod.rs +++ b/kernel/aster-nix/src/fs/mod.rs @@ -23,7 +23,6 @@ use crate::{ exfat::{ExfatFS, ExfatMountOptions}, ext2::Ext2, fs_resolver::FsPath, - utils::FileSystem, }, prelude::*, thread::kernel_thread::KernelThreadExt, @@ -65,26 +64,3 @@ pub fn lazy_init() { self::rootfs::mount_fs_at(exfat_fs, &target_path).unwrap(); } } - -pub fn get_fs(fs_type: &str) -> Result> { - 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"), - } -} diff --git a/kernel/aster-nix/src/fs/path/dentry.rs b/kernel/aster-nix/src/fs/path/dentry.rs index 8decb7d6c..4e7e1f74f 100644 --- a/kernel/aster-nix/src/fs/path/dentry.rs +++ b/kernel/aster-nix/src/fs/path/dentry.rs @@ -652,9 +652,9 @@ impl Dentry { pub fn do_loopback(&self, recursive: bool) -> Arc { if recursive { - self.mount_node.copy_mount_tree(self.inner.clone()) + self.mount_node.copy_mount_node_tree(self.inner.clone()) } else { - self.mount_node.clone_mount(self.inner.clone()) + self.mount_node.clone_mount_node(self.inner.clone()) } } diff --git a/kernel/aster-nix/src/fs/path/mount.rs b/kernel/aster-nix/src/fs/path/mount.rs index 9f29cf75c..f13d5f38b 100644 --- a/kernel/aster-nix/src/fs/path/mount.rs +++ b/kernel/aster-nix/src/fs/path/mount.rs @@ -98,11 +98,9 @@ impl MountNode { } /// Clone a mount node with the an root Dentry_. - /// The new mount node will have the same fs as the original one. - /// The new mount node root Dentry_ will be the given one. - /// The new mount node will have no parent and children. - /// We should set the parent and children manually. - pub fn clone_mount(&self, root_dentry: Arc) -> Arc { + /// The new mount node will have the same fs as the original one and + /// have no parent and children. We should set the parent and children manually. + pub fn clone_mount_node(&self, root_dentry: Arc) -> Arc { Arc::new_cyclic(|weak_self| Self { root_dentry, mountpoint_dentry: RwLock::new(None), @@ -113,12 +111,12 @@ impl MountNode { }) } - /// Copy a mount tree with the given root Dentry_. - /// The new mount tree will have the same structure as the original one. - /// But the new mount tree is a subtree which is rooted at the given root Dentry_. - /// The new mount tree is a new tree, which means the original one is not affected. - pub fn copy_mount_tree(&self, root_dentry: Arc) -> Arc { - let new_root_mount = self.clone_mount(root_dentry); + /// Copies a mount tree starting from the specified root `Dentry_`. + /// 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) -> Arc { + let new_root_mount = self.clone_mount_node(root_dentry); let mut stack = vec![self.this()]; let mut new_stack = vec![new_root_mount.clone()]; @@ -133,7 +131,7 @@ impl MountNode { } stack.push(old_child_mount.clone()); let new_child_mount = - old_child_mount.clone_mount(old_child_mount.root_dentry.clone()); + old_child_mount.clone_mount_node(old_child_mount.root_dentry.clone()); let key = mountpoint_dentry.key(); new_parent_mount .children @@ -149,8 +147,8 @@ impl MountNode { new_root_mount.clone() } - /// Unattach the mount node from the parent mount node. - fn unattach_mount(&self) { + /// Detach the mount node from the parent mount node. + fn detach_mount_node(&self) { if let Some(parent) = self.parent() { let parent = parent.upgrade().unwrap(); parent @@ -161,7 +159,7 @@ impl MountNode { } /// Attach the mount node to the mountpoint. - fn attach_mount(&self, mountpoint: Arc) { + fn attach_mount_node(&self, mountpoint: Arc) { let key = mountpoint.key(); mountpoint .mount_node() @@ -172,13 +170,13 @@ impl MountNode { mountpoint.set_mountpoint(self.this()); } - /// Graft the mount tree to the mountpoint. - pub fn graft_mount_tree(&self, mountpoint: Arc) -> Result<()> { + /// Graft the mount node tree to the mountpoint. + pub fn graft_mount_node_tree(&self, mountpoint: Arc) -> Result<()> { if mountpoint.type_() != InodeType::Dir { return_errno!(Errno::ENOTDIR); } - self.unattach_mount(); - self.attach_mount(mountpoint.clone()); + self.detach_mount_node(); + self.attach_mount_node(mountpoint.clone()); Ok(()) } diff --git a/kernel/aster-nix/src/syscall/arch/x86.rs b/kernel/aster-nix/src/syscall/arch/x86.rs index 3af2d818f..4af57d68e 100644 --- a/kernel/aster-nix/src/syscall/arch/x86.rs +++ b/kernel/aster-nix/src/syscall/arch/x86.rs @@ -109,6 +109,7 @@ use crate::syscall::{ timer_settime::{sys_timer_gettime, sys_timer_settime}, truncate::{sys_ftruncate, sys_truncate}, umask::sys_umask, + umount::sys_umount, uname::sys_uname, unlink::{sys_unlink, sys_unlinkat}, utimens::sys_utimensat, @@ -227,6 +228,7 @@ impl_syscall_nums_and_dispatch_fn! { SYS_CHROOT = 161 => sys_chroot(args[..1]); SYS_SYNC = 162 => sys_sync(args[..0]); SYS_MOUNT = 165 => sys_mount(args[..5]); + SYS_UMOUNT = 166 => sys_umount(args[..2]); SYS_GETTID = 186 => sys_gettid(args[..0]); SYS_TIME = 201 => sys_time(args[..1]); SYS_FUTEX = 202 => sys_futex(args[..6]); diff --git a/kernel/aster-nix/src/syscall/mod.rs b/kernel/aster-nix/src/syscall/mod.rs index c6306cc71..4d4afcddb 100644 --- a/kernel/aster-nix/src/syscall/mod.rs +++ b/kernel/aster-nix/src/syscall/mod.rs @@ -116,6 +116,7 @@ mod timer_create; mod timer_settime; mod truncate; mod umask; +mod umount; mod uname; mod unlink; mod utimens; diff --git a/kernel/aster-nix/src/syscall/mount.rs b/kernel/aster-nix/src/syscall/mount.rs index af8e340f5..88c56bdc0 100644 --- a/kernel/aster-nix/src/syscall/mount.rs +++ b/kernel/aster-nix/src/syscall/mount.rs @@ -3,14 +3,21 @@ use super::SyscallReturn; use crate::{ fs::{ + exfat::{ExfatFS, ExfatMountOptions}, + ext2::Ext2, fs_resolver::{FsPath, AT_FDCWD}, path::Dentry, + utils::{FileSystem, InodeType}, }, prelude::*, syscall::constants::MAX_FILENAME_LEN, util::read_cstring_from_user, }; +/// The data argument is interpreted by the different filesystems. +/// Typically it is a string of comma-separated options understood by +/// this filesystem. The current implementation only considers the case +/// where it is NULL. Because it should be interpreted by the specific filesystems. pub fn sys_mount( devname_addr: Vaddr, dirname_addr: Vaddr, @@ -81,8 +88,12 @@ fn do_loopback(old_name: CString, new_dentry: Arc, recursive: bool) -> R current.fs().read().lookup(&fs_path)? }; + if old_dentry.type_() != InodeType::Dir { + return_errno_with_message!(Errno::ENOTDIR, "old_name must be directory"); + }; + let new_mount = old_dentry.do_loopback(recursive); - new_mount.graft_mount_tree(new_dentry.clone())?; + new_mount.graft_mount_node_tree(new_dentry.clone())?; Ok(()) } @@ -111,23 +122,47 @@ fn do_move_mount_old(old_name: CString, new_dentry: Arc) -> Result<()> { old_dentry .mount_node() - .graft_mount_tree(new_dentry.clone())?; + .graft_mount_node_tree(new_dentry.clone())?; Ok(()) } /// Mount a new filesystem. fn do_new_mount(devname: CString, fs_type: Vaddr, target_dentry: Arc) -> Result<()> { + if target_dentry.type_() != InodeType::Dir { + return_errno_with_message!(Errno::ENOTDIR, "mountpoint must be directory"); + }; + let fs_type = read_cstring_from_user(fs_type, MAX_FILENAME_LEN)?; if fs_type.is_empty() { return_errno_with_message!(Errno::EINVAL, "fs_type is empty"); } - let fs_type = fs_type.to_str().unwrap(); - let fs = crate::fs::get_fs(fs_type)?; + let fs = get_fs(fs_type, devname)?; target_dentry.mount(fs)?; Ok(()) } +/// Get the filesystem by fs_type and devname. +fn get_fs(fs_type: CString, devname: CString) -> Result> { + let devname = devname.to_str().unwrap(); + let device = match aster_block::get_device(devname) { + Some(device) => device, + None => return_errno_with_message!(Errno::ENOENT, "Device does not exist"), + }; + let fs_type = fs_type.to_str().unwrap(); + match fs_type { + "ext2" => { + let ext2_fs = Ext2::open(device)?; + Ok(ext2_fs) + } + "exfat" => { + let exfat_fs = ExfatFS::open(device, ExfatMountOptions::default())?; + Ok(exfat_fs) + } + _ => return_errno_with_message!(Errno::EINVAL, "Invalid fs type"), + } +} + bitflags! { struct MountFlags: u32 { const MS_RDONLY = 1; /* Mount read-only */ @@ -137,16 +172,14 @@ bitflags! { const MS_SYNCHRONOUS = 1<<4; /* Writes are synced at once */ const MS_REMOUNT = 1<<5; /* Alter flags of a mounted FS */ const MS_MANDLOCK = 1<<6; /* Allow mandatory locks on an FS */ - const MS_DIRSYNS = 1<<7; /* Directory modifications are synchronous */ + const MS_DIRSYNC = 1<<7; /* Directory modifications are synchronous */ const MS_NOSYMFOLLOW = 1<<8; /* Do not follow symlinks */ const MS_NOATIME = 1<<10; /* Do not update access times. */ const MS_NODIRATIME = 1<<11; /* Do not update directory access times. */ const MS_BIND = 1<<12; /* Bind directory at different place. */ const MS_MOVE = 1<<13; /* Move mount from old to new. */ - const MS_REC = 1<<14; - const MS_VERBOSE = 1<<15; /* War is peace. Verbosity is silence. MS_VERBOSE is deprecated. */ - - const MS_SILENT = 1<<15; + const MS_REC = 1<<14; /* Create recursive mount. */ + const MS_SILENT = 1<<15; /* Suppress certain messages in kernel log. */ const MS_POSIXACL = 1<<16; /* VFS does not apply the umask. */ const MS_UNBINDABLE = 1<<17; /* Change to unbindable. */ const MS_PRIVATE = 1<<18; /* Change to private. */ diff --git a/kernel/aster-nix/src/syscall/umount.rs b/kernel/aster-nix/src/syscall/umount.rs new file mode 100644 index 000000000..99896a79c --- /dev/null +++ b/kernel/aster-nix/src/syscall/umount.rs @@ -0,0 +1,57 @@ +// SPDX-License-Identifier: MPL-2.0 + +use super::SyscallReturn; +use crate::{ + fs::fs_resolver::{FsPath, AT_FDCWD}, + prelude::*, + syscall::constants::MAX_FILENAME_LEN, + util::read_cstring_from_user, +}; + +pub fn sys_umount(path_addr: Vaddr, flags: u64) -> Result { + let path = read_cstring_from_user(path_addr, MAX_FILENAME_LEN)?; + let umount_flags = UmountFlags::from_bits_truncate(flags as u32); + debug!("path = {:?}, flags = {:?}", path, umount_flags); + + umount_flags.check_unsupported_flags()?; + + let current = current!(); + let path = path.to_string_lossy(); + if path.is_empty() { + return_errno_with_message!(Errno::ENOENT, "path is empty"); + } + let fs_path = FsPath::new(AT_FDCWD, path.as_ref())?; + + let target_dentry = if umount_flags.contains(UmountFlags::UMOUNT_NOFOLLOW) { + current.fs().read().lookup_no_follow(&fs_path)? + } else { + current.fs().read().lookup(&fs_path)? + }; + + target_dentry.umount()?; + + Ok(SyscallReturn::Return(0)) +} + +bitflags! { + struct UmountFlags: u32 { + const MNT_FORCE = 0x00000001; /* Attempt to forcibily umount */ + const MNT_DETACH = 0x00000002; /* Just detach from the tree */ + const MNT_EXPIRE = 0x00000004; /* Mark for expiry */ + const UMOUNT_NOFOLLOW = 0x00000008; /* Don't follow symlink on umount */ + } +} + +impl UmountFlags { + fn check_unsupported_flags(&self) -> Result<()> { + let supported_flags = UmountFlags::MNT_FORCE + | UmountFlags::MNT_DETACH + | UmountFlags::MNT_EXPIRE + | UmountFlags::UMOUNT_NOFOLLOW; + let unsupported_flags = *self - supported_flags; + if !unsupported_flags.is_empty() { + return_errno_with_message!(Errno::EINVAL, "unsupported flags"); + } + Ok(()) + } +}