Skip to content

Commit 333ab0d

Browse files
committed
Rework namespace functions and types
- Add `set_namespace` function. - Deprecate `move_into_link_name_space` and `move_into_thread_name_spaces` functions. - Add `NamespaceType`. - Deprecate `ThreadNameSpaceType` and `LinkNameSpaceType`. - Add nsfs ioctls. - Add `NoArgGetter` ioctl pattern. - Update libc to 0.2.174.
1 parent d0467e6 commit 333ab0d

File tree

7 files changed

+328
-146
lines changed

7 files changed

+328
-146
lines changed

Cargo.toml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,15 +32,15 @@ compiler_builtins = { version = '0.1.49', optional = true }
3232
[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]
3333
linux-raw-sys = { version = "0.9.2", default-features = false, features = ["general", "errno", "ioctl", "no_std", "elf"] }
3434
libc_errno = { package = "errno", version = "0.3.10", default-features = false, optional = true }
35-
libc = { version = "0.2.171", default-features = false, optional = true }
35+
libc = { version = "0.2.174", default-features = false, optional = true }
3636

3737
# Dependencies for platforms where only libc is supported:
3838
#
3939
# On all other Unix-family platforms, and under Miri, we always use the libc
4040
# backend, so enable its dependencies unconditionally.
4141
[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]
4242
libc_errno = { package = "errno", version = "0.3.10", default-features = false }
43-
libc = { version = "0.2.171", default-features = false }
43+
libc = { version = "0.2.174", default-features = false }
4444

4545
# Additional dependencies for Linux with the libc backend:
4646
#
@@ -66,7 +66,7 @@ default-features = false
6666

6767
[dev-dependencies]
6868
tempfile = "3.5.0"
69-
libc = "0.2.171"
69+
libc = "0.2.174"
7070
libc_errno = { package = "errno", version = "0.3.10", default-features = false }
7171
serial_test = "2.0.0"
7272
memoffset = "0.9.0"

src/backend/linux_raw/c.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -383,3 +383,8 @@ mod statx_flags {
383383
)
384384
))]
385385
pub(crate) use statx_flags::*;
386+
387+
#[cfg(feature = "thread")]
388+
pub(crate) use linux_raw_sys::ioctl::{
389+
NS_GET_NSTYPE, NS_GET_OWNER_UID, NS_GET_PARENT, NS_GET_USERNS,
390+
};

src/ioctl/patterns.rs

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,50 @@ unsafe impl<const OPCODE: Opcode> Ioctl for NoArg<OPCODE> {
5252
}
5353
}
5454

55+
/// Implements an `ioctl` with no real arguments.
56+
///
57+
/// To compute a value for the `OPCODE` argument, see the functions in the
58+
/// [`opcode`] module.
59+
///
60+
/// [`opcode`]: crate::ioctl::opcode
61+
pub struct NoArgGetter<const OPCODE: Opcode> {}
62+
impl<const OPCODE: Opcode> fmt::Debug for NoArgGetter<OPCODE> {
63+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
64+
f.debug_tuple("NoArgGetter").field(&OPCODE).finish()
65+
}
66+
}
67+
impl<const OPCODE: Opcode> NoArgGetter<OPCODE> {
68+
/// Create a new no-argument-getter `ioctl` object.
69+
///
70+
/// # Safety
71+
///
72+
/// - `OPCODE` must provide a valid opcode.
73+
#[inline]
74+
pub const unsafe fn new() -> Self {
75+
Self {}
76+
}
77+
}
78+
unsafe impl<const OPCODE: Opcode> Ioctl for NoArgGetter<OPCODE> {
79+
type Output = IoctlOutput;
80+
81+
const IS_MUTATING: bool = false;
82+
83+
fn opcode(&self) -> self::Opcode {
84+
OPCODE
85+
}
86+
87+
fn as_ptr(&mut self) -> *mut core::ffi::c_void {
88+
core::ptr::null_mut()
89+
}
90+
91+
unsafe fn output_from_ptr(
92+
output: IoctlOutput,
93+
_: *mut core::ffi::c_void,
94+
) -> Result<Self::Output> {
95+
Ok(output)
96+
}
97+
}
98+
5599
/// Implements the traditional “getter” pattern for `ioctl`s.
56100
///
57101
/// Some `ioctl`s just read data into the userspace. As this is a popular

