Introduce syscall6. Add unlinkat and remove unlink and rmdir.

This commit is contained in:
Ibuki Omatsu
2025-12-18 01:31:04 +00:00
committed by Jeremy Soller
parent d9eae6bb75
commit e30ed9ab6a
23 changed files with 204 additions and 112 deletions
Generated
+3 -2
View File
@@ -208,8 +208,9 @@ checksum = "64072665120942deff5fd5425d6c1811b854f4939e7f1c01ce755f64432bbea7"
[[package]]
name = "redox_syscall"
version = "0.5.18"
source = "git+https://gitlab.redox-os.org/redox-os/syscall.git?branch=master#2d346f1d610a358d74cd40360cf7e5dcfc2120d7"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec96166dafa0886eb81fe1c0a388bece180fbef2135f97c1e2cf8302e74b43b5"
dependencies = [
"bitflags 2.9.4",
]
+1 -1
View File
@@ -15,7 +15,7 @@ bitflags = "2"
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 = { git = "https://gitlab.redox-os.org/redox-os/syscall.git", branch = "master", default-features = false }
redox_syscall = { version = "0.6.0", default-features = false }
rmm = { path = "rmm", default-features = false }
slab = { version = "0.4", default-features = false }
spin = { version = "0.9.8" }
+3 -2
View File
@@ -6,7 +6,7 @@ use crate::{
exception_stack,
memory::{ArchIntCtx, GenericPfFlags},
sync::CleanLockToken,
syscall::{self, flag::*},
syscall,
};
use super::InterruptStack;
@@ -189,7 +189,8 @@ exception_stack!(synchronous_exception_at_el0, |stack| {
let scratch = &stack.scratch;
let mut token = unsafe { CleanLockToken::new() };
let ret = syscall::syscall(
scratch.x8, scratch.x0, scratch.x1, scratch.x2, scratch.x3, scratch.x4, &mut token,
scratch.x8, scratch.x0, scratch.x1, scratch.x2, scratch.x3, scratch.x4, scratch.x5,
&mut token,
);
stack.scratch.x0 = ret;
}
+5
View File
@@ -138,6 +138,11 @@ impl InterruptStack {
pub fn instr_pointer(&self) -> usize {
self.iret.elr_el1
}
pub fn set_arg1(&mut self, arg_opt: Option<usize>) {
if let Some(arg) = arg_opt {
self.scratch.x1 = arg;
}
}
pub fn dump(&self) {
self.iret.dump();
self.scratch.dump();
+1 -1
View File
@@ -161,7 +161,7 @@ unsafe fn handle_user_exception(scause: usize, regs: &mut InterruptStack) {
if scause == USERMODE_ECALL {
let r = &mut regs.registers;
regs.iret.sepc += 4; // skip ecall
let ret = syscall::syscall(r.x17, r.x10, r.x11, r.x12, r.x13, r.x14, &mut token);
let ret = syscall::syscall(r.x17, r.x10, r.x11, r.x12, r.x13, r.x14, r.x15, &mut token);
r.x10 = ret;
return;
}
+6
View File
@@ -122,6 +122,12 @@ impl InterruptStack {
self.registers.x10 = ret;
}
pub fn set_arg1(&mut self, arg_opt: Option<usize>) {
if let Some(arg) = arg_opt {
self.registers.x11 = arg;
}
}
pub fn dump(&self) {
self.iret.dump();
self.registers.dump();
+1
View File
@@ -38,6 +38,7 @@ interrupt_stack!(syscall, |stack| {
scratch.edx,
preserved.esi,
preserved.edi,
preserved.ebp,
&mut token,
);
stack.scratch.eax = ret;
+5 -1
View File
@@ -120,7 +120,11 @@ impl InterruptStack {
pub fn set_instr_pointer(&mut self, rip: usize) {
self.iret.rip = rip;
}
pub fn set_arg1(&mut self, arg_opt: Option<usize>) {
if let Some(arg) = arg_opt {
self.scratch.rsi = arg;
}
}
pub fn dump(&self) {
self.iret.dump();
self.scratch.dump();
+1
View File
@@ -83,6 +83,7 @@ pub unsafe extern "C" fn __inner_syscall_instruction(stack: *mut InterruptStack)
scratch.rdx,
scratch.r10,
scratch.r8,
scratch.r9,
&mut token,
);
(*stack).scratch.rax = ret;
+2 -2
View File
@@ -153,14 +153,14 @@ impl super::Context {
ptr::write(self.kfx.as_mut_ptr() as *mut FloatRegisters, new);
}
}
pub fn current_syscall(&self) -> Option<[usize; 6]> {
pub fn current_syscall(&self) -> Option<[usize; 7]> {
if !self.inside_syscall {
return None;
}
let regs = self.regs()?;
let scratch = &regs.scratch;
Some([
scratch.x8, scratch.x0, scratch.x1, scratch.x2, scratch.x3, scratch.x4,
scratch.x8, scratch.x0, scratch.x1, scratch.x2, scratch.x3, scratch.x4, scratch.x5,
])
}
+4 -2
View File
@@ -88,13 +88,15 @@ impl super::Context {
unimplemented!()
}
pub fn current_syscall(&self) -> Option<[usize; 6]> {
pub fn current_syscall(&self) -> Option<[usize; 7]> {
if !self.inside_syscall {
return None;
}
let regs = self.regs()?;
let regs = &regs.registers;
Some([regs.x17, regs.x10, regs.x11, regs.x12, regs.x13, regs.x14])
Some([
regs.x17, regs.x10, regs.x11, regs.x12, regs.x13, regs.x14, regs.x15,
])
}
pub(crate) fn write_current_env_regs(&mut self, regs: EnvRegisters) -> Result<()> {
+2 -1
View File
@@ -145,7 +145,7 @@ impl super::Context {
}
}
}
pub fn current_syscall(&self) -> Option<[usize; 6]> {
pub fn current_syscall(&self) -> Option<[usize; 7]> {
if !self.inside_syscall {
return None;
}
@@ -157,6 +157,7 @@ impl super::Context {
regs.scratch.edx,
regs.preserved.esi,
regs.preserved.edi,
regs.preserved.ebp,
])
}
+2 -1
View File
@@ -161,7 +161,7 @@ impl super::Context {
}
}
pub(crate) fn current_syscall(&self) -> Option<[usize; 6]> {
pub(crate) fn current_syscall(&self) -> Option<[usize; 7]> {
if !self.inside_syscall {
return None;
}
@@ -174,6 +174,7 @@ impl super::Context {
scratch.rdx,
scratch.r10,
scratch.r8,
scratch.r9,
])
}
+2 -2
View File
@@ -65,10 +65,10 @@ pub unsafe fn debugger(target_id: Option<*const ContextLock>, token: &mut CleanL
if !context.status_reason.is_empty() {
println!("reason: {}", context.status_reason);
}
if let Some([a, b, c, d, e, f]) = context.current_syscall() {
if let Some([a, b, c, d, e, f, g]) = context.current_syscall() {
println!(
"syscall: {}",
crate::syscall::debug::format_call(a, b, c, d, e, f)
crate::syscall::debug::format_call(a, b, c, d, e, f, g)
);
}
if let Some(ref addr_space) = context.addr_space {
+5 -2
View File
@@ -57,8 +57,11 @@ fn panic_handler_inner(info: &PanicInfo) -> ! {
let context = context_lock.read(token.token());
println!("NAME: {}, DEBUG ID: {}", context.name, context.debug_id);
if let Some([a, b, c, d, e, f]) = context.current_syscall() {
println!("SYSCALL: {}", syscall::debug::format_call(a, b, c, d, e, f));
if let Some([a, b, c, d, e, f, g]) = context.current_syscall() {
println!(
"SYSCALL: {}",
syscall::debug::format_call(a, b, c, d, e, f, g)
);
}
}
+21 -4
View File
@@ -559,10 +559,14 @@ pub trait KernelScheme: Send + Sync + 'static {
) -> Result<usize> {
Ok(0)
}
fn rmdir(&self, path: &str, ctx: CallerCtx, token: &mut CleanLockToken) -> Result<()> {
Err(Error::new(ENOENT))
}
fn unlink(&self, path: &str, ctx: CallerCtx, token: &mut CleanLockToken) -> Result<()> {
fn unlinkat(
&self,
file: usize,
path: &str,
flags: usize,
ctx: CallerCtx,
token: &mut CleanLockToken,
) -> Result<()> {
Err(Error::new(ENOENT))
}
fn close(&self, id: usize, token: &mut CleanLockToken) -> Result<()> {
@@ -611,6 +615,19 @@ pub struct CallerCtx {
pub uid: u32,
pub gid: u32,
}
impl CallerCtx {
pub fn filter_uid_gid(self, euid: u32, egid: u32) -> Self {
if self.uid == 0 && self.gid == 0 {
Self {
pid: self.pid,
uid: euid,
gid: egid,
}
} else {
self
}
}
}
#[derive(Clone)]
pub enum KernelSchemes {
+17 -1
View File
@@ -131,6 +131,7 @@ enum ContextHandle {
new: Arc<AddrSpaceWrapper>,
new_sp: usize,
new_ip: usize,
arg1: Option<usize>,
},
CurrentFiletable,
@@ -444,12 +445,19 @@ impl KernelScheme for ProcScheme {
new,
new_sp,
new_ip,
arg1,
},
} => {
let _ = try_stop_context(context, token, |context: &mut Context| {
let regs = context.regs_mut().ok_or(Error::new(EBADFD))?;
regs.set_instr_pointer(new_ip);
regs.set_stack_pointer(new_sp);
#[cfg(any(
target_arch = "x86_64",
target_arch = "aarch64",
target_arch = "riscv64"
))]
regs.set_arg1(arg1);
Ok(context.set_addr_space(Some(new)))
})?;
@@ -1089,6 +1097,7 @@ impl ContextHandle {
let addrspace_fd = iter.next().ok_or(Error::new(EINVAL))??;
let sp = iter.next().ok_or(Error::new(EINVAL))??;
let ip = iter.next().ok_or(Error::new(EINVAL))??;
let arg1 = iter.next().transpose()?;
let (hopefully_this_scheme, number) = extract_scheme_number(addrspace_fd, token)?;
verify_scheme(hopefully_this_scheme)?;
@@ -1108,10 +1117,17 @@ impl ContextHandle {
new: Arc::clone(addrspace),
new_sp: sp,
new_ip: ip,
arg1,
},
};
Ok(3 * mem::size_of::<usize>())
let written = if arg1.is_some() {
4 * mem::size_of::<usize>()
} else {
3 * mem::size_of::<usize>()
};
Ok(written)
}
Self::MmapMinAddr(ref addrspace) => {
let val = buf.read_usize()?;
+32 -6
View File
@@ -81,7 +81,10 @@ impl KernelScheme for RootScheme {
let new_close = flags & O_EXLOCK == O_EXLOCK;
if !v2 {
error!("Context {} tried to open a v1 scheme", context::current().read(token.token()).name);
error!(
"Context {} tried to open a v1 scheme",
context::current().read(token.token()).name
);
return Err(Error::new(EINVAL));
}
if !new_close {
@@ -138,21 +141,44 @@ impl KernelScheme for RootScheme {
}
}
fn unlink(&self, path: &str, ctx: CallerCtx, token: &mut CleanLockToken) -> Result<()> {
fn unlinkat(
&self,
fd: usize,
path: &str,
_flags: usize,
ctx: CallerCtx,
token: &mut CleanLockToken,
) -> Result<()> {
let path = path.trim_matches('/');
let ens = {
let handles = self.handles.read(token.token());
let Handle::List { ens } = handles.get(&fd).ok_or(Error::new(ENOENT))? else {
return Err(Error::new(EPERM));
};
*ens
};
if ctx.uid != 0 {
return Err(Error::new(EACCES));
}
{
let schemes = scheme::schemes(token.token());
if schemes.get_name(ens, path).is_none() {
return Err(Error::new(ENODEV));
}
}
let inner = {
let handles = self.handles.read(token.token());
handles
.iter()
.find_map(|(_id, handle)| {
if let Handle::Scheme(inner) = handle {
if path == inner.name.as_ref() {
return Some(inner.clone());
}
if let Handle::Scheme(inner) = handle
&& path == inner.name.as_ref()
{
return Some(inner.clone());
}
None
})
+2 -2
View File
@@ -21,11 +21,11 @@ pub fn resource(token: &mut CleanLockToken) -> Result<Vec<u8>> {
for &(id, ref name, sc) in rows.iter() {
let _ = writeln!(string, "{}: {}", id, name);
if let Some([a, b, c, d, e, f]) = sc {
if let Some([a, b, c, d, e, f, g]) = sc {
let _ = writeln!(
string,
" {}",
syscall::debug::format_call(a, b, c, d, e, f)
syscall::debug::format_call(a, b, c, d, e, f, g)
);
}
}
+10 -15
View File
@@ -1497,24 +1497,19 @@ impl KernelScheme for UserScheme {
}
}
fn rmdir(&self, path: &str, _ctx: CallerCtx, token: &mut CleanLockToken) -> Result<()> {
fn unlinkat(
&self,
file: usize,
path: &str,
flags: usize,
_ctx: CallerCtx,
token: &mut CleanLockToken,
) -> Result<()> {
let inner = self.inner.upgrade().ok_or(Error::new(ENODEV))?;
let mut address = inner.copy_and_capture_tail(path.as_bytes(), token)?;
inner.call(
Opcode::Rmdir,
[address.base(), address.len()],
address.span(),
token,
)?;
Ok(())
}
fn unlink(&self, path: &str, _ctx: CallerCtx, token: &mut CleanLockToken) -> Result<()> {
let inner = self.inner.upgrade().ok_or(Error::new(ENODEV))?;
let mut address = inner.copy_and_capture_tail(path.as_bytes(), token)?;
inner.call(
Opcode::Unlink,
[address.base(), address.len()],
Opcode::UnlinkAt,
[file, address.base(), address.len(), flags],
address.span(),
token,
)?;
+21 -13
View File
@@ -42,20 +42,28 @@ unsafe fn read_struct<T>(ptr: usize) -> Result<T> {
}
//TODO: calling format_call with arguments from another process space will not work
pub fn format_call(a: usize, b: usize, c: usize, d: usize, e: usize, f: usize) -> String {
pub fn format_call(a: usize, b: usize, c: usize, d: usize, e: usize, f: usize, g: usize) -> String {
match a {
SYS_OPEN => format!(
"open({:?}, {:#X})",
debug_path(b, c).as_ref().map(|p| ByteStr(p.as_bytes())),
d
),
SYS_RMDIR => format!(
"rmdir({:?})",
debug_path(b, c).as_ref().map(|p| ByteStr(p.as_bytes())),
SYS_OPENAT => format!(
"openat({} {:?}, {:#0x}, {}, {})",
b,
debug_path(c, d).as_ref().map(|p| ByteStr(p.as_bytes())),
e,
f,
g
),
SYS_UNLINK => format!(
"unlink({:?})",
debug_path(b, c).as_ref().map(|p| ByteStr(p.as_bytes())),
SYS_UNLINKAT => format!(
"unlinkat({} {:?}, {:#0x}, {}, {})",
b,
debug_path(c, d).as_ref().map(|p| ByteStr(p.as_bytes())),
e,
f,
g,
),
SYS_CLOSE => format!("close({})", b),
SYS_DUP => format!(
@@ -186,8 +194,8 @@ pub fn format_call(a: usize, b: usize, c: usize, d: usize, e: usize, f: usize) -
),
SYS_YIELD => format!("yield()"),
_ => format!(
"UNKNOWN{} {:#X}({:#X}, {:#X}, {:#X}, {:#X}, {:#X})",
a, a, b, c, d, e, f
"UNKNOWN{} {:#X}({:#X}, {:#X}, {:#X}, {:#X}, {:#X}, {:#X})",
a, a, b, c, d, e, f, g
),
}
}
@@ -219,7 +227,7 @@ impl SyscallDebugInfo {
}
#[cfg_attr(feature = "syscall_debug", inline)]
pub fn debug_start([a, b, c, d, e, f]: [usize; 6], token: &mut CleanLockToken) {
pub fn debug_start([a, b, c, d, e, f, g]: [usize; 7], token: &mut CleanLockToken) {
if cfg!(not(feature = "syscall_debug")) {
return;
}
@@ -252,7 +260,7 @@ pub fn debug_start([a, b, c, d, e, f]: [usize; 6], token: &mut CleanLockToken) {
// Do format_call outside print! so possible exception handlers cannot reentrantly
// deadlock.
let string = format_call(a, b, c, d, e, f);
let string = format_call(a, b, c, d, e, f, g);
println!("{}", string);
crate::time::monotonic()
@@ -271,7 +279,7 @@ pub fn debug_start([a, b, c, d, e, f]: [usize; 6], token: &mut CleanLockToken) {
#[cfg_attr(feature = "syscall_debug", inline)]
pub fn debug_end(
[a, b, c, d, e, f]: [usize; 6],
[a, b, c, d, e, f, g]: [usize; 7],
result: Result<usize>,
token: &mut CleanLockToken,
) {
@@ -297,7 +305,7 @@ pub fn debug_end(
// Do format_call outside print! so possible exception handlers cannot reentrantly
// deadlock.
let string = format_call(a, b, c, d, e, f);
let string = format_call(a, b, c, d, e, f, g);
print!("{} = ", string);
match result {
+36 -47
View File
@@ -1,4 +1,5 @@
//! Filesystem syscalls
use core::{mem::size_of, num::NonZeroUsize};
use alloc::{string::String, sync::Arc, vec::Vec};
@@ -145,6 +146,8 @@ pub fn openat(
raw_path: UserSliceRo,
flags: usize,
fcntl_flags: u32,
euid: u32,
egid: u32,
token: &mut CleanLockToken,
) -> Result<FileHandle> {
let path_buf = copy_path_to_buf(raw_path, PATH_MAX)?;
@@ -161,7 +164,10 @@ pub fn openat(
let description = pipe.description.read();
let caller_ctx = context::current().read(token.token()).caller_ctx();
let caller_ctx = context::current()
.read(token.token())
.caller_ctx()
.filter_uid_gid(euid, egid);
let new_description = {
let scheme = scheme::schemes(token.token())
@@ -185,7 +191,7 @@ pub fn openat(
internal_flags,
scheme: description.scheme,
number,
flags: description.flags,
flags: (flags & !O_CLOEXEC) as u32,
}))
}
OpenResult::External(desc) => desc,
@@ -196,59 +202,42 @@ pub fn openat(
.read(token.token())
.add_file(FileDescriptor {
description: new_description,
cloexec: false,
cloexec: flags as usize & O_CLOEXEC == O_CLOEXEC,
})
.ok_or(Error::new(EMFILE))
}
/// rmdir syscall
pub fn rmdir(raw_path: UserSliceRo, token: &mut CleanLockToken) -> Result<()> {
let (scheme_ns, caller_ctx) = {
let ctx = context::current();
let cx = &ctx.read(token.token());
(cx.ens, cx.caller_ctx())
};
/// Unlinkat syscall
pub fn unlinkat(
fh: FileHandle,
raw_path: UserSliceRo,
flags: usize,
euid: u32,
egid: u32,
token: &mut CleanLockToken,
) -> Result<()> {
let path_buf = copy_path_to_buf(raw_path, PATH_MAX)?;
let pipe = context::current()
.read(token.token())
.get_file(fh)
.ok_or(Error::new(EBADF))?;
let description = pipe.description.read();
let scheme = scheme::schemes(token.token())
.get(description.scheme)
.ok_or(Error::new(EBADF))?
.clone();
let caller_ctx = context::current()
.read(token.token())
.caller_ctx()
.filter_uid_gid(euid, egid);
/*
let mut path_buf = BorrowedHtBuf::head()?;
let path = path_buf.use_for_string(raw_path)?;
*/
let path_buf = copy_path_to_buf(raw_path, PATH_MAX)?;
let path = RedoxPath::from_absolute(&path_buf).ok_or(Error::new(EINVAL))?;
let (scheme_name, reference) = path.as_parts().ok_or(Error::new(EINVAL))?;
let scheme = {
let schemes = scheme::schemes(token.token());
let (_scheme_id, scheme) = schemes
.get_name(scheme_ns, scheme_name.as_ref())
.ok_or(Error::new(ENODEV))?;
scheme.clone()
};
scheme.rmdir(reference.as_ref(), caller_ctx, token)
}
/// Unlink syscall
pub fn unlink(raw_path: UserSliceRo, token: &mut CleanLockToken) -> Result<()> {
let (scheme_ns, caller_ctx) = {
let ctx = context::current();
let cx = &ctx.read(token.token());
(cx.ens, cx.caller_ctx())
};
/*
let mut path_buf = BorrowedHtBuf::head()?;
let path = path_buf.use_for_string(raw_path)?;
*/
let path_buf = copy_path_to_buf(raw_path, PATH_MAX)?;
let path = RedoxPath::from_absolute(&path_buf).ok_or(Error::new(EINVAL))?;
let (scheme_name, reference) = path.as_parts().ok_or(Error::new(EINVAL))?;
let scheme = {
let schemes = scheme::schemes(token.token());
let (_scheme_id, scheme) = schemes
.get_name(scheme_ns, scheme_name.as_ref())
.ok_or(Error::new(ENODEV))?;
scheme.clone()
};
scheme.unlink(reference.as_ref(), caller_ctx, token)
scheme.unlinkat(description.number, &path_buf, flags, caller_ctx, token)
}
/// Close syscall
+22 -7
View File
@@ -61,6 +61,7 @@ pub fn syscall(
d: usize,
e: usize,
f: usize,
g: usize,
token: &mut CleanLockToken,
) -> usize {
#[inline(always)]
@@ -71,6 +72,7 @@ pub fn syscall(
d: usize,
e: usize,
f: usize,
g: usize,
token: &mut CleanLockToken,
) -> Result<usize> {
let fd = FileHandle::from(b);
@@ -205,11 +207,24 @@ pub fn syscall(
UserSlice::ro(f, (e & 0xff) * 8)?,
token,
),
SYS_OPEN => open(UserSlice::ro(b, c)?, d, token).map(FileHandle::into),
SYS_OPENAT => openat(fd, UserSlice::ro(c, d)?, e, f as _, token).map(FileHandle::into),
SYS_RMDIR => rmdir(UserSlice::ro(b, c)?, token).map(|()| 0),
SYS_UNLINK => unlink(UserSlice::ro(b, c)?, token).map(|()| 0),
SYS_OPENAT => {
openat(fd, UserSlice::ro(c, d)?, e, f as _, 0, 0, token).map(FileHandle::into)
}
SYS_OPENAT_WITH_FILTER => openat(
fd,
UserSlice::ro(c, d)?,
e,
(e & syscall::O_FCNTL_MASK) as _,
f as _,
g as _,
token,
)
.map(FileHandle::into),
SYS_UNLINKAT => unlinkat(fd, UserSlice::ro(c, d)?, e, 0, 0, token).map(|()| 0),
SYS_UNLINKAT_WITH_FILTER => {
unlinkat(fd, UserSlice::ro(c, d)?, e, f as _, g as _, token).map(|()| 0)
}
SYS_YIELD => sched_yield(token).map(|()| 0),
SYS_NANOSLEEP => nanosleep(
UserSlice::ro(b, core::mem::size_of::<TimeSpec>())?,
@@ -239,11 +254,11 @@ pub fn syscall(
PercpuBlock::current().inside_syscall.set(true);
debug_start([a, b, c, d, e, f], token);
debug_start([a, b, c, d, e, f, g], token);
let result = inner(a, b, c, d, e, f, token);
let result = inner(a, b, c, d, e, f, g, token);
debug_end([a, b, c, d, e, f], result, token);
debug_end([a, b, c, d, e, f, g], result, token);
let percpu = PercpuBlock::current();
percpu.inside_syscall.set(false);