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:
@@ -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(),
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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) {
|
||||||
|
|||||||
Reference in New Issue
Block a user