diff --git a/Cargo.lock b/Cargo.lock index 816cb56e19..4f792bf3b8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -262,9 +262,9 @@ checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981" [[package]] name = "libredox" -version = "0.1.17" +version = "0.1.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f02ab6bace2054fb888a3c16f990117b579d14a3088e472d63c6011fa185c9d3" +checksum = "c943259e342f1e06ff2da7a83eabdfe7f92ce10262688dbf1895ff0b3e6e4652" dependencies = [ "bitflags", "libc", @@ -467,9 +467,9 @@ dependencies = [ [[package]] name = "redox_event" -version = "0.4.7" +version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c07d0d6d291e3a951bd847b1cd4af32fc6243d64116cf7702838c02797688b7" +checksum = "c5018d583d6d2f5499352aea8d177e9067d1eb03ab17c78169d5ba7a30001b15" dependencies = [ "bitflags", "libredox", @@ -478,9 +478,9 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.8.1" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b44b894f2a6e36457d665d1e08c3866add6ed5e70050c1b4ba8a8ddedb02ce7" +checksum = "c5102a6aaa05aa011a238e178e6bca86d2cb56fc9f586d37cb80f5bca6e07759" dependencies = [ "bitflags", ] diff --git a/Cargo.toml b/Cargo.toml index 8de1648ac0..7060a608b6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -69,8 +69,9 @@ bitflags = "2" ioslice = { version = "0.6", default-features = false } plain = "0.2" redox-path = "0.3" -redox_protocols = { package = "libredox", version = "0.1.17", default-features = false, features = ["protocol"] } -redox_syscall = "0.8.1" +redox_protocols = { package = "libredox", version = "0.1.18", default-features = false, features = ["protocol"] } +redox_syscall = "0.9.0" + [build-dependencies] cc = "1" @@ -123,7 +124,7 @@ sc = "0.2.7" redox_syscall.workspace = true redox-rt = { path = "redox-rt" } redox-path.workspace = true -redox_event = { version = "0.4.7", default-features = false, features = ["redox_syscall"] } +redox_event = { version = "0.4.8", default-features = false, features = ["redox_syscall"] } ioslice.workspace = true redox-ioctl = { path = "redox-ioctl" } redox_protocols.workspace = true diff --git a/redox-ioctl/Cargo.toml b/redox-ioctl/Cargo.toml index 5c89dfd6b6..c07bc043ab 100644 --- a/redox-ioctl/Cargo.toml +++ b/redox-ioctl/Cargo.toml @@ -8,6 +8,6 @@ description = "Ioctl definitions and (de)serialization for Redox" [dependencies] drm-sys = "0.8.0" -redox_syscall = "0.8.1" +redox_syscall = "0.9.0" [features] diff --git a/redox-rt/src/arch/aarch64.rs b/redox-rt/src/arch/aarch64.rs index f3f5b6445d..26e2af293f 100644 --- a/redox-rt/src/arch/aarch64.rs +++ b/redox-rt/src/arch/aarch64.rs @@ -79,11 +79,12 @@ pub struct ArchIntRegs { pub unsafe fn deactivate_tcb(open_via_dup: &FdGuardUpper) -> Result<()> { let mut env = syscall::EnvRegisters::default(); - let file = open_via_dup.dup(b"regs/env")?; + let file = open_via_dup.dup_into_upper(b"regs/env")?; env.tpidr_el0 = 0; file.write(&mut env)?; + crate::TLS_ACTIVATED.store(false, core::sync::atomic::Ordering::Relaxed); Ok(()) } @@ -93,7 +94,7 @@ unsafe extern "C" fn fork_impl(args: &ForkArgs, initial_rsp: *mut usize) -> usiz unsafe extern "C" fn child_hook(scratchpad: &ForkScratchpad) { //let _ = syscall::write(1, alloc::format!("CUR{cur_filetable_fd}PROC{new_proc_fd}THR{new_thr_fd}\n").as_bytes()); - let _ = syscall::close(scratchpad.cur_filetable_fd); + let _ = crate::sys::close(scratchpad.cur_filetable_fd); unsafe { crate::child_hook_common(crate::ChildHookCommonArgs { new_thr_fd: FdGuard::new(scratchpad.new_thr_fd), @@ -102,6 +103,9 @@ unsafe extern "C" fn child_hook(scratchpad: &ForkScratchpad) { } else { Some(FdGuard::new(scratchpad.new_proc_fd)) }, + new_filetable_fd: FdGuard::new(scratchpad.new_filetable_fd) + .to_upper() + .unwrap(), }) }; } diff --git a/redox-rt/src/arch/i686.rs b/redox-rt/src/arch/i686.rs index d5c0971243..6d35c67df5 100644 --- a/redox-rt/src/arch/i686.rs +++ b/redox-rt/src/arch/i686.rs @@ -61,12 +61,13 @@ pub struct ArchIntRegs { pub unsafe fn deactivate_tcb(open_via_dup: &FdGuardUpper) -> Result<()> { let mut env = syscall::EnvRegisters::default(); - let file = open_via_dup.dup(b"regs/env")?; + let file = open_via_dup.dup_into_upper(b"regs/env")?; env.fsbase = 0; env.gsbase = 0; file.write(&mut env)?; + crate::TLS_ACTIVATED.store(false, core::sync::atomic::Ordering::Relaxed); Ok(()) } @@ -76,7 +77,7 @@ unsafe extern "fastcall" fn fork_impl(args: &ForkArgs, initial_rsp: *mut usize) // TODO: duplicate code with x86_64 unsafe extern "cdecl" fn child_hook(scratchpad: ForkScratchpad) { - let _ = syscall::close(scratchpad.cur_filetable_fd); + let _ = crate::sys::close(scratchpad.cur_filetable_fd); unsafe { crate::child_hook_common(crate::ChildHookCommonArgs { new_thr_fd: FdGuard::new(scratchpad.new_thr_fd), @@ -85,6 +86,9 @@ unsafe extern "cdecl" fn child_hook(scratchpad: ForkScratchpad) { } else { Some(FdGuard::new(scratchpad.new_proc_fd)) }, + new_filetable_fd: FdGuard::new(scratchpad.new_filetable_fd) + .to_upper() + .unwrap(), }) }; } diff --git a/redox-rt/src/arch/mod.rs b/redox-rt/src/arch/mod.rs index 282f28ed5d..e763dc9d82 100644 --- a/redox-rt/src/arch/mod.rs +++ b/redox-rt/src/arch/mod.rs @@ -22,6 +22,7 @@ pub mod riscv64; #[repr(C)] pub struct ForkScratchpad { pub cur_filetable_fd: usize, + pub new_filetable_fd: usize, pub new_proc_fd: usize, pub new_thr_fd: usize, } diff --git a/redox-rt/src/arch/riscv64.rs b/redox-rt/src/arch/riscv64.rs index a5065f44a8..731f776da7 100644 --- a/redox-rt/src/arch/riscv64.rs +++ b/redox-rt/src/arch/riscv64.rs @@ -55,11 +55,12 @@ pub struct ArchIntRegs { pub unsafe fn deactivate_tcb(open_via_dup: &FdGuardUpper) -> Result<()> { let mut env = syscall::EnvRegisters::default(); - let file = open_via_dup.dup(b"regs/env")?; + let file = open_via_dup.dup_into_upper(b"regs/env")?; env.tp = 0; file.write(&mut env)?; + crate::TLS_ACTIVATED.store(false, core::sync::atomic::Ordering::Relaxed); Ok(()) } @@ -68,7 +69,7 @@ unsafe extern "C" fn fork_impl(args: &ForkArgs, initial_rsp: *mut usize) -> usiz } unsafe extern "C" fn child_hook(scratchpad: &ForkScratchpad) { - let _ = syscall::close(scratchpad.cur_filetable_fd); + let _ = crate::sys::close(scratchpad.cur_filetable_fd); unsafe { crate::child_hook_common(crate::ChildHookCommonArgs { new_thr_fd: FdGuard::new(scratchpad.new_thr_fd), @@ -77,6 +78,9 @@ unsafe extern "C" fn child_hook(scratchpad: &ForkScratchpad) { } else { Some(FdGuard::new(scratchpad.new_proc_fd)) }, + new_filetable_fd: FdGuard::new(scratchpad.new_filetable_fd) + .to_upper() + .unwrap(), }) }; } diff --git a/redox-rt/src/arch/x86_64.rs b/redox-rt/src/arch/x86_64.rs index 56e10e2b00..adffe424ef 100644 --- a/redox-rt/src/arch/x86_64.rs +++ b/redox-rt/src/arch/x86_64.rs @@ -75,12 +75,13 @@ pub struct ArchIntRegs { pub unsafe fn deactivate_tcb(open_via_dup: &FdGuardUpper) -> Result<()> { let mut env = syscall::EnvRegisters::default(); - let file = open_via_dup.dup(b"regs/env")?; + let file = open_via_dup.dup_into_upper(b"regs/env")?; env.fsbase = 0; env.gsbase = 0; file.write(&mut env)?; + crate::TLS_ACTIVATED.store(false, core::sync::atomic::Ordering::Relaxed); Ok(()) } @@ -90,7 +91,7 @@ unsafe extern "sysv64" fn fork_impl(args: &ForkArgs, initial_rsp: *mut usize) -> #[allow(unsafe_op_in_unsafe_fn)] unsafe extern "sysv64" fn child_hook(scratchpad: &ForkScratchpad) { - let _ = syscall::close(scratchpad.cur_filetable_fd); + let _ = crate::sys::close(scratchpad.cur_filetable_fd); crate::child_hook_common(crate::ChildHookCommonArgs { new_thr_fd: FdGuard::new(scratchpad.new_thr_fd), new_proc_fd: if scratchpad.new_proc_fd == usize::MAX { @@ -98,6 +99,9 @@ unsafe extern "sysv64" fn child_hook(scratchpad: &ForkScratchpad) { } else { Some(FdGuard::new(scratchpad.new_proc_fd)) }, + new_filetable_fd: FdGuard::new(scratchpad.new_filetable_fd) + .to_upper() + .unwrap(), }); } diff --git a/redox-rt/src/lib.rs b/redox-rt/src/lib.rs index 12835a6a7a..224aca415b 100644 --- a/redox-rt/src/lib.rs +++ b/redox-rt/src/lib.rs @@ -15,6 +15,7 @@ use syscall::Sigcontrol; use self::{ proc::{FdGuard, FdGuardUpper, STATIC_PROC_INFO}, sync::Mutex, + sys::FdTbl, }; extern crate alloc; @@ -67,6 +68,11 @@ impl RtTcb { pub type Tcb = GenericTcb; +// TODO: Remove this by keeping the TCB initialized during exec +// and informing ld.so through some other way. +pub static TLS_ACTIVATED: core::sync::atomic::AtomicBool = + core::sync::atomic::AtomicBool::new(false); + /// OS and architecture specific code to activate TLS - Redox aarch64 #[allow(unsafe_op_in_unsafe_fn)] #[cfg(target_arch = "aarch64")] @@ -78,6 +84,7 @@ pub unsafe fn tcb_activate(_tcb: &RtTcb, tls_end: usize, tls_len: usize) { "msr tpidr_el0, {}", in(reg) abi_ptr, ); + TLS_ACTIVATED.store(true, core::sync::atomic::Ordering::Relaxed); } /// OS and architecture specific code to activate TLS - Redox x86 @@ -86,16 +93,18 @@ pub unsafe fn tcb_activate(_tcb: &RtTcb, tls_end: usize, tls_len: usize) { pub unsafe fn tcb_activate(tcb: &RtTcb, tls_end: usize, _tls_len: usize) { let mut env = syscall::EnvRegisters::default(); - let file = tcb - .thread_fd() - .dup(b"regs/env") + let file_fd = crate::sys::dup_into_upper_raw(tcb.thread_fd().as_raw_fd(), b"regs/env") .expect_notls("failed to open handle for process registers"); - file.read(&mut env).expect_notls("failed to read gsbase"); + syscall::read(file_fd, &mut env).expect_notls("failed to read gsbase"); env.gsbase = tls_end as u32; - file.write(&env).expect_notls("failed to write gsbase"); + syscall::write(file_fd, &env).expect_notls("failed to write gsbase"); + + let _ = crate::sys::close_raw(file_fd); + + TLS_ACTIVATED.store(true, core::sync::atomic::Ordering::Relaxed); } /// OS and architecture specific code to activate TLS - Redox x86_64 @@ -104,16 +113,18 @@ pub unsafe fn tcb_activate(tcb: &RtTcb, tls_end: usize, _tls_len: usize) { pub unsafe fn tcb_activate(tcb: &RtTcb, tls_end_and_tcb_start: usize, _tls_len: usize) { let mut env = syscall::EnvRegisters::default(); - let file = tcb - .thread_fd() - .dup(b"regs/env") + let file_fd = crate::sys::dup_into_upper_raw(tcb.thread_fd().as_raw_fd(), b"regs/env") .expect_notls("failed to open handle for process registers"); - file.read(&mut env).expect_notls("failed to read fsbase"); + syscall::read(file_fd, &mut env).expect_notls("failed to read fsbase"); env.fsbase = tls_end_and_tcb_start as u64; - file.write(&env).expect_notls("failed to write fsbase"); + syscall::write(file_fd, &env).expect_notls("failed to write fsbase"); + + let _ = crate::sys::close_raw(file_fd); + + TLS_ACTIVATED.store(true, core::sync::atomic::Ordering::Relaxed); } /// OS and architecture specific code to activate TLS - Redox riscv64 @@ -129,6 +140,7 @@ pub unsafe fn tcb_activate(_tcb: &RtTcb, tls_end: usize, tls_len: usize) { "mv tp, {}", in(reg) tls_start ); + TLS_ACTIVATED.store(true, core::sync::atomic::Ordering::Relaxed); } /// Initialize redox-rt in situations where relibc is not used @@ -158,6 +170,17 @@ pub unsafe fn initialize_freestanding(this_thr_fd: FdGuardUpper) -> &'static FdG page.tcb_len = syscall::PAGE_SIZE; page.tls_end = (page as *mut Tcb).cast(); + let cur_filetable_fd = syscall::dup_into( + this_thr_fd.as_raw_fd(), + this_thr_fd.as_raw_fd() + 1, + b"filetable-binary", + ) + .expect("failed to open filetable-binary"); + + *current_filetable() = + FdTbl::from_binary_fd(FdGuard::new(cur_filetable_fd).to_upper().unwrap()) + .expect("failed to populate fds"); + // Make sure to use ptr::write to prevent dropping the existing FdGuard page.os_specific.thr_fd.get().write(Some(this_thr_fd)); @@ -176,6 +199,8 @@ pub unsafe fn initialize_freestanding(this_thr_fd: FdGuardUpper) -> &'static FdG let abi_ptr = core::ptr::addr_of_mut!(page.tcb_ptr) as usize; core::arch::asm!("mv tp, {}", in(reg) (abi_ptr + 8)); } + TLS_ACTIVATED.store(true, core::sync::atomic::Ordering::Relaxed); + initialize(); (*page.os_specific.thr_fd.get()).as_ref().unwrap() @@ -275,9 +300,30 @@ pub fn current_namespace_fd() -> syscall::Result { struct ChildHookCommonArgs { new_thr_fd: FdGuard, new_proc_fd: Option, + new_filetable_fd: FdGuardUpper, } unsafe fn child_hook_common(args: ChildHookCommonArgs) { + let new_filetable_fd = args.new_filetable_fd; + + let old_filetable_fd; + { + let mut guard = current_filetable(); + old_filetable_fd = guard.take(); + guard.set_fd(new_filetable_fd); + guard + .override_at(args.new_thr_fd.as_raw_fd(), args.new_thr_fd.as_raw_fd()) + .expect("failed to add new_thr_fd"); + if let Some(new_proc_fd) = args.new_proc_fd.as_ref() { + guard + .override_at(new_proc_fd.as_raw_fd(), new_proc_fd.as_raw_fd()) + .expect("failed to add new_proc_fd"); + } + if let Some(ref old) = old_filetable_fd { + let _ = guard.remove(old.as_raw_fd()); + } + } + let new_thr_fd = args.new_thr_fd.to_upper().unwrap(); let new_proc_fd = args.new_proc_fd.map(|x| x.to_upper().unwrap()); @@ -310,4 +356,13 @@ unsafe fn child_hook_common(args: ChildHookCommonArgs) { let old_thr_fd = unsafe { RtTcb::current().thr_fd.get().replace(Some(new_thr_fd)) }; drop(old_thr_fd); + + drop(old_filetable_fd); +} + +static FILETABLE: Mutex = Mutex::new(FdTbl::new()); + +#[inline] +pub fn current_filetable() -> crate::sync::MutexGuard<'static, FdTbl> { + FILETABLE.lock() } diff --git a/redox-rt/src/proc.rs b/redox-rt/src/proc.rs index 34b321e5ae..e7311ff90f 100644 --- a/redox-rt/src/proc.rs +++ b/redox-rt/src/proc.rs @@ -7,7 +7,7 @@ use crate::{ read_proc_meta, sys::{fstat, open, proc_call, thread_call}, }; -use redox_protocols::protocol::{ProcCall, ThreadCall}; +use redox_protocols::protocol::{O_CLOEXEC, ProcCall, ThreadCall}; use alloc::{boxed::Box, vec}; @@ -25,8 +25,8 @@ use goblin::elf64::{ }; use syscall::{ - CallFlags, F_GETFD, GrantDesc, GrantFlags, MAP_FIXED_NOREPLACE, MAP_SHARED, Map, O_CLOEXEC, - PAGE_SIZE, PROT_EXEC, PROT_READ, PROT_WRITE, SetSighandlerData, + CallFlags, GrantDesc, GrantFlags, MAP_FIXED_NOREPLACE, MAP_SHARED, Map, PAGE_SIZE, PROT_EXEC, + PROT_READ, PROT_WRITE, SetSighandlerData, error::*, flag::{MapFlags, SEEK_SET}, }; @@ -63,6 +63,8 @@ pub struct ExtraInfo<'a> { pub ns_fd: Option, /// CWD handle pub cwd_fd: Option, + /// Filetable handle + pub filetable_fd: Option, /// If the process for which the image is to be loaded the same as the currently running process pub same_process: bool, } @@ -93,9 +95,10 @@ pub fn fexec_impl( let grants_fd = if let Some(interp) = interp_override.as_ref() { FdGuard::new(interp.grants_fd).to_upper()? } else { - let current_addrspace_fd = thread_fd.dup(b"addrspace")?; - current_addrspace_fd.dup(b"empty")?.to_upper()? + let current_addrspace_fd = thread_fd.dup_into_upper(b"addrspace")?; + current_addrspace_fd.dup_into_upper(b"empty")? }; + grants_fd.fcntl(syscall::F_SETFD, O_CLOEXEC)?; // Never allow more than 1 MiB of program headers. const MAX_PH_SIZE: usize = 1024 * 1024; @@ -404,6 +407,8 @@ pub fn fexec_impl( push(AT_REDOX_NS_FD)?; push(extrainfo.cwd_fd.unwrap_or(usize::MAX))?; push(AT_REDOX_CWD_FD)?; + push(extrainfo.filetable_fd.unwrap_or(usize::MAX))?; + push(AT_REDOX_FILETABLE_FD)?; push(0)?; @@ -421,7 +426,7 @@ pub fn fexec_impl( push(argc)?; - if let Ok(sighandler_fd) = thread_fd.dup(b"sighandler") { + if let Ok(sighandler_fd) = thread_fd.dup_into_upper(b"sighandler") { let _ = sighandler_fd.write(&SetSighandlerData { user_handler: 0, excp_handler: 0, @@ -456,11 +461,11 @@ pub fn fexec_impl( ); if interp_override.is_some() { - let mmap_min_fd = grants_fd.dup(b"mmap-min-addr")?; + let mmap_min_fd = grants_fd.dup_into_upper(b"mmap-min-addr")?; let _ = mmap_min_fd.write(&usize::to_ne_bytes(min_mmap_addr)); } - let addrspace_selection_fd = thread_fd.dup(b"current-addrspace")?.to_upper()?; + let addrspace_selection_fd = thread_fd.dup_into_upper(b"current-addrspace")?; let _ = addrspace_selection_fd.write(&create_set_addr_space_buf( grants_fd.as_raw_fd(), @@ -474,29 +479,55 @@ pub fn fexec_impl( // scenarios. While execve() is undefined according to POSIX if there exist sibling // threads, it could still be allowed by keeping certain file descriptors and instead // set the active file table. - let files_fd = syscall::dup(thread_fd.as_raw_fd(), b"filetable-binary")?; - let mut files_reader = FileBufReader::from_fd(files_fd); - loop { - let fd = match files_reader.read_le_u64()? { - None => break, - Some(fd) => fd, - }; - let fd = usize::try_from(fd).unwrap(); + let _siglock = crate::signal::tmp_disable_signals(); + let fds_to_close = { + let guard = crate::current_filetable(); + let mut fds = alloc::vec::Vec::new(); + for (fd, flags) in guard.iter() { + if fd == addrspace_selection_fd.as_raw_fd() { + continue; // Will be closed below + } - if fd == addrspace_selection_fd.as_raw_fd() || fd == files_fd { - continue; // Will be closed below + if flags & O_CLOEXEC == O_CLOEXEC || fd == image_file.as_raw_fd() { + fds.push(fd); + } } - let flags = syscall::fcntl(fd, F_GETFD, 0)?; + fds + }; - if flags & O_CLOEXEC == O_CLOEXEC { - let _ = syscall::close(fd); + let fds_to_close_bytes: &[u8] = unsafe { + core::slice::from_raw_parts( + fds_to_close.as_ptr() as *mut u8, + fds_to_close.len() * core::mem::size_of::(), + ) + }; + + { + let filetable_fd = thread_fd.dup_into_upper(b"filetable-binary")?; + let _ = filetable_fd.call_wo( + fds_to_close_bytes, + CallFlags::empty(), + &[syscall::FileTableVerb::Close as u64], + ); + } + { + let mut guard = crate::current_filetable(); + for fd in fds_to_close { + let _ = guard.remove(fd); } } unsafe { deactivate_tcb(&thread_fd)?; } + + let old_filetable_fd = { + let mut guard = crate::current_filetable(); + guard.take() + }; + drop(old_filetable_fd); + // Dropping this FD will cause the address space switch. drop(addrspace_selection_fd); unreachable!(); @@ -705,7 +736,7 @@ impl<'a> Drop for MmapGuard<'a> { } } -struct FileBufReader { +pub(crate) struct FileBufReader { fd: usize, buf: [u8; 8192], pos: usize, @@ -724,10 +755,10 @@ impl FileBufReader { } impl FileBufReader { - fn read_le_u64(&mut self) -> Result> { + pub fn read_le_u64(&mut self) -> Result> { if self.pos >= self.cap { debug_assert!(self.pos == self.cap); - self.cap = crate::sys::posix_read(self.fd, &mut self.buf)?; + self.cap = syscall::read(self.fd, &mut self.buf)?; self.pos = 0; } @@ -746,7 +777,6 @@ impl FileBufReader { Ok(Some(num)) } } - #[repr(transparent)] pub struct FdGuard { fd: usize, @@ -757,18 +787,24 @@ impl FdGuard { pub fn new(fd: usize) -> Self { Self { fd } } - #[inline] pub fn open>(path: T, flags: usize) -> Result { open(path, flags).map(Self::new) } + #[inline] + pub fn open_into_upper>(path: T, flags: usize) -> Result { + crate::sys::open_into_upper(path, flags) + .map(FdGuard::new)? + .to_upper() + } + #[inline] pub fn to_upper(self) -> Result { // Move to upper table if necessary let fd = if self.fd & syscall::UPPER_FDTBL_TAG == 0 { //TODO: use F_DUPFD_CLOEXEC? - let fd = syscall::fcntl(self.fd, syscall::F_DUPFD, syscall::UPPER_FDTBL_TAG)?; + let fd = crate::sys::fcntl(self.fd, syscall::F_DUPFD, syscall::UPPER_FDTBL_TAG)?; drop(self); fd } else { @@ -791,16 +827,35 @@ impl FdGuard { flags: usize, fcntl_flags: usize, ) -> Result> { - syscall::openat(self.fd, path, flags, fcntl_flags).map(FdGuard::new) + crate::sys::openat(self.fd, path, flags, fcntl_flags).map(FdGuard::new) } + #[inline] + pub fn openat_into_upper>( + &self, + path: T, + flags: usize, + fcntl_flags: usize, + ) -> Result { + crate::sys::openat_into_upper(self.fd, path, flags, fcntl_flags) + .map(FdGuard::new)? + .to_upper() + } + #[inline] pub fn dup(&self, buf: &[u8]) -> Result> { - syscall::dup(self.fd, buf).map(FdGuard::new) + crate::sys::dup(self.fd, buf).map(FdGuard::new) + } + + #[inline] + pub fn dup_into_upper(&self, buf: &[u8]) -> Result { + crate::sys::dup_into_upper(self.fd, buf) + .map(FdGuard::new)? + .to_upper() } #[inline] pub fn fcntl(&self, cmd: usize, arg: usize) -> Result { - syscall::fcntl(self.fd, cmd, arg) + crate::sys::fcntl(self.fd, cmd, arg) } #[inline] @@ -825,17 +880,17 @@ impl FdGuard { #[inline] pub fn call_ro(&self, payload: &mut [u8], flags: CallFlags, metadata: &[u64]) -> Result { - syscall::call_ro(self.fd, payload, flags, metadata) + crate::sys::sys_call_ro(self.fd, payload, flags, metadata) } #[inline] pub fn call_wo(&self, payload: &[u8], flags: CallFlags, metadata: &[u64]) -> Result { - syscall::call_wo(self.fd, payload, flags, metadata) + crate::sys::sys_call_wo(self.fd, payload, flags, metadata) } #[inline] pub fn call_rw(&self, payload: &mut [u8], flags: CallFlags, metadata: &[u64]) -> Result { - syscall::call_rw(self.fd, payload, flags, metadata) + crate::sys::sys_call_rw(self.fd, payload, flags, metadata) } #[inline] @@ -853,7 +908,7 @@ impl FdGuard { impl Drop for FdGuard { #[inline] fn drop(&mut self) { - let _ = syscall::close(self.fd); + let _ = crate::sys::close(self.fd); } } impl Debug for FdGuard { @@ -912,7 +967,7 @@ pub enum ForkArgs<'a> { } pub fn fork_inner(initial_rsp: *mut usize, args: &ForkArgs) -> Result { - let (cur_filetable_fd, new_proc_fd, new_thr_fd, new_pid); + let (cur_filetable_fd, new_filetable_fd, new_proc_fd, new_thr_fd, new_pid); { let cur_thr_fd = match args { @@ -931,7 +986,8 @@ pub fn fork_inner(initial_rsp: *mut usize, args: &ForkArgs) -> Result { // Copy existing files into new file table, but do not reuse the same file table (i.e. new // parent FDs will not show up for the child). let scratchpad = { - cur_filetable_fd = cur_thr_fd.dup(b"filetable")?; + cur_filetable_fd = cur_thr_fd.dup_into_upper(b"filetable-binary")?; + new_filetable_fd = cur_filetable_fd.dup_into_upper(b"copy")?; // This must be done before the address space is copied. let proc_fd = new_proc_fd.as_ref().map_or(usize::MAX, |p| p.as_raw_fd()); @@ -939,6 +995,7 @@ pub fn fork_inner(initial_rsp: *mut usize, args: &ForkArgs) -> Result { ForkScratchpad { cur_filetable_fd: cur_filetable_fd.as_raw_fd(), + new_filetable_fd: new_filetable_fd.as_raw_fd(), new_proc_fd: proc_fd, new_thr_fd: new_thr_fd.as_raw_fd(), } @@ -960,10 +1017,10 @@ pub fn fork_inner(initial_rsp: *mut usize, args: &ForkArgs) -> Result { // CoW-duplicate address space. { - let new_addr_space_sel_fd = new_thr_fd.dup(b"current-addrspace")?; + let new_addr_space_sel_fd = new_thr_fd.dup_into_upper(b"current-addrspace")?; - let cur_addr_space_fd = cur_thr_fd.dup(b"addrspace")?.to_upper()?; - let new_addr_space_fd = cur_addr_space_fd.dup(b"exclusive")?.to_upper()?; + let cur_addr_space_fd = cur_thr_fd.dup_into_upper(b"addrspace")?; + let new_addr_space_fd = cur_addr_space_fd.dup_into_upper(b"exclusive")?; let mut grant_desc_buf = [GrantDesc::default(); 16]; loop { @@ -1006,7 +1063,7 @@ pub fn fork_inner(initial_rsp: *mut usize, args: &ForkArgs) -> Result { buf = alloc::format!("grant-fd-{:>08x}", grant.base).into_bytes(); } - let grant_fd = cur_addr_space_fd.dup(&buf)?.to_upper()?; + let grant_fd = cur_addr_space_fd.dup_into_upper(&buf)?; let mut flags = MAP_SHARED | MAP_FIXED_NOREPLACE; @@ -1049,7 +1106,7 @@ pub fn fork_inner(initial_rsp: *mut usize, args: &ForkArgs) -> Result { // Do this after the address space is cloned, since the kernel will get a shared // reference to the TCB and whatever pages stores the signal proc control struct. { - let new_sighandler_fd = new_thr_fd.dup(b"sighandler")?; + let new_sighandler_fd = new_thr_fd.dup_into_upper(b"sighandler")?; new_sighandler_fd.write(&crate::signal::current_setsighandler_struct())?; } if let Some(ref proc_fd) = new_proc_fd { @@ -1069,25 +1126,26 @@ pub fn fork_inner(initial_rsp: *mut usize, args: &ForkArgs) -> Result { } { // Copy environment registers. - let cur_env_regs_fd = cur_thr_fd.dup(b"regs/env")?; - let new_env_regs_fd = new_thr_fd.dup(b"regs/env")?; + let cur_env_regs_fd = cur_thr_fd.dup_into_upper(b"regs/env")?; + let new_env_regs_fd = new_thr_fd.dup_into_upper(b"regs/env")?; let mut env_regs = syscall::EnvRegisters::default(); cur_env_regs_fd.read(&mut env_regs)?; new_env_regs_fd.write(&env_regs)?; } } - // Copy the file table. We do this last to ensure that all previously used file descriptors are - // closed. The only exception -- the filetable selection fd and the current filetable fd -- - // will be closed by the child process. { // TODO: Use file descriptor forwarding or something similar to avoid copying the file // table in the kernel. - let new_filetable_fd = cur_filetable_fd.dup(b"copy")?; - let new_filetable_sel_fd = new_thr_fd.dup(b"current-filetable")?; + let new_filetable_sel_fd = new_thr_fd.dup_into_upper(b"current-filetable")?; new_filetable_sel_fd.write(&usize::to_ne_bytes(new_filetable_fd.as_raw_fd()))?; } - let start_fd = new_thr_fd.dup(b"start")?; + new_filetable_fd.call_wo( + &new_filetable_fd.as_raw_fd().to_ne_bytes(), + syscall::CallFlags::FD | syscall::CallFlags::FD_CLONE, + &[new_filetable_fd.as_raw_fd() as u64], + )?; + let start_fd = new_thr_fd.dup_into_upper(b"start")?; start_fd.write(&[0])?; Ok(new_pid) } @@ -1107,8 +1165,8 @@ pub fn new_child_process(args: &ForkArgs<'_>) -> Result { .proc_fd .as_ref() .expect("cannot use ForkArgs::Managed without an existing proc info"); - let child_proc_fd = this_proc_fd.dup(b"fork")?.to_upper()?; - let only_thread_fd = child_proc_fd.dup(b"thread-0")?.to_upper()?; + let child_proc_fd = this_proc_fd.dup_into_upper(b"fork")?; + let only_thread_fd = child_proc_fd.dup_into_upper(b"thread-0")?; let meta = read_proc_meta(&child_proc_fd)?; Ok(NewChildProc { proc_fd: Some(child_proc_fd), @@ -1121,7 +1179,7 @@ pub fn new_child_process(args: &ForkArgs<'_>) -> Result { #[cfg(not(feature = "proc"))] ForkArgs::Init { this_thr_fd, auth } => { - let thr_fd = auth.dup(b"new-context")?.to_upper()?; + let thr_fd = auth.dup_into_upper(b"new-context")?; let buf = syscall::ProcSchemeAttrs { pid: 0, euid: 0, @@ -1134,8 +1192,8 @@ pub fn new_child_process(args: &ForkArgs<'_>) -> Result { buf }, }; - let attr_fd = - thr_fd.dup(alloc::format!("auth-{}-attrs", auth.as_raw_fd()).as_bytes())?; + let attr_fd = thr_fd + .dup_into_upper(alloc::format!("auth-{}-attrs", auth.as_raw_fd()).as_bytes())?; attr_fd.write(&buf)?; Ok(NewChildProc { thr_fd, @@ -1148,24 +1206,27 @@ pub fn new_child_process(args: &ForkArgs<'_>) -> Result { pub unsafe fn make_init(proc_cap: usize) -> (&'static FdGuardUpper, &'static FdGuardUpper) { let proc_fd = FdGuard::new( - syscall::openat(proc_cap, "init", syscall::O_CLOEXEC, 0).expect("failed to create init"), + crate::sys::openat_into_upper(proc_cap, "init", 0, 0).expect("failed to create init"), ) .to_upper() .unwrap(); - syscall::sendfd( + crate::sys::sys_call_wo( proc_fd.as_raw_fd(), - RtTcb::current().thread_fd().dup(&[]).unwrap().take(), - 0, - 0, + &RtTcb::current() + .thread_fd() + .dup_into_upper(&[]) + .unwrap() + .take() + .to_ne_bytes(), + syscall::CallFlags::FD, + &[], ) .expect("failed to assign current thread to init process"); let managed_thr_fd = proc_fd - .dup(b"thread-0") - .expect("failed to get managed thread for init") - .to_upper() - .unwrap(); + .dup_into_upper(b"thread-0") + .expect("failed to get managed thread for init"); let managed_thr_fd = unsafe { (*RtTcb::current().thr_fd.get()).insert(managed_thr_fd) }; diff --git a/redox-rt/src/signal.rs b/redox-rt/src/signal.rs index bf30d98dae..71dad4ad5f 100644 --- a/redox-rt/src/signal.rs +++ b/redox-rt/src/signal.rs @@ -538,10 +538,14 @@ fn current_sigctl() -> &'static Sigcontrol { } pub struct TmpDisableSignalsGuard { - _inner: (), + active: bool, } pub fn tmp_disable_signals() -> TmpDisableSignalsGuard { + if !crate::TLS_ACTIVATED.load(Ordering::Relaxed) { + return TmpDisableSignalsGuard { active: false }; + } + unsafe { let ctl = ¤t_sigctl().control_flags; ctl.store( @@ -554,10 +558,13 @@ pub fn tmp_disable_signals() -> TmpDisableSignalsGuard { (*Tcb::current().unwrap().os_specific.arch.get()).disable_signals_depth += 1; } - TmpDisableSignalsGuard { _inner: () } + TmpDisableSignalsGuard { active: true } } impl Drop for TmpDisableSignalsGuard { fn drop(&mut self) { + if !self.active { + return; + } unsafe { let depth = &mut (*Tcb::current().unwrap().os_specific.arch.get()).disable_signals_depth; @@ -666,7 +673,7 @@ pub fn setup_sighandler(tcb: &RtTcb, first_thread: bool) { let fd = tcb .thread_fd() - .dup(b"sighandler") + .dup_into_upper(b"sighandler") .expect("failed to open sighandler fd"); fd.write(&data).expect("failed to write to sighandler fd"); this_thread_call( diff --git a/redox-rt/src/sys.rs b/redox-rt/src/sys.rs index ca839ab883..f3ebf8fd77 100644 --- a/redox-rt/src/sys.rs +++ b/redox-rt/src/sys.rs @@ -8,19 +8,21 @@ use ioslice::IoSlice; use syscall::{ Call, CallFlags, EINVAL, ERESTART, StdFsCallKind, TimeSpec, data::StdFsCallMeta, - error::{self, EINTR, ENODEV, ESRCH, Error, Result}, + error::{self, EBADF, EEXIST, EINTR, EMFILE, ENODEV, ESRCH, Error, Result}, }; +pub use redox_path::RedoxPath; + use crate::{ - DYNAMIC_PROC_INFO, DynamicProcInfo, RtTcb, Tcb, + DYNAMIC_PROC_INFO, DynamicProcInfo, FILETABLE, RtTcb, Tcb, arch::manually_enter_trampoline, proc::{FdGuard, FdGuardUpper}, read_proc_meta, signal::tmp_disable_signals, }; -use alloc::vec::Vec; +use alloc::{collections::btree_set::BTreeSet, vec::Vec}; use redox_protocols::protocol::{ - NsDup, ProcCall, ProcKillTarget, RtSigInfo, ThreadCall, WaitFlags, + F_DUPFD_CLOEXEC, NsDup, O_CLOEXEC, ProcCall, ProcKillTarget, RtSigInfo, ThreadCall, WaitFlags, }; #[inline] @@ -177,29 +179,123 @@ pub fn sys_call_ro( flags: CallFlags, metadata: &[u64], ) -> Result { - unsafe { + if !flags.contains(CallFlags::FD) { + return unsafe { + fd.raw_call( + payload.as_mut_ptr(), + payload.len(), + flags | CallFlags::READ, + metadata, + ) + }; + } + + let _siglock = tmp_disable_signals(); + + if payload.len() % size_of::() != 0 { + return Err(Error::new(EINVAL)); + } + + let fd_slice = unsafe { + core::slice::from_raw_parts_mut( + payload.as_mut_ptr() as *mut usize, + payload.len() / size_of::(), + ) + }; + + if fd_slice.is_empty() { + return Err(Error::new(EINVAL)); + } + + let is_automated = fd_slice[0] == usize::MAX; + let mut backup_handles = Vec::with_capacity(fd_slice.len()); + + if !is_automated { + backup_handles.extend_from_slice(fd_slice); + } + + let which = if flags.contains(CallFlags::FD_UPPER) { + syscall::UPPER_FDTBL_TAG + } else { + 0 + }; + let entry_flags = if flags.contains(CallFlags::FD_CLOEXEC) { + O_CLOEXEC + } else { + 0 + }; + + FILETABLE.lock().bulk_insert(which, fd_slice, entry_flags)?; + + if is_automated { + backup_handles.extend_from_slice(fd_slice); + } + + let res = unsafe { fd.raw_call( payload.as_mut_ptr(), payload.len(), flags | CallFlags::READ, metadata, ) + }; + + if res.is_err() { + let mut guard = FILETABLE.lock(); + for &handle in &backup_handles { + let _ = guard.remove(handle); + } + return res; } + + res } + pub fn sys_call_wo( fd: T, payload: &[u8], flags: CallFlags, metadata: &[u64], ) -> Result { - unsafe { + if !flags.contains(CallFlags::FD) { + return unsafe { + fd.raw_call( + payload.as_ptr(), + payload.len(), + flags | CallFlags::WRITE, + metadata, + ) + }; + } + let _siglock = tmp_disable_signals(); + + if payload.len() % size_of::() != 0 { + return Err(Error::new(EINVAL)); + } + let fd_slice = unsafe { + core::slice::from_raw_parts( + payload.as_ptr() as *const usize, + payload.len() / size_of::(), + ) + }; + + let res = unsafe { fd.raw_call( payload.as_ptr(), payload.len(), flags | CallFlags::WRITE, metadata, ) + }; + + if res.is_ok() && !flags.contains(CallFlags::FD_CLONE) { + let mut guard = FILETABLE.lock(); + for &handle in fd_slice { + let _ = guard.remove(handle); + } } + + res } pub fn sys_call_rw( fd: T, @@ -486,19 +582,19 @@ pub fn getns() -> Result { Ok(cur_ns) } } + pub fn open>(path: T, flags: usize) -> Result { - let path = path.as_ref(); + let _siglock = tmp_disable_signals(); let fcntl_flags = flags & syscall::O_FCNTL_MASK; - unsafe { - syscall::syscall5( - syscall::SYS_OPENAT, - crate::current_namespace_fd()?, - path.as_ptr() as usize, - path.len(), - flags, - fcntl_flags, - ) - } + let redox_path = RedoxPath::from_absolute(path.as_ref()).ok_or(Error::new(EINVAL))?; + let (_, reference) = redox_path.as_parts().ok_or(Error::new(EINVAL))?; + let root_fd = FdGuard::new(openat_into_upper( + crate::current_namespace_fd()?, + path.as_ref(), + syscall::O_DIRECTORY | O_CLOEXEC, + 0, + )?); + openat_into_posix(root_fd.as_raw_fd(), reference.as_ref(), flags, fcntl_flags) } pub fn openat>( fd: usize, @@ -506,26 +602,128 @@ pub fn openat>( flags: usize, fcntl_flags: usize, ) -> Result { + openat_into_posix(fd, path, flags, fcntl_flags) +} +fn openat_into_posix>( + fd: usize, + path: T, + flags: usize, + fcntl_flags: usize, +) -> Result { + let _siglock = tmp_disable_signals(); let path = path.as_ref(); - unsafe { - syscall::syscall5( - syscall::SYS_OPENAT, + + let out = { + let mut guard = FILETABLE.lock(); + guard.add_posix(flags)? + }; + + let res = unsafe { + syscall::syscall6( + syscall::SYS_OPENAT_INTO, fd, path.as_ptr() as usize, path.len(), flags, fcntl_flags, + out, ) + }; + + if res.is_err() { + let mut guard = FILETABLE.lock(); + let _ = guard.remove(out); + return res; } + + Ok(out) } +pub fn open_into_upper>(path: T, flags: usize) -> Result { + let _siglock = tmp_disable_signals(); + let fcntl_flags = flags & syscall::O_FCNTL_MASK; + let redox_path = RedoxPath::from_absolute(path.as_ref()).ok_or(Error::new(EINVAL))?; + let (_, reference) = redox_path.as_parts().ok_or(Error::new(EINVAL))?; + let root_fd = FdGuard::new(openat_into_upper( + crate::current_namespace_fd()?, + path.as_ref(), + syscall::O_DIRECTORY | O_CLOEXEC, + 0, + )?); + openat_into_upper(root_fd.as_raw_fd(), reference.as_ref(), flags, fcntl_flags) +} +pub fn dup(fd: usize, buf: &[u8]) -> Result { + let _siglock = tmp_disable_signals(); + + let out = { + let mut guard = FILETABLE.lock(); + guard.add_posix(0)? + }; + + let res = unsafe { + syscall::syscall4( + syscall::SYS_DUP_INTO, + fd, + buf.as_ptr() as usize, + buf.len(), + out, + ) + }; + + if res.is_err() { + let mut guard = FILETABLE.lock(); + let _ = guard.remove(out); + return res; + } + + Ok(out) +} + +pub fn dup2(fd: usize, newfd: usize, buf: &[u8]) -> Result { + let _siglock = tmp_disable_signals(); + + let out = { + let mut guard = FILETABLE.lock(); + guard.override_at(fd, newfd)? + }; + + let res = unsafe { + syscall::syscall4( + syscall::SYS_DUP2, + fd, + newfd, + buf.as_ptr() as usize, + buf.len(), + ) + }; + + if res.is_err() { + let mut guard = FILETABLE.lock(); + let _ = guard.remove(out); + return res; + } + + Ok(out) +} + pub fn unlink>(path: T, flags: usize) -> Result { + let _siglock = tmp_disable_signals(); let path = path.as_ref(); + let fcntl_flags = flags & syscall::O_FCNTL_MASK; + let redox_path = RedoxPath::from_absolute(path).ok_or(Error::new(EINVAL))?; + let (_, reference) = redox_path.as_parts().ok_or(Error::new(EINVAL))?; + let root_fd = FdGuard::new(openat_into_upper( + crate::current_namespace_fd()?, + path, + syscall::O_DIRECTORY | O_CLOEXEC, + 0, + )?); + let reference_str = reference.as_ref(); unsafe { syscall::syscall4( syscall::SYS_UNLINKAT, - crate::current_namespace_fd()?, - path.as_ptr() as usize, - path.len(), + root_fd.as_raw_fd(), + reference_str.as_ptr() as usize, + reference_str.len(), flags, ) } @@ -539,12 +737,12 @@ pub fn mkns(names: &[IoSlice]) -> Result { buf.extend_from_slice(&len.to_ne_bytes()); buf.extend_from_slice(name_bytes); } - FdGuard::new(syscall::dup(crate::current_namespace_fd()?, &buf)?).to_upper() + FdGuard::new(dup_into_upper(crate::current_namespace_fd()?, &buf)?).to_upper() } pub fn register_scheme_to_ns(ns_fd: usize, name: &str, cap_fd: usize) -> Result<()> { let mut buf = alloc::vec::Vec::from((NsDup::IssueRegister as usize).to_ne_bytes()); buf.extend_from_slice(name.as_bytes()); - let ns_this_scheme = FdGuard::new(syscall::dup(ns_fd, &buf)?); + let ns_this_scheme = FdGuard::new(crate::sys::dup(ns_fd, &buf)?); let cap_bytes = cap_fd.to_ne_bytes(); ns_this_scheme.call_wo(&cap_bytes, CallFlags::FD, &[])?; Ok(()) @@ -569,3 +767,1233 @@ pub fn std_fs_call_rw( pub fn fstat(fd: usize, stat: &mut syscall::Stat) -> Result { std_fs_call_ro(fd, stat, &StdFsCallMeta::new(StdFsCallKind::Fstat, 0, 0)) } + +pub fn fcntl(fd: usize, cmd: usize, arg: usize) -> Result { + if cmd == syscall::F_DUPFD || cmd == F_DUPFD_CLOEXEC { + let _siglock = tmp_disable_signals(); + + let cloexec_flag = if cmd == F_DUPFD_CLOEXEC { O_CLOEXEC } else { 0 }; + + let out = { + let mut guard = FILETABLE.lock(); + if arg & syscall::UPPER_FDTBL_TAG != 0 { + guard.insert_upper(cloexec_flag)? | syscall::UPPER_FDTBL_TAG + } else { + guard.add_posix(cloexec_flag)? + } + }; + + let res = unsafe { syscall::syscall3(syscall::SYS_FCNTL, fd, syscall::F_DUPFD, out) }; + + if res.is_err() { + let mut guard = FILETABLE.lock(); + let _ = guard.remove(out); + return res; + } + + let actual_fd = res.unwrap(); + if actual_fd != out { + let mut guard = FILETABLE.lock(); + let _ = guard.remove(out); + guard.override_at(actual_fd, actual_fd)?; + if cloexec_flag != 0 { + guard.set_fd_flags(actual_fd, cloexec_flag)?; + } + } + + return Ok(actual_fd); + } + + if cmd == syscall::F_GETFD { + let _siglock = tmp_disable_signals(); + return FILETABLE.lock().get_fd_flags(fd); + } + + if cmd == syscall::F_SETFD { + let _siglock = tmp_disable_signals(); + + let res = unsafe { syscall::syscall3(syscall::SYS_FCNTL, fd, cmd, arg) }; + if res.is_err() { + return res; + } + + FILETABLE.lock().set_fd_flags(fd, arg)?; + return Ok(0); + } + + unsafe { syscall::syscall3(syscall::SYS_FCNTL, fd, cmd, arg) } +} + +pub fn openat_into_upper>( + fd: usize, + path: T, + flags: usize, + fcntl_flags: usize, +) -> Result { + let _siglock = tmp_disable_signals(); + let path = path.as_ref(); + + let out_idx = { + let mut guard = FILETABLE.lock(); + guard.insert_upper(flags)? + }; + let out = out_idx | syscall::UPPER_FDTBL_TAG; + + let res = unsafe { + syscall::syscall6( + syscall::SYS_OPENAT_INTO, + fd, + path.as_ptr() as usize, + path.len(), + flags, + fcntl_flags, + out, + ) + }; + + if res.is_err() { + let mut guard = FILETABLE.lock(); + let _ = guard.remove(out); + return res; + } + + Ok(out) +} + +pub fn dup_into_upper(fd: usize, buf: &[u8]) -> Result { + let _siglock = tmp_disable_signals(); + + let out_idx = { + let mut guard = FILETABLE.lock(); + guard.insert_upper(0)? + }; + let out = out_idx | syscall::UPPER_FDTBL_TAG; + + let res = unsafe { + syscall::syscall4( + syscall::SYS_DUP_INTO, + fd, + buf.as_ptr() as usize, + buf.len(), + out, + ) + }; + + if res.is_err() { + let mut guard = FILETABLE.lock(); + let _ = guard.remove(out); + return res; + } + + Ok(out) +} + +pub fn dup_into_upper_raw(fd: usize, buf: &[u8]) -> Result { + let out_idx = { + let mut guard = FILETABLE.lock(); + guard.insert_upper(0)? + }; + let out = out_idx | syscall::UPPER_FDTBL_TAG; + + let res = unsafe { + syscall::syscall4( + syscall::SYS_DUP_INTO, + fd, + buf.as_ptr() as usize, + buf.len(), + out, + ) + }; + + if res.is_err() { + let mut guard = FILETABLE.lock(); + let _ = guard.remove(out); + return res; + } + + Ok(out) +} + +pub fn close(fd: usize) -> Result { + let _siglock = tmp_disable_signals(); + + let is_upper = (fd & syscall::UPPER_FDTBL_TAG) != 0; + + let res = unsafe { syscall::syscall1(syscall::SYS_CLOSE, fd) }; + + if res.is_ok() || res.err().map_or(false, |e| e.errno == EBADF) { + let mut guard = FILETABLE.lock(); + let _ = guard.remove(fd); + } + + res +} + +pub fn close_raw(fd: usize) -> Result { + let res = unsafe { syscall::syscall1(syscall::SYS_CLOSE, fd) }; + + if res.is_ok() || res.err().map_or(false, |e| e.errno == EBADF) { + let mut guard = FILETABLE.lock(); + let _ = guard.remove(fd); + } + + res +} + +pub struct FdTbl { + fd: Option, + posix_fdtbl: PosixFdTbl, + upper_fdtbl: UpperFdTbl, + active_count: usize, +} + +impl FdTbl { + pub const CONTEXT_MAX_FILES: u32 = 65_536; + pub const DEFAULT_CAPACITY: usize = usize::BITS as usize; + + pub const fn new() -> Self { + Self { + fd: None, + posix_fdtbl: PosixFdTbl::new(), + upper_fdtbl: UpperFdTbl::new(), + active_count: 0, + } + } + + pub fn from_binary_fd(filetable_fd: FdGuardUpper) -> Result { + let mut fdtbl = Self::new(); + let files_reader_fd = filetable_fd.as_raw_fd(); + let _ = filetable_fd.lseek(0, syscall::flag::SEEK_SET); + fdtbl.set_fd(filetable_fd); + fdtbl.resize(Self::DEFAULT_CAPACITY); + + let mut reader = crate::proc::FileBufReader::from_fd(files_reader_fd); + fdtbl.populate(&mut reader)?; + + // Manually mark the filetable_fd itself as occupied in userspace FILETABLE + fdtbl.override_at(files_reader_fd, files_reader_fd)?; + + Ok(fdtbl) + } + + pub fn with_capacity(capacity: usize, fd: FdGuardUpper) -> Self { + Self { + fd: Some(fd), + posix_fdtbl: PosixFdTbl::with_capacity(capacity), + upper_fdtbl: UpperFdTbl::with_capacity(capacity), + active_count: 0, + } + } + + pub fn fd(&self) -> Option<&FdGuardUpper> { + self.fd.as_ref() + } + + pub fn take(&mut self) -> Option { + self.fd.take() + } + + pub fn set_fd(&mut self, fd: FdGuardUpper) { + self.fd = Some(fd); + } + + pub fn resize(&mut self, size: usize) { + self.posix_fdtbl.resize(size); + self.upper_fdtbl.resize(size); + } + + pub fn upper_capacity(&self) -> usize { + self.upper_fdtbl.capacity() + } + + pub fn upper_len(&self) -> usize { + self.upper_fdtbl.len() + } + + fn strip_tags(index: usize) -> usize { + index & !syscall::UPPER_FDTBL_TAG + } + + fn is_upper(index: usize) -> bool { + (index & syscall::UPPER_FDTBL_TAG) != 0 + } + + pub(crate) fn populate(&mut self, reader: &mut crate::proc::FileBufReader) -> Result<()> { + while let Some(fd) = reader.read_le_u64()? { + let fd = fd as usize; + self.override_at(fd, fd)?; + } + Ok(()) + } + + pub fn get_fd_flags(&self, fd: usize) -> Result { + if Self::is_upper(fd) { + let flags = self.upper_fdtbl.get_flags(fd)?; + let mut raw_flags = 0; + if flags & (O_CLOEXEC as u32) != 0 { + raw_flags |= O_CLOEXEC; + } + Ok(raw_flags) + } else { + let flags = self.posix_fdtbl.get_flags(fd)?; + let mut raw_flags = 0; + if flags.contains(FdFlags::CLOEXEC) { + raw_flags |= O_CLOEXEC; + } + Ok(raw_flags) + } + } + + pub fn set_fd_flags(&mut self, fd: usize, raw_flags: usize) -> Result<()> { + if Self::is_upper(fd) { + let old_flags = self.upper_fdtbl.get_flags(fd)?; + let mut new_flags = old_flags & !(O_CLOEXEC as u32); + if raw_flags & O_CLOEXEC != 0 { + new_flags |= O_CLOEXEC as u32; + } + self.upper_fdtbl.set_flags(fd, new_flags)?; + } else { + let mut new_flags = FdFlags::empty(); + if raw_flags & O_CLOEXEC != 0 { + new_flags.insert(FdFlags::CLOEXEC); + } + self.posix_fdtbl.set_flags(fd, new_flags)?; + } + Ok(()) + } + + fn sync_size(fd: Option<&FdGuardUpper>, new_size: usize, tag: usize) -> Result<()> { + if let Some(ref fd) = fd { + fd.call_wo( + &[], + CallFlags::empty(), + &[ + syscall::FileTableVerb::Resize as u64, + tag as u64, + new_size as u64, + ], + )?; + } + Ok(()) + } + + pub fn override_at(&mut self, fd: usize, new_fd: usize) -> Result { + let existed = self.remove(new_fd).is_ok(); + + if Self::is_upper(new_fd) { + let handle = Self::strip_tags(new_fd); + self.upper_fdtbl.insert_at( + handle, + UpperFdTbl::flags_into_entry(0), + self.fd.as_ref(), + )?; + } else { + self.posix_fdtbl.insert_at( + new_fd, + PosixFdTbl::flags_into_entry(0), + self.fd.as_ref(), + )?; + } + + if !existed { + self.active_count += 1; + } + Ok(new_fd) + } + pub fn add_posix(&mut self, entry: usize) -> Result { + if self.active_count >= Self::CONTEXT_MAX_FILES as usize { + return Err(Error::new(EMFILE)); + } + + let out_idx = self + .posix_fdtbl + .add(PosixFdTbl::flags_into_entry(entry), self.fd.as_ref())?; + self.active_count += 1; + + Ok(out_idx) + } + + pub fn insert_upper(&mut self, entry: usize) -> Result { + if self.active_count >= Self::CONTEXT_MAX_FILES as usize { + return Err(Error::new(EMFILE)); + } + + let old_capacity = self.upper_fdtbl.capacity(); + + let out_idx = self + .upper_fdtbl + .insert(UpperFdTbl::flags_into_entry(entry), self.fd.as_ref())?; + self.active_count += 1; + + Ok(out_idx) + } + + pub fn insert_at_upper(&mut self, new_fd: usize, entry: usize) -> Result { + if self.active_count >= Self::CONTEXT_MAX_FILES as usize { + return Err(Error::new(EMFILE)); + } + + let old_capacity = self.upper_fdtbl.capacity(); + if !Self::is_upper(new_fd) { + return Err(Error::new(EINVAL)); + } + let handle = Self::strip_tags(new_fd); + + let out_idx = self.upper_fdtbl.insert_at( + handle, + UpperFdTbl::flags_into_entry(entry), + self.fd.as_ref(), + )?; + self.active_count += 1; + + Ok(out_idx) + } + + pub fn bulk_add( + &mut self, + which: usize, + fd_slice: &mut [usize], + flags: usize, + ) -> Result { + let cnt = fd_slice.len(); + if cnt == 0 { + return Ok(0); + } + + if self.active_count + cnt > Self::CONTEXT_MAX_FILES as usize { + return Err(Error::new(EMFILE)); + } + + if which & syscall::UPPER_FDTBL_TAG == 0 { + let old_capacity = self.posix_fdtbl.capacity(); + + let initial_flag = PosixFdTbl::flags_into_entry(flags); + let entries = alloc::vec![initial_flag; cnt]; + + let handles = self.posix_fdtbl.bulk_add_posix(entries, self.fd.as_ref())?; + self.active_count += cnt; + + for (i, &handle) in handles.iter().enumerate() { + fd_slice[i] = handle; + } + } else { + let old_capacity = self.upper_fdtbl.capacity(); + + let entries = alloc::vec![UpperFdTbl::flags_into_entry(flags); cnt]; + let handles = self.upper_fdtbl.bulk_insert(entries, self.fd.as_ref())?; + self.active_count += cnt; + + for (i, &handle) in handles.iter().enumerate() { + fd_slice[i] = handle | syscall::UPPER_FDTBL_TAG; + } + } + + Ok(cnt) + } + + pub fn bulk_insert( + &mut self, + which: usize, + fd_slice: &mut [usize], + flags: usize, + ) -> Result { + let cnt = fd_slice.len(); + if cnt == 0 { + return Ok(0); + } + + if fd_slice[0] == usize::MAX { + return self.bulk_add(which, fd_slice, flags); + } + + if self.active_count + cnt > Self::CONTEXT_MAX_FILES as usize { + return Err(Error::new(EMFILE)); + } + + if which & syscall::UPPER_FDTBL_TAG == 0 { + let old_capacity = self.posix_fdtbl.capacity(); + + let initial_flag = PosixFdTbl::flags_into_entry(flags); + let entries = alloc::vec![initial_flag; cnt]; + + self.posix_fdtbl + .bulk_insert_manual(entries, fd_slice, self.fd.as_ref())?; + self.active_count += cnt; + } else { + let old_capacity = self.upper_fdtbl.capacity(); + + let entries = alloc::vec![UpperFdTbl::flags_into_entry(flags); cnt]; + self.upper_fdtbl + .bulk_insert_manual(entries, fd_slice, self.fd.as_ref())?; + self.active_count += cnt; + } + + Ok(cnt) + } + + pub fn remove(&mut self, fd: usize) -> Result<()> { + if Self::is_upper(fd) { + let handle = Self::strip_tags(fd); + if self.upper_fdtbl.remove(handle).is_some() { + self.active_count -= 1; + Ok(()) + } else { + Err(Error::new(EBADF)) + } + } else { + if self.posix_fdtbl.remove(fd).is_some() { + self.active_count -= 1; + Ok(()) + } else { + Err(Error::new(EBADF)) + } + } + } +} + +pub struct PosixFdTbl { + table: Vec, + lowest_idx: u32, +} + +bitflags::bitflags! { + #[derive(Clone, Copy, Default, PartialEq, Eq)] + pub struct FdFlags: u8 { + const VACANT = 0; + const OCCUPIED = 1 << 0; + const CLOEXEC = 1 << 1; + const CLOFORK = 1 << 2; + } +} + +impl PosixFdTbl { + pub const fn new() -> Self { + Self { + table: Vec::new(), + lowest_idx: 0, + } + } + + pub fn with_capacity(capacity: usize) -> Self { + Self { + table: Vec::with_capacity(capacity), + lowest_idx: 0, + } + } + + pub fn get_flags(&self, handle: usize) -> Result { + self.table + .get(handle) + .copied() + .filter(|flags| flags.contains(FdFlags::OCCUPIED)) + .ok_or(Error::new(EBADF)) + } + + pub fn set_flags(&mut self, handle: usize, flags: FdFlags) -> Result<()> { + if !self.is_occupied(handle) { + return Err(Error::new(EBADF)); + } + let entry = self.table[handle]; + self.table[handle] = (entry & FdFlags::OCCUPIED) | flags; + Ok(()) + } + + pub fn flags_into_entry(flags: usize) -> FdFlags { + let mut new_entry = FdFlags::OCCUPIED; + if flags & O_CLOEXEC != 0 { + new_entry.insert(FdFlags::CLOEXEC); + } + /* TODO: Support O_CLOFORK + if flags & syscall::O_CLOFORK != 0 { + new_entry.insert(FdFlags::CLOFORK); + } + */ + new_entry + } + + fn is_vacant(&self, handle: usize) -> bool { + self.table + .get(handle) + .map_or(true, |&flags| !flags.contains(FdFlags::OCCUPIED)) + } + + fn is_occupied(&self, handle: usize) -> bool { + self.table + .get(handle) + .map_or(false, |&flags| flags.contains(FdFlags::OCCUPIED)) + } + + pub fn resize(&mut self, size: usize) { + self.table.resize(size, FdFlags::VACANT); + } + + pub fn capacity(&self) -> usize { + self.table.capacity() + } + + pub fn len(&self) -> usize { + self.table + .iter() + .filter(|&&e| e.contains(FdFlags::OCCUPIED)) + .count() + } + + pub fn with_transaction(&mut self, rollback_len: usize, f: F) -> Result + where + F: FnOnce(&mut Self) -> Result, + { + match f(self) { + Ok(res) => Ok(res), + Err(e) => { + self.table.truncate(rollback_len); + if (self.lowest_idx as usize) > self.table.len() { + self.lowest_idx = self.table.len() as u32; + } + Err(e) + } + } + } + + fn update_lowest_idx(&mut self, start_from: usize) { + let mut next_lowest = start_from; + while next_lowest < self.table.len() && self.is_occupied(next_lowest) { + next_lowest += 1; + } + self.lowest_idx = next_lowest as u32; + } + + fn validate_handles(&self, handles: &[usize]) -> Result<()> { + let mut checked_handles = BTreeSet::new(); + for &handle in handles { + if handle >= FdTbl::CONTEXT_MAX_FILES as usize { + return Err(Error::new(EMFILE)); + } + if !checked_handles.insert(handle) || !self.is_occupied(handle) { + return Err(Error::new(EBADF)); + } + } + Ok(()) + } + + fn validate_free_slots(&self, handles: &[usize]) -> Result<()> { + let mut checked_handles = BTreeSet::new(); + for &handle in handles { + if handle >= FdTbl::CONTEXT_MAX_FILES as usize { + return Err(Error::new(EMFILE)); + } + if !checked_handles.insert(handle) { + return Err(Error::new(EBADF)); + } + if self.is_occupied(handle) { + return Err(Error::new(EEXIST)); + } + } + Ok(()) + } + + pub fn find_free_posix_slots(&self, count: usize) -> Vec { + let mut free_slots = Vec::with_capacity(count); + + for i in (self.lowest_idx as usize)..self.table.len() { + if self.is_vacant(i) { + free_slots.push(i); + if free_slots.len() == count { + return free_slots; + } + } + } + + let mut current_len = self.table.len(); + while free_slots.len() < count { + free_slots.push(current_len); + current_len += 1; + } + free_slots + } + + pub fn add(&mut self, flags: FdFlags, sync_fd: Option<&FdGuardUpper>) -> Result { + let handle = self.lowest_idx as usize; + let old_len = self.table.len(); + let entry_flags = flags | FdFlags::OCCUPIED; + + if handle >= old_len { + self.with_transaction(old_len, |this| { + let new_len = handle + 1; + FdTbl::sync_size(sync_fd, new_len, 0)?; + this.table.push(entry_flags); + this.lowest_idx = (handle + 1) as u32; + Ok(handle) + }) + } else { + self.table[handle] = entry_flags; + self.update_lowest_idx(handle + 1); + Ok(handle) + } + } + + pub fn bulk_add_posix( + &mut self, + entries: Vec, + sync_fd: Option<&FdGuardUpper>, + ) -> Result> { + let count = entries.len(); + if count == 0 { + return Ok(Vec::new()); + } + + let handles = self.find_free_posix_slots(count); + let max_index = handles[count - 1]; + + if max_index >= FdTbl::CONTEXT_MAX_FILES as usize { + return Err(Error::new(EMFILE)); + } + + let old_len = self.table.len(); + let new_len = if old_len <= max_index { + max_index + 1 + } else { + old_len + }; + + self.with_transaction(old_len, |this| { + if old_len != new_len { + FdTbl::sync_size(sync_fd, new_len, 0)?; + this.resize(new_len); + } + + for (&handle, flags) in handles.iter().zip(entries) { + this.table[handle] = flags | FdFlags::OCCUPIED; + } + + let mut next_lowest = this.lowest_idx as usize; + while next_lowest < this.table.len() && this.is_occupied(next_lowest) { + next_lowest += 1; + } + this.lowest_idx = next_lowest as u32; + + Ok(handles) + }) + } + + pub fn insert_at( + &mut self, + handle: usize, + flags: FdFlags, + sync_fd: Option<&FdGuardUpper>, + ) -> Result { + if handle >= FdTbl::CONTEXT_MAX_FILES as usize { + return Err(Error::new(EMFILE)); + } + let old_len = self.table.len(); + let new_len = if handle >= old_len { + handle + 1 + } else { + old_len + }; + let entry_flags = flags | FdFlags::OCCUPIED; + + self.with_transaction(old_len, |this| { + if handle >= old_len { + FdTbl::sync_size(sync_fd, new_len, 0)?; + this.resize(handle + 1); + } + this.table[handle] = entry_flags; + + if handle <= this.lowest_idx as usize { + this.update_lowest_idx(handle); + } + Ok(handle) + }) + } + + pub fn bulk_insert_manual( + &mut self, + entries: Vec, + handles: &[usize], + sync_fd: Option<&FdGuardUpper>, + ) -> Result<()> { + if handles.len() != entries.len() { + return Err(Error::new(EINVAL)); + } + let count = entries.len(); + if count == 0 { + return Ok(()); + } + + self.validate_free_slots(handles)?; + + let max_index = handles.iter().max().cloned().unwrap_or(0); + let old_len = self.table.len(); + let new_len = if old_len <= max_index { + max_index + 1 + } else { + old_len + }; + + self.with_transaction(old_len, |this| { + if old_len <= max_index { + FdTbl::sync_size(sync_fd, new_len, 0)?; + this.resize(max_index + 1); + } + + for (entry, &index) in entries.into_iter().zip(handles) { + this.table[index] = entry | FdFlags::OCCUPIED; + } + + this.update_lowest_idx(0); + + Ok(()) + }) + } + + pub fn remove(&mut self, handle: usize) -> Option { + if !self.is_occupied(handle) { + return None; + } + + let old_entry = core::mem::replace(&mut self.table[handle], FdFlags::VACANT); + + if (handle as u32) < self.lowest_idx { + self.lowest_idx = handle as u32; + } + + Some(old_entry) + } + + pub fn bulk_remove(&mut self, handles: &[usize]) -> Option> { + self.validate_handles(handles).ok()?; + + let files = handles + .iter() + .map(|&i| self.remove(i).expect("fd should exist")) + .collect(); + + Some(files) + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum FdTblEntry { + Vacant { next_vacant_idx: u32 }, + Occupied { flag: u32 }, +} + +impl FdTblEntry { + pub fn new_occupied(flag: u32) -> Self { + Self::Occupied { flag } + } +} + +pub struct UpperFdTbl { + table: Vec, + len: u32, + first_vacant_idx: u32, +} + +impl UpperFdTbl { + pub const fn new() -> Self { + Self { + table: Vec::new(), + len: 0, + first_vacant_idx: FdTbl::CONTEXT_MAX_FILES, + } + } + + pub fn with_capacity(capacity: usize) -> Self { + Self { + table: Vec::with_capacity(capacity), + len: 0, + first_vacant_idx: FdTbl::CONTEXT_MAX_FILES, + } + } + + pub fn get_flags(&self, handle: usize) -> Result { + let index = Self::strip_tags(handle); + match self.table.get(index) { + Some(FdTblEntry::Occupied { flag }) => Ok(*flag), + _ => Err(Error::new(EBADF)), + } + } + + pub fn set_flags(&mut self, handle: usize, new_flag: u32) -> Result<()> { + let index = Self::strip_tags(handle); + match self.table.get_mut(index) { + Some(entry @ FdTblEntry::Occupied { .. }) => { + *entry = FdTblEntry::Occupied { flag: new_flag }; + Ok(()) + } + _ => Err(Error::new(EBADF)), + } + } + + pub fn flags_into_entry(flags: usize) -> FdTblEntry { + FdTblEntry::new_occupied(flags as u32) + } + + fn strip_tags(index: usize) -> usize { + index & !syscall::UPPER_FDTBL_TAG + } + + pub fn resize(&mut self, size: usize) { + self.table.resize( + size, + FdTblEntry::Vacant { + next_vacant_idx: FdTbl::CONTEXT_MAX_FILES, + }, + ); + self.rebuild_free_list(); + } + + pub fn capacity(&self) -> usize { + self.table.capacity() + } + + pub fn len(&self) -> usize { + self.len as usize + } + + pub fn is_empty(&self) -> bool { + self.len == 0 + } + + pub fn with_transaction(&mut self, rollback_len: usize, f: F) -> Result + where + F: FnOnce(&mut Self) -> Result, + { + match f(self) { + Ok(res) => Ok(res), + Err(e) => { + self.table.truncate(rollback_len); + Err(e) + } + } + } + + fn validate_handles(&self, handles: &[usize]) -> Result<()> { + let mut checked_handles = BTreeSet::new(); + for &handle in handles { + let handle = Self::strip_tags(handle); + if Self::strip_tags(handle) >= FdTbl::CONTEXT_MAX_FILES as usize { + return Err(Error::new(EMFILE)); + } + if !checked_handles.insert(handle) { + return Err(Error::new(EBADF)); // Duplicate handle + } + if !matches!(self.table.get(handle), Some(FdTblEntry::Occupied { .. })) { + return Err(Error::new(EBADF)); + } + } + Ok(()) + } + + fn validate_free_slots(&self, handles: &[usize]) -> Result<()> { + let mut checked_handles = BTreeSet::new(); + for &handle in handles { + let handle = Self::strip_tags(handle); + if handle >= FdTbl::CONTEXT_MAX_FILES as usize { + return Err(Error::new(EMFILE)); + } + if !checked_handles.insert(handle) { + return Err(Error::new(EBADF)); // Duplicate handle + } + if matches!(self.table.get(handle), Some(FdTblEntry::Occupied { .. })) { + return Err(Error::new(EEXIST)); + } + } + Ok(()) + } + + fn find_free_block(&self, len: usize) -> usize { + let mut start = 0; + let mut count = 0; + + for (i, entry) in self.table.iter().enumerate() { + if matches!(entry, FdTblEntry::Vacant { .. }) { + if count == 0 { + start = i; + } + count += 1; + if count == len { + return start; + } + } else { + count = 0; + } + } + + if count == 0 { self.table.len() } else { start } + } + + pub fn insert(&mut self, entry: FdTblEntry, sync_fd: Option<&FdGuardUpper>) -> Result { + let handle = self.first_vacant_idx as usize; + + if self.first_vacant_idx == FdTbl::CONTEXT_MAX_FILES { + let old_len = self.table.len(); + let new_len = old_len + 1; + self.with_transaction(old_len, |this| { + FdTbl::sync_size(sync_fd, new_len, syscall::UPPER_FDTBL_TAG)?; + this.table.push(entry); + this.len += 1; + Ok(old_len) + }) + } else { + if let FdTblEntry::Vacant { next_vacant_idx } = self.table[handle] { + self.first_vacant_idx = next_vacant_idx; + self.table[handle] = entry; + self.len += 1; + Ok(handle) + } else { + unreachable!(); + } + } + } + + pub fn bulk_insert( + &mut self, + entries: Vec, + sync_fd: Option<&FdGuardUpper>, + ) -> Result> { + let count = entries.len(); + if count == 0 { + return Ok(Vec::new()); + } + if self.len() + count > FdTbl::CONTEXT_MAX_FILES as usize { + return Err(Error::new(EMFILE)); + } + + let old_len = self.table.len(); + + let start_index = self.find_free_block(count); + let needed_len = start_index + count; + + let new_len = if old_len < needed_len { + needed_len + } else { + old_len + }; + + self.with_transaction(old_len, |this| { + if old_len != new_len { + FdTbl::sync_size(sync_fd, new_len, syscall::UPPER_FDTBL_TAG)?; + this.resize(new_len); + } + + let mut handles = Vec::with_capacity(count); + for (i, entry) in entries.into_iter().enumerate() { + let current_index = start_index + i; + this.table[current_index] = entry; + handles.push(current_index); + } + + this.len += count as u32; + + this.rebuild_free_list(); + + Ok(handles) + }) + } + + pub fn insert_at( + &mut self, + handle: usize, + entry: FdTblEntry, + sync_fd: Option<&FdGuardUpper>, + ) -> Result { + let old_len = self.table.len(); + let new_len = if handle >= old_len { + handle + 1 + } else { + old_len + }; + + self.with_transaction(old_len, |this| { + if handle >= old_len { + FdTbl::sync_size(sync_fd, new_len, syscall::UPPER_FDTBL_TAG)?; + this.resize(handle + 1); + } + + let vacant = matches!(this.table[handle], FdTblEntry::Vacant { .. }); + if vacant { + this.len += 1; + } + + this.table[handle] = entry; + + if vacant { + this.rebuild_free_list(); + } + Ok(handle) + }) + } + + pub fn bulk_insert_manual( + &mut self, + entries: Vec, + handles: &[usize], + sync_fd: Option<&FdGuardUpper>, + ) -> Result<()> { + if handles.len() != entries.len() { + return Err(Error::new(EINVAL)); + } + let count = entries.len(); + if count == 0 { + return Ok(()); + } + if self.len() + count > FdTbl::CONTEXT_MAX_FILES as usize { + return Err(Error::new(EMFILE)); + } + + self.validate_free_slots(handles)?; + + let max_index = handles + .iter() + .map(|&h| Self::strip_tags(h)) + .max() + .unwrap_or(0); + let old_len = self.table.len(); + let new_len = if old_len <= max_index { + max_index + 1 + } else { + old_len + }; + + self.with_transaction(old_len, |this| { + if old_len <= max_index { + FdTbl::sync_size(sync_fd, new_len, syscall::UPPER_FDTBL_TAG)?; + this.resize(max_index + 1); + } + + for (entry, &index) in entries.into_iter().zip(handles) { + this.table[Self::strip_tags(index)] = entry; + } + + this.len += count as u32; + + this.rebuild_free_list(); + + Ok(()) + }) + } + + pub fn remove(&mut self, handle: usize) -> Option { + if handle >= self.table.len() || matches!(self.table[handle], FdTblEntry::Vacant { .. }) { + return None; + } + + let old_entry = core::mem::replace( + &mut self.table[handle], + FdTblEntry::Vacant { + next_vacant_idx: self.first_vacant_idx, + }, + ); + self.first_vacant_idx = handle as u32; + self.len -= 1; + + Some(old_entry) + } + + pub fn bulk_remove(&mut self, handles: &[usize]) -> Option> { + self.validate_handles(handles).ok()?; + + let files = handles + .iter() + .map(|&i| self.remove(i).expect("fd should exist")) + .collect(); + + Some(files) + } + + fn rebuild_free_list(&mut self) { + let mut next_vacant = FdTbl::CONTEXT_MAX_FILES; + for i in (0..self.table.len()).rev() { + if let FdTblEntry::Vacant { next_vacant_idx } = &mut self.table[i] { + *next_vacant_idx = next_vacant; + next_vacant = i as u32; + } + } + self.first_vacant_idx = next_vacant; + } +} + +pub struct FdTblIter<'a> { + fdtbl: &'a FdTbl, + stage: u8, + cursor: usize, +} + +impl<'a> FdTblIter<'a> { + fn new(fdtbl: &'a FdTbl) -> Self { + Self { + fdtbl, + stage: 0, + cursor: 0, + } + } +} + +impl<'a> Iterator for FdTblIter<'a> { + type Item = (usize, usize); + + fn next(&mut self) -> Option { + loop { + match self.stage { + 0 => { + let table = &self.fdtbl.posix_fdtbl.table; + if self.cursor < table.len() { + let idx = self.cursor; + self.cursor += 1; + + let internal_flags = table[idx]; + if internal_flags.contains(FdFlags::OCCUPIED) { + let mut raw_flags = 0; + if internal_flags.contains(FdFlags::CLOEXEC) { + raw_flags |= O_CLOEXEC; + } + /* + if internal_flags.contains(FdFlags::CLOFORK) { + raw_flags |= syscall::O_CLOFORK; + } + */ + return Some((idx, raw_flags)); + } + } else { + self.stage = 1; + self.cursor = 0; + } + } + 1 => { + let table = &self.fdtbl.upper_fdtbl.table; + if self.cursor < table.len() { + let idx = self.cursor; + self.cursor += 1; + + if let FdTblEntry::Occupied { flag } = table[idx] { + let raw_flags = flag as usize; + + let full_fd = idx | syscall::UPPER_FDTBL_TAG; + return Some((full_fd, raw_flags)); + } + } else { + self.stage = 2; + } + } + _ => return None, + } + } + } +} + +impl FdTbl { + pub fn iter(&self) -> FdTblIter<'_> { + FdTblIter::new(self) + } +} + +impl<'a> IntoIterator for &'a FdTbl { + type Item = (usize, usize); + type IntoIter = FdTblIter<'a>; + + fn into_iter(self) -> Self::IntoIter { + self.iter() + } +} diff --git a/redox-rt/src/thread.rs b/redox-rt/src/thread.rs index 9ad1d4615a..7b0059ed08 100644 --- a/redox-rt/src/thread.rs +++ b/redox-rt/src/thread.rs @@ -10,12 +10,12 @@ pub unsafe fn rlct_clone_impl(stack: *mut usize, tcb: &RtTcb) -> Result { let cur_proc_fd = proc_info.proc_fd.as_ref().unwrap(); let cur_thr_fd = RtTcb::current().thread_fd(); - let new_thr_fd = cur_proc_fd.dup(b"new-thread")?.to_upper().unwrap(); + let new_thr_fd = cur_proc_fd.dup_into_upper(b"new-thread")?; // Inherit existing address space { - let cur_addr_space_fd = cur_thr_fd.dup(b"addrspace")?; - let new_addr_space_sel_fd = new_thr_fd.dup(b"current-addrspace")?; + let cur_addr_space_fd = cur_thr_fd.dup_into_upper(b"addrspace")?; + let new_addr_space_sel_fd = new_thr_fd.dup_into_upper(b"current-addrspace")?; let buf = create_set_addr_space_buf( cur_addr_space_fd.as_raw_fd(), @@ -27,8 +27,8 @@ pub unsafe fn rlct_clone_impl(stack: *mut usize, tcb: &RtTcb) -> Result { // Inherit reference to file table { - let cur_filetable_fd = cur_thr_fd.dup(b"filetable")?; - let new_filetable_sel_fd = new_thr_fd.dup(b"current-filetable")?; + let cur_filetable_fd = cur_thr_fd.dup_into_upper(b"filetable")?; + let new_filetable_sel_fd = new_thr_fd.dup_into_upper(b"current-filetable")?; new_filetable_sel_fd.write(&usize::to_ne_bytes(cur_filetable_fd.as_raw_fd()))?; } @@ -38,7 +38,7 @@ pub unsafe fn rlct_clone_impl(stack: *mut usize, tcb: &RtTcb) -> Result { // the same process) will be discarded. Process-specific signals will ignore this new thread, // until it has initialized its own signal handler. - let start_fd = new_thr_fd.dup(b"start")?; + let start_fd = new_thr_fd.dup_into_upper(b"start")?; let fd = new_thr_fd.as_raw_fd(); unsafe { @@ -56,9 +56,12 @@ pub unsafe fn exit_this_thread(stack_base: *mut (), stack_size: usize) -> ! { let tcb = RtTcb::current(); // TODO: modify interface so it writes directly to the thread fd? - let status_fd = tcb.thread_fd().dup(b"status").unwrap(); + let status_fd = tcb.thread_fd().dup_into_upper(b"status").unwrap(); - let _ = unsafe { syscall::funmap(tcb as *const RtTcb as usize, syscall::PAGE_SIZE) }; + let tcb_ptr = unsafe { crate::Tcb::current_ptr() }; + if let Some(tcb_ptr) = tcb_ptr { + let _ = unsafe { syscall::funmap(tcb_ptr as usize, syscall::PAGE_SIZE) }; + } let mut buf = [0; size_of::() * 3]; plain::slice_from_mut_bytes(&mut buf) diff --git a/src/header/sys_ioctl/redox/mod.rs b/src/header/sys_ioctl/redox/mod.rs index 04ed92dc7a..d678417dc9 100644 --- a/src/header/sys_ioctl/redox/mod.rs +++ b/src/header/sys_ioctl/redox/mod.rs @@ -18,7 +18,7 @@ mod drm; // TODO: some of the structs passed as T have padding bytes, so casting to a byte slice is UB fn dup_read(fd: c_int, name: &str, t: &mut T) -> syscall::Result { - let dup = FdGuard::new(syscall::dup(fd as usize, name.as_bytes())?); + let dup = FdGuard::new(redox_rt::sys::dup(fd as usize, name.as_bytes())?); let size = mem::size_of::(); @@ -29,7 +29,7 @@ fn dup_read(fd: c_int, name: &str, t: &mut T) -> syscall::Result { // FIXME: unsound fn dup_write(fd: c_int, name: &str, t: &T) -> Result { - let dup = FdGuard::new(syscall::dup(fd as usize, name.as_bytes())?); + let dup = FdGuard::new(redox_rt::sys::dup(fd as usize, name.as_bytes())?); let size = mem::size_of::(); diff --git a/src/ld_so/mod.rs b/src/ld_so/mod.rs index 750f218987..ffd21c4604 100644 --- a/src/ld_so/mod.rs +++ b/src/ld_so/mod.rs @@ -159,7 +159,7 @@ pub unsafe fn init( { let file = thr_fd - .dup(b"regs/env") + .dup_into_upper(b"regs/env") .expect_notls("failed to open handle for process registers"); file.read(&mut env).expect_notls("failed to read gsbase"); @@ -173,7 +173,7 @@ pub unsafe fn init( { let file = thr_fd - .dup(b"regs/env") + .dup_into_upper(b"regs/env") .expect_notls("failed to open handle for process registers"); file.read(&mut env).expect_notls("failed to read fsbase"); diff --git a/src/ld_so/start.rs b/src/ld_so/start.rs index 0a5e4af773..b366d6b9b3 100644 --- a/src/ld_so/start.rs +++ b/src/ld_so/start.rs @@ -317,6 +317,23 @@ fn stage2( let thr_fd = crate::platform::get_auxv_raw(auxv, redox_rt::auxv_defs::AT_REDOX_THR_FD) .expect_notls("no thread fd present"); + #[cfg(target_os = "redox")] + { + if redox_rt::current_filetable().fd().is_none() { + let filetable_fd = crate::platform::get_auxv_raw( + sp.auxv().cast(), + redox_rt::auxv_defs::AT_REDOX_FILETABLE_FD, + ) + .expect_notls("no filetable fd present"); + let filetable_guard = redox_rt::proc::FdGuard::new(filetable_fd) + .to_upper() + .expect_notls("failed to move filetable fd to upper table"); + *redox_rt::current_filetable() = + redox_rt::sys::FdTbl::from_binary_fd(filetable_guard) + .expect_notls("failed to initialize FILETABLE"); + } + } + let tcb = Tcb::new(0).expect_notls("[ld.so]: failed to allocate bootstrap TCB"); tcb.activate( #[cfg(target_os = "redox")] diff --git a/src/platform/auxv_defs.rs b/src/platform/auxv_defs.rs index 748800aa44..face5b17d4 100644 --- a/src/platform/auxv_defs.rs +++ b/src/platform/auxv_defs.rs @@ -58,3 +58,6 @@ pub const AT_REDOX_NS_FD: usize = 43; #[cfg(target_os = "redox")] pub const AT_REDOX_CWD_FD: usize = 44; + +#[cfg(target_os = "redox")] +pub const AT_REDOX_FILETABLE_FD: usize = 45; diff --git a/src/platform/redox/event.rs b/src/platform/redox/event.rs index 9458aeeb16..ef979920af 100644 --- a/src/platform/redox/event.rs +++ b/src/platform/redox/event.rs @@ -83,5 +83,5 @@ pub unsafe extern "C" fn redox_event_queue_ctl_v1( } #[unsafe(no_mangle)] pub unsafe extern "C" fn redox_event_queue_destroy_v1(queue: usize) -> RawResult { - Error::mux(syscall::close(queue)) + Error::mux(redox_rt::sys::close(queue)) } diff --git a/src/platform/redox/exec.rs b/src/platform/redox/exec.rs index 950be3c481..ce088c0e13 100644 --- a/src/platform/redox/exec.rs +++ b/src/platform/redox/exec.rs @@ -41,6 +41,10 @@ fn fexec_impl( )? .unwrap(); + if let Some(filetable_fd) = extrainfo.filetable_fd { + let _ = redox_rt::sys::close(filetable_fd); + } + // According to elf(5), PT_INTERP requires that the interpreter path be // null-terminated. Violating this should therefore give the "format error" ENOEXEC. let path_cstr = CStr::from_bytes_with_nul(&path).map_err(|_| Error::new(ENOEXEC))?; @@ -228,6 +232,9 @@ pub fn execve( let sigprocmask = redox_rt::signal::get_sigmask().unwrap(); + let filetable_binary_fd = RtTcb::current() + .thread_fd() + .dup_into_upper(b"filetable-binary")?; let extrainfo = ExtraInfo { cwd: Some(cwd.as_bytes()), sigignmask: redox_rt::signal::get_sigignmask_to_inherit(), @@ -239,6 +246,7 @@ pub fn execve( cwd_fd: super::path::current_dir() .ok() .map(|fd| fd.as_ref().unwrap().fd.as_raw_fd()), + filetable_fd: Some(filetable_binary_fd.as_raw_fd()), same_process: true, }; diff --git a/src/platform/redox/libcscheme.rs b/src/platform/redox/libcscheme.rs index a089606055..b4f07ccf6b 100644 --- a/src/platform/redox/libcscheme.rs +++ b/src/platform/redox/libcscheme.rs @@ -38,9 +38,9 @@ pub fn open(path: &str, flags: usize) -> Result { // Linux seems to allow you to read from or write to any of /dev/{stdin,stderr,stdout} match basename { - "stderr" => syscall::dup(2, &[]), - "stdin" => syscall::dup(0, &[]), - "stdout" => syscall::dup(1, &[]), + "stderr" => redox_rt::sys::dup(2, &[]), + "stdin" => redox_rt::sys::dup(0, &[]), + "stdout" => redox_rt::sys::dup(1, &[]), "tty" => { if let Some(tty) = env_str!("TTY") { return redox_rt::sys::open(tty, flags); diff --git a/src/platform/redox/libredox.rs b/src/platform/redox/libredox.rs index 60ac55183c..4c935f0296 100644 --- a/src/platform/redox/libredox.rs +++ b/src/platform/redox/libredox.rs @@ -32,7 +32,7 @@ pub fn openat(dirfd: c_int, path: &str, oflag: c_int, mode: mode_t) -> Result RawResult { - Error::mux(syscall::openat( + Error::mux(redox_rt::sys::openat( fd, unsafe { str::from_utf8_unchecked(slice::from_raw_parts(path_base, path_len)) }, flags as usize, @@ -250,7 +250,7 @@ pub unsafe extern "C" fn redox_openat_v1( } #[unsafe(no_mangle)] pub unsafe extern "C" fn redox_dup_v1(fd: usize, buf: *const u8, len: usize) -> RawResult { - Error::mux(syscall::dup(fd, unsafe { + Error::mux(redox_rt::sys::dup(fd, unsafe { core::slice::from_raw_parts(buf, len) })) } @@ -261,7 +261,7 @@ pub unsafe extern "C" fn redox_dup2_v1( buf: *const u8, len: usize, ) -> RawResult { - Error::mux(syscall::dup2(old_fd, new_fd, unsafe { + Error::mux(redox_rt::sys::dup2(old_fd, new_fd, unsafe { core::slice::from_raw_parts(buf, len) })) } @@ -359,7 +359,7 @@ pub unsafe extern "C" fn redox_fpath_v1(fd: usize, dst_base: *mut u8, dst_len: u } #[unsafe(no_mangle)] pub unsafe extern "C" fn redox_close_v1(fd: usize) -> RawResult { - Error::mux(syscall::close(fd)) + Error::mux(redox_rt::sys::close(fd)) } #[unsafe(no_mangle)] @@ -569,12 +569,18 @@ pub unsafe extern "C" fn redox_sys_call_v0( metadata: *const u64, metadata_len: usize, ) -> RawResult { - Error::mux(redox_rt::sys::sys_call( - fd, - unsafe { slice::from_raw_parts_mut(payload, payload_len) }, - syscall::CallFlags::from_bits_retain(flags), - unsafe { slice::from_raw_parts(metadata, metadata_len) }, - )) + let flags = syscall::CallFlags::from_bits_retain(flags); + let read = flags.contains(syscall::CallFlags::READ); + let write = flags.contains(syscall::CallFlags::WRITE); + let payload = unsafe { slice::from_raw_parts_mut(payload, payload_len) }; + let metadata = unsafe { slice::from_raw_parts(metadata, metadata_len) }; + + Error::mux(match (read, write) { + (true, true) => redox_rt::sys::sys_call_rw(fd, payload, flags, metadata), + (true, false) => redox_rt::sys::sys_call_ro(fd, payload, flags, metadata), + (false, true) => redox_rt::sys::sys_call_wo(fd, payload, flags, metadata), + (false, false) => redox_rt::sys::sys_call(fd, payload, flags, metadata), + }) } #[unsafe(no_mangle)] @@ -630,3 +636,8 @@ pub unsafe extern "C" fn redox_relpathat_v0( &StdFsCallMeta::new(StdFsCallKind::Relpathat, 0, 0), )) } + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn redox_fcntl_v0(fd: usize, cmd: usize, arg: usize) -> RawResult { + Error::mux(redox_rt::sys::fcntl(fd, cmd, arg)) +} diff --git a/src/platform/redox/mod.rs b/src/platform/redox/mod.rs index b849ab0287..6b5c95b61f 100644 --- a/src/platform/redox/mod.rs +++ b/src/platform/redox/mod.rs @@ -239,12 +239,12 @@ impl Pal for Sys { } fn close(fd: c_int) -> Result<()> { - syscall::close(fd as usize)?; + redox_rt::sys::close(fd as usize)?; Ok(()) } fn dup2(fd1: c_int, fd2: c_int) -> Result { - Ok(syscall::dup2(fd1 as usize, fd2 as usize, &[])? as c_int) + Ok(redox_rt::sys::dup2(fd1 as usize, fd2 as usize, &[])? as c_int) } fn exit(status: c_int) -> ! { @@ -442,7 +442,7 @@ impl Pal for Sys { _ => {} } - Ok(syscall::fcntl(fd as usize, cmd as usize, args as usize)? as c_int) + Ok(redox_rt::sys::fcntl(fd as usize, cmd as usize, args as usize)? as c_int) } fn fdatasync(fd: c_int) -> Result<()> { @@ -484,7 +484,7 @@ impl Pal for Sys { let file = openat2(dirfd, path, flags, fcntl::O_PATH)?; // Close the file descriptor after fstat(2) regardless of success or failure. let fstat_res = unsafe { libredox::fstat(*file as usize, buf.as_mut_ptr()) }; - let close_res = syscall::close(*file as usize); + let close_res = redox_rt::sys::close(*file as usize); if let Err(err) = fstat_res { return Err(err.into()); } @@ -664,7 +664,7 @@ impl Pal for Sys { "/scheme/rand" }; - let mut open_flags = syscall::O_RDONLY | syscall::O_CLOEXEC; + let mut open_flags = syscall::O_RDONLY | redox_protocols::protocol::O_CLOEXEC; if flags & sys_random::GRND_NONBLOCK != 0 { open_flags |= syscall::O_NONBLOCK; } @@ -1215,13 +1215,11 @@ impl Pal for Sys { let mut cwd_fd = FdGuard::open(cwd.as_str(), syscall::O_STAT)?.to_upper()?; let proc_fd = child.proc_fd.unwrap(); let curr_proc_fd = redox_rt::current_proc_fd(); - let file_table = RtTcb::current() - .thread_fd() - .dup(b"filetable")? - .dup(b"copy")?; + let cur_filetable_fd = RtTcb::current().thread_fd().dup_into_upper(b"filetable")?; + let file_table = cur_filetable_fd.dup_into_upper(b"copy")?; { - let new_file_table = child.thr_fd.dup(b"current-filetable")?; + let new_file_table = child.thr_fd.dup_into_upper(b"current-filetable")?; new_file_table.write(&file_table.as_raw_fd().to_ne_bytes())?; } @@ -1240,7 +1238,7 @@ impl Pal for Sys { args[0] = &program.to_bytes(); - let new_file_table = child.thr_fd.dup(b"filetable")?; + let new_file_table = child.thr_fd.dup_into_upper(b"filetable-binary")?; if let Some(fac) = fac { for action in fac { @@ -1262,11 +1260,10 @@ impl Pal for Sys { flag, mode, )? as usize; - syscall::sendfd( - new_file_table.as_raw_fd(), - src_fd, - 0, - u64::try_from(fd).map_err(|_| Errno(EBADFD))?, + new_file_table.call_wo( + &src_fd.to_ne_bytes(), + syscall::CallFlags::FD, + &[u64::try_from(fd).map_err(|_| Errno(EBADFD))?], )?; } crate::header::spawn::Action::Close(fd) => { @@ -1336,9 +1333,47 @@ impl Pal for Sys { // ); new_file_table.call_wo( - &[], - syscall::CallFlags::empty(), - &[syscall::flag::FileTableVerb::CloseCloExec as u64], + &new_file_table.as_raw_fd().to_ne_bytes(), + syscall::CallFlags::FD | syscall::CallFlags::FD_CLONE, + &[new_file_table.as_raw_fd() as u64], + )?; + + { + let fds_to_close = { + let guard = redox_rt::current_filetable(); + let mut fds = alloc::vec::Vec::new(); + for (fd, flags) in guard.iter() { + if flags & redox_protocols::protocol::O_CLOEXEC + == redox_protocols::protocol::O_CLOEXEC + || fd == executable.as_raw_fd() + { + fds.push(fd); + } + } + + fds.push(cur_filetable_fd.as_raw_fd()); + + fds + }; + + let fds_to_close_bytes: &[u8] = unsafe { + core::slice::from_raw_parts( + fds_to_close.as_ptr() as *mut u8, + fds_to_close.len() * core::mem::size_of::(), + ) + }; + + let _ = new_file_table.call_wo( + fds_to_close_bytes, + syscall::CallFlags::empty(), + &[syscall::FileTableVerb::Close as u64], + ); + } + + new_file_table.call_wo( + &cwd_fd.as_raw_fd().to_ne_bytes(), + syscall::CallFlags::FD | syscall::CallFlags::FD_CLONE, + &[cwd_fd.as_raw_fd() as u64], )?; let extra_info = redox_rt::proc::ExtraInfo { @@ -1360,6 +1395,7 @@ impl Pal for Sys { proc_fd: proc_fd.as_raw_fd(), ns_fd: redox_rt::current_namespace_fd().ok(), cwd_fd: Some(cwd_fd.as_raw_fd()), + filetable_fd: Some(new_file_table.as_raw_fd()), same_process: false, }; @@ -1460,7 +1496,7 @@ impl Pal for Sys { .unwrap(); } - let start_fd = child.thr_fd.dup(b"start")?; + let start_fd = child.thr_fd.dup_into_upper(b"start")?; start_fd.write(&[0])?; Ok(pid_t::try_from(child.pid).unwrap()) @@ -1507,7 +1543,7 @@ impl Pal for Sys { CLOCK_MONOTONIC => "/scheme/time/4", _ => return Err(Errno(EINVAL)), }; - let timerfd = FdGuard::open(&path, syscall::O_RDWR)?.to_upper()?; + let timerfd = FdGuard::open_into_upper(&path, syscall::O_RDWR)?; let eventfd = FdGuard::new(Error::demux(unsafe { event::redox_event_queue_create_v1(0) })?) @@ -1561,8 +1597,8 @@ impl Pal for Sys { return Err(Errno(EINVAL)); } let timer_st = unsafe { timer_internal_t::from_raw(timerid) }; - let _ = syscall::close(timer_st.timerfd); - let _ = syscall::close(timer_st.eventfd); + let _ = redox_rt::sys::close(timer_st.timerfd); + let _ = redox_rt::sys::close(timer_st.eventfd); if !timer_st.thread.is_null() { let _ = unsafe { pthread_cancel(timer_st.thread) }; } diff --git a/src/platform/redox/path.rs b/src/platform/redox/path.rs index 2a149b308b..2d94bdf6a8 100644 --- a/src/platform/redox/path.rs +++ b/src/platform/redox/path.rs @@ -9,6 +9,7 @@ use core::{ ffi::c_int, str::{self, FromStr}, }; +use redox_protocols::protocol::O_CLOEXEC; use redox_rt::{proc::FdGuardUpper, signal::tmp_disable_signals}; use syscall::{data::Stat, error::*, flag::*}; @@ -97,9 +98,7 @@ pub fn chdir(path: &str) -> Result<()> { .as_ref() .unwrap() .fd - .openat(&path, O_STAT, 0)? - .to_upper() - .unwrap(); + .openat_into_upper(&path, O_STAT, 0)?; let mut stat = Stat::default(); if fd.fstat(&mut stat).is_err() || (stat.st_mode & MODE_TYPE) != MODE_DIR { return Err(Error::new(ENOTDIR)); @@ -138,7 +137,7 @@ pub fn fchdir(fd: c_int) -> Result<()> { let res = Sys::fpath(fd, buf.as_bytes_mut())?; buf.set_len(res); } - let fd = FdGuard::new(syscall::fcntl( + let fd = FdGuard::new(redox_rt::sys::fcntl( fd as usize, syscall::F_DUPFD, syscall::UPPER_FDTBL_TAG, diff --git a/src/platform/redox/socket.rs b/src/platform/redox/socket.rs index 199434df55..c7b0836928 100644 --- a/src/platform/redox/socket.rs +++ b/src/platform/redox/socket.rs @@ -1,6 +1,6 @@ use alloc::vec::Vec; use core::{cmp, mem, ptr, slice, str}; -use redox_protocols::protocol::{FsCall, SocketCall}; +use redox_protocols::protocol::{FsCall, O_CLOEXEC, SocketCall}; use redox_rt::proc::FdGuard; use syscall::{self, flag::*}; @@ -83,7 +83,7 @@ unsafe fn bind_or_connect( }, _ => return Err(Errno(EAFNOSUPPORT)), }; - let fd = syscall::dup(socket as usize, path.as_bytes())?; + let fd = redox_rt::sys::dup(socket as usize, path.as_bytes())?; Ok(fd) } @@ -95,7 +95,7 @@ pub unsafe fn bind_or_connect_into( ) -> Result { // Duplicate the socket, and then duplicate the copy back to the original fd let fd = FdGuard::new(unsafe { bind_or_connect(op, socket, address, address_len) }?); - syscall::dup2(fd.as_raw_fd(), socket as usize, &[])?; + redox_rt::sys::dup2(fd.as_raw_fd(), socket as usize, &[])?; Ok(0) } @@ -537,10 +537,10 @@ impl PalSocket for Sys { address: *mut sockaddr, address_len: *mut socklen_t, ) -> Result { - let stream = syscall::dup(socket as usize, b"listen")?; + let stream = redox_rt::sys::dup(socket as usize, b"listen")?; if address != ptr::null_mut() && address_len != ptr::null_mut() { if let Err(err) = unsafe { Self::getpeername(stream as c_int, address, address_len) } { - let _ = syscall::close(stream); + let _ = redox_rt::sys::close(stream); return Err(err); } } @@ -588,10 +588,17 @@ impl PalSocket for Sys { let fs_bind_result = (|| -> Result<()> { let dirfd = FdGuard::open( &dir_path, - syscall::O_RDONLY | syscall::O_DIRECTORY | syscall::O_CLOEXEC, + syscall::O_RDONLY | syscall::O_DIRECTORY | O_CLOEXEC, + )?; + let fd_to_send = + FdGuard::new(redox_rt::sys::dup_into_upper(socket as usize, &[])?) + .to_upper() + .unwrap(); + dirfd.call_wo( + &fd_to_send.as_raw_fd().to_ne_bytes(), + syscall::CallFlags::FD, + &[], )?; - let fd_to_send = FdGuard::new(syscall::dup(socket as usize, &[])?); - syscall::sendfd(dirfd.as_raw_fd(), fd_to_send.as_raw_fd(), 0, 0)?; Ok(()) })(); @@ -1027,7 +1034,7 @@ impl PalSocket for Sys { let timeval = unsafe { &*(option_value as *const timeval) }; - let fd = FdGuard::new(syscall::dup(socket as usize, timeout_name)?); + let fd = FdGuard::new(redox_rt::sys::dup(socket as usize, timeout_name)?); let Some(tv_nsec) = timeval.tv_usec.checked_mul(1000) else { return Err(Errno(EDOM)); diff --git a/src/start.rs b/src/start.rs index 8be309d761..d6761fbdb9 100644 --- a/src/start.rs +++ b/src/start.rs @@ -174,6 +174,24 @@ pub unsafe extern "C" fn relibc_start_v1( .to_upper() .expect_notls("failed to move thread fd to upper table"); + #[cfg(target_os = "redox")] + { + if redox_rt::current_filetable().fd().is_none() { + let filetable_fd = unsafe { + crate::platform::get_auxv_raw( + sp.auxv().cast(), + redox_rt::auxv_defs::AT_REDOX_FILETABLE_FD, + ) + } + .expect_notls("no filetable fd present"); + let filetable_guard = redox_rt::proc::FdGuard::new(filetable_fd) + .to_upper() + .expect_notls("failed to move filetable fd to upper table"); + *redox_rt::current_filetable() = redox_rt::sys::FdTbl::from_binary_fd(filetable_guard) + .expect_notls("failed to initialize FILETABLE"); + } + } + // Initialize TLS, if necessary unsafe { ld_so::init(