mirror of
https://github.com/asterinas/asterinas.git
synced 2025-06-22 17:03:23 +00:00
Add syscall mount
Signed-off-by: Zhenchen Wang <m202372036@hust.edu.cn>
This commit is contained in:
committed by
Tate, Hongliang Tian
parent
ff83ce7667
commit
a893ceca4a
@ -23,6 +23,7 @@ use crate::{
|
|||||||
exfat::{ExfatFS, ExfatMountOptions},
|
exfat::{ExfatFS, ExfatMountOptions},
|
||||||
ext2::Ext2,
|
ext2::Ext2,
|
||||||
fs_resolver::FsPath,
|
fs_resolver::FsPath,
|
||||||
|
utils::FileSystem,
|
||||||
},
|
},
|
||||||
prelude::*,
|
prelude::*,
|
||||||
thread::kernel_thread::KernelThreadExt,
|
thread::kernel_thread::KernelThreadExt,
|
||||||
@ -64,3 +65,26 @@ pub fn lazy_init() {
|
|||||||
self::rootfs::mount_fs_at(exfat_fs, &target_path).unwrap();
|
self::rootfs::mount_fs_at(exfat_fs, &target_path).unwrap();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -104,6 +104,18 @@ impl Dentry_ {
|
|||||||
DentryFlags::from_bits(flags).unwrap()
|
DentryFlags::from_bits(flags).unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Check if the Dentry_ is a subdir of the given Dentry_.
|
||||||
|
pub fn is_subdir(&self, dentry: Arc<Self>) -> bool {
|
||||||
|
let mut parent = self.parent();
|
||||||
|
while let Some(p) = parent {
|
||||||
|
if Arc::ptr_eq(&p, &dentry) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
parent = p.parent();
|
||||||
|
}
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
pub fn is_mountpoint(&self) -> bool {
|
pub fn is_mountpoint(&self) -> bool {
|
||||||
self.flags().contains(DentryFlags::MOUNTED)
|
self.flags().contains(DentryFlags::MOUNTED)
|
||||||
}
|
}
|
||||||
@ -561,7 +573,7 @@ impl Dentry {
|
|||||||
|
|
||||||
/// Make this Dentry' inner to be a mountpoint,
|
/// Make this Dentry' inner to be a mountpoint,
|
||||||
/// and set the mountpoint of the child mount to this Dentry's inner.
|
/// 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>) {
|
||||||
child_mount.set_mountpoint_dentry(self.inner.clone());
|
child_mount.set_mountpoint_dentry(self.inner.clone());
|
||||||
self.inner.set_mountpoint_dentry();
|
self.inner.set_mountpoint_dentry();
|
||||||
}
|
}
|
||||||
@ -638,6 +650,14 @@ impl Dentry {
|
|||||||
self.inner.rename(old_name, &new_dir.inner, new_name)
|
self.inner.rename(old_name, &new_dir.inner, new_name)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn do_loopback(&self, recursive: bool) -> Arc<MountNode> {
|
||||||
|
if recursive {
|
||||||
|
self.mount_node.copy_mount_tree(self.inner.clone())
|
||||||
|
} else {
|
||||||
|
self.mount_node.clone_mount(self.inner.clone())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Get the arc reference to self.
|
/// Get the arc reference to self.
|
||||||
fn this(&self) -> Arc<Self> {
|
fn this(&self) -> Arc<Self> {
|
||||||
self.this.upgrade().unwrap()
|
self.this.upgrade().unwrap()
|
||||||
@ -669,4 +689,6 @@ impl Dentry {
|
|||||||
pub fn set_mtime(&self, time: Duration);
|
pub fn set_mtime(&self, time: Duration);
|
||||||
pub fn key(&self) -> DentryKey;
|
pub fn key(&self) -> DentryKey;
|
||||||
pub fn inode(&self) -> &Arc<dyn Inode>;
|
pub fn inode(&self) -> &Arc<dyn Inode>;
|
||||||
|
pub fn is_root_of_mount(&self) -> bool;
|
||||||
|
pub fn is_mountpoint(&self) -> bool;
|
||||||
}
|
}
|
||||||
|
@ -97,6 +97,91 @@ impl MountNode {
|
|||||||
Ok(child_mount)
|
Ok(child_mount)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 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<Dentry_>) -> Arc<Self> {
|
||||||
|
Arc::new_cyclic(|weak_self| Self {
|
||||||
|
root_dentry,
|
||||||
|
mountpoint_dentry: RwLock::new(None),
|
||||||
|
parent: RwLock::new(None),
|
||||||
|
children: Mutex::new(BTreeMap::new()),
|
||||||
|
fs: self.fs.clone(),
|
||||||
|
this: weak_self.clone(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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<Dentry_>) -> Arc<Self> {
|
||||||
|
let new_root_mount = self.clone_mount(root_dentry);
|
||||||
|
let mut stack = vec![self.this()];
|
||||||
|
let mut new_stack = vec![new_root_mount.clone()];
|
||||||
|
|
||||||
|
let mut dentry = new_root_mount.root_dentry.clone();
|
||||||
|
while let Some(old_mount) = stack.pop() {
|
||||||
|
let new_parent_mount = new_stack.pop().unwrap().clone();
|
||||||
|
let old_children = old_mount.children.lock();
|
||||||
|
for old_child_mount in old_children.values() {
|
||||||
|
let mountpoint_dentry = old_child_mount.mountpoint_dentry().unwrap();
|
||||||
|
if !dentry.is_subdir(mountpoint_dentry.clone()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
stack.push(old_child_mount.clone());
|
||||||
|
let new_child_mount =
|
||||||
|
old_child_mount.clone_mount(old_child_mount.root_dentry.clone());
|
||||||
|
let key = mountpoint_dentry.key();
|
||||||
|
new_parent_mount
|
||||||
|
.children
|
||||||
|
.lock()
|
||||||
|
.insert(key, new_child_mount.clone());
|
||||||
|
new_child_mount.set_parent(new_parent_mount.clone());
|
||||||
|
new_child_mount
|
||||||
|
.set_mountpoint_dentry(old_child_mount.mountpoint_dentry().unwrap().clone());
|
||||||
|
new_stack.push(new_child_mount.clone());
|
||||||
|
dentry = new_child_mount.root_dentry.clone();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
new_root_mount.clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Unattach the mount node from the parent mount node.
|
||||||
|
fn unattach_mount(&self) {
|
||||||
|
if let Some(parent) = self.parent() {
|
||||||
|
let parent = parent.upgrade().unwrap();
|
||||||
|
parent
|
||||||
|
.children
|
||||||
|
.lock()
|
||||||
|
.remove(&self.mountpoint_dentry().unwrap().key());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Attach the mount node to the mountpoint.
|
||||||
|
fn attach_mount(&self, mountpoint: Arc<Dentry>) {
|
||||||
|
let key = mountpoint.key();
|
||||||
|
mountpoint
|
||||||
|
.mount_node()
|
||||||
|
.children
|
||||||
|
.lock()
|
||||||
|
.insert(key, self.this());
|
||||||
|
self.set_parent(mountpoint.mount_node().clone());
|
||||||
|
mountpoint.set_mountpoint(self.this());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Graft the mount tree to the mountpoint.
|
||||||
|
pub fn graft_mount_tree(&self, mountpoint: Arc<Dentry>) -> Result<()> {
|
||||||
|
if mountpoint.type_() != InodeType::Dir {
|
||||||
|
return_errno!(Errno::ENOTDIR);
|
||||||
|
}
|
||||||
|
self.unattach_mount();
|
||||||
|
self.attach_mount(mountpoint.clone());
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
/// Try to get a child mount node from the mountpoint.
|
/// Try to get a child mount node from the mountpoint.
|
||||||
pub fn get(&self, mountpoint: &Dentry) -> Option<Arc<Self>> {
|
pub fn get(&self, mountpoint: &Dentry) -> Option<Arc<Self>> {
|
||||||
if !Arc::ptr_eq(mountpoint.mount_node(), &self.this()) {
|
if !Arc::ptr_eq(mountpoint.mount_node(), &self.this()) {
|
||||||
|
@ -53,6 +53,7 @@ use crate::syscall::{
|
|||||||
madvise::sys_madvise,
|
madvise::sys_madvise,
|
||||||
mkdir::{sys_mkdir, sys_mkdirat},
|
mkdir::{sys_mkdir, sys_mkdirat},
|
||||||
mmap::sys_mmap,
|
mmap::sys_mmap,
|
||||||
|
mount::sys_mount,
|
||||||
mprotect::sys_mprotect,
|
mprotect::sys_mprotect,
|
||||||
munmap::sys_munmap,
|
munmap::sys_munmap,
|
||||||
nanosleep::{sys_clock_nanosleep, sys_nanosleep},
|
nanosleep::{sys_clock_nanosleep, sys_nanosleep},
|
||||||
@ -225,6 +226,7 @@ impl_syscall_nums_and_dispatch_fn! {
|
|||||||
SYS_ARCH_PRCTL = 158 => sys_arch_prctl(args[..2], &mut context);
|
SYS_ARCH_PRCTL = 158 => sys_arch_prctl(args[..2], &mut context);
|
||||||
SYS_CHROOT = 161 => sys_chroot(args[..1]);
|
SYS_CHROOT = 161 => sys_chroot(args[..1]);
|
||||||
SYS_SYNC = 162 => sys_sync(args[..0]);
|
SYS_SYNC = 162 => sys_sync(args[..0]);
|
||||||
|
SYS_MOUNT = 165 => sys_mount(args[..5]);
|
||||||
SYS_GETTID = 186 => sys_gettid(args[..0]);
|
SYS_GETTID = 186 => sys_gettid(args[..0]);
|
||||||
SYS_TIME = 201 => sys_time(args[..1]);
|
SYS_TIME = 201 => sys_time(args[..1]);
|
||||||
SYS_FUTEX = 202 => sys_futex(args[..6]);
|
SYS_FUTEX = 202 => sys_futex(args[..6]);
|
||||||
|
@ -60,6 +60,7 @@ mod lseek;
|
|||||||
mod madvise;
|
mod madvise;
|
||||||
mod mkdir;
|
mod mkdir;
|
||||||
mod mmap;
|
mod mmap;
|
||||||
|
mod mount;
|
||||||
mod mprotect;
|
mod mprotect;
|
||||||
mod munmap;
|
mod munmap;
|
||||||
mod nanosleep;
|
mod nanosleep;
|
||||||
|
158
kernel/aster-nix/src/syscall/mount.rs
Normal file
158
kernel/aster-nix/src/syscall/mount.rs
Normal file
@ -0,0 +1,158 @@
|
|||||||
|
// SPDX-License-Identifier: MPL-2.0
|
||||||
|
|
||||||
|
use super::SyscallReturn;
|
||||||
|
use crate::{
|
||||||
|
fs::{
|
||||||
|
fs_resolver::{FsPath, AT_FDCWD},
|
||||||
|
path::Dentry,
|
||||||
|
},
|
||||||
|
prelude::*,
|
||||||
|
syscall::constants::MAX_FILENAME_LEN,
|
||||||
|
util::read_cstring_from_user,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn sys_mount(
|
||||||
|
devname_addr: Vaddr,
|
||||||
|
dirname_addr: Vaddr,
|
||||||
|
fstype_addr: Vaddr,
|
||||||
|
flags: u64,
|
||||||
|
data: Vaddr,
|
||||||
|
) -> Result<SyscallReturn> {
|
||||||
|
let devname = read_cstring_from_user(devname_addr, MAX_FILENAME_LEN)?;
|
||||||
|
let dirname = read_cstring_from_user(dirname_addr, MAX_FILENAME_LEN)?;
|
||||||
|
let mount_flags = MountFlags::from_bits_truncate(flags as u32);
|
||||||
|
debug!(
|
||||||
|
"devname = {:?}, dirname = {:?}, fstype = 0x{:x}, flags = {:?}, data = 0x{:x}",
|
||||||
|
devname, dirname, fstype_addr, mount_flags, data,
|
||||||
|
);
|
||||||
|
|
||||||
|
let current = current!();
|
||||||
|
let target_dentry = {
|
||||||
|
let dirname = dirname.to_string_lossy();
|
||||||
|
if dirname.is_empty() {
|
||||||
|
return_errno_with_message!(Errno::ENOENT, "dirname is empty");
|
||||||
|
}
|
||||||
|
let fs_path = FsPath::new(AT_FDCWD, dirname.as_ref())?;
|
||||||
|
current.fs().read().lookup(&fs_path)?
|
||||||
|
};
|
||||||
|
|
||||||
|
if mount_flags.contains(MountFlags::MS_REMOUNT) && mount_flags.contains(MountFlags::MS_BIND) {
|
||||||
|
do_reconfigure_mnt()?;
|
||||||
|
} else if mount_flags.contains(MountFlags::MS_REMOUNT) {
|
||||||
|
do_remount()?;
|
||||||
|
} else if mount_flags.contains(MountFlags::MS_BIND) {
|
||||||
|
do_loopback(
|
||||||
|
devname.clone(),
|
||||||
|
target_dentry.clone(),
|
||||||
|
mount_flags.contains(MountFlags::MS_REC),
|
||||||
|
)?;
|
||||||
|
} else if mount_flags.contains(MountFlags::MS_SHARED)
|
||||||
|
| mount_flags.contains(MountFlags::MS_PRIVATE)
|
||||||
|
| mount_flags.contains(MountFlags::MS_SLAVE)
|
||||||
|
| mount_flags.contains(MountFlags::MS_UNBINDABLE)
|
||||||
|
{
|
||||||
|
do_change_type()?;
|
||||||
|
} else if mount_flags.contains(MountFlags::MS_MOVE) {
|
||||||
|
do_move_mount_old(devname, target_dentry)?;
|
||||||
|
} else {
|
||||||
|
do_new_mount(devname, fstype_addr, target_dentry)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(SyscallReturn::Return(0))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn do_reconfigure_mnt() -> Result<()> {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn do_remount() -> Result<()> {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Bind a mount to a new location.
|
||||||
|
fn do_loopback(old_name: CString, new_dentry: Arc<Dentry>, recursive: bool) -> Result<()> {
|
||||||
|
let current = current!();
|
||||||
|
let old_dentry = {
|
||||||
|
let old_name = old_name.to_string_lossy();
|
||||||
|
if old_name.is_empty() {
|
||||||
|
return_errno_with_message!(Errno::ENOENT, "old_name is empty");
|
||||||
|
}
|
||||||
|
let fs_path = FsPath::new(AT_FDCWD, old_name.as_ref())?;
|
||||||
|
current.fs().read().lookup(&fs_path)?
|
||||||
|
};
|
||||||
|
|
||||||
|
let new_mount = old_dentry.do_loopback(recursive);
|
||||||
|
new_mount.graft_mount_tree(new_dentry.clone())?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn do_change_type() -> Result<()> {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Move a mount from old location to new location.
|
||||||
|
fn do_move_mount_old(old_name: CString, new_dentry: Arc<Dentry>) -> Result<()> {
|
||||||
|
let current = current!();
|
||||||
|
let old_dentry = {
|
||||||
|
let old_name = old_name.to_string_lossy();
|
||||||
|
if old_name.is_empty() {
|
||||||
|
return_errno_with_message!(Errno::ENOENT, "old_name is empty");
|
||||||
|
}
|
||||||
|
let fs_path = FsPath::new(AT_FDCWD, old_name.as_ref())?;
|
||||||
|
current.fs().read().lookup(&fs_path)?
|
||||||
|
};
|
||||||
|
|
||||||
|
if !old_dentry.is_root_of_mount() {
|
||||||
|
return_errno_with_message!(Errno::EINVAL, "old_name can not be moved");
|
||||||
|
};
|
||||||
|
if old_dentry.mount_node().parent().is_none() {
|
||||||
|
return_errno_with_message!(Errno::EINVAL, "old_name can not be moved");
|
||||||
|
}
|
||||||
|
|
||||||
|
old_dentry
|
||||||
|
.mount_node()
|
||||||
|
.graft_mount_tree(new_dentry.clone())?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Mount a new filesystem.
|
||||||
|
fn do_new_mount(devname: CString, fs_type: Vaddr, target_dentry: Arc<Dentry>) -> Result<()> {
|
||||||
|
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)?;
|
||||||
|
target_dentry.mount(fs)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
bitflags! {
|
||||||
|
struct MountFlags: u32 {
|
||||||
|
const MS_RDONLY = 1; /* Mount read-only */
|
||||||
|
const MS_NOSUID = 1<<1; /* Ignore suid and sgid bits */
|
||||||
|
const MS_NODEV = 1<<2; /* Disallow access to device special files */
|
||||||
|
const MS_NOEXEC = 1<<3; /* Disallow program execution */
|
||||||
|
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_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_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. */
|
||||||
|
const MS_SLAVE = 1<<19; /* Change to slave. */
|
||||||
|
const MS_SHARED = 1<<20; /* Change to shared. */
|
||||||
|
const MS_RELATIME = 1<<21; /* Update atime relative to mtime/ctime. */
|
||||||
|
const MS_KERNMOUNT = 1<<22; /* This is a kern_mount call. */
|
||||||
|
}
|
||||||
|
}
|
@ -19,6 +19,7 @@ TESTS ?= \
|
|||||||
lseek_test \
|
lseek_test \
|
||||||
mkdir_test \
|
mkdir_test \
|
||||||
mmap_test \
|
mmap_test \
|
||||||
|
mount_test \
|
||||||
open_create_test \
|
open_create_test \
|
||||||
open_test \
|
open_test \
|
||||||
pty_test \
|
pty_test \
|
||||||
|
17
regression/syscall_test/blocklists/mount_test
Normal file
17
regression/syscall_test/blocklists/mount_test
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
MountTest.MountBadFilesystem
|
||||||
|
MountTest.MountInvalidTarget
|
||||||
|
MountTest.MountPermDenied
|
||||||
|
MountTest.UmountPermDenied
|
||||||
|
MountTest.MountOverBusy
|
||||||
|
MountTest.OpenFileBusy
|
||||||
|
MountTest.UmountDetach
|
||||||
|
MountTest.ActiveSubmountBusy
|
||||||
|
MountTest.MountTmpfs
|
||||||
|
MountTest.MountTmpfsMagicValIgnored
|
||||||
|
MountTest.NullData
|
||||||
|
MountTest.MountReadonly
|
||||||
|
MountTest.MountNoAtime
|
||||||
|
MountTest.MountNoExec
|
||||||
|
MountTest.RenameRemoveMountPoint
|
||||||
|
MountTest.MountFuseFilesystemNoDevice
|
||||||
|
MountTest.MountFuseFilesystem
|
Reference in New Issue
Block a user