From e847768f2bd3e8181091c6925314056fa595a302 Mon Sep 17 00:00:00 2001 From: Ibuki Omatsu Date: Fri, 29 May 2026 11:07:36 +0000 Subject: [PATCH] feat: Implement multiple fds variant for call and std_fs_call --- Cargo.lock | 4 +- Cargo.toml | 2 +- src/scheme/memory.rs | 6 ++- src/scheme/mod.rs | 71 ++++++++++++++++++----------- src/scheme/pipe.rs | 1 - src/scheme/proc.rs | 6 ++- src/scheme/user.rs | 73 ++++++++++++++++++++++-------- src/syscall/fs.rs | 104 ++++++++++++++++++++++++++++++++++--------- src/syscall/mod.rs | 41 +++++++++++++---- 9 files changed, 229 insertions(+), 79 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 44f84c4740..d8d073374d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -201,9 +201,9 @@ checksum = "64072665120942deff5fd5425d6c1811b854f4939e7f1c01ce755f64432bbea7" [[package]] name = "redox_syscall" -version = "0.7.4" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f450ad9c3b1da563fb6948a8e0fb0fb9269711c9c73d9ea1de5058c79c8d643a" +checksum = "7c7591fa2c6b601dfcfe5f043f65a1c39fcdf50efefcd7f1572e538c1f4b398d" dependencies = [ "bitflags 2.11.1", ] diff --git a/Cargo.toml b/Cargo.toml index 6d4f059ace..4dd3b99a24 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,7 +19,7 @@ fdt = { git = "https://github.com/repnop/fdt.git", rev = "2fb1409edd1877c714a0aa hashbrown = { version = "0.14.3", default-features = false, features = ["ahash", "inline-more"] } linked_list_allocator = "0.9.0" redox-path = "0.2.0" -redox_syscall = { version = "0.7.4", default-features = false } +redox_syscall = { version = "0.8.0", default-features = false } rmm = { path = "rmm", default-features = false } slab = { version = "0.4", default-features = false } smallvec = { version = "1.15.1", default-features = false } diff --git a/src/scheme/memory.rs b/src/scheme/memory.rs index b34252c385..3092c0cbc8 100644 --- a/src/scheme/memory.rs +++ b/src/scheme/memory.rs @@ -250,12 +250,16 @@ impl KernelScheme for MemoryScheme { } fn kcall( &self, - id: usize, + fds: &[usize], payload: UserSliceRw, _flags: syscall::CallFlags, _metadata: &[u64], token: &mut CleanLockToken, ) -> Result { + if fds.len() != 1 { + return Err(Error::new(EINVAL)); + } + let id = fds[0]; let (handle_ty, _, _) = u32::try_from(id) .ok() .and_then(from_raw) diff --git a/src/scheme/mod.rs b/src/scheme/mod.rs index d30272c129..78ebc964dd 100644 --- a/src/scheme/mod.rs +++ b/src/scheme/mod.rs @@ -395,12 +395,11 @@ impl KernelScheme for SchemeList { id: usize, descs: Vec>, flags: CallFlags, - arg: u64, metadata: &[u64], token: &mut CleanLockToken, ) -> Result { match self.get_user_inner(id, token) { - Some(inner) => inner.call_fdwrite(descs, flags, arg, metadata, token), + Some(inner) => inner.call_fdwrite(descs, flags, metadata, token), None => Err(Error::new(EBADF)), } } @@ -678,6 +677,15 @@ pub trait KernelScheme: Send + Sync + 'static { ) -> Result<()> { Err(Error::new(ENOENT)) } + fn relpathat( + &self, + dirfd: usize, + fd: usize, + payload: UserSliceRo, + token: &mut CleanLockToken, + ) -> Result { + Err(Error::new(EOPNOTSUPP)) + } fn close(&self, id: usize, token: &mut CleanLockToken) -> Result<()> { Ok(()) } @@ -686,7 +694,7 @@ pub trait KernelScheme: Send + Sync + 'static { } fn kcall( &self, - id: usize, + fds: &[usize], payload: UserSliceRw, flags: CallFlags, metadata: &[u64], @@ -696,7 +704,7 @@ pub trait KernelScheme: Send + Sync + 'static { } fn kstdfscall( &self, - id: usize, + fds: &[usize], kind: StdFsCallKind, desc: Arc, payload: UserSliceRw, @@ -711,7 +719,6 @@ pub trait KernelScheme: Send + Sync + 'static { id: usize, descs: Vec>, flags: CallFlags, - args: u64, metadata: &[u64], token: &mut CleanLockToken, ) -> Result { @@ -730,7 +737,7 @@ pub trait KernelScheme: Send + Sync + 'static { fn translate_std_fs_call( &self, - id: usize, + fds: &[usize], desc: Arc, payload: UserSliceRw, flags: CallFlags, @@ -744,26 +751,40 @@ pub trait KernelScheme: Send + Sync + 'static { return Err(Error::new(EOPNOTSUPP)); }; let metadata = StdFsCallMeta::new(kind, arg1, arg2); - use syscall::flag::StdFsCallKind::*; + use StdFsCallKind::*; match kind { - Fchmod => self.fchmod(id, metadata.arg1 as u16, token).map(|_| 0), - Getdents => self.getdents( - id, - payload.into_wo()?, - metadata.arg2 as u16, - metadata.arg1, - token, - ), - Fstat => self.kfstat(id, payload.into_wo()?, token).map(|_| 0), - Fstatvfs => self.kfstatvfs(id, payload.into_wo()?, token).map(|_| 0), - Fsync => self.fsync(id, token).map(|_| 0), - Ftruncate => self.ftruncate(id, metadata.arg1 as usize, token).map(|_| 0), - Futimens => self.kfutimens(id, payload.into_ro()?, token), - /* TODO: Support Fchown and Unlinkat using std_fs_call - Fchown => self.kstdfscall(id, kind, desc, payload, flags, metadata, token), - Unlinkat => self.kstdfscall(fd, kind, payload, metadata, &caller).map(|_| 0) - */ - _ => Err(Error::new(EOPNOTSUPP)), + Relpathat => { + if fds.len() != 2 { + return Err(Error::new(EINVAL)); + } + self.relpathat(fds[0], fds[1], payload.into_ro()?, token) + } + _ => { + if fds.len() != 1 { + return Err(Error::new(EINVAL)); + } + let id = fds[0]; + match kind { + Fchmod => self.fchmod(id, metadata.arg1 as u16, token).map(|_| 0), + Getdents => self.getdents( + id, + payload.into_wo()?, + metadata.arg2 as u16, + metadata.arg1, + token, + ), + Fstat => self.kfstat(id, payload.into_wo()?, token).map(|_| 0), + Fstatvfs => self.kfstatvfs(id, payload.into_wo()?, token).map(|_| 0), + Fsync => self.fsync(id, token).map(|_| 0), + Ftruncate => self.ftruncate(id, metadata.arg1 as usize, token).map(|_| 0), + Futimens => self.kfutimens(id, payload.into_ro()?, token), + /* TODO: Support Fchown and Unlinkat using std_fs_call + Fchown => self.kstdfscall(fds, kind, desc, payload, flags, metadata, token), + Unlinkat => self.kstdfscall(fds, kind, payload, metadata, &caller).map(|_| 0) + */ + _ => Err(Error::new(EOPNOTSUPP)), + } + } } } } diff --git a/src/scheme/pipe.rs b/src/scheme/pipe.rs index 44e303d878..41b3792f66 100644 --- a/src/scheme/pipe.rs +++ b/src/scheme/pipe.rs @@ -385,7 +385,6 @@ impl KernelScheme for PipeScheme { id: usize, mut descs: Vec>, _flags: CallFlags, - _args: u64, _metadata: &[u64], token: &mut CleanLockToken, ) -> Result { diff --git a/src/scheme/proc.rs b/src/scheme/proc.rs index 1c1fa2bca0..348c4e365d 100644 --- a/src/scheme/proc.rs +++ b/src/scheme/proc.rs @@ -638,12 +638,16 @@ impl KernelScheme for ProcScheme { } fn kcall( &self, - id: usize, + fds: &[usize], _payload: UserSliceRw, _flags: CallFlags, metadata: &[u64], token: &mut CleanLockToken, ) -> Result { + if fds.len() != 1 { + return Err(Error::new(EINVAL)); + } + let id = fds[0]; // TODO: simplify let handle = { let handles = HANDLES.read(token.token()); diff --git a/src/scheme/user.rs b/src/scheme/user.rs index 84349dd617..906e69fb9f 100644 --- a/src/scheme/user.rs +++ b/src/scheme/user.rs @@ -1154,7 +1154,6 @@ impl UserInner { &self, descs: Vec>, flags: CallFlags, - _arg: u64, metadata: &[u64], token: &mut CleanLockToken, ) -> Result { @@ -1927,7 +1926,7 @@ impl KernelScheme for UserScheme { } fn kcall( &self, - id: usize, + fds: &[usize], payload: UserSliceRw, _flags: CallFlags, metadata: &[u64], @@ -1938,22 +1937,36 @@ impl KernelScheme for UserScheme { let mut address = inner.capture_user(payload, token)?; let ctx = { context::current().read(token.token()).caller_ctx() }; + let mut sqe_flags = SqeFlags::empty(); + let mut last_arg = 0; + if fds.len() == 2 { + sqe_flags |= SqeFlags::MULTIPLE_IDS; + last_arg = fds[1] as u64; + } else if fds.len() > 2 || fds.is_empty() { + return Err(Error::new(EINVAL)); + } + let mut sqe = Sqe { opcode: Opcode::Call as u8, - sqe_flags: SqeFlags::empty(), + sqe_flags, _rsvd: 0, tag: inner.next_id(token)?, caller: ctx.pid as u64, args: [ - id as u64, + fds[0] as u64, address.base() as u64, address.len() as u64, 0, 0, - 0, + last_arg, ], }; - { + + if fds.len() == 2 { + let dst = &mut sqe.args[3..5]; + let len = dst.len().min(metadata.len()); + dst[..len].copy_from_slice(&metadata[..len]); + } else { let dst = &mut sqe.args[3..]; let len = dst.len().min(metadata.len()); dst[..len].copy_from_slice(&metadata[..len]); @@ -1971,7 +1984,7 @@ impl KernelScheme for UserScheme { } fn kstdfscall( &self, - id: usize, + fds: &[usize], _kind: StdFsCallKind, desc: Arc, payload: UserSliceRw, @@ -1984,22 +1997,35 @@ impl KernelScheme for UserScheme { let mut address = inner.capture_user(payload, token)?; let ctx = { context::current().read(token.token()).caller_ctx() }; + let mut sqe_flags = SqeFlags::empty(); + let mut last_arg = 0; + if fds.len() == 2 { + sqe_flags |= SqeFlags::MULTIPLE_IDS; + last_arg = fds[1] as u64; + } else if fds.len() > 2 || fds.is_empty() { + return Err(Error::new(EINVAL)); + } + let mut sqe = Sqe { opcode: Opcode::StdFsCall as u8, - sqe_flags: SqeFlags::empty(), + sqe_flags, _rsvd: 0, tag: inner.next_id(token)?, caller: ctx.pid as u64, args: [ - id as u64, + fds[0] as u64, address.base() as u64, address.len() as u64, 0, 0, - 0, + last_arg, ], }; - { + if fds.len() == 2 { + let dst = &mut sqe.args[3..5]; + let len = dst.len().min(metadata.len()); + dst[..len].copy_from_slice(&metadata[..len]); + } else { let dst = &mut sqe.args[3..]; let len = dst.len().min(metadata.len()); dst[..len].copy_from_slice(&metadata[..len]); @@ -2024,8 +2050,7 @@ impl KernelScheme for UserScheme { number: usize, descs: Vec>, flags: CallFlags, - arg: u64, - _metadata: &[u64], + metadata: &[u64], token: &mut CleanLockToken, ) -> Result { let inner = self.inner.clone(); @@ -2042,7 +2067,12 @@ impl KernelScheme for UserScheme { ctx, descs, Opcode::Sendfd, - [number, sendfd_flags.bits(), arg as usize, len], + [ + number, + sendfd_flags.bits(), + *metadata.first().unwrap_or(&0) as usize, + len, + ], &mut PageSpan::empty(), token, )? @@ -2053,7 +2083,7 @@ impl KernelScheme for UserScheme { id: usize, payload: UserSliceRw, flags: CallFlags, - _metadata: &[u64], + metadata: &[u64], token: &mut CleanLockToken, ) -> Result { let inner = self.inner.clone(); @@ -2075,7 +2105,13 @@ impl KernelScheme for UserScheme { ctx, Vec::new(), Opcode::Recvfd, - [id, recvfd_flags.bits(), len], + [ + id, + recvfd_flags.bits(), + len, + *metadata.first().unwrap_or(&0) as usize, + *metadata.get(1).unwrap_or(&0) as usize, + ], &mut PageSpan::empty(), token, )?; @@ -2114,9 +2150,10 @@ impl KernelScheme for UserScheme { Ok(num_fds) } + fn translate_std_fs_call( &self, - id: usize, + fds: &[usize], desc: Arc, payload: UserSliceRw, flags: CallFlags, @@ -2130,7 +2167,7 @@ impl KernelScheme for UserScheme { return Err(Error::new(EOPNOTSUPP)); }; let metadata = StdFsCallMeta::new(kind, arg1, arg2); - self.kstdfscall(id, kind, desc, payload, flags, metadata, token) + self.kstdfscall(fds, kind, desc, payload, flags, metadata, token) } } diff --git a/src/syscall/fs.rs b/src/syscall/fs.rs index 259ef71ce9..b6577b9b6b 100644 --- a/src/syscall/fs.rs +++ b/src/syscall/fs.rs @@ -255,7 +255,7 @@ pub fn dup2( } } pub fn call( - fd: FileHandle, + fds: &[usize], payload: UserSliceRw, flags: CallFlags, metadata: UserSliceRo, @@ -270,42 +270,103 @@ pub fn call( match flags { f if f.contains(CallFlags::WRITE | CallFlags::FD) => { - call_fdwrite(fd, payload, flags, &meta[..copied / 8], token) + if fds.len() != 1 { + return Err(Error::new(EINVAL)); + } + call_fdwrite( + FileHandle::from(fds[0]), + payload, + flags, + &meta[..copied / 8], + token, + ) } f if f.contains(CallFlags::READ | CallFlags::FD) => { - call_fdread(fd, payload, flags, &meta[..copied / 8], token) + if fds.len() != 1 { + return Err(Error::new(EINVAL)); + } + call_fdread( + FileHandle::from(fds[0]), + payload, + flags, + &meta[..copied / 8], + token, + ) } - _ => call_normal(fd, payload, flags, &meta[..copied / 8], token), + _ => call_normal(fds, payload, flags, &meta[..copied / 8], token), } } fn call_normal( - fd: FileHandle, + fds: &[usize], payload: UserSliceRw, flags: CallFlags, metadata: &[u64], token: &mut CleanLockToken, ) -> Result { - let file = { - let current_lock = context::current(); - let mut current = current_lock.read(token.token()); - match (current.token_split(), flags.contains(CallFlags::CONSUME)) { - ((ctxt, mut token), true) => ctxt.remove_file(fd, &mut token), - ((ctxt, mut token), false) => ctxt.get_file(fd, &mut token), - } + if fds.len() > 2 || fds.is_empty() { + return Err(Error::new(EINVAL)); } - .ok_or(Error::new(EBADF))?; - let (scheme_id, number) = { - let desc = file.description.read(token.token()); - (desc.scheme, desc.number) + let mut nums = Vec::new(); + + let current_lock = context::current(); + let consume = flags.contains(CallFlags::CONSUME); + let mut fds = fds.iter(); + + let (target_file, scheme_id) = { + let fd = FileHandle::from(fds.next().copied().unwrap()); + let mut current = current_lock.read(token.token()); + + let (file, mut split_token) = match (current.token_split(), consume) { + ((ctxt, mut split_token), true) => { + (ctxt.remove_file(fd, &mut split_token), split_token) + } + ((ctxt, mut split_token), false) => (ctxt.get_file(fd, &mut split_token), split_token), + }; + let file = file.ok_or(Error::new(EBADF))?; + + let desc = file.description.read(split_token.token()); + + let scheme_id = desc.scheme; + nums.push(desc.number); + drop(desc); + let target_file = file; + (target_file, scheme_id) }; + + for &fd in fds { + let fd = FileHandle::from(fd); + let mut current = current_lock.read(token.token()); + + let (file, mut split_token) = match (current.token_split(), consume) { + ((ctxt, mut split_token), true) => { + (ctxt.remove_file(fd, &mut split_token), split_token) + } + ((ctxt, mut split_token), false) => (ctxt.get_file(fd, &mut split_token), split_token), + }; + let file = file.ok_or(Error::new(EBADF))?; + + let desc = file.description.read(split_token.token()); + if desc.scheme != scheme_id { + return Err(Error::new(EXDEV)); + } + nums.push(desc.number); + } + let scheme = scheme::get_scheme(token.token(), scheme_id)?; if flags.contains(CallFlags::STD_FS) { - scheme.translate_std_fs_call(number, file.description, payload, flags, metadata, token) + scheme.translate_std_fs_call( + &nums, + target_file.description, + payload, + flags, + metadata, + token, + ) } else { - scheme.kcall(number, payload, flags, metadata, token) + scheme.kcall(&nums, payload, flags, metadata, token) } } @@ -326,7 +387,7 @@ fn call_fdwrite( let len = fds.len(); - fdwrite_inner(fd, fds, flags, 0, metadata, token)?; + fdwrite_inner(fd, fds, flags, metadata, token)?; Ok(len) } @@ -335,7 +396,6 @@ fn fdwrite_inner( socket: FileHandle, target_fds: Vec, flags: CallFlags, - arg: u64, metadata: &[u64], token: &mut CleanLockToken, ) -> Result { @@ -385,7 +445,7 @@ fn fdwrite_inner( CallFlags::empty() }; - scheme.kfdwrite(number, descs_to_send, flags_to_scheme, arg, metadata, token) + scheme.kfdwrite(number, descs_to_send, flags_to_scheme, metadata, token) } fn call_fdread( @@ -427,7 +487,7 @@ pub fn sendfd( if sendfd_flags.contains(SendFdFlags::EXCLUSIVE) { call_flags |= CallFlags::FD_EXCLUSIVE; } - fdwrite_inner(socket, Vec::from([fd]), call_flags, arg, &[], token) + fdwrite_inner(socket, Vec::from([fd]), call_flags, &[arg], token) } /// File descriptor controls diff --git a/src/syscall/mod.rs b/src/syscall/mod.rs index 450a9d112f..37f4f699c3 100644 --- a/src/syscall/mod.rs +++ b/src/syscall/mod.rs @@ -195,13 +195,39 @@ pub fn syscall( }), SYS_CLOSE => close(fd, token).map(|()| 0), - SYS_CALL => call( - fd, - UserSlice::rw(c, d)?, - CallFlags::from_bits(e & !0xff).ok_or(Error::new(EINVAL))?, - UserSlice::ro(f, (e & 0xff) * 8)?, - token, - ), + SYS_CALL => { + let flags = CallFlags::from_bits(e & !0xff).ok_or(Error::new(EINVAL))?; + if flags.contains(CallFlags::MULTIPLE_FDS) { + if g / core::mem::size_of::() > 16 { + return Err(Error::new(EINVAL)); + }; + let mut fds = [0_usize; 16]; + let fds_slice = UserSlice::ro(b, g)?; + + // TODO: bytemuck/plain + let copied = fds_slice.copy_common_bytes_to_slice(unsafe { + core::slice::from_raw_parts_mut( + fds.as_mut_ptr().cast(), + fds.len() * core::mem::size_of::(), + ) + })?; + call( + &fds[..copied / core::mem::size_of::()], + UserSlice::rw(c, d)?, + flags, + UserSlice::ro(f, (e & 0xff) * 8)?, + token, + ) + } else { + call( + &[b], + UserSlice::rw(c, d)?, + flags, + UserSlice::ro(f, (e & 0xff) * 8)?, + token, + ) + } + } SYS_OPENAT => { openat(fd, UserSlice::ro(c, d)?, e, f as _, 0, 0, token).map(FileHandle::into) } @@ -233,7 +259,6 @@ pub fn syscall( SYS_MPROTECT => mprotect(b, c, MapFlags::from_bits_truncate(d), token).map(|()| 0), SYS_MREMAP => mremap(b, c, d, e, f, token), - _ => Err(Error::new(ENOSYS)), } }