diff --git a/Cargo.toml b/Cargo.toml index 77caf6a40..6137e6bd3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -32,7 +32,7 @@ compiler_builtins = { version = '0.1.49', optional = true } [target.'cfg(all(not(rustix_use_libc), not(miri), target_os = "linux", any(target_endian = "little", any(target_arch = "s390x", target_arch = "powerpc")), any(target_arch = "arm", all(target_arch = "aarch64", target_pointer_width = "64"), target_arch = "riscv64", all(rustix_use_experimental_asm, target_arch = "powerpc"), all(rustix_use_experimental_asm, target_arch = "powerpc64"), all(rustix_use_experimental_asm, target_arch = "s390x"), all(rustix_use_experimental_asm, target_arch = "mips"), all(rustix_use_experimental_asm, target_arch = "mips32r6"), all(rustix_use_experimental_asm, target_arch = "mips64"), all(rustix_use_experimental_asm, target_arch = "mips64r6"), target_arch = "x86", all(target_arch = "x86_64", target_pointer_width = "64"))))'.dependencies] linux-raw-sys = { version = "0.9.2", default-features = false, features = ["general", "errno", "ioctl", "no_std", "elf"] } libc_errno = { package = "errno", version = "0.3.10", default-features = false, optional = true } -libc = { version = "0.2.171", default-features = false, optional = true } +libc = { version = "0.2.174", default-features = false, optional = true } # Dependencies for platforms where only libc is supported: # @@ -40,7 +40,7 @@ libc = { version = "0.2.171", default-features = false, optional = true } # backend, so enable its dependencies unconditionally. [target.'cfg(all(not(windows), any(rustix_use_libc, miri, not(all(target_os = "linux", any(target_endian = "little", any(target_arch = "s390x", target_arch = "powerpc")), any(target_arch = "arm", all(target_arch = "aarch64", target_pointer_width = "64"), target_arch = "riscv64", all(rustix_use_experimental_asm, target_arch = "powerpc"), all(rustix_use_experimental_asm, target_arch = "powerpc64"), all(rustix_use_experimental_asm, target_arch = "s390x"), all(rustix_use_experimental_asm, target_arch = "mips"), all(rustix_use_experimental_asm, target_arch = "mips32r6"), all(rustix_use_experimental_asm, target_arch = "mips64"), all(rustix_use_experimental_asm, target_arch = "mips64r6"), target_arch = "x86", all(target_arch = "x86_64", target_pointer_width = "64")))))))'.dependencies] libc_errno = { package = "errno", version = "0.3.10", default-features = false } -libc = { version = "0.2.171", default-features = false } +libc = { version = "0.2.174", default-features = false } # Additional dependencies for Linux with the libc backend: # @@ -66,7 +66,7 @@ default-features = false [dev-dependencies] tempfile = "3.5.0" -libc = "0.2.171" +libc = "0.2.174" libc_errno = { package = "errno", version = "0.3.10", default-features = false } serial_test = "2.0.0" memoffset = "0.9.0" diff --git a/src/backend/linux_raw/c.rs b/src/backend/linux_raw/c.rs index 4dc9c8cd7..95a4375ac 100644 --- a/src/backend/linux_raw/c.rs +++ b/src/backend/linux_raw/c.rs @@ -383,3 +383,12 @@ mod statx_flags { ) ))] pub(crate) use statx_flags::*; + +#[cfg(feature = "thread")] +pub(crate) use linux_raw_sys::ioctl::{ + NS_GET_NSTYPE, + NS_GET_OWNER_UID, + NS_GET_PARENT, + NS_GET_USERNS, + // NS_GET_PID_FROM_PIDNS, NS_GET_PID_IN_PIDNS, NS_GET_TGID_FROM_PIDNS, NS_GET_TGID_IN_PIDNS, +}; diff --git a/src/ioctl/patterns.rs b/src/ioctl/patterns.rs index a08aae74e..26e48cdec 100644 --- a/src/ioctl/patterns.rs +++ b/src/ioctl/patterns.rs @@ -39,7 +39,7 @@ unsafe impl Ioctl for NoArg { const IS_MUTATING: bool = false; - fn opcode(&self) -> self::Opcode { + fn opcode(&self) -> Opcode { OPCODE } @@ -52,6 +52,97 @@ unsafe impl Ioctl for NoArg { } } +/// Implements an `ioctl` with no real arguments that returns an integer. +/// +/// To compute a value for the `OPCODE` argument, see the functions in the +/// [`opcode`] module. +/// +/// [`opcode`]: crate::ioctl::opcode +pub struct NoArgGetter {} +impl fmt::Debug for NoArgGetter { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_tuple("NoArgGetter").field(&OPCODE).finish() + } +} +impl NoArgGetter { + /// Create a new no-argument-getter `ioctl` object. + /// + /// # Safety + /// + /// - `OPCODE` must provide a valid opcode. + #[inline] + pub const unsafe fn new() -> Self { + Self {} + } +} +unsafe impl Ioctl for NoArgGetter { + type Output = IoctlOutput; + + const IS_MUTATING: bool = false; + + fn opcode(&self) -> Opcode { + OPCODE + } + + fn as_ptr(&mut self) -> *mut c::c_void { + core::ptr::null_mut() + } + + unsafe fn output_from_ptr(output: IoctlOutput, _: *mut c::c_void) -> Result { + Ok(output) + } +} + +/// Implements an `ioctl` with one real argument that returns an integer. +/// +/// To compute a value for the `OPCODE` argument, see the functions in the +/// [`opcode`] module. +/// +/// [`opcode`]: crate::ioctl::opcode +pub struct ParameterizedReturnGetter { + value: *const c::c_void, +} + +impl fmt::Debug for ParameterizedReturnGetter { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_tuple("ParameterizedReturnGetter") + .field(&OPCODE) + .field(&self.value) + .finish() + } +} + +impl ParameterizedReturnGetter { + /// Create a new `ioctl` object. + /// + /// # Safety + /// + /// - `OPCODE` must provide a valid opcode. + #[inline] + pub const unsafe fn new(value: usize) -> Self { + Self { + value: core::ptr::without_provenance(value), + } + } +} +unsafe impl Ioctl for ParameterizedReturnGetter { + type Output = IoctlOutput; + + const IS_MUTATING: bool = false; + + fn opcode(&self) -> Opcode { + OPCODE + } + + fn as_ptr(&mut self) -> *mut c::c_void { + self.value.cast_mut() + } + + unsafe fn output_from_ptr(output: IoctlOutput, _: *mut c::c_void) -> Result { + Ok(output) + } +} + /// Implements the traditional “getter” pattern for `ioctl`s. /// /// Some `ioctl`s just read data into the userspace. As this is a popular @@ -93,7 +184,7 @@ unsafe impl Ioctl for Getter { const IS_MUTATING: bool = true; - fn opcode(&self) -> self::Opcode { + fn opcode(&self) -> Opcode { OPCODE } @@ -148,7 +239,7 @@ unsafe impl Ioctl for Setter { const IS_MUTATING: bool = false; - fn opcode(&self) -> self::Opcode { + fn opcode(&self) -> Opcode { OPCODE } @@ -175,6 +266,15 @@ pub struct Updater<'a, const OPCODE: Opcode, Value> { value: &'a mut Value, } +impl<'a, const OPCODE: Opcode, Value: fmt::Debug> fmt::Debug for Updater<'a, OPCODE, Value> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_tuple("Setter") + .field(&OPCODE) + .field(&self.value) + .finish() + } +} + impl<'a, const OPCODE: Opcode, Value> Updater<'a, OPCODE, Value> { /// Create a new pointer updater-style `ioctl` object. /// @@ -194,7 +294,7 @@ unsafe impl<'a, const OPCODE: Opcode, T> Ioctl for Updater<'a, OPCODE, T> { const IS_MUTATING: bool = true; - fn opcode(&self) -> self::Opcode { + fn opcode(&self) -> Opcode { OPCODE } @@ -220,6 +320,15 @@ pub struct IntegerSetter { value: *mut c::c_void, } +impl fmt::Debug for IntegerSetter { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_tuple("IntegerSetter") + .field(&OPCODE) + .field(&self.value) + .finish() + } +} + impl IntegerSetter { /// Create a new integer `Ioctl` helper containing a `usize`. /// @@ -251,7 +360,7 @@ unsafe impl Ioctl for IntegerSetter { const IS_MUTATING: bool = false; - fn opcode(&self) -> self::Opcode { + fn opcode(&self) -> Opcode { OPCODE } diff --git a/src/thread/mod.rs b/src/thread/mod.rs index 26d1de427..b4af31202 100644 --- a/src/thread/mod.rs +++ b/src/thread/mod.rs @@ -11,12 +11,12 @@ mod libcap; #[cfg(linux_kernel)] mod membarrier; #[cfg(linux_kernel)] +mod ns; +#[cfg(linux_kernel)] mod prctl; #[cfg(any(freebsdlike, linux_kernel, target_os = "fuchsia"))] mod sched; mod sched_yield; -#[cfg(linux_kernel)] -mod setns; #[cfg(not(target_os = "redox"))] pub use clock::*; @@ -29,9 +29,9 @@ pub use libcap::{capabilities, set_capabilities, CapabilityFlags, CapabilitySet, #[cfg(linux_kernel)] pub use membarrier::*; #[cfg(linux_kernel)] +pub use ns::*; +#[cfg(linux_kernel)] pub use prctl::*; #[cfg(any(freebsdlike, linux_kernel, target_os = "fuchsia"))] pub use sched::*; pub use sched_yield::sched_yield; -#[cfg(linux_kernel)] -pub use setns::*; diff --git a/src/thread/ns.rs b/src/thread/ns.rs new file mode 100644 index 000000000..fd5920cde --- /dev/null +++ b/src/thread/ns.rs @@ -0,0 +1,341 @@ +use bitflags::bitflags; +use linux_raw_sys::general::{ + CLONE_FILES, CLONE_FS, CLONE_NEWCGROUP, CLONE_NEWIPC, CLONE_NEWNET, CLONE_NEWNS, CLONE_NEWPID, + CLONE_NEWTIME, CLONE_NEWUSER, CLONE_NEWUTS, CLONE_SYSVSEM, +}; + +use crate::backend::c::{ + c_int, NS_GET_NSTYPE, NS_GET_OWNER_UID, NS_GET_PARENT, NS_GET_PID_FROM_PIDNS, + NS_GET_PID_IN_PIDNS, NS_GET_TGID_FROM_PIDNS, NS_GET_TGID_IN_PIDNS, NS_GET_USERNS, +}; +use crate::backend::thread::syscalls; +use crate::fd::BorrowedFd; +use crate::fd::{AsFd, FromRawFd, OwnedFd}; +use crate::io::{self, Errno}; +use crate::ioctl; + +use super::{Pid, RawUid, Uid}; + +bitflags! { + /// Namespace type. + #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] + #[repr(transparent)] + pub struct NamespaceType: u32 { + /// Control group (CGroup) namespace. + const CGROUP = CLONE_NEWCGROUP; + /// System V IPC and POSIX message queue namespace. + const IPC = CLONE_NEWIPC; + /// Mount namespace. + const MOUNT = CLONE_NEWNS; + /// Network namespace. + const NETWORK = CLONE_NEWNET; + /// Process ID namespace. + const PID = CLONE_NEWPID; + /// Time namespace. + const TIME = CLONE_NEWTIME; + /// User and group ID namespace. + const USER = CLONE_NEWUSER; + /// `Host name` and `NIS domain name` (UTS) namespace. + const UTS = CLONE_NEWUTS; + + /// + const _ = !0; + } +} + +bitflags! { + /// `CLONE_*` for use with [`unshare`]. + #[repr(transparent)] + #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] + pub struct UnshareFlags: u32 { + /// `CLONE_FILES` + const FILES = CLONE_FILES; + /// `CLONE_FS` + const FS = CLONE_FS; + /// `CLONE_NEWCGROUP` + const NEWCGROUP = CLONE_NEWCGROUP; + /// `CLONE_NEWIPC` + const NEWIPC = CLONE_NEWIPC; + /// `CLONE_NEWNET` + const NEWNET = CLONE_NEWNET; + /// `CLONE_NEWNS` + const NEWNS = CLONE_NEWNS; + /// `CLONE_NEWPID` + const NEWPID = CLONE_NEWPID; + /// `CLONE_NEWTIME` + const NEWTIME = CLONE_NEWTIME; + /// `CLONE_NEWUSER` + const NEWUSER = CLONE_NEWUSER; + /// `CLONE_NEWUTS` + const NEWUTS = CLONE_NEWUTS; + /// `CLONE_SYSVSEM` + const SYSVSEM = CLONE_SYSVSEM; + + /// + const _ = !0; + } +} + +pub use allow_deprecated_workaround::*; +mod allow_deprecated_workaround { + #![allow(deprecated)] + + use linux_raw_sys::general::{ + CLONE_NEWCGROUP, CLONE_NEWIPC, CLONE_NEWNET, CLONE_NEWNS, CLONE_NEWPID, CLONE_NEWTIME, + CLONE_NEWUSER, CLONE_NEWUTS, + }; + + bitflags::bitflags! { + /// Thread name space type. + #[deprecated(since = "1.1.0", note = "Use NamespaceType instead")] + #[repr(transparent)] + #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] + pub struct ThreadNameSpaceType: u32 { + /// Time name space. + const TIME = CLONE_NEWTIME; + /// Mount name space. + const MOUNT = CLONE_NEWNS; + /// Control group (CGroup) name space. + const CONTROL_GROUP = CLONE_NEWCGROUP; + /// `Host name` and `NIS domain name` (UTS) name space. + const HOST_NAME_AND_NIS_DOMAIN_NAME = CLONE_NEWUTS; + /// Inter-process communication (IPC) name space. + const INTER_PROCESS_COMMUNICATION = CLONE_NEWIPC; + /// User name space. + const USER = CLONE_NEWUSER; + /// Process ID name space. + const PROCESS_ID = CLONE_NEWPID; + /// Network name space. + const NETWORK = CLONE_NEWNET; + + /// + const _ = !0; + } + } +} + +/// Type of name space referred to by a link. +#[deprecated(since = "1.1.0", note = "Use NamespaceType instead")] +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +#[repr(u32)] +pub enum LinkNameSpaceType { + /// Time name space. + Time = CLONE_NEWTIME, + /// Mount name space. + Mount = CLONE_NEWNS, + /// Control group (CGroup) name space. + ControlGroup = CLONE_NEWCGROUP, + /// `Host name` and `NIS domain name` (UTS) name space. + HostNameAndNISDomainName = CLONE_NEWUTS, + /// Inter-process communication (IPC) name space. + InterProcessCommunication = CLONE_NEWIPC, + /// User name space. + User = CLONE_NEWUSER, + /// Process ID name space. + ProcessID = CLONE_NEWPID, + /// Network name space. + Network = CLONE_NEWNET, +} + +/// Move the calling thread into different namespaces +/// +/// This function has two different semantics depending on the `fd` argument. +/// +/// - If `fd` refers to one of the magic links in a `/proc/[pid]/ns/` directory +/// or a bind mount to such a link, the calling thread is moved to the namespaces +/// referred to by `fd`. `namespace_type` must either be [`NamespaceType::empty()`] +/// in which case all namespace types can be joined or a single [`NamespaceType`] +/// bit in which case only namespaces of this type can be joined. +/// - If `fd` refers to a pidfd, the calling thread is moved to all namespaces of this +/// process that are specified in `namespace_type`. +/// +/// # References +/// - [Linux] +/// +/// [Linux]: https://man7.org/linux/man-pages/man2/setns.2.html +#[deprecated(since = "1.1.0", note = "Use setns instead")] +#[doc(alias = "setns")] +pub fn set_namespace(fd: BorrowedFd<'_>, namespace_type: NamespaceType) -> io::Result<()> { + syscalls::setns(fd, namespace_type.bits() as c_int)?; + + Ok(()) +} + +/// Reassociate the calling thread with the namespace associated with link +/// referred to by `fd`. +/// +/// `fd` must refer to one of the magic links in a `/proc/[pid]/ns/` directory, +/// or a bind mount to such a link. +/// +/// # References +/// - [Linux] +/// +/// [Linux]: https://man7.org/linux/man-pages/man2/setns.2.html +#[deprecated(since = "1.1.0", note = "Use setns instead")] +#[doc(alias = "setns")] +#[allow(deprecated)] +pub fn move_into_link_name_space( + fd: BorrowedFd<'_>, + allowed_type: Option, +) -> io::Result<()> { + let allowed_type = allowed_type.map_or(0, |t| t as c_int); + syscalls::setns(fd, allowed_type).map(|_r| ()) +} + +/// Atomically move the calling thread into one or more of the same namespaces +/// as the thread referred to by `fd`. +/// +/// `fd` must refer to a thread ID. See: `pidfd_open` and `clone`. +/// +/// # References +/// - [Linux] +/// +/// [Linux]: https://man7.org/linux/man-pages/man2/setns.2.html +#[deprecated(since = "1.1.0", note = "Use setns instead")] +#[doc(alias = "setns")] +#[allow(deprecated)] +pub fn move_into_thread_name_spaces( + fd: BorrowedFd<'_>, + allowed_types: ThreadNameSpaceType, +) -> io::Result<()> { + syscalls::setns(fd, allowed_types.bits() as c_int).map(|_r| ()) +} + +/// `unshare(flags)`—Disassociate parts of the current thread's execution +/// context with other threads. +/// +/// # References +/// - [Linux] +/// +/// [Linux]: https://man7.org/linux/man-pages/man2/unshare.2.html +pub fn unshare(flags: UnshareFlags) -> io::Result<()> { + syscalls::unshare(flags) +} + +/// `ioctl(ns_fd, NS_GET_USERNS)` +/// +/// # Safety +/// +/// `fd` must refer to a `/proc/{pid}/ns/*` file. +#[inline] +#[doc(alias = "NS_GET_USERNS")] +pub fn ioctl_ns_get_userns(fd: FD) -> io::Result { + #[allow(unsafe_code)] + unsafe { + let ctl = ioctl::NoArgGetter::<{ NS_GET_USERNS }>::new(); + ioctl::ioctl(fd, ctl).map(|fd| OwnedFd::from_raw_fd(fd)) + } +} + +/// `ioctl(ns_fd, NS_GET_PARENT)` +/// +/// # Safety +/// +/// `fd` must refer to a `/proc/{pid}/ns/*` file. +#[inline] +#[doc(alias = "NS_GET_PARENT")] +pub fn ioctl_ns_get_parent(fd: FD) -> io::Result { + #[allow(unsafe_code)] + unsafe { + let ctl = ioctl::NoArgGetter::<{ NS_GET_PARENT }>::new(); + ioctl::ioctl(fd, ctl).map(|fd| OwnedFd::from_raw_fd(fd)) + } +} + +/// `ioctl(ns_fd, NS_GET_NSTYPE)` +/// +/// # Safety +/// +/// `fd` must refer to a `/proc/{pid}/ns/*` file. +#[inline] +#[doc(alias = "NS_GET_NSTYPE")] +pub fn ioctl_ns_get_nstype(fd: FD) -> io::Result { + #[allow(unsafe_code)] + unsafe { + let ctl = ioctl::NoArgGetter::<{ NS_GET_NSTYPE }>::new(); + ioctl::ioctl(fd, ctl).map(|ns| NamespaceType::from_bits_retain(ns as u32)) + } +} + +/// `ioctl(ns_fd, NS_GET_OWNER_UID)` +/// +/// # Safety +/// +/// `fd` must refer to a `/proc/{pid}/ns/*` file. +#[inline] +#[doc(alias = "NS_GET_OWNER_UID")] +pub fn ioctl_ns_get_owner_uid(fd: FD) -> io::Result { + #[allow(unsafe_code)] + unsafe { + let ctl = ioctl::Getter::<{ NS_GET_OWNER_UID }, RawUid>::new(); + ioctl::ioctl(fd, ctl).map(Uid::from_raw) + } +} + +/// `ioctl(ns_fd, NS_GET_PID_FROM_PIDNS, pid)` +/// +/// # Safety +/// +/// `fd` must refer to a `/proc/{pid}/ns/pid` file. +#[inline] +#[doc(alias = "NS_GET_PID_FROM_PIDNS")] +pub fn ioctl_ns_get_pid_from_pidns(fd: FD, pid: Pid) -> io::Result { + #[allow(unsafe_code)] + unsafe { + let ctl = ioctl::ParameterizedReturnGetter::<{ NS_GET_PID_FROM_PIDNS }>::new( + pid.as_raw_pid() as usize, + ); + ioctl::ioctl(fd, ctl).and_then(|pid| Pid::from_raw(pid).ok_or(Errno::INVAL)) + } +} + +/// `ioctl(ns_fd, NS_GET_TGID_FROM_PIDNS, tgid)` +/// +/// # Safety +/// +/// `fd` must refer to a `/proc/{pid}/ns/pid` file. +#[inline] +#[doc(alias = "NS_GET_TGID_FROM_PIDNS")] +pub fn ioctl_ns_get_tgid_from_pidns(fd: FD, tgid: Pid) -> io::Result { + #[allow(unsafe_code)] + unsafe { + let ctl = ioctl::ParameterizedReturnGetter::<{ NS_GET_TGID_FROM_PIDNS }>::new( + tgid.as_raw_pid() as usize, + ); + ioctl::ioctl(fd, ctl).and_then(|tgid| Pid::from_raw(tgid).ok_or(Errno::INVAL)) + } +} + +/// `ioctl(ns_fd, NS_GET_PID_IN_PIDNS, pid)` +/// +/// # Safety +/// +/// `fd` must refer to a `/proc/{pid}/ns/pid` file. +#[inline] +#[doc(alias = "NS_GET_PID_IN_PIDNS")] +pub fn ioctl_ns_get_pid_in_pidns(fd: FD, pid: Pid) -> io::Result { + #[allow(unsafe_code)] + unsafe { + let ctl = ioctl::ParameterizedReturnGetter::<{ NS_GET_PID_IN_PIDNS }>::new( + pid.as_raw_pid() as usize, + ); + ioctl::ioctl(fd, ctl).and_then(|pid| Pid::from_raw(pid).ok_or(Errno::INVAL)) + } +} + +/// `ioctl(ns_fd, NS_GET_TGID_IN_PIDNS, tgid)` +/// +/// # Safety +/// +/// `fd` must refer to a `/proc/{pid}/ns/pid` file. +#[inline] +#[doc(alias = "NS_GET_TGID_IN_PIDNS")] +pub fn ioctl_ns_get_tgid_in_pidns(fd: FD, tgid: Pid) -> io::Result { + #[allow(unsafe_code)] + unsafe { + let ctl = ioctl::ParameterizedReturnGetter::<{ NS_GET_TGID_IN_PIDNS }>::new( + tgid.as_raw_pid() as usize, + ); + ioctl::ioctl(fd, ctl).and_then(|tgid| Pid::from_raw(tgid).ok_or(Errno::INVAL)) + } +} diff --git a/src/thread/setns.rs b/src/thread/setns.rs deleted file mode 100644 index 0eaee2f2a..000000000 --- a/src/thread/setns.rs +++ /dev/null @@ -1,139 +0,0 @@ -use bitflags::bitflags; -use linux_raw_sys::general::{ - CLONE_FILES, CLONE_FS, CLONE_NEWCGROUP, CLONE_NEWIPC, CLONE_NEWNET, CLONE_NEWNS, CLONE_NEWPID, - CLONE_NEWTIME, CLONE_NEWUSER, CLONE_NEWUTS, CLONE_SYSVSEM, -}; - -use crate::backend::c::c_int; -use crate::backend::thread::syscalls; -use crate::fd::BorrowedFd; -use crate::io; - -bitflags! { - /// Thread name space type. - #[repr(transparent)] - #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] - pub struct ThreadNameSpaceType: u32 { - /// Time name space. - const TIME = CLONE_NEWTIME; - /// Mount name space. - const MOUNT = CLONE_NEWNS; - /// Control group (CGroup) name space. - const CONTROL_GROUP = CLONE_NEWCGROUP; - /// `Host name` and `NIS domain name` (UTS) name space. - const HOST_NAME_AND_NIS_DOMAIN_NAME = CLONE_NEWUTS; - /// Inter-process communication (IPC) name space. - const INTER_PROCESS_COMMUNICATION = CLONE_NEWIPC; - /// User name space. - const USER = CLONE_NEWUSER; - /// Process ID name space. - const PROCESS_ID = CLONE_NEWPID; - /// Network name space. - const NETWORK = CLONE_NEWNET; - - /// - const _ = !0; - } -} - -/// Type of name space referred to by a link. -#[derive(Copy, Clone, Debug, Eq, PartialEq)] -#[repr(u32)] -pub enum LinkNameSpaceType { - /// Time name space. - Time = CLONE_NEWTIME, - /// Mount name space. - Mount = CLONE_NEWNS, - /// Control group (CGroup) name space. - ControlGroup = CLONE_NEWCGROUP, - /// `Host name` and `NIS domain name` (UTS) name space. - HostNameAndNISDomainName = CLONE_NEWUTS, - /// Inter-process communication (IPC) name space. - InterProcessCommunication = CLONE_NEWIPC, - /// User name space. - User = CLONE_NEWUSER, - /// Process ID name space. - ProcessID = CLONE_NEWPID, - /// Network name space. - Network = CLONE_NEWNET, -} - -bitflags! { - /// `CLONE_*` for use with [`unshare`]. - #[repr(transparent)] - #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] - pub struct UnshareFlags: u32 { - /// `CLONE_FILES` - const FILES = CLONE_FILES; - /// `CLONE_FS` - const FS = CLONE_FS; - /// `CLONE_NEWCGROUP` - const NEWCGROUP = CLONE_NEWCGROUP; - /// `CLONE_NEWIPC` - const NEWIPC = CLONE_NEWIPC; - /// `CLONE_NEWNET` - const NEWNET = CLONE_NEWNET; - /// `CLONE_NEWNS` - const NEWNS = CLONE_NEWNS; - /// `CLONE_NEWPID` - const NEWPID = CLONE_NEWPID; - /// `CLONE_NEWTIME` - const NEWTIME = CLONE_NEWTIME; - /// `CLONE_NEWUSER` - const NEWUSER = CLONE_NEWUSER; - /// `CLONE_NEWUTS` - const NEWUTS = CLONE_NEWUTS; - /// `CLONE_SYSVSEM` - const SYSVSEM = CLONE_SYSVSEM; - - /// - const _ = !0; - } -} - -/// Reassociate the calling thread with the namespace associated with link -/// referred to by `fd`. -/// -/// `fd` must refer to one of the magic links in a `/proc/[pid]/ns/` directory, -/// or a bind mount to such a link. -/// -/// # References -/// - [Linux] -/// -/// [Linux]: https://man7.org/linux/man-pages/man2/setns.2.html -#[doc(alias = "setns")] -pub fn move_into_link_name_space( - fd: BorrowedFd<'_>, - allowed_type: Option, -) -> io::Result<()> { - let allowed_type = allowed_type.map_or(0, |t| t as c_int); - syscalls::setns(fd, allowed_type).map(|_r| ()) -} - -/// Atomically move the calling thread into one or more of the same namespaces -/// as the thread referred to by `fd`. -/// -/// `fd` must refer to a thread ID. See: `pidfd_open` and `clone`. -/// -/// # References -/// - [Linux] -/// -/// [Linux]: https://man7.org/linux/man-pages/man2/setns.2.html -#[doc(alias = "setns")] -pub fn move_into_thread_name_spaces( - fd: BorrowedFd<'_>, - allowed_types: ThreadNameSpaceType, -) -> io::Result<()> { - syscalls::setns(fd, allowed_types.bits() as c_int).map(|_r| ()) -} - -/// `unshare(flags)`—Disassociate parts of the current thread's execution -/// context with other threads. -/// -/// # References -/// - [Linux] -/// -/// [Linux]: https://man7.org/linux/man-pages/man2/unshare.2.html -pub fn unshare(flags: UnshareFlags) -> io::Result<()> { - syscalls::unshare(flags) -} diff --git a/tests/thread/setns.rs b/tests/thread/setns.rs index 71b0bb785..9b282a1a0 100644 --- a/tests/thread/setns.rs +++ b/tests/thread/setns.rs @@ -8,6 +8,7 @@ use rustix::thread::*; #[test] #[ignore = "Requires CAP_SYS_ADMIN capability"] +#[allow(deprecated)] fn test_move_into_link_name_space() { let f = File::open("/proc/self/ns/uts").unwrap(); @@ -21,6 +22,7 @@ fn test_move_into_link_name_space() { #[test] #[ignore = "Requires CAP_SYS_ADMIN capability"] #[cfg(not(target_os = "android"))] // Android libc bindings don't have `SYS_pidfd_open` yet. +#[allow(deprecated)] fn test_move_into_thread_name_spaces() { let fd = unsafe { libc::syscall(libc::SYS_pidfd_open, std::process::id() as usize, 0_usize) }; if fd == -1 {