src/thread/mod.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,12 @@ mod libcap;
1111
#[cfg(linux_kernel)]
1212
mod membarrier;
1313
#[cfg(linux_kernel)]
14+
mod ns;
15+
#[cfg(linux_kernel)]
1416
mod prctl;
1517
#[cfg(any(freebsdlike, linux_kernel, target_os = "fuchsia"))]
1618
mod sched;
1719
mod sched_yield;
18-
#[cfg(linux_kernel)]
19-
mod setns;
2020

2121
#[cfg(not(target_os = "redox"))]
2222
pub use clock::*;
@@ -29,9 +29,9 @@ pub use libcap::{capabilities, set_capabilities, CapabilityFlags, CapabilitySet,
2929
#[cfg(linux_kernel)]
3030
pub use membarrier::*;
3131
#[cfg(linux_kernel)]
32+
pub use ns::*;
33+
#[cfg(linux_kernel)]
3234
pub use prctl::*;
3335
#[cfg(any(freebsdlike, linux_kernel, target_os = "fuchsia"))]
3436
pub use sched::*;
3537
pub use sched_yield::sched_yield;
36-
#[cfg(linux_kernel)]
37-
pub use setns::*;

src/thread/ns.rs

Lines changed: 270 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,270 @@
1+
use bitflags::bitflags;
2+
use linux_raw_sys::general::{
3+
CLONE_FILES, CLONE_FS, CLONE_NEWCGROUP, CLONE_NEWIPC, CLONE_NEWNET, CLONE_NEWNS, CLONE_NEWPID,
4+
CLONE_NEWTIME, CLONE_NEWUSER, CLONE_NEWUTS, CLONE_SYSVSEM,
5+
};
6+
7+
use crate::backend::c::{c_int, NS_GET_NSTYPE, NS_GET_OWNER_UID, NS_GET_PARENT, NS_GET_USERNS};
8+
use crate::backend::thread::syscalls;
9+
use crate::fd::BorrowedFd;
10+
use crate::fd::{AsFd, FromRawFd, OwnedFd};
11+
use crate::io;
12+
use crate::ioctl;
13+
14+
use super::{RawUid, Uid};
15+
16+
bitflags! {
17+
/// Namespace type.
18+
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
19+
#[repr(transparent)]
20+
pub struct NamespaceType: u32 {
21+
/// Control group (CGroup) namespace.
22+
const CGROUP = CLONE_NEWCGROUP;
23+
/// System V IPC and POSIX message queue namespace.
24+
const IPC = CLONE_NEWIPC;
25+
/// Mount namespace.
26+
const MOUNT = CLONE_NEWNS;
27+
/// Network namespace.
28+
const NETWORK = CLONE_NEWNET;
29+
/// Process ID namespace.
30+
const PID = CLONE_NEWPID;
31+
/// Time namespace.
32+
const TIME = CLONE_NEWTIME;
33+
/// User and group ID namespace.
34+
const USER = CLONE_NEWUSER;
35+
/// `Host name` and `NIS domain name` (UTS) namespace.
36+
const UTS = CLONE_NEWUTS;
37+
38+
/// <https://docs.rs/bitflags/*/bitflags/#externally-defined-flags>
39+
const _ = !0;
40+
}
41+
}
42+
43+
bitflags! {
44+
/// `CLONE_*` for use with [`unshare`].
45+
#[repr(transparent)]
46+
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
47+
pub struct UnshareFlags: u32 {
48+
/// `CLONE_FILES`
49+
const FILES = CLONE_FILES;
50+
/// `CLONE_FS`
51+
const FS = CLONE_FS;
52+
/// `CLONE_NEWCGROUP`
53+
const NEWCGROUP = CLONE_NEWCGROUP;
54+
/// `CLONE_NEWIPC`
55+
const NEWIPC = CLONE_NEWIPC;
56+
/// `CLONE_NEWNET`
57+
const NEWNET = CLONE_NEWNET;
58+
/// `CLONE_NEWNS`
59+
const NEWNS = CLONE_NEWNS;
60+
/// `CLONE_NEWPID`
61+
const NEWPID = CLONE_NEWPID;
62+
/// `CLONE_NEWTIME`
63+
const NEWTIME = CLONE_NEWTIME;
64+
/// `CLONE_NEWUSER`
65+
const NEWUSER = CLONE_NEWUSER;
66+
/// `CLONE_NEWUTS`
67+
const NEWUTS = CLONE_NEWUTS;
68+
/// `CLONE_SYSVSEM`
69+
const SYSVSEM = CLONE_SYSVSEM;
70+
71+
/// <https://docs.rs/bitflags/*/bitflags/#externally-defined-flags>
72+
const _ = !0;
73+
}
74+
}
75+
76+
pub use allow_deprecated_workaround::*;
77+
mod allow_deprecated_workaround {
78+
#![allow(deprecated)]
79+
80+
use linux_raw_sys::general::{
81+
CLONE_NEWCGROUP, CLONE_NEWIPC, CLONE_NEWNET, CLONE_NEWNS, CLONE_NEWPID, CLONE_NEWTIME,
82+
CLONE_NEWUSER, CLONE_NEWUTS,
83+
};
84+
85+
bitflags::bitflags! {
86+
/// Thread name space type.
87+
#[deprecated(since = "1.1.0", note = "Use NamespaceType instead")]
88+
#[repr(transparent)]
89+
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
90+
pub struct ThreadNameSpaceType: u32 {
91+
/// Time name space.
92+
const TIME = CLONE_NEWTIME;
93+
/// Mount name space.
94+
const MOUNT = CLONE_NEWNS;
95+
/// Control group (CGroup) name space.
96+
const CONTROL_GROUP = CLONE_NEWCGROUP;
97+
/// `Host name` and `NIS domain name` (UTS) name space.
98+
const HOST_NAME_AND_NIS_DOMAIN_NAME = CLONE_NEWUTS;
99+
/// Inter-process communication (IPC) name space.
100+
const INTER_PROCESS_COMMUNICATION = CLONE_NEWIPC;
101+
/// User name space.
102+
const USER = CLONE_NEWUSER;
103+
/// Process ID name space.
104+
const PROCESS_ID = CLONE_NEWPID;
105+
/// Network name space.
106+
const NETWORK = CLONE_NEWNET;
107+
108+
/// <https://docs.rs/bitflags/*/bitflags/#externally-defined-flags>
109+
const _ = !0;
110+
}
111+
}
112+
}
113+
114+
/// Type of name space referred to by a link.
115+
#[deprecated(since = "1.1.0", note = "Use NamespaceType instead")]
116+
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
117+
#[repr(u32)]
118+
pub enum LinkNameSpaceType {
119+
/// Time name space.
120+
Time = CLONE_NEWTIME,
121+
/// Mount name space.
122+
Mount = CLONE_NEWNS,
123+
/// Control group (CGroup) name space.
124+
ControlGroup = CLONE_NEWCGROUP,
125+
/// `Host name` and `NIS domain name` (UTS) name space.
126+
HostNameAndNISDomainName = CLONE_NEWUTS,
127+
/// Inter-process communication (IPC) name space.
128+
InterProcessCommunication = CLONE_NEWIPC,
129+
/// User name space.
130+
User = CLONE_NEWUSER,
131+
/// Process ID name space.
132+
ProcessID = CLONE_NEWPID,
133+
/// Network name space.
134+
Network = CLONE_NEWNET,
135+
}
136+
137+
/// Move the calling thread into different namespaces
138+
///
139+
/// This function has two different semantics depending on the `fd` argument.
140+
///
141+
/// - If `fd` refers to one of the magic links in a `/proc/[pid]/ns/` directory
142+
/// or a bind mount to such a link, the calling thread is moved to the namespaces
143+
/// referred to by `fd`. `namespace_type` must either be [`NamespaceType::empty()`]
144+
/// in which case all namespace types can be joined or a single [`NamespaceType`]
145+
/// bit in which case only namespaces of this type can be joined.
146+
/// - If `fd` refers to a pidfd, the calling thread is moved to all namespaces of this
147+
/// process that are specified in `namespace_type`.
148+
///
149+
/// # References
150+
/// - [Linux]
151+
///
152+
/// [Linux]: https://man7.org/linux/man-pages/man2/setns.2.html
153+
#[deprecated(since = "1.1.0", note = "Use setns instead")]
154+
#[doc(alias = "setns")]
155+
pub fn set_namespace(fd: BorrowedFd<'_>, namespace_type: NamespaceType) -> io::Result<()> {
156+
syscalls::setns(fd, namespace_type.bits() as c_int)?;
157+
158+
Ok(())
159+
}
160+
161+
/// Reassociate the calling thread with the namespace associated with link
162+
/// referred to by `fd`.
163+
///
164+
/// `fd` must refer to one of the magic links in a `/proc/[pid]/ns/` directory,
165+
/// or a bind mount to such a link.
166+
///
167+
/// # References
168+
/// - [Linux]
169+
///
170+
/// [Linux]: https://man7.org/linux/man-pages/man2/setns.2.html
171+
#[deprecated(since = "1.1.0", note = "Use setns instead")]
172+
#[doc(alias = "setns")]
173+
#[allow(deprecated)]
174+
pub fn move_into_link_name_space(
175+
fd: BorrowedFd<'_>,
176+
allowed_type: Option<LinkNameSpaceType>,
177+
) -> io::Result<()> {
178+
let allowed_type = allowed_type.map_or(0, |t| t as c_int);
179+
syscalls::setns(fd, allowed_type).map(|_r| ())
180+
}
181+
182+
/// Atomically move the calling thread into one or more of the same namespaces
183+
/// as the thread referred to by `fd`.
184+
///
185+
/// `fd` must refer to a thread ID. See: `pidfd_open` and `clone`.
186+
///
187+
/// # References
188+
/// - [Linux]
189+
///
190+
/// [Linux]: https://man7.org/linux/man-pages/man2/setns.2.html
191+
#[deprecated(since = "1.1.0", note = "Use setns instead")]
192+
#[doc(alias = "setns")]
193+
#[allow(deprecated)]
194+
pub fn move_into_thread_name_spaces(
195+
fd: BorrowedFd<'_>,
196+
allowed_types: ThreadNameSpaceType,
197+
) -> io::Result<()> {
198+
syscalls::setns(fd, allowed_types.bits() as c_int).map(|_r| ())
199+
}
200+
201+
/// `unshare(flags)`—Disassociate parts of the current thread's execution
202+
/// context with other threads.
203+
///
204+
/// # References
205+
/// - [Linux]
206+
///
207+
/// [Linux]: https://man7.org/linux/man-pages/man2/unshare.2.html
208+
pub fn unshare(flags: UnshareFlags) -> io::Result<()> {
209+
syscalls::unshare(flags)
210+
}
211+
212+
/// `ioctl(ns_fd, NS_GET_USERNS)`
213+
///
214+
/// # Safety
215+
///
216+
/// `fd` must refer to a `/proc/pid/ns/*` file.
217+
#[inline]
218+
#[doc(alias = "NS_GET_USERNS")]
219+
pub fn ioctl_ns_get_userns<FD: AsFd>(fd: FD) -> io::Result<OwnedFd> {
220+
#[allow(unsafe_code)]
221+
unsafe {
222+
let ctl = ioctl::NoArgGetter::<{ NS_GET_USERNS }>::new();
223+
ioctl::ioctl(fd, ctl).map(|fd| OwnedFd::from_raw_fd(fd))
224+
}
225+
}
226+
227+
/// `ioctl(ns_fd, NS_GET_PARENT)`
228+
///
229+
/// # Safety
230+
///
231+
/// `fd` must refer to a `/proc/pid/ns/*` file.
232+
#[inline]
233+
#[doc(alias = "NS_GET_PARENT")]
234+
pub fn ioctl_ns_get_parent<FD: AsFd>(fd: FD) -> io::Result<OwnedFd> {
235+
#[allow(unsafe_code)]
236+
unsafe {
237+
let ctl = ioctl::NoArgGetter::<{ NS_GET_PARENT }>::new();
238+
ioctl::ioctl(fd, ctl).map(|fd| OwnedFd::from_raw_fd(fd))
239+
}
240+
}
241+
242+
/// `ioctl(ns_fd, NS_GET_NSTYPE)`
243+
///
244+
/// # Safety
245+
///
246+
/// `fd` must refer to a `/proc/pid/ns/*` file.
247+
#[inline]
248+
#[doc(alias = "NS_GET_NSTYPE")]
249+
pub fn ioctl_ns_get_nstype<FD: AsFd>(fd: FD) -> io::Result<NamespaceType> {
250+
#[allow(unsafe_code)]
251+
unsafe {
252+
let ctl = ioctl::NoArgGetter::<{ NS_GET_NSTYPE }>::new();
253+
ioctl::ioctl(fd, ctl).map(|ns| NamespaceType::from_bits_retain(ns as u32))
254+
}
255+
}
256+
257+
/// `ioctl(ns_fd, NS_GET_OWNER_UID)`
258+
///
259+
/// # Safety
260+
///
261+
/// `fd` must refer to a `/proc/pid/ns/*` file.
262+
#[inline]
263+
#[doc(alias = "NS_GET_OWNER_UID")]
264+
pub fn ioctl_ns_get_owner_uid<FD: AsFd>(fd: FD) -> io::Result<Uid> {
265+
#[allow(unsafe_code)]
266+
unsafe {
267+
let ctl = ioctl::Getter::<{ NS_GET_OWNER_UID }, RawUid>::new();
268+
ioctl::ioctl(fd, ctl).map(Uid::from_raw)
269+
}
270+
}

0 commit comments

Comments
 (0)