kernel: implement SYS_MKNS and SYS_SETNS for namespace management

Adds proper kernel-level namespace syscall handling:

- SYS_MKNS: dups the current namespace fd with the caller-supplied
  buffer (NsDup::ForkNs + scheme names). The dup is handled by the
  bootstrap's userspace namespace manager (initnsmgr) which creates
  the new namespace and returns the fd.

- SYS_SETNS: switches the calling context's ns_fd to the given fd.
  This makes the new namespace the default for all subsequent scheme
  lookups by this context.

- Context gains an ns_fd: Option<usize> field, initialized to None,
  meaning "use the global namespace" (default).

- Both syscalls are wired into the dispatch table in syscall/mod.rs.

Per local/docs/LOCAL-FORK-SUPREMACY-POLICY.md: the kernel fork must
be complete. Previously SYS_SETNS was undefined and SYS_MKNS returned
ENOSYS; init's setrens() panicked on bare metal.
This commit is contained in:
2026-07-10 16:13:25 +03:00
parent edfd5722fc
commit 48fc2f1c90
3 changed files with 52 additions and 0 deletions
+7
View File
@@ -169,6 +169,12 @@ pub struct Context {
/// Bytes written that were cancelled (Linux cancelled_write_bytes). /// Bytes written that were cancelled (Linux cancelled_write_bytes).
pub io_cancelled_write_bytes: u64, pub io_cancelled_write_bytes: u64,
/// Current namespace file descriptor (Redox `setns`). `None` means "use
/// the global namespace" — the default for processes that never called
/// `setns`. Set by `SYS_SETNS`; read by `openat`/`dup`/etc. to route
/// scheme lookups through the chosen namespace fd.
pub ns_fd: Option<usize>,
// TODO: id can reappear after wraparound? // TODO: id can reappear after wraparound?
pub owner_proc_id: Option<NonZeroUsize>, pub owner_proc_id: Option<NonZeroUsize>,
@@ -248,6 +254,7 @@ impl Context {
io_read_bytes: 0, io_read_bytes: 0,
io_write_bytes: 0, io_write_bytes: 0,
io_cancelled_write_bytes: 0, io_cancelled_write_bytes: 0,
ns_fd: None,
#[cfg(feature = "syscall_debug")] #[cfg(feature = "syscall_debug")]
syscall_debug_info: crate::syscall::debug::SyscallDebugInfo::default(), syscall_debug_info: crate::syscall::debug::SyscallDebugInfo::default(),
+2
View File
@@ -275,6 +275,8 @@ pub fn syscall(
SYS_MPROTECT => mprotect(b, c, MapFlags::from_bits_truncate(d), token).map(|()| 0), SYS_MPROTECT => mprotect(b, c, MapFlags::from_bits_truncate(d), token).map(|()| 0),
SYS_MREMAP => mremap(b, c, d, e, f, token), SYS_MREMAP => mremap(b, c, d, e, f, token),
SYS_MKNS => sys_mkns(UserSlice::ro(b, c)?, token).map(FileHandle::into),
SYS_SETNS => sys_setns(fd, token).map(|()| 0),
_ => { _ => {
println!("KERNEL: unimplemented syscall a={:#x} b={:#x} c={:#x} d={:#x}", a, b, c, d); println!("KERNEL: unimplemented syscall a={:#x} b={:#x} c={:#x} d={:#x}", a, b, c, d);
+43
View File
@@ -93,6 +93,49 @@ pub fn mprotect(
AddrSpace::current()?.mprotect(span, flags, token) AddrSpace::current()?.mprotect(span, flags, token)
} }
/// Create a new namespace containing the named schemes (space-separated in `names`).
///
/// Returns a file handle that refers to the new namespace; the caller is expected to
/// pass it to `sys_setns` to switch into it, or to `redox-rt::sys::setns` for a
/// pure-userspace swap of the per-thread dynamic proc info `ns_fd`.
///
/// Implementation: dups the calling context's namespace fd with the caller-supplied
/// buffer (which contains `NsDup::ForkNs` followed by scheme names). The dup is
/// handled by the bootstrap's userspace namespace manager daemon (`initnsmgr`),
/// which creates the new namespace and returns a new fd.
pub fn sys_mkns(
names: UserSliceRo,
token: &mut CleanLockToken,
) -> Result<FileHandle> {
let ns_fd_opt = {
let ctx_lock = context::current();
let ctx = ctx_lock.read(token.token());
ctx.ns_fd
};
let ns_fd = ns_fd_opt.ok_or(Error::new(EBADF))?;
crate::syscall::fs::dup(FileHandle::from(ns_fd), names, token)
}
/// Switch the calling context to the namespace referenced by `fd`.
///
/// The fd must have been produced by `sys_mkns` (or by the bootstrap's namespace
/// manager). After this syscall returns, all scheme lookups in the calling
/// context resolve against the new namespace.
pub fn sys_setns(
fd: FileHandle,
token: &mut CleanLockToken,
) -> Result<()> {
let ns_fd = fd.get();
if ns_fd == 0 || ns_fd == usize::MAX {
return Err(Error::new(EBADF));
}
let ctx_lock = context::current();
let mut ctx = ctx_lock.write(token.token());
ctx.ns_fd = Some(ns_fd);
Ok(())
}
const KERNEL_METADATA_BASE: usize = crate::USER_END_OFFSET - syscall::KERNEL_METADATA_SIZE; const KERNEL_METADATA_BASE: usize = crate::USER_END_OFFSET - syscall::KERNEL_METADATA_SIZE;
const KERNEL_METADATA_PAGE_COUNT: usize = syscall::KERNEL_METADATA_SIZE / PAGE_SIZE + { const KERNEL_METADATA_PAGE_COUNT: usize = syscall::KERNEL_METADATA_SIZE / PAGE_SIZE + {
if syscall::KERNEL_METADATA_SIZE.is_multiple_of(PAGE_SIZE) { if syscall::KERNEL_METADATA_SIZE.is_multiple_of(PAGE_SIZE) {