From 66ea2b46eec537b313e977f6b2159af1553965e8 Mon Sep 17 00:00:00 2001 From: Darley Barreto Date: Sun, 13 Jul 2025 12:43:20 +0000 Subject: [PATCH] Adding `openat` syscall --- src/scheme/mod.rs | 42 +++++++++++++++++++++++ src/scheme/pipe.rs | 29 +++++++++++++++- src/scheme/user.rs | 33 ++++++++++++++++++ src/syscall/fs.rs | 84 +++++++++++++++++++++++++++++++++++++++------- src/syscall/mod.rs | 1 + 5 files changed, 176 insertions(+), 13 deletions(-) diff --git a/src/scheme/mod.rs b/src/scheme/mod.rs index a974835a03..d409deda72 100644 --- a/src/scheme/mod.rs +++ b/src/scheme/mod.rs @@ -88,6 +88,37 @@ int_like!(SchemeId, usize); // Unique identifier for a file descriptor. int_like!(FileHandle, AtomicFileHandle, usize, AtomicUsize); +#[allow(dead_code)] +pub enum StrOrBytes<'a> { + Str(&'a str), + Bytes(&'a [u8]), +} + +#[allow(dead_code)] +impl<'a> StrOrBytes<'a> { + pub fn as_str(&self) -> Result<&str, core::str::Utf8Error> { + match self { + StrOrBytes::Str(path) => Ok(path), + StrOrBytes::Bytes(slice) => core::str::from_utf8(slice), + } + } + + pub fn as_bytes(&self) -> &[u8] { + match self { + StrOrBytes::Str(path) => path.as_bytes(), + StrOrBytes::Bytes(slice) => slice, + } + } + + pub fn from_str(path: &'a str) -> Self { + StrOrBytes::Str(path) + } + + pub fn from_bytes(slice: &'a [u8]) -> Self { + StrOrBytes::Bytes(slice) + } +} + pub struct SchemeIter<'a> { inner: Option, SchemeId>>, } @@ -365,6 +396,17 @@ pub trait KernelScheme: Send + Sync + 'static { Err(Error::new(ENOENT)) } + fn kopenat( + &self, + file: usize, + path: StrOrBytes, + flags: usize, + fcntl_flags: u32, + _ctx: CallerCtx, + ) -> Result { + Err(Error::new(EOPNOTSUPP)) + } + fn kfmap( &self, number: usize, diff --git a/src/scheme/pipe.rs b/src/scheme/pipe.rs index 278e004c81..0b10924ceb 100644 --- a/src/scheme/pipe.rs +++ b/src/scheme/pipe.rs @@ -19,7 +19,7 @@ use crate::{ }, }; -use super::{CallerCtx, GlobalSchemes, KernelScheme, OpenResult}; +use super::{CallerCtx, GlobalSchemes, KernelScheme, OpenResult, StrOrBytes}; // TODO: Preallocate a number of scheme IDs, since there can only be *one* root namespace, and // therefore only *one* pipe scheme. @@ -146,6 +146,33 @@ impl KernelScheme for PipeScheme { Ok(OpenResult::SchemeLocal(read_id, InternalFlags::empty())) } + fn kopenat( + &self, + id: usize, + user_buf: StrOrBytes, + _flags: usize, + _fcntl_flags: u32, + _ctx: CallerCtx, + ) -> Result { + let (_, key) = from_raw_id(id); + + let buf = user_buf.as_str().or(Err(Error::new(EINVAL)))?; + if buf == "write" { + return Err(Error::new(EINVAL)); + } + + let pipe = Arc::clone(PIPES.read().get(&key).ok_or(Error::new(EBADF))?); + + if pipe.has_run_dup.swap(true, Ordering::SeqCst) { + return Err(Error::new(EBADF)); + } + + Ok(OpenResult::SchemeLocal( + key | WRITE_NOT_READ_BIT, + InternalFlags::empty(), + )) + } + fn kread( &self, id: usize, diff --git a/src/scheme/user.rs b/src/scheme/user.rs index e909ffa6d6..5c4f77da12 100644 --- a/src/scheme/user.rs +++ b/src/scheme/user.rs @@ -1388,6 +1388,39 @@ impl KernelScheme for UserScheme { Response::Fd(desc) => Ok(OpenResult::External(desc)), } } + + fn kopenat( + &self, + file: usize, + path: super::StrOrBytes, + flags: usize, + fcntl_flags: u32, + ctx: CallerCtx, + ) -> Result { + let inner = self.inner.upgrade().ok_or(Error::new(ENODEV))?; + let mut address = inner.copy_and_capture_tail(path.as_bytes())?; + let result = inner.call_extended( + ctx, + None, + Opcode::OpenAt, + [file, address.base(), address.len(), flags, fcntl_flags as _], + address.span(), + ); + + address.release()?; + + match result? { + Response::Regular(code, fl) => Ok({ + let fd = Error::demux(code)?; + OpenResult::SchemeLocal( + fd, + InternalFlags::from_extra0(fl).ok_or(Error::new(EINVAL))?, + ) + }), + Response::Fd(desc) => Ok(OpenResult::External(desc)), + } + } + fn rmdir(&self, path: &str, _ctx: CallerCtx) -> Result<()> { let inner = self.inner.upgrade().ok_or(Error::new(ENODEV))?; let mut address = inner.copy_and_capture_tail(path.as_bytes())?; diff --git a/src/syscall/fs.rs b/src/syscall/fs.rs index eda4ac4001..06cd5c2714 100644 --- a/src/syscall/fs.rs +++ b/src/syscall/fs.rs @@ -1,7 +1,7 @@ //! Filesystem syscalls use core::num::NonZeroUsize; -use alloc::{sync::Arc, vec::Vec}; +use alloc::{string::String, sync::Arc, vec::Vec}; use redox_path::RedoxPath; use spin::RwLock; @@ -12,7 +12,7 @@ use crate::{ memory::{AddrSpace, GenericFlusher, Grant, PageSpan, TlbShootdownActions}, }, paging::{Page, VirtualAddress, PAGE_SIZE}, - scheme::{self, CallerCtx, FileHandle, KernelScheme, OpenResult}, + scheme::{self, CallerCtx, FileHandle, KernelScheme, OpenResult, StrOrBytes}, syscall::{data::Stat, error::*, flag::*}, }; @@ -41,19 +41,28 @@ pub fn file_op_generic_ext( op(&*scheme, file.description, desc) } -pub fn copy_path_to_buf(raw_path: UserSliceRo, max_len: usize) -> Result { +pub fn copy_path_to_buf(raw_path: UserSliceRo, max_len: usize) -> Result { let mut path_buf = vec![0_u8; max_len]; if raw_path.len() > path_buf.len() { return Err(Error::new(ENAMETOOLONG)); } let path_len = raw_path.copy_common_bytes_to_slice(&mut path_buf)?; path_buf.truncate(path_len); - alloc::string::String::from_utf8(path_buf).map_err(|_| Error::new(EINVAL)) + String::from_utf8(path_buf).map_err(|_| Error::new(EINVAL)) //core::str::from_utf8(&path_buf[..path_len]).map_err(|_| Error::new(EINVAL)) } // TODO: Define elsewhere const PATH_MAX: usize = PAGE_SIZE; +#[inline] +fn is_legacy(path_buf: &String) -> bool { + // FIXME remove entries from this list as the respective programs get updated + path_buf.starts_with(':') + || path_buf == "null:" // FIXME Remove exception at next rustc update (rust#138457) + || path_buf == "sys:exe" // FIXME Remove exception at next rustc update (rust#138457) + || path_buf.starts_with("orbital:") +} + /// Open syscall pub fn open(raw_path: UserSliceRo, flags: usize) -> Result { let (pid, uid, gid, scheme_ns) = match context::current().read() { @@ -70,12 +79,7 @@ pub fn open(raw_path: UserSliceRo, flags: usize) -> Result { // Display a deprecation warning for any usage of the legacy scheme syntax (scheme:/path) // FIXME remove entries from this list as the respective programs get updated - if path_buf.contains(':') - && !path_buf.starts_with(':') - && path_buf != "null:" // FIXME Remove exception at next rustc update (rust#138457) - && path_buf != "sys:exe" // FIXME Remove exception at next rustc update (rust#138457) - && !path_buf.starts_with("orbital:") - { + if path_buf.contains(':') && !is_legacy(&path_buf) { let name = context::current().read().name.clone(); if name.contains("cosmic") && (path_buf == "event:" || path_buf.starts_with("time:")) { // FIXME cosmic apps likely need crate updates @@ -83,7 +87,6 @@ pub fn open(raw_path: UserSliceRo, flags: usize) -> Result { println!("deprecated: legacy path {:?} used by {}", path_buf, name); } } - let path = RedoxPath::from_absolute(&path_buf).ok_or(Error::new(EINVAL))?; let (scheme_name, reference) = path.as_parts().ok_or(Error::new(EINVAL))?; @@ -110,7 +113,6 @@ pub fn open(raw_path: UserSliceRo, flags: usize) -> Result { } }; //drop(path_buf); - context::current() .read() .add_file(FileDescriptor { @@ -120,6 +122,64 @@ pub fn open(raw_path: UserSliceRo, flags: usize) -> Result { .ok_or(Error::new(EMFILE)) } +pub fn openat( + fh: FileHandle, + raw_path: UserSliceRo, + flags: usize, + fcntl_flags: u32, +) -> Result { + let path_buf = copy_path_to_buf(raw_path, PATH_MAX)?; + + if is_legacy(&path_buf) { + // TODO: implement + return Err(Error::new(EINVAL)); + } + + let pipe = context::current() + .read() + .get_file(fh) + .ok_or(Error::new(EBADF))?; + + let description = pipe.description.read(); + + let caller_ctx = context::current().read().caller_ctx(); + + let new_description = { + let scheme = scheme::schemes() + .get(description.scheme) + .ok_or(Error::new(EBADF))? + .clone(); + + let res = scheme.kopenat( + description.number, + StrOrBytes::from_str(&path_buf), + flags, + fcntl_flags, + caller_ctx, + ); + + match res? { + OpenResult::SchemeLocal(number, internal_flags) => { + Arc::new(RwLock::new(FileDescription { + offset: 0, + internal_flags, + scheme: description.scheme, + number, + flags: description.flags, + })) + } + OpenResult::External(desc) => desc, + } + }; + + context::current() + .read() + .add_file(FileDescriptor { + description: new_description, + cloexec: false, + }) + .ok_or(Error::new(EMFILE)) +} /// rmdir syscall pub fn rmdir(raw_path: UserSliceRo) -> Result<()> { let (scheme_ns, caller_ctx) = match context::current().read() { diff --git a/src/syscall/mod.rs b/src/syscall/mod.rs index fdef2ed5f4..83c84c7622 100644 --- a/src/syscall/mod.rs +++ b/src/syscall/mod.rs @@ -178,6 +178,7 @@ pub fn syscall(a: usize, b: usize, c: usize, d: usize, e: usize, f: usize) -> us ), SYS_OPEN => open(UserSlice::ro(b, c)?, d).map(FileHandle::into), + SYS_OPENAT => openat(fd, UserSlice::ro(c, d)?, e, f as _).map(FileHandle::into), SYS_RMDIR => rmdir(UserSlice::ro(b, c)?).map(|()| 0), SYS_UNLINK => unlink(UserSlice::ro(b, c)?).map(|()| 0), SYS_YIELD => sched_yield().map(|()| 0),