feat: Implement multiple fds variant for call and std_fs_call

This commit is contained in:
Ibuki Omatsu
2026-05-29 11:07:36 +00:00
committed by Jacob Lorentzon
parent 91ab8301f2
commit e847768f2b
9 changed files with 229 additions and 79 deletions
Generated
+2 -2
View File
@@ -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",
]
+1 -1
View File
@@ -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 }
+5 -1
View File
@@ -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<usize> {
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)
+46 -25
View File
@@ -395,12 +395,11 @@ impl KernelScheme for SchemeList {
id: usize,
descs: Vec<Arc<LockedFileDescription>>,
flags: CallFlags,
arg: u64,
metadata: &[u64],
token: &mut CleanLockToken,
) -> Result<usize> {
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<usize> {
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<LockedFileDescription>,
payload: UserSliceRw,
@@ -711,7 +719,6 @@ pub trait KernelScheme: Send + Sync + 'static {
id: usize,
descs: Vec<Arc<LockedFileDescription>>,
flags: CallFlags,
args: u64,
metadata: &[u64],
token: &mut CleanLockToken,
) -> Result<usize> {
@@ -730,7 +737,7 @@ pub trait KernelScheme: Send + Sync + 'static {
fn translate_std_fs_call(
&self,
id: usize,
fds: &[usize],
desc: Arc<LockedFileDescription>,
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)),
}
}
}
}
}
-1
View File
@@ -385,7 +385,6 @@ impl KernelScheme for PipeScheme {
id: usize,
mut descs: Vec<Arc<LockedFileDescription>>,
_flags: CallFlags,
_args: u64,
_metadata: &[u64],
token: &mut CleanLockToken,
) -> Result<usize> {
+5 -1
View File
@@ -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<usize> {
if fds.len() != 1 {
return Err(Error::new(EINVAL));
}
let id = fds[0];
// TODO: simplify
let handle = {
let handles = HANDLES.read(token.token());
+55 -18
View File
@@ -1154,7 +1154,6 @@ impl UserInner {
&self,
descs: Vec<Arc<LockedFileDescription>>,
flags: CallFlags,
_arg: u64,
metadata: &[u64],
token: &mut CleanLockToken,
) -> Result<usize> {
@@ -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<LockedFileDescription>,
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<Arc<LockedFileDescription>>,
flags: CallFlags,
arg: u64,
_metadata: &[u64],
metadata: &[u64],
token: &mut CleanLockToken,
) -> Result<usize> {
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<usize> {
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<LockedFileDescription>,
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)
}
}
+82 -22
View File
@@ -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<usize> {
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<FileHandle>,
flags: CallFlags,
arg: u64,
metadata: &[u64],
token: &mut CleanLockToken,
) -> Result<usize> {
@@ -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
+33 -8
View File
@@ -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::<usize>() > 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::<usize>(),
)
})?;
call(
&fds[..copied / core::mem::size_of::<usize>()],
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)),
}
}