180 lines
5.9 KiB
Rust
180 lines
5.9 KiB
Rust
// SPDX-License-Identifier: MPL-2.0
|
|
|
|
use spin::Once;
|
|
|
|
use crate::{
|
|
fs::path::MountNamespace,
|
|
net::uts_ns::UtsNamespace,
|
|
prelude::*,
|
|
process::{CloneFlags, UserNamespace, posix_thread::PosixThread},
|
|
};
|
|
|
|
/// A struct that acts as a per-thread proxy to give access to most namespaces.
|
|
///
|
|
/// Each `PosixThread` owns an instance of `NsProxy`
|
|
/// and keeps a local copy in `ThreadLocal` for fast access.
|
|
/// `NsProxy` contains all types of namespaces except
|
|
/// 1. The user namespace, which is included in the `Process` struct.
|
|
/// 2. The PID namespace, which is included in the `Process` struct (TODO).
|
|
pub struct NsProxy {
|
|
uts_ns: Arc<UtsNamespace>,
|
|
mnt_ns: Arc<MountNamespace>,
|
|
}
|
|
|
|
impl NsProxy {
|
|
/// Returns a reference to the singleton initial `NsProxy`.
|
|
pub(in crate::process) fn get_init_singleton() -> &'static Arc<Self> {
|
|
static INIT: Once<Arc<NsProxy>> = Once::new();
|
|
INIT.call_once(|| {
|
|
Arc::new(NsProxy {
|
|
uts_ns: UtsNamespace::get_init_singleton().clone(),
|
|
mnt_ns: MountNamespace::get_init_singleton().clone(),
|
|
})
|
|
})
|
|
}
|
|
|
|
/// Creates a new `NsProxy` by cloning from an existing `NsProxy`.
|
|
///
|
|
/// If no namespaces need to be cloned, this method simply clones `self` and returns.
|
|
/// Otherwise, a new `NsProxy` will be created
|
|
/// by selectively cloning fields from the proxy and newly created namespaces.
|
|
//
|
|
// FIXME: This method is currently used by both `unshare()` and `clone()`.
|
|
// Once we support PID and time namespaces, their semantics diverge.
|
|
// We will need to refactor (or split) this method accordingly.
|
|
pub(in crate::process) fn new_clone(
|
|
self: &Arc<Self>,
|
|
user_ns: &Arc<UserNamespace>,
|
|
clone_flags: CloneFlags,
|
|
posix_thread: &PosixThread,
|
|
) -> Result<Arc<Self>> {
|
|
let clone_ns_flags = (clone_flags & CloneFlags::CLONE_NS_FLAGS) - CloneFlags::CLONE_NEWUSER;
|
|
|
|
// Fast path: If there are no new namespaces to clone,
|
|
// we can directly clone the proxy and return.
|
|
if clone_ns_flags.is_empty() {
|
|
return Ok(self.clone());
|
|
}
|
|
|
|
// Slow path: One or more namespaces need to be cloned,
|
|
// so a new `NsProxy` must be created.
|
|
|
|
check_unsupported_ns_flags(clone_ns_flags)?;
|
|
|
|
let mut builder = NsProxyBuilder::new(self);
|
|
|
|
if clone_ns_flags.contains(CloneFlags::CLONE_NEWUTS) {
|
|
let uts_ns = self.uts_ns.new_clone(user_ns.clone(), posix_thread)?;
|
|
builder.uts_ns(uts_ns);
|
|
}
|
|
|
|
if clone_ns_flags.contains(CloneFlags::CLONE_NEWNS) {
|
|
let new_mnt_ns = self.mnt_ns.new_clone(user_ns.clone(), posix_thread)?;
|
|
builder.mnt_ns(new_mnt_ns);
|
|
}
|
|
|
|
// TODO: Support other namespaces.
|
|
|
|
Ok(Arc::new(builder.build()))
|
|
}
|
|
|
|
/// Returns the associated UTS namespace.
|
|
pub fn uts_ns(&self) -> &Arc<UtsNamespace> {
|
|
&self.uts_ns
|
|
}
|
|
|
|
/// Returns the associated mount namespace.
|
|
pub fn mnt_ns(&self) -> &Arc<MountNamespace> {
|
|
&self.mnt_ns
|
|
}
|
|
}
|
|
|
|
/// A builder for creating a new `NsProxy` by selectively cloning namespaces
|
|
/// from an existing one.
|
|
pub struct NsProxyBuilder<'a> {
|
|
old_proxy: &'a NsProxy,
|
|
|
|
// Fields for new namespaces.
|
|
uts_ns: Option<Arc<UtsNamespace>>,
|
|
mnt_ns: Option<Arc<MountNamespace>>,
|
|
}
|
|
|
|
impl<'a> NsProxyBuilder<'a> {
|
|
/// Creates a builder based on an existing `NsProxy`.
|
|
pub fn new(old_proxy: &'a NsProxy) -> Self {
|
|
Self {
|
|
old_proxy,
|
|
uts_ns: None,
|
|
mnt_ns: None,
|
|
}
|
|
}
|
|
|
|
/// Sets the new UTS namespace.
|
|
pub fn uts_ns(&mut self, uts_ns: Arc<UtsNamespace>) -> &mut Self {
|
|
self.uts_ns = Some(uts_ns);
|
|
self
|
|
}
|
|
|
|
/// Sets the new mount namespace for the context being built.
|
|
pub fn mnt_ns(&mut self, mnt_ns: Arc<MountNamespace>) -> &mut Self {
|
|
self.mnt_ns = Some(mnt_ns);
|
|
self
|
|
}
|
|
|
|
/// Builds the new `NsProxy`.
|
|
pub fn build(self) -> NsProxy {
|
|
let Self {
|
|
old_proxy,
|
|
uts_ns: new_uts,
|
|
mnt_ns: new_mnt,
|
|
} = self;
|
|
|
|
let new_uts = new_uts.unwrap_or_else(|| old_proxy.uts_ns.clone());
|
|
let new_mnt = new_mnt.unwrap_or_else(|| old_proxy.mnt_ns.clone());
|
|
|
|
NsProxy {
|
|
uts_ns: new_uts,
|
|
mnt_ns: new_mnt,
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Checks if the given `flags` contain any unsupported namespace-related flags.
|
|
///
|
|
/// This method does _not_ check CLONE_NEWUSER since it's handled separately.
|
|
pub fn check_unsupported_ns_flags(flags: CloneFlags) -> Result<()> {
|
|
const SUPPORTED_FLAGS: CloneFlags = CloneFlags::CLONE_NEWUTS.union(CloneFlags::CLONE_NEWNS);
|
|
|
|
let unsupported_flags =
|
|
(flags & CloneFlags::CLONE_NS_FLAGS) - SUPPORTED_FLAGS - CloneFlags::CLONE_NEWUSER;
|
|
if unsupported_flags.is_empty() {
|
|
return Ok(());
|
|
}
|
|
|
|
warn!("unsupported clone ns flags: {:?}", unsupported_flags);
|
|
return_errno_with_message!(Errno::EINVAL, "unsupported clone namespace flags");
|
|
}
|
|
|
|
/// Provides administrative APIs for switching to existing namespaces.
|
|
pub trait ContextSetNsAdminApi {
|
|
/// Sets the namespace proxy for this context.
|
|
fn set_ns_proxy(&self, ns_proxy: Arc<NsProxy>);
|
|
}
|
|
|
|
impl ContextSetNsAdminApi for Context<'_> {
|
|
fn set_ns_proxy(&self, ns_proxy: Arc<NsProxy>) {
|
|
let mut pthread_ns_proxy = self.posix_thread.ns_proxy().lock();
|
|
let mut thread_local_ns_proxy = self.thread_local.borrow_ns_proxy_mut();
|
|
|
|
// TODO: When setting a specific namespace,
|
|
// other dependent fields of a posix thread may also need to be updated.
|
|
|
|
if !Arc::ptr_eq(&thread_local_ns_proxy.unwrap().mnt_ns, &ns_proxy.mnt_ns) {
|
|
*self.thread_local.borrow_fs().resolver().write() = ns_proxy.mnt_ns.new_path_resolver();
|
|
}
|
|
|
|
*pthread_ns_proxy = Some(ns_proxy.clone());
|
|
thread_local_ns_proxy.replace(Some(ns_proxy));
|
|
}
|
|
}
|