Add `proc/self/mounts` and `proc/mounts`
This commit is contained in:
parent
ab4e0d9012
commit
b0407dd517
|
|
@ -36,7 +36,12 @@ pub fn init_in_first_process(ctx: &Context) -> Result<()> {
|
||||||
|
|
||||||
// Mount devtmpfs.
|
// Mount devtmpfs.
|
||||||
let dev_path = path_resolver.lookup(&FsPath::try_from("/dev")?)?;
|
let dev_path = path_resolver.lookup(&FsPath::try_from("/dev")?)?;
|
||||||
dev_path.mount(RamFs::new(), PerMountFlags::default(), ctx)?;
|
dev_path.mount(
|
||||||
|
RamFs::new(),
|
||||||
|
PerMountFlags::default(),
|
||||||
|
Some("ramfs".to_string()),
|
||||||
|
ctx,
|
||||||
|
)?;
|
||||||
|
|
||||||
tty::init_in_first_process()?;
|
tty::init_in_first_process()?;
|
||||||
pty::init_in_first_process(&path_resolver, ctx)?;
|
pty::init_in_first_process(&path_resolver, ctx)?;
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,12 @@ pub fn init_in_first_process(path_resolver: &PathResolver, ctx: &Context) -> Res
|
||||||
let dev = path_resolver.lookup(&FsPath::try_from("/dev")?)?;
|
let dev = path_resolver.lookup(&FsPath::try_from("/dev")?)?;
|
||||||
// Create the "pts" directory and mount devpts on it.
|
// Create the "pts" directory and mount devpts on it.
|
||||||
let devpts_path = dev.new_fs_child("pts", InodeType::Dir, mkmod!(a+rx, u+w))?;
|
let devpts_path = dev.new_fs_child("pts", InodeType::Dir, mkmod!(a+rx, u+w))?;
|
||||||
let devpts_mount = devpts_path.mount(DevPts::new(), PerMountFlags::default(), ctx)?;
|
let devpts_mount = devpts_path.mount(
|
||||||
|
DevPts::new(),
|
||||||
|
PerMountFlags::default(),
|
||||||
|
Some("devpts".to_string()),
|
||||||
|
ctx,
|
||||||
|
)?;
|
||||||
|
|
||||||
DEV_PTS.call_once(|| Path::new_fs_root(devpts_mount));
|
DEV_PTS.call_once(|| Path::new_fs_root(devpts_mount));
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,12 @@ pub fn init_in_first_process(path_resolver: &PathResolver, ctx: &Context) -> Res
|
||||||
// Create the "shm" directory under "/dev" and mount a ramfs on it.
|
// Create the "shm" directory under "/dev" and mount a ramfs on it.
|
||||||
let shm_path =
|
let shm_path =
|
||||||
dev_path.new_fs_child("shm", InodeType::Dir, chmod!(InodeMode::S_ISVTX, a+rwx))?;
|
dev_path.new_fs_child("shm", InodeType::Dir, chmod!(InodeMode::S_ISVTX, a+rwx))?;
|
||||||
shm_path.mount(RamFs::new(), PerMountFlags::default(), ctx)?;
|
shm_path.mount(
|
||||||
|
RamFs::new(),
|
||||||
|
PerMountFlags::default(),
|
||||||
|
Some("tmpfs".to_string()),
|
||||||
|
ctx,
|
||||||
|
)?;
|
||||||
log::debug!("Mount RamFs at \"/dev/shm\"");
|
log::debug!("Mount RamFs at \"/dev/shm\"");
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@ use crate::{
|
||||||
};
|
};
|
||||||
|
|
||||||
/// A `Dentry` represents a cached filesystem node in the VFS tree.
|
/// A `Dentry` represents a cached filesystem node in the VFS tree.
|
||||||
pub(super) struct Dentry {
|
pub(in crate::fs) struct Dentry {
|
||||||
inode: Arc<dyn Inode>,
|
inode: Arc<dyn Inode>,
|
||||||
type_: InodeType,
|
type_: InodeType,
|
||||||
name_and_parent: NameAndParent,
|
name_and_parent: NameAndParent,
|
||||||
|
|
@ -203,7 +203,7 @@ impl Dentry {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Gets the absolute path name of this `Dentry` within the filesystem.
|
/// Gets the absolute path name of this `Dentry` within the filesystem.
|
||||||
pub(super) fn path_name(&self) -> String {
|
pub(in crate::fs) fn path_name(&self) -> String {
|
||||||
let mut path_name = self.name().to_string();
|
let mut path_name = self.name().to_string();
|
||||||
let mut current_dir = self.this();
|
let mut current_dir = self.this();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -78,7 +78,7 @@ impl Path {
|
||||||
Self::new(mount, dentry)
|
Self::new(mount, dentry)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn new(mount: Arc<Mount>, dentry: Arc<Dentry>) -> Self {
|
pub(in crate::fs) fn new(mount: Arc<Mount>, dentry: Arc<Dentry>) -> Self {
|
||||||
Self { mount, dentry }
|
Self { mount, dentry }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -87,6 +87,11 @@ impl Path {
|
||||||
&self.mount
|
&self.mount
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Gets the dentry of current `Path`.
|
||||||
|
pub(in crate::fs) fn dentry(&self) -> &Arc<Dentry> {
|
||||||
|
&self.dentry
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns true if the current `Path` is the root of its mount.
|
/// Returns true if the current `Path` is the root of its mount.
|
||||||
pub fn is_mount_root(&self) -> bool {
|
pub fn is_mount_root(&self) -> bool {
|
||||||
Arc::ptr_eq(&self.dentry, self.mount.root_dentry())
|
Arc::ptr_eq(&self.dentry, self.mount.root_dentry())
|
||||||
|
|
@ -203,6 +208,7 @@ impl Path {
|
||||||
&self,
|
&self,
|
||||||
fs: Arc<dyn FileSystem>,
|
fs: Arc<dyn FileSystem>,
|
||||||
flags: PerMountFlags,
|
flags: PerMountFlags,
|
||||||
|
source: Option<String>,
|
||||||
ctx: &Context,
|
ctx: &Context,
|
||||||
) -> Result<Arc<Mount>> {
|
) -> Result<Arc<Mount>> {
|
||||||
if self.type_() != InodeType::Dir {
|
if self.type_() != InodeType::Dir {
|
||||||
|
|
@ -222,7 +228,7 @@ impl Path {
|
||||||
return_errno_with_message!(Errno::EINVAL, "the path is not in this mount namespace");
|
return_errno_with_message!(Errno::EINVAL, "the path is not in this mount namespace");
|
||||||
}
|
}
|
||||||
|
|
||||||
let child_mount = self.mount.do_mount(fs, flags, &self.dentry)?;
|
let child_mount = self.mount.do_mount(fs, flags, &self.dentry, source)?;
|
||||||
|
|
||||||
Ok(child_mount)
|
Ok(child_mount)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -160,6 +160,15 @@ pub struct Mount {
|
||||||
mountpoint: RwLock<Option<Arc<Dentry>>>,
|
mountpoint: RwLock<Option<Arc<Dentry>>>,
|
||||||
/// The associated FS.
|
/// The associated FS.
|
||||||
fs: Arc<dyn FileSystem>,
|
fs: Arc<dyn FileSystem>,
|
||||||
|
/// The mount source (e.g., a device path like "/dev/vda" or a filesystem name like "proc").
|
||||||
|
///
|
||||||
|
/// The source is stored in `Mount` instead of requiring each filesystem to provide it.
|
||||||
|
/// If a filesystem does not provide a source, it falls back to the value stored in `Mount`.
|
||||||
|
/// This behavior aligns with that of Linux. Concrete examples can be found here:
|
||||||
|
/// <https://github.com/asterinas/asterinas/pull/2929#discussion_r2729739818>.
|
||||||
|
///
|
||||||
|
/// Reference: <https://elixir.bootlin.com/linux/v6.17/source/fs/mount.h#L68>
|
||||||
|
source: Option<String>,
|
||||||
/// The parent mount node.
|
/// The parent mount node.
|
||||||
parent: RwLock<Option<Weak<Mount>>>,
|
parent: RwLock<Option<Weak<Mount>>>,
|
||||||
/// Child mount nodes which are mounted on one dentry of self.
|
/// Child mount nodes which are mounted on one dentry of self.
|
||||||
|
|
@ -186,7 +195,8 @@ impl Mount {
|
||||||
fs: Arc<dyn FileSystem>,
|
fs: Arc<dyn FileSystem>,
|
||||||
mnt_ns: Weak<MountNamespace>,
|
mnt_ns: Weak<MountNamespace>,
|
||||||
) -> Arc<Self> {
|
) -> Arc<Self> {
|
||||||
Self::new(fs, PerMountFlags::default(), None, mnt_ns)
|
let source = fs.name().to_string();
|
||||||
|
Self::new(fs, PerMountFlags::default(), None, mnt_ns, Some(source))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates a pseudo mount node with an associated FS.
|
/// Creates a pseudo mount node with an associated FS.
|
||||||
|
|
@ -194,7 +204,7 @@ impl Mount {
|
||||||
/// This pseudo mount is not mounted on other mount nodes, has no parent, and does not
|
/// This pseudo mount is not mounted on other mount nodes, has no parent, and does not
|
||||||
/// belong to any mount namespace.
|
/// belong to any mount namespace.
|
||||||
pub(in crate::fs) fn new_pseudo(fs: Arc<dyn FileSystem>) -> Arc<Self> {
|
pub(in crate::fs) fn new_pseudo(fs: Arc<dyn FileSystem>) -> Arc<Self> {
|
||||||
Self::new(fs, PerMountFlags::KERNMOUNT, None, Weak::new())
|
Self::new(fs, PerMountFlags::KERNMOUNT, None, Weak::new(), None)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The internal constructor.
|
/// The internal constructor.
|
||||||
|
|
@ -210,6 +220,7 @@ impl Mount {
|
||||||
flags: PerMountFlags,
|
flags: PerMountFlags,
|
||||||
parent_mount: Option<Weak<Mount>>,
|
parent_mount: Option<Weak<Mount>>,
|
||||||
mnt_ns: Weak<MountNamespace>,
|
mnt_ns: Weak<MountNamespace>,
|
||||||
|
source: Option<String>,
|
||||||
) -> Arc<Self> {
|
) -> Arc<Self> {
|
||||||
let id = ID_ALLOCATOR.get().unwrap().lock().alloc().unwrap();
|
let id = ID_ALLOCATOR.get().unwrap().lock().alloc().unwrap();
|
||||||
|
|
||||||
|
|
@ -221,6 +232,7 @@ impl Mount {
|
||||||
children: RwLock::new(HashMap::new()),
|
children: RwLock::new(HashMap::new()),
|
||||||
propagation: RwLock::new(MountPropType::default()),
|
propagation: RwLock::new(MountPropType::default()),
|
||||||
fs,
|
fs,
|
||||||
|
source,
|
||||||
mnt_ns,
|
mnt_ns,
|
||||||
flags: AtomicPerMountFlags::new(flags),
|
flags: AtomicPerMountFlags::new(flags),
|
||||||
this: weak_self.clone(),
|
this: weak_self.clone(),
|
||||||
|
|
@ -232,6 +244,11 @@ impl Mount {
|
||||||
self.id
|
self.id
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the mount source.
|
||||||
|
pub(in crate::fs) fn source(&self) -> Option<&str> {
|
||||||
|
self.fs.source().or(self.source.as_deref())
|
||||||
|
}
|
||||||
|
|
||||||
/// Mounts a fs on the mountpoint, it will create a new child mount node.
|
/// Mounts a fs on the mountpoint, it will create a new child mount node.
|
||||||
///
|
///
|
||||||
/// If the given mountpoint has already been mounted, then its mounted child mount
|
/// If the given mountpoint has already been mounted, then its mounted child mount
|
||||||
|
|
@ -242,19 +259,28 @@ impl Mount {
|
||||||
/// It is allowed to mount a fs even if the fs has been provided to another
|
/// It is allowed to mount a fs even if the fs has been provided to another
|
||||||
/// mountpoint. It is the fs's responsibility to ensure the data consistency.
|
/// mountpoint. It is the fs's responsibility to ensure the data consistency.
|
||||||
///
|
///
|
||||||
|
/// If the source is provided by user, it will be recorded in the new mount.
|
||||||
|
///
|
||||||
/// Return the mounted child mount.
|
/// Return the mounted child mount.
|
||||||
pub(super) fn do_mount(
|
pub(super) fn do_mount(
|
||||||
self: &Arc<Self>,
|
self: &Arc<Self>,
|
||||||
fs: Arc<dyn FileSystem>,
|
fs: Arc<dyn FileSystem>,
|
||||||
flags: PerMountFlags,
|
flags: PerMountFlags,
|
||||||
mountpoint: &Arc<Dentry>,
|
mountpoint: &Arc<Dentry>,
|
||||||
|
source: Option<String>,
|
||||||
) -> Result<Arc<Self>> {
|
) -> Result<Arc<Self>> {
|
||||||
if mountpoint.type_() != InodeType::Dir {
|
if mountpoint.type_() != InodeType::Dir {
|
||||||
return_errno!(Errno::ENOTDIR);
|
return_errno!(Errno::ENOTDIR);
|
||||||
}
|
}
|
||||||
|
|
||||||
let key = mountpoint.key();
|
let key = mountpoint.key();
|
||||||
let child_mount = Self::new(fs, flags, Some(Arc::downgrade(self)), self.mnt_ns.clone());
|
let child_mount = Self::new(
|
||||||
|
fs,
|
||||||
|
flags,
|
||||||
|
Some(Arc::downgrade(self)),
|
||||||
|
self.mnt_ns.clone(),
|
||||||
|
source,
|
||||||
|
);
|
||||||
self.children.write().insert(key, child_mount.clone());
|
self.children.write().insert(key, child_mount.clone());
|
||||||
child_mount.set_mountpoint(mountpoint);
|
child_mount.set_mountpoint(mountpoint);
|
||||||
|
|
||||||
|
|
@ -296,6 +322,7 @@ impl Mount {
|
||||||
children: RwLock::new(HashMap::new()),
|
children: RwLock::new(HashMap::new()),
|
||||||
propagation: RwLock::new(MountPropType::default()),
|
propagation: RwLock::new(MountPropType::default()),
|
||||||
fs: self.fs.clone(),
|
fs: self.fs.clone(),
|
||||||
|
source: self.source.clone(),
|
||||||
mnt_ns: new_ns.cloned().unwrap_or_else(|| self.mnt_ns.clone()),
|
mnt_ns: new_ns.cloned().unwrap_or_else(|| self.mnt_ns.clone()),
|
||||||
flags: AtomicPerMountFlags::new(self.flags.load(Ordering::Relaxed)),
|
flags: AtomicPerMountFlags::new(self.flags.load(Ordering::Relaxed)),
|
||||||
this: weak_self.clone(),
|
this: weak_self.clone(),
|
||||||
|
|
@ -405,12 +432,12 @@ impl Mount {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Gets the root `Dentry` of this mount node.
|
/// Gets the root `Dentry` of this mount node.
|
||||||
pub(super) fn root_dentry(&self) -> &Arc<Dentry> {
|
pub(in crate::fs) fn root_dentry(&self) -> &Arc<Dentry> {
|
||||||
&self.root_dentry
|
&self.root_dentry
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Gets the mountpoint `Dentry` of this mount node if any.
|
/// Gets the mountpoint `Dentry` of this mount node if any.
|
||||||
pub(super) fn mountpoint(&self) -> Option<Arc<Dentry>> {
|
pub(in crate::fs) fn mountpoint(&self) -> Option<Arc<Dentry>> {
|
||||||
self.mountpoint.read().clone()
|
self.mountpoint.read().clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -481,7 +508,7 @@ impl Mount {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Gets the parent mount node if any.
|
/// Gets the parent mount node if any.
|
||||||
pub(super) fn parent(&self) -> Option<Weak<Self>> {
|
pub(in crate::fs) fn parent(&self) -> Option<Weak<Self>> {
|
||||||
self.parent.read().as_ref().cloned()
|
self.parent.read().as_ref().cloned()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -491,11 +518,11 @@ impl Mount {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Gets the associated FS.
|
/// Gets the associated FS.
|
||||||
pub(super) fn fs(&self) -> &Arc<dyn FileSystem> {
|
pub(in crate::fs) fn fs(&self) -> &Arc<dyn FileSystem> {
|
||||||
&self.fs
|
&self.fs
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn flags(&self) -> PerMountFlags {
|
pub(in crate::fs) fn flags(&self) -> PerMountFlags {
|
||||||
self.flags.load(Ordering::Relaxed)
|
self.flags.load(Ordering::Relaxed)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,15 +2,14 @@
|
||||||
|
|
||||||
use alloc::str;
|
use alloc::str;
|
||||||
|
|
||||||
use aster_util::printer::VmPrinter;
|
|
||||||
use ostd::task::Task;
|
use ostd::task::Task;
|
||||||
|
|
||||||
use super::Path;
|
use super::{Mount, Path};
|
||||||
use crate::{
|
use crate::{
|
||||||
fs::{
|
fs::{
|
||||||
file_table::{FileDesc, get_file_fast},
|
file_table::{FileDesc, get_file_fast},
|
||||||
path::{MountNamespace, PerMountFlags},
|
path::MountNamespace,
|
||||||
utils::{FsFlags, InodeType, NAME_MAX, PATH_MAX, Permission, SYMLINKS_MAX, SymbolicLink},
|
utils::{InodeType, NAME_MAX, PATH_MAX, Permission, SYMLINKS_MAX, SymbolicLink},
|
||||||
},
|
},
|
||||||
prelude::*,
|
prelude::*,
|
||||||
process::posix_thread::AsThreadLocal,
|
process::posix_thread::AsThreadLocal,
|
||||||
|
|
@ -253,122 +252,43 @@ impl AbsPathResult {
|
||||||
|
|
||||||
// Mount info reading implementation
|
// Mount info reading implementation
|
||||||
impl PathResolver {
|
impl PathResolver {
|
||||||
/// Reads the information of the mounts visible to this resolver.
|
/// Collects the mounts visible to this resolver.
|
||||||
///
|
///
|
||||||
/// Here, the visible mounts are defined as follows:
|
/// Here, the visible mounts are defined as follows:
|
||||||
/// 1. If the resolver's root is a mount point, the visible mounts are the mount of the
|
/// 1. If the resolver's root is a mount point, the visible mounts are the mount of the
|
||||||
/// resolver's root directory and all of its descendant mounts in the mount tree.
|
/// resolver's root directory and all of its descendant mounts in the mount tree.
|
||||||
/// 2. If the resolver's root is not a mount point, the visible mounts are all descendant
|
/// 2. If the resolver's root is not a mount point, the visible mounts are all descendant
|
||||||
/// mounts that are mounted under the resolver's root directory.
|
/// mounts that are mounted under the resolver's root directory.
|
||||||
pub fn read_mount_info(&self, offset: usize, writer: &mut VmWriter) -> Result<usize> {
|
///
|
||||||
let mut printer = VmPrinter::new_skip(writer, offset);
|
/// The mounts are collected in depth-first order.
|
||||||
|
pub(in crate::fs) fn collect_visible_mounts(&self) -> Vec<Arc<Mount>> {
|
||||||
let mut stack = Vec::new();
|
let mut visible = Vec::new();
|
||||||
if self.root.is_mount_root() {
|
let mut stack = vec![self.root.mount.clone()];
|
||||||
stack.push(self.root.mount.clone());
|
let is_root_mount_root = self.root.is_mount_root();
|
||||||
} else {
|
|
||||||
// The root is not a mount root, so we need to find the visible child mounts.
|
|
||||||
let children = self.root.mount.children.read();
|
|
||||||
for child_mount in children.values() {
|
|
||||||
if child_mount
|
|
||||||
.mountpoint()
|
|
||||||
.is_some_and(|dentry| dentry.is_equal_or_descendant_of(&self.root.dentry))
|
|
||||||
{
|
|
||||||
stack.push(child_mount.clone());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
while let Some(mount) = stack.pop() {
|
while let Some(mount) = stack.pop() {
|
||||||
let mount_id = mount.id();
|
let is_root_mount = Arc::ptr_eq(&mount, &self.root.mount);
|
||||||
let parent = mount.parent().and_then(|parent| parent.upgrade());
|
|
||||||
let parent_id = parent.as_ref().map_or(mount_id, |p| p.id());
|
// Add the root mount only if `self` is at the mount root.
|
||||||
let root = mount.root_dentry().path_name();
|
if !is_root_mount || is_root_mount_root {
|
||||||
let mount_point = if let Some(parent) = parent {
|
visible.push(mount.clone());
|
||||||
if let Some(mount_point_dentry) = mount.mountpoint() {
|
|
||||||
self.make_abs_path(&Path::new(parent, mount_point_dentry))
|
|
||||||
.into_string()
|
|
||||||
} else {
|
|
||||||
"".to_string()
|
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
// No parent means it's the root of the namespace.
|
|
||||||
"/".to_string()
|
|
||||||
};
|
|
||||||
let mount_flags = mount.flags();
|
|
||||||
let fs_type = mount.fs().name();
|
|
||||||
let fs_flags = mount.fs().flags();
|
|
||||||
|
|
||||||
// The following fields are dummy for now.
|
|
||||||
let major = 0;
|
|
||||||
let minor = 0;
|
|
||||||
let source = "none";
|
|
||||||
|
|
||||||
let entry = MountInfoEntry {
|
|
||||||
mount_id,
|
|
||||||
parent_id,
|
|
||||||
major,
|
|
||||||
minor,
|
|
||||||
root: &root,
|
|
||||||
mount_point: &mount_point,
|
|
||||||
mount_flags,
|
|
||||||
fs_type,
|
|
||||||
source,
|
|
||||||
fs_flags,
|
|
||||||
};
|
|
||||||
|
|
||||||
writeln!(printer, "{}", entry)?;
|
|
||||||
|
|
||||||
let children = mount.children.read();
|
let children = mount.children.read();
|
||||||
for child_mount in children.values() {
|
for child_mount in children.values() {
|
||||||
|
if is_root_mount && !is_root_mount_root {
|
||||||
|
let Some(mountpoint) = child_mount.mountpoint() else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
if !mountpoint.is_equal_or_descendant_of(&self.root.dentry) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
stack.push(child_mount.clone());
|
stack.push(child_mount.clone());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(printer.bytes_written())
|
visible
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A single entry in the mountinfo file.
|
|
||||||
struct MountInfoEntry<'a> {
|
|
||||||
/// A unique ID for the mount (but not guaranteed to be unique across reboots).
|
|
||||||
mount_id: usize,
|
|
||||||
/// The ID of the parent mount (or self if it has no parent).
|
|
||||||
parent_id: usize,
|
|
||||||
/// The major device ID of the filesystem.
|
|
||||||
major: u32,
|
|
||||||
/// The minor device ID of the filesystem.
|
|
||||||
minor: u32,
|
|
||||||
/// The root of the mount within the filesystem.
|
|
||||||
root: &'a str,
|
|
||||||
/// The mount point relative to the process's root directory.
|
|
||||||
mount_point: &'a str,
|
|
||||||
/// Per-mount flags.
|
|
||||||
mount_flags: PerMountFlags,
|
|
||||||
/// The type of the filesystem in the form "type[.subtype]".
|
|
||||||
fs_type: &'a str,
|
|
||||||
/// Filesystem-specific information or "none".
|
|
||||||
source: &'a str,
|
|
||||||
/// Per-filesystem flags.
|
|
||||||
fs_flags: FsFlags,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl core::fmt::Display for MountInfoEntry<'_> {
|
|
||||||
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
|
||||||
write!(
|
|
||||||
f,
|
|
||||||
"{} {} {}:{} {} {} {} - {} {} {}",
|
|
||||||
self.mount_id,
|
|
||||||
self.parent_id,
|
|
||||||
self.major,
|
|
||||||
self.minor,
|
|
||||||
&self.root,
|
|
||||||
&self.mount_point,
|
|
||||||
&self.mount_flags,
|
|
||||||
&self.fs_type,
|
|
||||||
&self.source,
|
|
||||||
&self.fs_flags,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@ use self::{
|
||||||
cpuinfo::CpuInfoFileOps,
|
cpuinfo::CpuInfoFileOps,
|
||||||
loadavg::LoadAvgFileOps,
|
loadavg::LoadAvgFileOps,
|
||||||
meminfo::MemInfoFileOps,
|
meminfo::MemInfoFileOps,
|
||||||
|
mounts::MountsSymOps,
|
||||||
pid::PidDirOps,
|
pid::PidDirOps,
|
||||||
self_::SelfSymOps,
|
self_::SelfSymOps,
|
||||||
sys::SysDirOps,
|
sys::SysDirOps,
|
||||||
|
|
@ -41,6 +42,7 @@ mod cpuinfo;
|
||||||
mod filesystems;
|
mod filesystems;
|
||||||
mod loadavg;
|
mod loadavg;
|
||||||
mod meminfo;
|
mod meminfo;
|
||||||
|
mod mounts;
|
||||||
mod pid;
|
mod pid;
|
||||||
mod self_;
|
mod self_;
|
||||||
mod stat;
|
mod stat;
|
||||||
|
|
@ -159,6 +161,7 @@ impl RootDirOps {
|
||||||
("filesystems", FileSystemsFileOps::new_inode),
|
("filesystems", FileSystemsFileOps::new_inode),
|
||||||
("loadavg", LoadAvgFileOps::new_inode),
|
("loadavg", LoadAvgFileOps::new_inode),
|
||||||
("meminfo", MemInfoFileOps::new_inode),
|
("meminfo", MemInfoFileOps::new_inode),
|
||||||
|
("mounts", MountsSymOps::new_inode),
|
||||||
("self", SelfSymOps::new_inode),
|
("self", SelfSymOps::new_inode),
|
||||||
("stat", StatFileOps::new_inode),
|
("stat", StatFileOps::new_inode),
|
||||||
("sys", SysDirOps::new_inode),
|
("sys", SysDirOps::new_inode),
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,30 @@
|
||||||
|
// SPDX-License-Identifier: MPL-2.0
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
fs::{
|
||||||
|
procfs::{ProcSymBuilder, SymOps},
|
||||||
|
utils::{Inode, SymbolicLink, mkmod},
|
||||||
|
},
|
||||||
|
prelude::*,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Represents the inode at `/proc/mounts`.
|
||||||
|
pub struct MountsSymOps;
|
||||||
|
|
||||||
|
impl MountsSymOps {
|
||||||
|
pub fn new_inode(parent: Weak<dyn Inode>) -> Arc<dyn Inode> {
|
||||||
|
// Reference:
|
||||||
|
// <https://elixir.bootlin.com/linux/v6.16.5/source/fs/proc/root.c#L291>
|
||||||
|
// <https://elixir.bootlin.com/linux/v6.16.5/source/fs/proc/generic.c#L466>
|
||||||
|
ProcSymBuilder::new(Self, mkmod!(a+rwx))
|
||||||
|
.parent(parent)
|
||||||
|
.build()
|
||||||
|
.unwrap()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SymOps for MountsSymOps {
|
||||||
|
fn read_link(&self) -> Result<SymbolicLink> {
|
||||||
|
Ok(SymbolicLink::Plain("self/mounts".to_string()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -12,8 +12,8 @@ use crate::{
|
||||||
cgroup::CgroupFileOps, cmdline::CmdlineFileOps, comm::CommFileOps,
|
cgroup::CgroupFileOps, cmdline::CmdlineFileOps, comm::CommFileOps,
|
||||||
environ::EnvironFileOps, exe::ExeSymOps, fd::FdDirOps, gid_map::GidMapFileOps,
|
environ::EnvironFileOps, exe::ExeSymOps, fd::FdDirOps, gid_map::GidMapFileOps,
|
||||||
maps::MapsFileOps, mem::MemFileOps, mountinfo::MountInfoFileOps,
|
maps::MapsFileOps, mem::MemFileOps, mountinfo::MountInfoFileOps,
|
||||||
oom_score_adj::OomScoreAdjFileOps, stat::StatFileOps, status::StatusFileOps,
|
mounts::MountsFileOps, oom_score_adj::OomScoreAdjFileOps, stat::StatFileOps,
|
||||||
uid_map::UidMapFileOps,
|
status::StatusFileOps, uid_map::UidMapFileOps,
|
||||||
},
|
},
|
||||||
template::{
|
template::{
|
||||||
DirOps, ProcDir, ProcDirBuilder, lookup_child_from_table,
|
DirOps, ProcDir, ProcDirBuilder, lookup_child_from_table,
|
||||||
|
|
@ -37,6 +37,7 @@ mod gid_map;
|
||||||
mod maps;
|
mod maps;
|
||||||
mod mem;
|
mod mem;
|
||||||
mod mountinfo;
|
mod mountinfo;
|
||||||
|
mod mounts;
|
||||||
mod oom_score_adj;
|
mod oom_score_adj;
|
||||||
mod stat;
|
mod stat;
|
||||||
mod status;
|
mod status;
|
||||||
|
|
@ -115,6 +116,7 @@ impl TidDirOps {
|
||||||
("status", StatusFileOps::new_inode),
|
("status", StatusFileOps::new_inode),
|
||||||
("uid_map", UidMapFileOps::new_inode),
|
("uid_map", UidMapFileOps::new_inode),
|
||||||
("maps", MapsFileOps::new_inode),
|
("maps", MapsFileOps::new_inode),
|
||||||
|
("mounts", MountsFileOps::new_inode),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,84 @@
|
||||||
// SPDX-License-Identifier: MPL-2.0
|
// SPDX-License-Identifier: MPL-2.0
|
||||||
|
|
||||||
|
use aster_util::printer::VmPrinter;
|
||||||
|
|
||||||
use super::TidDirOps;
|
use super::TidDirOps;
|
||||||
use crate::{
|
use crate::{
|
||||||
fs::{
|
fs::{
|
||||||
|
path::{Mount, Path, PathResolver, PerMountFlags},
|
||||||
procfs::template::{FileOps, ProcFileBuilder},
|
procfs::template::{FileOps, ProcFileBuilder},
|
||||||
utils::{Inode, mkmod},
|
utils::{FsFlags, Inode, mkmod},
|
||||||
},
|
},
|
||||||
prelude::*,
|
prelude::*,
|
||||||
process::posix_thread::AsPosixThread,
|
process::posix_thread::AsPosixThread,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// A helper function to create the mount point path for a given mount (used by `mounts` and `mountinfo`).
|
||||||
|
pub(super) fn make_mount_point_path(
|
||||||
|
is_resolver_root_mount: bool,
|
||||||
|
parent: Option<&Arc<Mount>>,
|
||||||
|
mount: &Mount,
|
||||||
|
path_resolver: &PathResolver,
|
||||||
|
) -> String {
|
||||||
|
if is_resolver_root_mount {
|
||||||
|
"/".to_string()
|
||||||
|
} else if let Some(parent) = parent {
|
||||||
|
if let Some(mount_point_dentry) = mount.mountpoint() {
|
||||||
|
path_resolver
|
||||||
|
.make_abs_path(&Path::new(parent.clone(), mount_point_dentry))
|
||||||
|
.into_string()
|
||||||
|
} else {
|
||||||
|
"".to_string()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// No parent means it's the root of the namespace.
|
||||||
|
"/".to_string()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A single entry in the mountinfo file.
|
||||||
|
struct MountInfoEntry<'a> {
|
||||||
|
/// A unique ID for the mount (but not guaranteed to be unique across reboots).
|
||||||
|
mount_id: usize,
|
||||||
|
/// The ID of the parent mount (or self if it has no parent).
|
||||||
|
parent_id: usize,
|
||||||
|
/// The major device ID of the filesystem.
|
||||||
|
major: u32,
|
||||||
|
/// The minor device ID of the filesystem.
|
||||||
|
minor: u32,
|
||||||
|
/// The root of the mount within the filesystem.
|
||||||
|
root: &'a str,
|
||||||
|
/// The mount point relative to the process's root directory.
|
||||||
|
mount_point: &'a str,
|
||||||
|
/// Per-mount flags.
|
||||||
|
mount_flags: PerMountFlags,
|
||||||
|
/// The type of the filesystem in the form "type[.subtype]".
|
||||||
|
fs_type: &'a str,
|
||||||
|
/// Filesystem-specific information or "none".
|
||||||
|
source: &'a str,
|
||||||
|
/// Per-filesystem flags.
|
||||||
|
fs_flags: FsFlags,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl core::fmt::Display for MountInfoEntry<'_> {
|
||||||
|
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||||
|
write!(
|
||||||
|
f,
|
||||||
|
"{} {} {}:{} {} {} {} - {} {} {}",
|
||||||
|
self.mount_id,
|
||||||
|
self.parent_id,
|
||||||
|
self.major,
|
||||||
|
self.minor,
|
||||||
|
&self.root,
|
||||||
|
&self.mount_point,
|
||||||
|
&self.mount_flags,
|
||||||
|
&self.fs_type,
|
||||||
|
&self.source,
|
||||||
|
&self.fs_flags,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Represents the inode at `/proc/[pid]/task/[tid]/mountinfo` (and also `/proc/[pid]/mountinfo`).
|
/// Represents the inode at `/proc/[pid]/task/[tid]/mountinfo` (and also `/proc/[pid]/mountinfo`).
|
||||||
pub struct MountInfoFileOps(TidDirOps);
|
pub struct MountInfoFileOps(TidDirOps);
|
||||||
|
|
||||||
|
|
@ -21,6 +90,62 @@ impl MountInfoFileOps {
|
||||||
.build()
|
.build()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Reads mount information for `/proc/[pid]/mountinfo`.
|
||||||
|
///
|
||||||
|
/// Provides detailed mount information including mount IDs, parent relationships,
|
||||||
|
/// and device numbers.
|
||||||
|
fn read_mount_info(
|
||||||
|
&self,
|
||||||
|
path_resolver: &PathResolver,
|
||||||
|
offset: usize,
|
||||||
|
writer: &mut VmWriter,
|
||||||
|
) -> Result<usize> {
|
||||||
|
let mut printer = VmPrinter::new_skip(writer, offset);
|
||||||
|
|
||||||
|
for mount in path_resolver.collect_visible_mounts() {
|
||||||
|
let mount_id = mount.id();
|
||||||
|
let parent = mount.parent().and_then(|parent| parent.upgrade());
|
||||||
|
let parent_id = parent.as_ref().map_or(mount_id, |p| p.id());
|
||||||
|
let is_resolver_root_mount = Arc::ptr_eq(&mount, path_resolver.root().mount_node());
|
||||||
|
let root = if is_resolver_root_mount {
|
||||||
|
path_resolver.root().dentry().path_name()
|
||||||
|
} else {
|
||||||
|
mount.root_dentry().path_name()
|
||||||
|
};
|
||||||
|
let mount_point = make_mount_point_path(
|
||||||
|
is_resolver_root_mount,
|
||||||
|
parent.as_ref(),
|
||||||
|
mount.as_ref(),
|
||||||
|
path_resolver,
|
||||||
|
);
|
||||||
|
let mount_flags = mount.flags();
|
||||||
|
let fs_type = mount.fs().name();
|
||||||
|
let source = mount.source().unwrap_or("none");
|
||||||
|
let fs_flags = mount.fs().flags();
|
||||||
|
|
||||||
|
// The following fields are dummy for now.
|
||||||
|
let major = 0;
|
||||||
|
let minor = 0;
|
||||||
|
|
||||||
|
let entry = MountInfoEntry {
|
||||||
|
mount_id,
|
||||||
|
parent_id,
|
||||||
|
major,
|
||||||
|
minor,
|
||||||
|
root: &root,
|
||||||
|
mount_point: &mount_point,
|
||||||
|
mount_flags,
|
||||||
|
fs_type,
|
||||||
|
source,
|
||||||
|
fs_flags,
|
||||||
|
};
|
||||||
|
|
||||||
|
writeln!(printer, "{}", entry)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(printer.bytes_written())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FileOps for MountInfoFileOps {
|
impl FileOps for MountInfoFileOps {
|
||||||
|
|
@ -30,7 +155,6 @@ impl FileOps for MountInfoFileOps {
|
||||||
|
|
||||||
let fs = posix_thread.read_fs();
|
let fs = posix_thread.read_fs();
|
||||||
let path_resolver = fs.resolver().read();
|
let path_resolver = fs.resolver().read();
|
||||||
|
self.read_mount_info(&path_resolver, offset, writer)
|
||||||
path_resolver.read_mount_info(offset, writer)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,121 @@
|
||||||
|
// SPDX-License-Identifier: MPL-2.0
|
||||||
|
|
||||||
|
use aster_util::printer::VmPrinter;
|
||||||
|
|
||||||
|
use super::TidDirOps;
|
||||||
|
use crate::{
|
||||||
|
fs::{
|
||||||
|
path::{PathResolver, PerMountFlags},
|
||||||
|
procfs::{
|
||||||
|
pid::task::mountinfo::make_mount_point_path,
|
||||||
|
template::{FileOps, ProcFileBuilder},
|
||||||
|
},
|
||||||
|
utils::{Inode, mkmod},
|
||||||
|
},
|
||||||
|
prelude::*,
|
||||||
|
process::posix_thread::AsPosixThread,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// A single entry in the mounts file.
|
||||||
|
struct MountEntry<'a> {
|
||||||
|
/// Filesystem-specific information or "none".
|
||||||
|
source: &'a str,
|
||||||
|
/// Mount point relative to the process's root directory.
|
||||||
|
mount_point: &'a str,
|
||||||
|
/// The type of the filesystem in the form "type[.subtype]".
|
||||||
|
fs_type: &'a str,
|
||||||
|
/// Per-mount flags.
|
||||||
|
mount_flags: PerMountFlags,
|
||||||
|
/// The dump field is used by the dump(8) program to determine which
|
||||||
|
/// filesystems need to be dumped.
|
||||||
|
dump: u32,
|
||||||
|
/// The fsck(8) program uses this field.
|
||||||
|
pass: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl core::fmt::Display for MountEntry<'_> {
|
||||||
|
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||||
|
write!(
|
||||||
|
f,
|
||||||
|
"{} {} {} {} {} {}",
|
||||||
|
&self.source,
|
||||||
|
&self.mount_point,
|
||||||
|
&self.fs_type,
|
||||||
|
&self.mount_flags,
|
||||||
|
&self.dump,
|
||||||
|
&self.pass,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Represents the inode at `/proc/[pid]/task/[tid]/mounts` (and also `/proc/[pid]/mounts`).
|
||||||
|
pub struct MountsFileOps(TidDirOps);
|
||||||
|
|
||||||
|
impl MountsFileOps {
|
||||||
|
pub fn new_inode(dir: &TidDirOps, parent: Weak<dyn Inode>) -> Arc<dyn Inode> {
|
||||||
|
// Reference: <https://elixir.bootlin.com/linux/v6.16.5/source/fs/proc/base.c#L3351>
|
||||||
|
ProcFileBuilder::new(Self(dir.clone()), mkmod!(a+r))
|
||||||
|
.parent(parent)
|
||||||
|
.build()
|
||||||
|
.unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Reads mount information for `/proc/[pid]/mounts` and `/proc/mounts`.
|
||||||
|
///
|
||||||
|
/// Provides a simplified view of mounted filesystems in the traditional
|
||||||
|
/// `/etc/fstab` format.
|
||||||
|
fn read_mounts(
|
||||||
|
&self,
|
||||||
|
path_resolver: &PathResolver,
|
||||||
|
offset: usize,
|
||||||
|
writer: &mut VmWriter,
|
||||||
|
) -> Result<usize> {
|
||||||
|
let mut printer = VmPrinter::new_skip(writer, offset);
|
||||||
|
|
||||||
|
for mount in path_resolver.collect_visible_mounts() {
|
||||||
|
let parent = mount.parent().and_then(|parent| parent.upgrade());
|
||||||
|
let is_resolver_root_mount = Arc::ptr_eq(&mount, path_resolver.root().mount_node());
|
||||||
|
let mount_point = make_mount_point_path(
|
||||||
|
is_resolver_root_mount,
|
||||||
|
parent.as_ref(),
|
||||||
|
mount.as_ref(),
|
||||||
|
path_resolver,
|
||||||
|
);
|
||||||
|
let mount_flags = mount.flags();
|
||||||
|
let fs_type = mount.fs().name();
|
||||||
|
let source = mount.source().unwrap_or("none");
|
||||||
|
|
||||||
|
// The dump and pass fields are hardcoded to 0, because the kernel considers them
|
||||||
|
// userspace policy (managed by /etc/fstab) and does not store them in the VFS layer.
|
||||||
|
// This behavior is consistent with Linux.
|
||||||
|
//
|
||||||
|
// Reference: <https://elixir.bootlin.com/linux/v6.16.5/source/fs/proc_namespace.c#L130>.
|
||||||
|
let dump = 0;
|
||||||
|
let pass = 0;
|
||||||
|
|
||||||
|
let entry = MountEntry {
|
||||||
|
source,
|
||||||
|
mount_point: &mount_point,
|
||||||
|
fs_type,
|
||||||
|
mount_flags,
|
||||||
|
dump,
|
||||||
|
pass,
|
||||||
|
};
|
||||||
|
|
||||||
|
writeln!(printer, "{}", entry)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(printer.bytes_written())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FileOps for MountsFileOps {
|
||||||
|
fn read_at(&self, offset: usize, writer: &mut VmWriter) -> Result<usize> {
|
||||||
|
let thread = self.0.thread();
|
||||||
|
let posix_thread = thread.as_posix_thread().unwrap();
|
||||||
|
|
||||||
|
let fs = posix_thread.read_fs();
|
||||||
|
let path_resolver = fs.resolver().read();
|
||||||
|
self.read_mounts(&path_resolver, offset, writer)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -150,6 +150,11 @@ pub trait FileSystem: Any + Sync + Send {
|
||||||
/// Gets the name of this FS type such as `"ext4"` or `"sysfs"`.
|
/// Gets the name of this FS type such as `"ext4"` or `"sysfs"`.
|
||||||
fn name(&self) -> &'static str;
|
fn name(&self) -> &'static str;
|
||||||
|
|
||||||
|
/// Gets the source of this file system, e.g., the device name or user-provided source string.
|
||||||
|
fn source(&self) -> Option<&str> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
/// Syncs the file system.
|
/// Syncs the file system.
|
||||||
fn sync(&self) -> Result<()>;
|
fn sync(&self) -> Result<()>;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ use super::SyscallReturn;
|
||||||
use crate::{
|
use crate::{
|
||||||
fs::{
|
fs::{
|
||||||
path::{AT_FDCWD, FsPath, MountPropType, Path, PerMountFlags},
|
path::{AT_FDCWD, FsPath, MountPropType, Path, PerMountFlags},
|
||||||
registry::FsProperties,
|
registry::{FsProperties, FsType},
|
||||||
utils::{FileSystem, FsFlags, InodeType},
|
utils::{FileSystem, FsFlags, InodeType},
|
||||||
},
|
},
|
||||||
prelude::*,
|
prelude::*,
|
||||||
|
|
@ -192,22 +192,44 @@ fn do_new_mount(
|
||||||
return_errno_with_message!(Errno::ENOTDIR, "mountpoint must be directory");
|
return_errno_with_message!(Errno::ENOTDIR, "mountpoint must be directory");
|
||||||
};
|
};
|
||||||
|
|
||||||
let fs_type = ctx
|
let fs_type = {
|
||||||
|
let fs_type_cstr = ctx
|
||||||
.user_space()
|
.user_space()
|
||||||
.read_cstring(fs_type_addr, MAX_FILENAME_LEN)?;
|
.read_cstring(fs_type_addr, MAX_FILENAME_LEN)?;
|
||||||
if fs_type.is_empty() {
|
if fs_type_cstr.is_empty() {
|
||||||
return_errno_with_message!(Errno::EINVAL, "fs_type is empty");
|
return_errno_with_message!(Errno::EINVAL, "empty file system type");
|
||||||
}
|
}
|
||||||
let fs = get_fs(src_name_addr, flags, fs_type, data_addr, ctx)?;
|
|
||||||
target_path.mount(fs, flags.into(), ctx)?;
|
let fs_type_str = fs_type_cstr
|
||||||
|
.to_str()
|
||||||
|
.map_err(|_| Error::with_message(Errno::ENODEV, "invalid file system type"))?;
|
||||||
|
crate::fs::registry::look_up(fs_type_str).ok_or(Error::with_message(
|
||||||
|
Errno::ENODEV,
|
||||||
|
"the filesystem is not configured in the kernel",
|
||||||
|
))?
|
||||||
|
};
|
||||||
|
|
||||||
|
let source = if src_name_addr == 0 {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
let source = ctx
|
||||||
|
.user_space()
|
||||||
|
.read_cstring(src_name_addr, MAX_FILENAME_LEN)?
|
||||||
|
.to_string_lossy()
|
||||||
|
.into_owned();
|
||||||
|
Some(source)
|
||||||
|
};
|
||||||
|
|
||||||
|
let fs = open_fs(source.as_deref(), flags, fs_type, data_addr, ctx)?;
|
||||||
|
target_path.mount(fs, flags.into(), source, ctx)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Gets the filesystem by fs_type and devname.
|
/// Gets the filesystem by fs_type and dev_name.
|
||||||
fn get_fs(
|
fn open_fs(
|
||||||
src_name_addr: Vaddr,
|
dev_name: Option<&str>,
|
||||||
flags: MountFlags,
|
flags: MountFlags,
|
||||||
fs_type: CString,
|
fs_type: &dyn FsType,
|
||||||
data_addr: Vaddr,
|
data_addr: Vaddr,
|
||||||
ctx: &Context,
|
ctx: &Context,
|
||||||
) -> Result<Arc<dyn FileSystem>> {
|
) -> Result<Arc<dyn FileSystem>> {
|
||||||
|
|
@ -218,18 +240,10 @@ fn get_fs(
|
||||||
Some(user_space.read_cstring(data_addr, MAX_FILENAME_LEN)?)
|
Some(user_space.read_cstring(data_addr, MAX_FILENAME_LEN)?)
|
||||||
};
|
};
|
||||||
|
|
||||||
let fs_type = fs_type
|
|
||||||
.to_str()
|
|
||||||
.map_err(|_| Error::with_message(Errno::ENODEV, "invalid file system type"))?;
|
|
||||||
let fs_type = crate::fs::registry::look_up(fs_type).ok_or(Error::with_message(
|
|
||||||
Errno::ENODEV,
|
|
||||||
"the filesystem is not configured in the kernel",
|
|
||||||
))?;
|
|
||||||
|
|
||||||
let disk = if fs_type.properties().contains(FsProperties::NEED_DISK) {
|
let disk = if fs_type.properties().contains(FsProperties::NEED_DISK) {
|
||||||
let devname = user_space.read_cstring(src_name_addr, MAX_FILENAME_LEN)?;
|
let dev_name = dev_name
|
||||||
let path = devname.to_string_lossy();
|
.ok_or_else(|| Error::with_message(Errno::EINVAL, "the source is not specified"))?;
|
||||||
let fs_path = FsPath::from_fd_and_path(AT_FDCWD, path.as_ref())?;
|
let fs_path = FsPath::from_fd_and_path(AT_FDCWD, dev_name)?;
|
||||||
let path = ctx
|
let path = ctx
|
||||||
.thread_local
|
.thread_local
|
||||||
.borrow_fs()
|
.borrow_fs()
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,6 @@ ProcCpuinfo.RequiredFieldsArePresent
|
||||||
ProcCpuinfo.DeniesWriteNonRoot
|
ProcCpuinfo.DeniesWriteNonRoot
|
||||||
ProcFilesystems.OverflowID
|
ProcFilesystems.OverflowID
|
||||||
ProcFilesystems.PresenceOfShmMaxMniAll
|
ProcFilesystems.PresenceOfShmMaxMniAll
|
||||||
ProcMounts.IsSymlink
|
|
||||||
ProcPid.AccessDeny
|
ProcPid.AccessDeny
|
||||||
ProcPidCwd.Subprocess
|
ProcPidCwd.Subprocess
|
||||||
ProcPidRoot.Subprocess
|
ProcPidRoot.Subprocess
|
||||||
|
|
@ -31,8 +30,6 @@ ProcSelfFdInfo.Flags
|
||||||
# TODO: Mappings created with only `PROT_WRITE` should be shown as `-w-`.
|
# TODO: Mappings created with only `PROT_WRITE` should be shown as `-w-`.
|
||||||
ProcSelfMaps.Map2
|
ProcSelfMaps.Map2
|
||||||
ProcSelfMaps.MapUnmap
|
ProcSelfMaps.MapUnmap
|
||||||
ProcSelfMounts.ContainsProcfsEntry
|
|
||||||
ProcSelfMounts.RequiredFieldsArePresent
|
|
||||||
ProcSelfRoot.IsRoot
|
ProcSelfRoot.IsRoot
|
||||||
ProcSelfStat.PopulateWriteRSS
|
ProcSelfStat.PopulateWriteRSS
|
||||||
ProcSysKernelHostname.Exists
|
ProcSysKernelHostname.Exists
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue