Support MS_PRIVATE flag for mount

This commit is contained in:
Chen Chengjun 2025-09-02 07:05:24 +00:00 committed by Tate, Hongliang Tian
parent 2b18c893a8
commit 07d2d1db02
4 changed files with 93 additions and 11 deletions

View File

@ -58,7 +58,6 @@ Partially supported mount flags:
Unsupported mount flags:
* `MS_REMOUNT`
* `MS_SHARED`
* `MS_PRIVATE`
* `MS_SLAVE`
* `MS_UNBINDABLE`

View File

@ -5,7 +5,7 @@
use core::time::Duration;
use inherit_methods_macro::inherit_methods;
pub use mount::Mount;
pub use mount::{Mount, MountPropType};
pub use mount_namespace::MountNamespace;
use crate::{
@ -321,6 +321,28 @@ impl Path {
self.mount.graft_mount_tree(dst_path)
}
/// Sets the propagation type of the mount of this `Path`.
pub fn set_mount_propagation(
&self,
prop: MountPropType,
recursive: bool,
ctx: &Context,
) -> Result<()> {
if !self.is_mount_root() {
return_errno_with_message!(Errno::EINVAL, "the path is not a mount root");
};
let current_ns_proxy = ctx.thread_local.borrow_ns_proxy();
let current_mnt_ns = current_ns_proxy.unwrap().mnt_ns();
if !current_mnt_ns.owns(&self.mount) {
return_errno_with_message!(Errno::EINVAL, "the path is not in this mount namespace");
}
self.mount.set_propagation(prop, recursive);
Ok(())
}
/// Creates a `Path` by making an inode of the `type_` with the `mode`.
pub fn mknod(&self, name: &str, mode: InodeMode, type_: MknodType) -> Result<Self> {
let inner = self.dentry.mknod(name, mode, type_)?;

View File

@ -14,6 +14,24 @@ use crate::{
prelude::*,
};
/// Mount propagation types.
///
/// This type defines how mount and unmount events are propagated
/// from this mount to other mounts.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum MountPropType {
/// A private type is the default mount type. Mount and unmount events
/// do not propagate to or from the private mounts.
Private,
// TODO: Implement other propagation types.
}
impl Default for MountPropType {
fn default() -> Self {
Self::Private
}
}
/// A `Mount` represents a mounted filesystem instance in the VFS.
///
/// Each `Mount` can be viewed as a node in the mount tree, maintaining
@ -32,6 +50,8 @@ pub struct Mount {
pub(super) children: RwLock<HashMap<DentryKey, Arc<Self>>>,
/// The associated mount namespace.
mnt_ns: Weak<MountNamespace>,
/// Propagation type of this mount (e.g., private, shared).
propagation: RwLock<MountPropType>,
/// Reference to self.
this: Weak<Self>,
}
@ -69,6 +89,7 @@ impl Mount {
mountpoint: RwLock::new(None),
parent: RwLock::new(parent_mount),
children: RwLock::new(HashMap::new()),
propagation: RwLock::new(MountPropType::default()),
fs,
mnt_ns,
this: weak_self.clone(),
@ -135,6 +156,7 @@ impl Mount {
mountpoint: RwLock::new(None),
parent: RwLock::new(None),
children: RwLock::new(HashMap::new()),
propagation: RwLock::new(MountPropType::default()),
fs: self.fs.clone(),
mnt_ns: new_ns.cloned().unwrap_or_else(|| self.mnt_ns.clone()),
this: weak_self.clone(),
@ -190,6 +212,20 @@ impl Mount {
new_root_mount
}
/// Sets the propagation type of this mount.
pub(super) fn set_propagation(&self, prop: MountPropType, recursive: bool) {
*self.propagation.write() = prop;
if !recursive {
return;
}
let mut worklist: VecDeque<Arc<Mount>> = self.children.read().values().cloned().collect();
while let Some(mount) = worklist.pop_front() {
*mount.propagation.write() = prop;
worklist.extend(mount.children.read().values().cloned());
}
}
/// Detaches the mount node from the parent mount node.
pub(super) fn detach_from_parent(&self) {
if let Some(parent) = self.parent() {

View File

@ -4,7 +4,7 @@ use super::SyscallReturn;
use crate::{
fs::{
fs_resolver::{FsPath, AT_FDCWD},
path::Path,
path::{MountPropType, Path},
registry::FsProperties,
utils::{FileSystem, InodeType},
},
@ -57,12 +57,8 @@ pub fn sys_mount(
mount_flags.contains(MountFlags::MS_REC),
ctx,
)?;
} 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.intersects(MS_PROPAGATION) {
do_change_type(dst_path, mount_flags, ctx)?;
} else if mount_flags.contains(MountFlags::MS_MOVE) {
do_move_mount_old(devname, dst_path, ctx)?;
} else {
@ -106,8 +102,37 @@ fn do_bind_mount(src_name: CString, dst_path: Path, recursive: bool, ctx: &Conte
Ok(())
}
fn do_change_type() -> Result<()> {
return_errno_with_message!(Errno::EINVAL, "do_change_type is not supported");
// All valid propagation flags.
const MS_PROPAGATION: MountFlags = MountFlags::MS_SHARED
.union(MountFlags::MS_PRIVATE)
.union(MountFlags::MS_SLAVE)
.union(MountFlags::MS_UNBINDABLE);
fn do_change_type(target_path: Path, flags: MountFlags, ctx: &Context) -> Result<()> {
// All flags that are allowed during a propagation change.
const ALLOWED_FLAGS: MountFlags = MS_PROPAGATION
.union(MountFlags::MS_REC)
.union(MountFlags::MS_SILENT);
if !(flags & !ALLOWED_FLAGS).is_empty() {
return_errno_with_message!(Errno::EINVAL, "the mount propagation flags are unsupported");
}
let propagation_flags = flags & MS_PROPAGATION;
if propagation_flags.bits().count_ones() > 1 {
return_errno_with_message!(
Errno::EINVAL,
"mount flags includes more than one of MS_SHARED, MS_PRIVATE, MS_SLAVE, or MS_UNBINDABLE"
);
}
if flags.contains(MountFlags::MS_PRIVATE) {
let recursive = flags.contains(MountFlags::MS_REC);
target_path.set_mount_propagation(MountPropType::Private, recursive, ctx)?;
Ok(())
} else {
return_errno_with_message!(Errno::EINVAL, "the mount propagation type is unsupported");
}
}
/// Move a mount from src location to dst location.