diff --git a/kernel/src/device/evdev/file.rs b/kernel/src/device/evdev/file.rs index e7e769f90..69a7af54a 100644 --- a/kernel/src/device/evdev/file.rs +++ b/kernel/src/device/evdev/file.rs @@ -502,6 +502,10 @@ impl FileIo for EvdevFile { Ok(0) } + + fn as_any(&self) -> &dyn Any { + self + } } impl Drop for EvdevFile { diff --git a/kernel/src/device/fb.rs b/kernel/src/device/fb.rs index 6d05b5ea4..7e5e13d1a 100644 --- a/kernel/src/device/fb.rs +++ b/kernel/src/device/fb.rs @@ -517,6 +517,10 @@ impl FileIo for FbHandle { } }) } + + fn as_any(&self) -> &dyn Any { + self + } } pub(super) fn init_in_first_kthread() { diff --git a/kernel/src/device/mem/file.rs b/kernel/src/device/mem/file.rs index 969cdbff6..75fdf4bce 100644 --- a/kernel/src/device/mem/file.rs +++ b/kernel/src/device/mem/file.rs @@ -1,6 +1,7 @@ // SPDX-License-Identifier: MPL-2.0 use alloc::vec; +use core::any::Any; use ostd::mm::{FallibleVmWrite, VmReader, VmWriter}; @@ -150,4 +151,8 @@ impl FileIo for MemFile { fn is_offset_aware(&self) -> bool { false } + + fn as_any(&self) -> &dyn Any { + self + } } diff --git a/kernel/src/device/misc/tdxguest.rs b/kernel/src/device/misc/tdxguest.rs index 214e6dc87..80a5a1472 100644 --- a/kernel/src/device/misc/tdxguest.rs +++ b/kernel/src/device/misc/tdxguest.rs @@ -170,6 +170,10 @@ impl FileIo for TdxGuestFile { _ => return_errno_with_message!(Errno::ENOTTY, "the ioctl command is unknown"), }) } + + fn as_any(&self) -> &dyn Any { + self + } } pub fn tdx_get_quote(inblob: &[u8]) -> Result> { diff --git a/kernel/src/device/pty/file.rs b/kernel/src/device/pty/file.rs index 3a4327026..3fb23d6c2 100644 --- a/kernel/src/device/pty/file.rs +++ b/kernel/src/device/pty/file.rs @@ -91,4 +91,8 @@ impl FileIo for PtySlaveFile { fn is_offset_aware(&self) -> bool { false } + + fn as_any(&self) -> &dyn Any { + self + } } diff --git a/kernel/src/device/pty/master.rs b/kernel/src/device/pty/master.rs index eb767d417..39fde6e2e 100644 --- a/kernel/src/device/pty/master.rs +++ b/kernel/src/device/pty/master.rs @@ -252,6 +252,10 @@ impl FileIo for PtyMaster { Ok(0) } + + fn as_any(&self) -> &dyn Any { + self + } } impl Drop for PtyMaster { diff --git a/kernel/src/device/registry/block.rs b/kernel/src/device/registry/block.rs index 7c9b2c507..bd25717e5 100644 --- a/kernel/src/device/registry/block.rs +++ b/kernel/src/device/registry/block.rs @@ -151,6 +151,10 @@ impl FileIo for OpenBlockFile { ), }) } + + fn as_any(&self) -> &dyn Any { + self + } } pub(super) fn lookup(id: DeviceId) -> Option> { diff --git a/kernel/src/device/tty/n_tty.rs b/kernel/src/device/tty/n_tty.rs index c68fc7e12..b0f55df2f 100644 --- a/kernel/src/device/tty/n_tty.rs +++ b/kernel/src/device/tty/n_tty.rs @@ -185,6 +185,10 @@ impl FileIo for TtyFile { fn is_offset_aware(&self) -> bool { false } + + fn as_any(&self) -> &dyn Any { + self + } } static TTY1: Once>> = Once::new(); diff --git a/kernel/src/fs/inode_handle.rs b/kernel/src/fs/inode_handle.rs index db694f36e..a05fb06be 100644 --- a/kernel/src/fs/inode_handle.rs +++ b/kernel/src/fs/inode_handle.rs @@ -14,6 +14,7 @@ use crate::{ file_table::FdFlags, path::Path, pipe::PipeHandle, + pseudofs::{NsCommonOps, NsFile}, utils::{ AccessMode, AtomicStatusFlags, CreationFlags, DirentVisitor, FallocMode, FileRange, FlockItem, InodeType, OFFSET_MAX, RangeLockItem, RangeLockType, SeekFrom, StatusFlags, @@ -215,6 +216,11 @@ impl InodeHandle { Ok(()) } + + pub fn as_ns_file(&self) -> Option<&NsFile> { + let file_io = self.file_io.as_ref()?; + file_io.as_any().downcast_ref::>() + } } impl Pollable for InodeHandle { @@ -519,6 +525,8 @@ pub trait FileIo: Pollable + InodeIo + Any + Send + Sync + 'static { fn ioctl(&self, _raw_ioctl: RawIoctl) -> Result { return_errno_with_message!(Errno::ENOTTY, "ioctl is not supported"); } + + fn as_any(&self) -> &dyn Any; } fn do_seek_util(offset: &Mutex, pos: SeekFrom, end: Option) -> Result { diff --git a/kernel/src/fs/pipe/common.rs b/kernel/src/fs/pipe/common.rs index fe89ae636..d66589a0b 100644 --- a/kernel/src/fs/pipe/common.rs +++ b/kernel/src/fs/pipe/common.rs @@ -136,6 +136,10 @@ impl FileIo for PipeHandle { fn is_offset_aware(&self) -> bool { false } + + fn as_any(&self) -> &dyn Any { + self + } } /// A pipe (FIFO) that provides inter-process communication. diff --git a/kernel/src/syscall/setns.rs b/kernel/src/syscall/setns.rs index e6bdf7f86..fc6bc64c5 100644 --- a/kernel/src/syscall/setns.rs +++ b/kernel/src/syscall/setns.rs @@ -11,7 +11,10 @@ //! 2. A `PidFile` opened by `pidfd_open` or by opening `/proc/[pid]` directory. use crate::{ - fs::{file_table::FileDesc, path::MountNamespace}, + fs::{ + file_handle::FileLike, file_table::FileDesc, inode_handle::InodeHandle, + path::MountNamespace, pseudofs::NsCommonOps, + }, net::uts_ns::UtsNamespace, prelude::*, process::{ @@ -34,13 +37,8 @@ pub fn sys_setns(fd: FileDesc, flags: u32, ctx: &Context) -> Result() { build_proxy_from_pid_file(pid_file, ns_type_flags, ctx)? - } - // TODO: Support setting namespaces from `/proc/[pid]/ns`. - else { - return_errno_with_message!( - Errno::EINVAL, - "the FD does not refer to a supported namespace file" - ); + } else { + build_proxy_from_ns_file(file.as_ref(), ns_type_flags, ctx)? }; // Install the newly created `NsProxy`. @@ -105,6 +103,56 @@ fn build_proxy_from_pid_file( Ok(builder.build()) } +fn build_proxy_from_ns_file( + file: &dyn FileLike, + flags: CloneFlags, + ctx: &Context, +) -> Result { + check_unsupported_ns_flags(flags)?; + + let inode_handle = file + .downcast_ref::() + .ok_or_else(|| Error::with_message(Errno::EINVAL, "the file is not a ns file"))?; + + let current_proxy = ctx.thread_local.borrow_ns_proxy(); + let current_proxy = current_proxy.unwrap(); + + let mut builder = NsProxyBuilder::new(current_proxy); + + let applied = try_apply_ns::(inode_handle, flags, |ns| { + builder.uts_ns(ns); + })? || try_apply_ns::(inode_handle, flags, |ns| { + builder.mnt_ns(ns); + })?; + // TODO: Support setting other namespaces from the ns file. + + if !applied { + return_errno_with_message!(Errno::EINVAL, "invalid flags are specified with a ns file"); + } + + Ok(builder.build()) +} + +fn try_apply_ns( + inode_handle: &InodeHandle, + flags: CloneFlags, + apply: impl FnOnce(Arc), +) -> Result { + let Some(ns_file) = inode_handle.as_ns_file::() else { + return Ok(false); + }; + + if !flags.is_empty() && flags != T::TYPE.into() { + return_errno_with_message!( + Errno::EINVAL, + "the flags do not match the type of the ns file" + ); + } + + apply(ns_file.ns().clone()); + Ok(true) +} + fn set_uts_ns( builder: &mut NsProxyBuilder, target_ns: &Arc, diff --git a/test/initramfs/src/syscall/gvisor/Makefile b/test/initramfs/src/syscall/gvisor/Makefile index f29eb7caf..0d225f394 100644 --- a/test/initramfs/src/syscall/gvisor/Makefile +++ b/test/initramfs/src/syscall/gvisor/Makefile @@ -52,6 +52,7 @@ TESTS ?= \ sched_yield_test \ semaphore_test \ sendfile_test \ + setns_test \ sigaction_test \ sigaltstack_test \ signalfd_test \ diff --git a/test/initramfs/src/syscall/gvisor/blocklists/setns_test b/test/initramfs/src/syscall/gvisor/blocklists/setns_test new file mode 100644 index 000000000..c3d3a161e --- /dev/null +++ b/test/initramfs/src/syscall/gvisor/blocklists/setns_test @@ -0,0 +1,2 @@ +SetnsTest.ChangeIPCNamespace +SetnsTest.ChangePIDNamespace \ No newline at end of file