From bf0fc66ac18b1bad73effd79a40cb5c502fc9e2f Mon Sep 17 00:00:00 2001 From: Jacob Lorentzon <4ldo2@protonmail.com> Date: Fri, 14 Jun 2024 11:31:51 +0000 Subject: [PATCH] Improved scheme protocol. --- Cargo.lock | 10 + Cargo.toml | 1 + src/context/file.rs | 31 +- src/event.rs | 4 +- src/main.rs | 4 +- src/scheme/acpi.rs | 75 ++-- src/scheme/debug.rs | 28 +- src/scheme/event.rs | 28 +- src/scheme/irq.rs | 112 +++--- src/scheme/itimer.rs | 10 +- src/scheme/memory.rs | 10 +- src/scheme/mod.rs | 50 ++- src/scheme/pipe.rs | 55 +-- src/scheme/proc.rs | 147 ++++---- src/scheme/root.rs | 48 ++- src/scheme/serio.rs | 26 +- src/scheme/sys/mod.rs | 33 +- src/scheme/time.rs | 8 +- src/scheme/user.rs | 771 +++++++++++++++++++++++++---------------- src/sync/wait_queue.rs | 17 + src/syscall/fs.rs | 134 ++++--- src/syscall/mod.rs | 18 +- syscall | 2 +- 23 files changed, 874 insertions(+), 748 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 40b4ee0230..68a134563c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -131,6 +131,7 @@ dependencies = [ "rmm", "rustc-cfg", "rustc-demangle", + "slab", "slab_allocator", "spin 0.9.8", "spinning_top 0.3.0", @@ -287,6 +288,15 @@ dependencies = [ "serde", ] +[[package]] +name = "slab" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" +dependencies = [ + "autocfg", +] + [[package]] name = "slab_allocator" version = "0.3.1" diff --git a/Cargo.toml b/Cargo.toml index fb0c2f1346..0c86bd99c8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,6 +22,7 @@ spin = "0.9.8" spinning_top = { version = "0.3", features = ["arc_lock"] } rmm = { path = "rmm", default-features = false } arrayvec = { version = "0.7.4", default-features = false } +slab = { version = "0.4", default-features = false } [dependencies.goblin] version = "0.2.1" diff --git a/src/context/file.rs b/src/context/file.rs index 4e49dd81a1..60ba29bb70 100644 --- a/src/context/file.rs +++ b/src/context/file.rs @@ -7,18 +7,43 @@ use crate::{ }; use alloc::sync::Arc; use spin::RwLock; +use syscall::{schemev2::NewFdFlags, RwFlags, O_APPEND, O_NONBLOCK}; /// A file description #[derive(Clone, Copy, Debug)] pub struct FileDescription { - /// The namespace the file was opened from (used for debugging) - pub namespace: SchemeNamespace, + /// The current file offset (seek) + pub offset: u64, /// The scheme that this file refers to pub scheme: SchemeId, /// The number the scheme uses to refer to this file pub number: usize, /// The flags passed to open or fcntl(SETFL) - pub flags: usize, + pub flags: u32, + pub internal_flags: InternalFlags, +} +bitflags! { + #[derive(Clone, Copy, Debug)] + pub struct InternalFlags: u32 { + const POSITIONED = 1; + } +} +impl FileDescription { + pub fn rw_flags(&self, rw: RwFlags) -> u32 { + let mut ret = self.flags & !(O_NONBLOCK | O_APPEND) as u32; + if rw.contains(RwFlags::APPEND) { + ret |= O_APPEND as u32; + } + if rw.contains(RwFlags::NONBLOCK) { + ret |= O_NONBLOCK as u32; + } + ret + } +} +impl InternalFlags { + pub fn from_extra0(fl: u8) -> Option { + Some(NewFdFlags::from_bits(fl)?.iter().map(|fd| if fd == NewFdFlags::POSITIONED { Self::POSITIONED } else { Self::empty() }).collect()) + } } /// A file descriptor diff --git a/src/event.rs b/src/event.rs index ec74065cf0..a8506f25a5 100644 --- a/src/event.rs +++ b/src/event.rs @@ -30,8 +30,8 @@ impl EventQueue { } } - pub fn read(&self, buf: UserSliceWo) -> Result { - self.queue.receive_into_user(buf, true, "EventQueue::read") + pub fn read(&self, buf: UserSliceWo, block: bool) -> Result { + self.queue.receive_into_user(buf, block, "EventQueue::read") } pub fn write(&self, events: &[Event]) -> Result { diff --git a/src/main.rs b/src/main.rs index eb88994a52..2b90b325b0 100644 --- a/src/main.rs +++ b/src/main.rs @@ -3,6 +3,8 @@ //! The Redox OS Kernel is a microkernel that supports `x86_64` systems and //! provides Unix-like syscalls for primarily Rust applications +// Necessary for alternative! macro. +#![allow(unexpected_cfgs)] // Useful for adding comments about different branches #![allow(clippy::if_same_then_else)] // Useful in the syscall function @@ -40,14 +42,12 @@ // Ensure that all must_use results are used #![deny(unused_must_use)] #![feature(allocator_api)] -#![feature(array_methods)] #![feature(asm_const)] // TODO: Relax requirements of most asm invocations #![feature(int_roundings)] #![feature(iter_next_chunk)] #![feature(let_chains)] #![feature(naked_functions)] #![feature(new_uninit)] -#![feature(offset_of)] #![feature(sync_unsafe_cell)] #![feature(variant_count)] #![no_std] diff --git a/src/scheme/acpi.rs b/src/scheme/acpi.rs index 91aef9b9ef..9161a949dc 100644 --- a/src/scheme/acpi.rs +++ b/src/scheme/acpi.rs @@ -9,9 +9,7 @@ use alloc::{boxed::Box, collections::BTreeMap}; use spin::{Mutex, Once, RwLock}; use crate::{ - acpi::{RxsdtEnum, RXSDT_ENUM}, - event, - sync::WaitCondition, + acpi::{RxsdtEnum, RXSDT_ENUM}, context::file::InternalFlags, event, sync::WaitCondition }; use crate::syscall::{ @@ -30,7 +28,6 @@ use super::{CallerCtx, GlobalSchemes, KernelScheme, OpenResult}; pub struct AcpiScheme; struct Handle { - offset: usize, kind: HandleKind, stat: bool, } @@ -121,25 +118,25 @@ impl KernelScheme for AcpiScheme { if flags & O_ACCMODE != O_RDONLY && flags & O_STAT != O_STAT { return Err(Error::new(EROFS)); } - let handle_kind = match path { + let (handle_kind, int_flags) = match path { "" => { if flags & O_DIRECTORY != O_DIRECTORY && flags & O_STAT != O_STAT { return Err(Error::new(EISDIR)); } - HandleKind::TopLevel + (HandleKind::TopLevel, InternalFlags::POSITIONED) } "rxsdt" => { if flags & O_DIRECTORY == O_DIRECTORY && flags & O_STAT != O_STAT { return Err(Error::new(ENOTDIR)); } - HandleKind::Rxsdt + (HandleKind::Rxsdt, InternalFlags::POSITIONED) } "kstop" => { if flags & O_DIRECTORY == O_DIRECTORY && flags & O_STAT != O_STAT { return Err(Error::new(ENOTDIR)); } - HandleKind::ShutdownPipe + (HandleKind::ShutdownPipe, InternalFlags::empty()) } _ => return Err(Error::new(ENOENT)), }; @@ -150,15 +147,15 @@ impl KernelScheme for AcpiScheme { let _ = handles_guard.insert( fd, Handle { - offset: 0, kind: handle_kind, + // TODO: Redundant stat: flags & O_STAT == O_STAT, }, ); - Ok(OpenResult::SchemeLocal(fd)) + Ok(OpenResult::SchemeLocal(fd, int_flags)) } - fn seek(&self, id: usize, pos: isize, whence: usize) -> Result { + fn fsize(&self, id: usize) -> Result { let mut handles = HANDLES.write(); let handle = handles.get_mut(&id).ok_or(Error::new(EBADF))?; @@ -166,39 +163,11 @@ impl KernelScheme for AcpiScheme { return Err(Error::new(EBADF)); } - let file_len = match handle.kind { - HandleKind::Rxsdt => DATA.get().ok_or(Error::new(EBADFD))?.len(), + Ok(match handle.kind { + HandleKind::Rxsdt => DATA.get().ok_or(Error::new(EBADFD))?.len() as u64, HandleKind::ShutdownPipe => 1, - HandleKind::TopLevel => TOPLEVEL_CONTENTS.len(), - }; - - let new_offset = match whence { - SEEK_SET => pos as usize, - SEEK_CUR => { - if pos < 0 { - handle - .offset - .checked_sub((-pos) as usize) - .ok_or(Error::new(EINVAL))? - } else { - handle.offset.saturating_add(pos as usize) - } - } - SEEK_END => { - if pos < 0 { - file_len - .checked_sub((-pos) as usize) - .ok_or(Error::new(EINVAL))? - } else { - file_len - } - } - _ => return Err(Error::new(EINVAL)), - }; - - handle.offset = new_offset; - - Ok(new_offset) + HandleKind::TopLevel => TOPLEVEL_CONTENTS.len() as u64, + }) } // TODO fn fevent(&self, id: usize, _flags: EventFlags) -> Result { @@ -217,10 +186,11 @@ impl KernelScheme for AcpiScheme { } Ok(()) } - fn kwrite(&self, _id: usize, _buf: UserSliceRo) -> Result { - Err(Error::new(EBADF)) - } - fn kread(&self, id: usize, dst_buf: UserSliceWo) -> Result { + fn kreadoff(&self, id: usize, dst_buf: UserSliceWo, offset: u64, flags: u32, stored_flags: u32) -> Result { + let Ok(offset) = usize::try_from(offset) else { + return Ok(0); + }; + let mut handles = HANDLES.write(); let handle = handles.get_mut(&id).ok_or(Error::new(EBADF))?; @@ -244,23 +214,18 @@ impl KernelScheme for AcpiScheme { } } - dst_buf.copy_exactly(&[0x42])?; - handle.offset = 1; - return Ok(1); + return dst_buf.copy_exactly(&[0x42]).map(|()| 1); } HandleKind::Rxsdt => DATA.get().ok_or(Error::new(EBADFD))?, HandleKind::TopLevel => TOPLEVEL_CONTENTS, }; - let src_offset = core::cmp::min(handle.offset, data.len()); + let src_offset = core::cmp::min(offset, data.len()); let src_buf = data .get(src_offset..) .expect("expected data to be at least data.len() bytes long"); - let bytes_copied = dst_buf.copy_common_bytes_from_slice(src_buf)?; - handle.offset += bytes_copied; - - Ok(bytes_copied) + dst_buf.copy_common_bytes_from_slice(src_buf) } fn kfstat(&self, id: usize, buf: UserSliceWo) -> Result<()> { let handles = HANDLES.read(); diff --git a/src/scheme/debug.rs b/src/scheme/debug.rs index ac1ce6703b..f4baa3556b 100644 --- a/src/scheme/debug.rs +++ b/src/scheme/debug.rs @@ -19,7 +19,6 @@ static INPUT: WaitQueue = WaitQueue::new(); #[derive(Clone, Copy)] struct Handle { - flags: usize, num: usize, } @@ -41,7 +40,7 @@ pub fn debug_notify() { pub struct DebugScheme; impl KernelScheme for DebugScheme { - fn kopen(&self, path: &str, flags: usize, ctx: CallerCtx) -> Result { + fn kopen(&self, path: &str, _flags: usize, ctx: CallerCtx) -> Result { if ctx.uid != 0 { return Err(Error::new(EPERM)); } @@ -61,28 +60,11 @@ impl KernelScheme for DebugScheme { HANDLES.write().insert( id, Handle { - flags: flags & !O_ACCMODE, num, }, ); - Ok(OpenResult::SchemeLocal(id)) - } - - fn fcntl(&self, id: usize, cmd: usize, arg: usize) -> Result { - let mut handles = HANDLES.write(); - if let Some(handle) = handles.get_mut(&id) { - match cmd { - F_GETFL => Ok(handle.flags), - F_SETFL => { - handle.flags = arg & !O_ACCMODE; - Ok(0) - } - _ => Err(Error::new(EINVAL)), - } - } else { - Err(Error::new(EBADF)) - } + Ok(OpenResult::SchemeLocal(id, InternalFlags::empty())) } fn fevent(&self, id: usize, _flags: EventFlags) -> Result { @@ -112,7 +94,7 @@ impl KernelScheme for DebugScheme { Ok(()) } - fn kread(&self, id: usize, buf: UserSliceWo) -> Result { + fn kread(&self, id: usize, buf: UserSliceWo, flags: u32, _stored_flags: u32) -> Result { let handle = { let handles = HANDLES.read(); *handles.get(&id).ok_or(Error::new(EBADF))? @@ -128,12 +110,12 @@ impl KernelScheme for DebugScheme { INPUT.receive_into_user( buf, - handle.flags & O_NONBLOCK != O_NONBLOCK, + flags & O_NONBLOCK as u32 == 0, "DebugScheme::read", ) } - fn kwrite(&self, id: usize, buf: UserSliceRo) -> Result { + fn kwrite(&self, id: usize, buf: UserSliceRo, _flags: u32, _stored_flags: u32) -> Result { let handle = { let handles = HANDLES.read(); *handles.get(&id).ok_or(Error::new(EBADF))? diff --git a/src/scheme/event.rs b/src/scheme/event.rs index 2d6476662e..04c7819106 100644 --- a/src/scheme/event.rs +++ b/src/scheme/event.rs @@ -1,13 +1,13 @@ use alloc::sync::Arc; +use syscall::O_NONBLOCK; use core::mem; use crate::{ - event::{next_queue_id, queues, queues_mut, EventQueue, EventQueueId}, - syscall::{ + context::file::InternalFlags, event::{next_queue_id, queues, queues_mut, EventQueue, EventQueueId}, syscall::{ data::Event, error::*, usercopy::{UserSliceRo, UserSliceWo}, - }, + } }; use super::{CallerCtx, KernelScheme, OpenResult}; @@ -19,21 +19,7 @@ impl KernelScheme for EventScheme { let id = next_queue_id(); queues_mut().insert(id, Arc::new(EventQueue::new(id))); - Ok(OpenResult::SchemeLocal(id.get())) - } - - fn fcntl(&self, id: usize, _cmd: usize, _arg: usize) -> Result { - let id = EventQueueId::from(id); - - let handles = queues(); - handles.get(&id).ok_or(Error::new(EBADF)).and(Ok(0)) - } - - fn fsync(&self, id: usize) -> Result<()> { - let id = EventQueueId::from(id); - - let handles = queues(); - handles.get(&id).ok_or(Error::new(EBADF)).and(Ok(())) + Ok(OpenResult::SchemeLocal(id.get(), InternalFlags::empty())) } fn close(&self, id: usize) -> Result<()> { @@ -43,7 +29,7 @@ impl KernelScheme for EventScheme { .ok_or(Error::new(EBADF)) .and(Ok(())) } - fn kread(&self, id: usize, buf: UserSliceWo) -> Result { + fn kread(&self, id: usize, buf: UserSliceWo, flags: u32, _stored_flags: u32) -> Result { let id = EventQueueId::from(id); let queue = { @@ -52,10 +38,10 @@ impl KernelScheme for EventScheme { handle.clone() }; - queue.read(buf) + queue.read(buf, flags & O_NONBLOCK as u32 == 0) } - fn kwrite(&self, id: usize, buf: UserSliceRo) -> Result { + fn kwrite(&self, id: usize, buf: UserSliceRo, flags: u32, _stored_flags: u32) -> Result { let id = EventQueueId::from(id); let queue = { diff --git a/src/scheme/irq.rs b/src/scheme/irq.rs index 28e8ca000d..93d1d36f56 100644 --- a/src/scheme/irq.rs +++ b/src/scheme/irq.rs @@ -1,3 +1,6 @@ +// TODO: Rewrite this entire scheme. Legacy x86 APIs should be abstracted by a userspace scheme, +// this scheme should only handle raw IRQ registration and delivery to userspace. + use core::{ mem, str, str::FromStr, @@ -8,7 +11,7 @@ use alloc::{collections::BTreeMap, string::String, vec::Vec}; use spin::{Mutex, Once, RwLock}; -use crate::arch::interrupt::{available_irqs_iter, bsp_apic_id, is_reserved, set_reserved}; +use crate::{arch::interrupt::{available_irqs_iter, bsp_apic_id, is_reserved, set_reserved}, context::file::InternalFlags}; use crate::{ cpu_set::LogicalCpuId, @@ -22,7 +25,7 @@ use crate::{ }, }; -use super::{calc_seek_offset, CallerCtx, GlobalSchemes, OpenResult}; +use super::{CallerCtx, GlobalSchemes, OpenResult}; /// IRQ queues pub(super) static COUNTS: Mutex<[usize; 224]> = Mutex::new([0; 224]); @@ -62,8 +65,8 @@ pub extern "C" fn irq_trigger(irq: u8) { enum Handle { Irq { ack: AtomicUsize, irq: u8 }, - Avail(u8, Vec, AtomicUsize), // CPU id, data, offset - TopLevel(Vec, AtomicUsize), // data, offset + Avail(u8, Vec), // CPU id, data, offset + TopLevel(Vec), // data, offset Bsp, } impl Handle { @@ -105,7 +108,7 @@ impl IrqScheme { CPUS.call_once(|| cpus); } - fn open_ext_irq(flags: usize, cpu_id: u8, path_str: &str) -> Result { + fn open_ext_irq(flags: usize, cpu_id: u8, path_str: &str) -> Result<(Handle, InternalFlags)> { let irq_number = u8::from_str(path_str).or(Err(Error::new(ENOENT)))?; Ok( @@ -114,10 +117,10 @@ impl IrqScheme { // // The only CPUs don't have the legacy IRQs in their IDTs. - Handle::Irq { + (Handle::Irq { ack: AtomicUsize::new(0), irq: irq_number, - } + }, InternalFlags::empty()) } else if irq_number < TOTAL_IRQ_COUNT { if flags & O_CREAT == 0 && flags & O_STAT == 0 { return Err(Error::new(EINVAL)); @@ -132,10 +135,10 @@ impl IrqScheme { true, ); } - Handle::Irq { + (Handle::Irq { ack: AtomicUsize::new(0), irq: irq_number, - } + }, InternalFlags::empty()) } else { return Err(Error::new(ENOENT)); }, @@ -158,7 +161,7 @@ impl crate::scheme::KernelScheme for IrqScheme { let path_str = path.trim_start_matches('/'); - let handle: Handle = if path_str.is_empty() { + let (handle, int_flags) = if path_str.is_empty() { if flags & O_DIRECTORY == 0 && flags & O_STAT == 0 { return Err(Error::new(EISDIR)); } @@ -180,13 +183,13 @@ impl crate::scheme::KernelScheme for IrqScheme { // TODO: When signals are used for IRQs, there will probably also be a file // `irq:signal` that maps IRQ numbers and their source APIC IDs to signal numbers. - Handle::TopLevel(bytes.into_bytes(), AtomicUsize::new(0)) + (Handle::TopLevel(bytes.into_bytes()), InternalFlags::POSITIONED) } else { if path_str == "bsp" { if bsp_apic_id().is_none() { return Err(Error::new(ENOENT)); } - Handle::Bsp + (Handle::Bsp, InternalFlags::empty()) } else if path_str.starts_with("cpu-") { let path_str = &path_str[4..]; let cpu_id = u8::from_str_radix(&path_str[..2], 16).or(Err(Error::new(ENOENT)))?; @@ -204,7 +207,7 @@ impl crate::scheme::KernelScheme for IrqScheme { writeln!(data, "{}", irq).unwrap(); } - Handle::Avail(cpu_id, data.into_bytes(), AtomicUsize::new(0)) + (Handle::Avail(cpu_id, data.into_bytes()), InternalFlags::POSITIONED) } else if path_str.starts_with('/') { let path_str = &path_str[1..]; Self::open_ext_irq(flags, cpu_id, path_str)? @@ -213,10 +216,10 @@ impl crate::scheme::KernelScheme for IrqScheme { } } else if let Ok(plain_irq_number) = u8::from_str(path_str) { if plain_irq_number < BASE_IRQ_COUNT { - Handle::Irq { + (Handle::Irq { ack: AtomicUsize::new(0), irq: plain_irq_number, - } + }, InternalFlags::empty()) } else { return Err(Error::new(ENOENT)); } @@ -226,20 +229,15 @@ impl crate::scheme::KernelScheme for IrqScheme { }; let fd = NEXT_FD.fetch_add(1, Ordering::Relaxed); HANDLES.write().insert(fd, handle); - Ok(OpenResult::SchemeLocal(fd)) + Ok(OpenResult::SchemeLocal(fd, int_flags)) } - fn seek(&self, id: usize, pos: isize, whence: usize) -> Result { + fn fsize(&self, id: usize) -> Result { let handles_guard = HANDLES.read(); let handle = handles_guard.get(&id).ok_or(Error::new(EBADF))?; - match handle { - &Handle::Avail(_, ref buf, ref offset) | &Handle::TopLevel(ref buf, ref offset) => { - let cur_offset = offset.load(Ordering::SeqCst); - let new_offset = calc_seek_offset(cur_offset, pos, whence, buf.len())?; - offset.store(new_offset as usize, Ordering::SeqCst); - Ok(new_offset as usize) - } + match *handle { + Handle::Avail(_, ref buf) | Handle::TopLevel(ref buf) => Ok(buf.len() as u64), _ => Err(Error::new(ESPIPE)), } } @@ -270,7 +268,7 @@ impl crate::scheme::KernelScheme for IrqScheme { } Ok(()) } - fn kwrite(&self, file: usize, buffer: UserSliceRo) -> Result { + fn kwrite(&self, file: usize, buffer: UserSliceRo, _flags: u32, _stored_flags: u32) -> Result { let handles_guard = HANDLES.read(); let handle = handles_guard.get(&file).ok_or(Error::new(EBADF))?; @@ -279,22 +277,20 @@ impl crate::scheme::KernelScheme for IrqScheme { irq: handle_irq, ack: ref handle_ack, } => { - if buffer.len() >= mem::size_of::() { - let ack = buffer.read_usize()?; - let current = COUNTS.lock()[handle_irq as usize]; - - if ack == current { - handle_ack.store(ack, Ordering::SeqCst); - unsafe { - acknowledge(handle_irq as usize); - } - Ok(mem::size_of::()) - } else { - Ok(0) - } - } else { - Err(Error::new(EINVAL)) + if buffer.len() < mem::size_of::() { + return Err(Error::new(EINVAL)); } + let ack = buffer.read_usize()?; + let current = COUNTS.lock()[handle_irq as usize]; + + if ack != current { + return Ok(0); + } + handle_ack.store(ack, Ordering::SeqCst); + unsafe { + acknowledge(handle_irq as usize); + } + Ok(mem::size_of::()) } _ => Err(Error::new(EBADF)), } @@ -325,14 +321,14 @@ impl crate::scheme::KernelScheme for IrqScheme { st_nlink: 1, ..Default::default() }, - Handle::Avail(cpu_id, ref buf, _) => Stat { + Handle::Avail(cpu_id, ref buf) => Stat { st_mode: MODE_DIR | 0o700, st_size: buf.len() as u64, st_ino: INO_AVAIL | u64::from(cpu_id) << 32, st_nlink: 2, ..Default::default() }, - Handle::TopLevel(ref buf, _) => Stat { + Handle::TopLevel(ref buf) => Stat { st_mode: MODE_DIR | 0o500, st_size: buf.len() as u64, st_ino: INO_TOPLEVEL, @@ -350,14 +346,14 @@ impl crate::scheme::KernelScheme for IrqScheme { let scheme_path = match handle { Handle::Irq { irq, .. } => format!("irq:{}", irq), Handle::Bsp => format!("irq:bsp"), - Handle::Avail(cpu_id, _, _) => format!("irq:cpu-{:2x}", cpu_id), - Handle::TopLevel(_, _) => format!("irq:"), + Handle::Avail(cpu_id, _) => format!("irq:cpu-{:2x}", cpu_id), + Handle::TopLevel(_) => format!("irq:"), } .into_bytes(); buf.copy_common_bytes_from_slice(&scheme_path) } - fn kread(&self, file: usize, buffer: UserSliceWo) -> Result { + fn kreadoff(&self, file: usize, buffer: UserSliceWo, offset: u64, flags: u32, _stored_flags: u32) -> Result { let handles_guard = HANDLES.read(); let handle = handles_guard.get(&file).ok_or(Error::new(EBADF))?; @@ -367,16 +363,15 @@ impl crate::scheme::KernelScheme for IrqScheme { irq: handle_irq, ack: ref handle_ack, } => { - if buffer.len() >= mem::size_of::() { - let current = COUNTS.lock()[handle_irq as usize]; - if handle_ack.load(Ordering::SeqCst) != current { - buffer.write_usize(current)?; - Ok(mem::size_of::()) - } else { - Ok(0) - } + if buffer.len() < mem::size_of::() { + return Err(Error::new(EINVAL)); + } + let current = COUNTS.lock()[handle_irq as usize]; + if handle_ack.load(Ordering::SeqCst) != current { + buffer.write_usize(current)?; + Ok(mem::size_of::()) } else { - Err(Error::new(EINVAL)) + Ok(0) } } Handle::Bsp => { @@ -390,12 +385,9 @@ impl crate::scheme::KernelScheme for IrqScheme { Err(Error::new(EBADFD)) } } - Handle::Avail(_, ref buf, ref offset) | Handle::TopLevel(ref buf, ref offset) => { - let cur_offset = offset.load(Ordering::SeqCst); - let avail_buf = buf.get(cur_offset..).unwrap_or(&[]); - let bytes_read = buffer.copy_common_bytes_from_slice(avail_buf)?; - offset.fetch_add(bytes_read, Ordering::SeqCst); - Ok(bytes_read) + Handle::Avail(_, ref buf) | Handle::TopLevel(ref buf) => { + let src = usize::try_from(offset).ok().and_then(|o| buf.get(o..)).unwrap_or(&[]); + buffer.copy_common_bytes_from_slice(src) } } } diff --git a/src/scheme/itimer.rs b/src/scheme/itimer.rs index 0ac65fc3ae..939f3fdc50 100644 --- a/src/scheme/itimer.rs +++ b/src/scheme/itimer.rs @@ -5,12 +5,12 @@ use core::{ }; use spin::RwLock; -use crate::syscall::{ +use crate::{context::file::InternalFlags, syscall::{ data::ITimerSpec, error::*, flag::{EventFlags, CLOCK_MONOTONIC, CLOCK_REALTIME}, usercopy::{UserSliceRo, UserSliceWo}, -}; +}}; use super::{CallerCtx, KernelScheme, OpenResult}; pub struct ITimerScheme; @@ -32,7 +32,7 @@ impl KernelScheme for ITimerScheme { let id = NEXT_ID.fetch_add(1, Ordering::Relaxed); HANDLES.write().insert(id, clock); - Ok(OpenResult::SchemeLocal(id)) + Ok(OpenResult::SchemeLocal(id, InternalFlags::empty())) } fn fcntl(&self, _id: usize, _cmd: usize, _arg: usize) -> Result { @@ -58,7 +58,7 @@ impl KernelScheme for ITimerScheme { .ok_or(Error::new(EBADF)) .and(Ok(())) } - fn kread(&self, id: usize, buf: UserSliceWo) -> Result { + fn kread(&self, id: usize, buf: UserSliceWo, _flags: u32, _stored_flags: u32) -> Result { let _clock = { let handles = HANDLES.read(); *handles.get(&id).ok_or(Error::new(EBADF))? @@ -75,7 +75,7 @@ impl KernelScheme for ITimerScheme { Ok(specs_read * mem::size_of::()) } - fn kwrite(&self, id: usize, buf: UserSliceRo) -> Result { + fn kwrite(&self, id: usize, buf: UserSliceRo, _flags: u32, _stored_flags: u32) -> Result { let _clock = { let handles = HANDLES.read(); *handles.get(&id).ok_or(Error::new(EBADF))? diff --git a/src/scheme/memory.rs b/src/scheme/memory.rs index b51b113dd5..879c83f577 100644 --- a/src/scheme/memory.rs +++ b/src/scheme/memory.rs @@ -4,7 +4,7 @@ use alloc::{sync::Arc, vec::Vec}; use rmm::PhysicalAddress; use crate::{ - context::memory::{handle_notify_files, AddrSpace, Grant, PageSpan, AddrSpaceWrapper}, + context::{file::InternalFlags, memory::{handle_notify_files, AddrSpace, AddrSpaceWrapper, Grant, PageSpan}}, memory::{free_frames, used_frames, Frame, PAGE_SIZE}, paging::VirtualAddress, }; @@ -223,16 +223,10 @@ impl KernelScheme for MemoryScheme { Ok(OpenResult::SchemeLocal( (handle_ty as usize) | ((mem_ty as usize) << 8) | (usize::from(flags.bits()) << 16), + InternalFlags::empty(), )) } - fn fcntl(&self, _id: usize, _cmd: usize, _arg: usize) -> Result { - Ok(0) - } - - fn close(&self, _id: usize) -> Result<()> { - Ok(()) - } fn kfmap( &self, id: usize, diff --git a/src/scheme/mod.rs b/src/scheme/mod.rs index 86ddc5fc35..367a7597dd 100644 --- a/src/scheme/mod.rs +++ b/src/scheme/mod.rs @@ -13,7 +13,7 @@ use spin::{Once, RwLock, RwLockReadGuard, RwLockWriteGuard}; use syscall::{EventFlags, MunmapFlags, SendFdFlags, SEEK_CUR, SEEK_END, SEEK_SET}; use crate::{ - context::{file::FileDescription, memory::AddrSpaceWrapper}, + context::{file::{FileDescription, InternalFlags}, memory::AddrSpaceWrapper}, syscall::{ error::*, usercopy::{UserSliceRo, UserSliceWo}, @@ -394,10 +394,22 @@ pub trait KernelScheme: Send + Sync + 'static { fn kdup(&self, old_id: usize, buf: UserSliceRo, _caller: CallerCtx) -> Result { Err(Error::new(EOPNOTSUPP)) } - fn kwrite(&self, id: usize, buf: UserSliceRo) -> Result { + fn kwriteoff(&self, id: usize, buf: UserSliceRo, offset: u64, flags: u32, stored_flags: u32) -> Result { + if offset != u64::MAX { + return Err(Error::new(ESPIPE)); + } + self.kwrite(id, buf, flags, stored_flags) + } + fn kreadoff(&self, id: usize, buf: UserSliceWo, offset: u64, flags: u32, stored_flags: u32) -> Result { + if offset != u64::MAX { + return Err(Error::new(ESPIPE)); + } + self.kread(id, buf, flags, stored_flags) + } + fn kwrite(&self, id: usize, buf: UserSliceRo, flags: u32, stored_flags: u32) -> Result { Err(Error::new(EBADF)) } - fn kread(&self, id: usize, buf: UserSliceWo) -> Result { + fn kread(&self, id: usize, buf: UserSliceWo, flags: u32, stored_flags: u32) -> Result { Err(Error::new(EBADF)) } fn kfpath(&self, id: usize, buf: UserSliceWo) -> Result { @@ -424,14 +436,17 @@ pub trait KernelScheme: Send + Sync + 'static { } fn fsync(&self, id: usize) -> Result<()> { - Err(Error::new(EBADF)) + Ok(()) } fn ftruncate(&self, id: usize, len: usize) -> Result<()> { Err(Error::new(EBADF)) } - fn seek(&self, id: usize, pos: isize, whence: usize) -> Result { + fn fsize(&self, id: usize) -> Result { Err(Error::new(ESPIPE)) } + fn legacy_seek(&self, id: usize, pos: isize, whence: usize) -> Option> { + None + } fn fchmod(&self, id: usize, new_mode: u16) -> Result<()> { Err(Error::new(EBADF)) } @@ -439,13 +454,13 @@ pub trait KernelScheme: Send + Sync + 'static { Err(Error::new(EBADF)) } fn fevent(&self, id: usize, flags: EventFlags) -> Result { - Err(Error::new(EBADF)) + Ok(EventFlags::empty()) } fn frename(&self, id: usize, new_path: &str, caller_ctx: CallerCtx) -> Result<()> { Err(Error::new(EBADF)) } fn fcntl(&self, id: usize, cmd: usize, arg: usize) -> Result { - Err(Error::new(EBADF)) + Ok(0) } fn rmdir(&self, path: &str, ctx: CallerCtx) -> Result<()> { Err(Error::new(ENOENT)) @@ -454,13 +469,13 @@ pub trait KernelScheme: Send + Sync + 'static { Err(Error::new(ENOENT)) } fn close(&self, id: usize) -> Result<()> { - Err(Error::new(EBADF)) + Ok(()) } } #[derive(Debug)] pub enum OpenResult { - SchemeLocal(usize), + SchemeLocal(usize, InternalFlags), External(Arc>), } pub struct CallerCtx { @@ -469,23 +484,6 @@ pub struct CallerCtx { pub gid: u32, } -pub fn calc_seek_offset( - cur_pos: usize, - rel_pos: isize, - whence: usize, - len: usize, -) -> Result { - match whence { - SEEK_SET => usize::try_from(rel_pos).map_err(|_| Error::new(EINVAL)), - SEEK_CUR => cur_pos - .checked_add_signed(rel_pos) - .ok_or(Error::new(EOVERFLOW)), - SEEK_END => len.checked_add_signed(rel_pos).ok_or(Error::new(EOVERFLOW)), - - _ => return Err(Error::new(EINVAL)), - } -} - #[derive(Clone)] pub enum KernelSchemes { Root(Arc), diff --git a/src/scheme/pipe.rs b/src/scheme/pipe.rs index dbeed71b77..4bdd21951a 100644 --- a/src/scheme/pipe.rs +++ b/src/scheme/pipe.rs @@ -8,16 +8,14 @@ use alloc::{ use spin::{Mutex, RwLock}; use crate::{ - event, - sync::WaitCondition, - syscall::{ + context::file::InternalFlags, event, sync::WaitCondition, syscall::{ data::Stat, error::{Error, Result, EAGAIN, EBADF, EINTR, EINVAL, ENOENT, EPIPE, ESPIPE}, flag::{ EventFlags, EVENT_READ, EVENT_WRITE, F_GETFL, F_SETFL, MODE_FIFO, O_ACCMODE, O_NONBLOCK, }, usercopy::{UserSliceRo, UserSliceWo}, - }, + } }; use super::{CallerCtx, GlobalSchemes, KernelScheme, OpenResult}; @@ -40,14 +38,12 @@ fn from_raw_id(id: usize) -> (bool, usize) { (id & WRITE_NOT_READ_BIT != 0, id & !WRITE_NOT_READ_BIT) } -pub fn pipe(flags: usize) -> Result<(usize, usize)> { +pub fn pipe() -> Result<(usize, usize)> { let id = PIPE_NEXT_ID.fetch_add(1, Ordering::Relaxed); PIPES.write().insert( id, Arc::new(Pipe { - read_flags: AtomicUsize::new(flags), - write_flags: AtomicUsize::new(flags), queue: Mutex::new(VecDeque::new()), read_condition: WaitCondition::new(), write_condition: WaitCondition::new(), @@ -63,26 +59,6 @@ pub fn pipe(flags: usize) -> Result<(usize, usize)> { pub struct PipeScheme; impl KernelScheme for PipeScheme { - fn fcntl(&self, id: usize, cmd: usize, arg: usize) -> Result { - let (is_writer_not_reader, key) = from_raw_id(id); - let pipe = Arc::clone(PIPES.read().get(&key).ok_or(Error::new(EBADF))?); - - let flags = if is_writer_not_reader { - &pipe.write_flags - } else { - &pipe.read_flags - }; - - match cmd { - F_GETFL => Ok(flags.load(Ordering::SeqCst)), - F_SETFL => { - flags.store(arg & !O_ACCMODE, Ordering::SeqCst); - Ok(0) - } - _ => Err(Error::new(EINVAL)), - } - } - fn fevent(&self, id: usize, flags: EventFlags) -> Result { let (is_writer_not_reader, key) = from_raw_id(id); let pipe = Arc::clone(PIPES.read().get(&key).ok_or(Error::new(EBADF))?); @@ -99,10 +75,6 @@ impl KernelScheme for PipeScheme { Ok(ready) } - fn fsync(&self, _id: usize) -> Result<()> { - Ok(()) - } - fn close(&self, id: usize) -> Result<()> { let (is_write_not_read, key) = from_raw_id(id); @@ -132,9 +104,6 @@ impl KernelScheme for PipeScheme { Ok(()) } - fn seek(&self, _id: usize, _pos: isize, _whence: usize) -> Result { - Err(Error::new(ESPIPE)) - } fn kdup(&self, old_id: usize, user_buf: UserSliceRo, _ctx: CallerCtx) -> Result { let (is_writer_not_reader, key) = from_raw_id(old_id); @@ -154,19 +123,19 @@ impl KernelScheme for PipeScheme { return Err(Error::new(EBADF)); } - Ok(OpenResult::SchemeLocal(key | WRITE_NOT_READ_BIT)) + Ok(OpenResult::SchemeLocal(key | WRITE_NOT_READ_BIT, InternalFlags::empty())) } - fn kopen(&self, path: &str, flags: usize, _ctx: CallerCtx) -> Result { + fn kopen(&self, path: &str, _flags: usize, _ctx: CallerCtx) -> Result { if !path.trim_start_matches('/').is_empty() { return Err(Error::new(ENOENT)); } - let (read_id, _) = pipe(flags)?; + let (read_id, _) = pipe()?; - Ok(OpenResult::SchemeLocal(read_id)) + Ok(OpenResult::SchemeLocal(read_id, InternalFlags::empty())) } - fn kread(&self, id: usize, user_buf: UserSliceWo) -> Result { + fn kread(&self, id: usize, user_buf: UserSliceWo, fcntl_flags: u32, _stored_flags: u32) -> Result { let (is_write_not_read, key) = from_raw_id(id); if is_write_not_read { @@ -209,14 +178,14 @@ impl KernelScheme for PipeScheme { if !pipe.writer_is_alive.load(Ordering::SeqCst) { return Ok(0); - } else if pipe.read_flags.load(Ordering::SeqCst) & O_NONBLOCK == O_NONBLOCK { + } else if fcntl_flags & O_NONBLOCK as u32 != 0 { return Err(Error::new(EAGAIN)); } else if !pipe.read_condition.wait(vec, "PipeRead::read") { return Err(Error::new(EINTR)); } } } - fn kwrite(&self, id: usize, user_buf: UserSliceRo) -> Result { + fn kwrite(&self, id: usize, user_buf: UserSliceRo, fcntl_flags: u32, _stored_flags: u32) -> Result { let (is_write_not_read, key) = from_raw_id(id); if !is_write_not_read { @@ -260,7 +229,7 @@ impl KernelScheme for PipeScheme { if !pipe.reader_is_alive.load(Ordering::SeqCst) { return Err(Error::new(EPIPE)); - } else if pipe.write_flags.load(Ordering::SeqCst) & O_NONBLOCK == O_NONBLOCK { + } else if fcntl_flags & O_NONBLOCK as u32 != 0 { return Err(Error::new(EAGAIN)); } else if !pipe.write_condition.wait(vec, "PipeWrite::write") { return Err(Error::new(EINTR)); @@ -278,8 +247,6 @@ impl KernelScheme for PipeScheme { } pub struct Pipe { - read_flags: AtomicUsize, // fcntl read flags - write_flags: AtomicUsize, // fcntl write flags read_condition: WaitCondition, // signals whether there are available bytes to read write_condition: WaitCondition, // signals whether there is room for additional bytes queue: Mutex>, diff --git a/src/scheme/proc.rs b/src/scheme/proc.rs index 183d20f32a..7fe8d38d03 100644 --- a/src/scheme/proc.rs +++ b/src/scheme/proc.rs @@ -1,17 +1,14 @@ use crate::{ arch::paging::{Page, RmmA, RmmArch, VirtualAddress}, context::{ - self, - file::FileDescriptor, - memory::{handle_notify_files, Grant, PageSpan, AddrSpaceWrapper}, - Context, ContextId, Status, context::{HardBlockedReason, Altstack, SignalHandler}, + self, context::{Altstack, HardBlockedReason, SignalHandler}, file::{FileDescriptor, InternalFlags}, memory::{handle_notify_files, AddrSpaceWrapper, Grant, PageSpan}, Context, ContextId, Status }, memory::PAGE_SIZE, ptrace, scheme::{self, FileHandle, KernelScheme}, syscall::{ self, - data::{GrantDesc, Map, PtraceEvent, SigAction, SetSighandlerData, Stat}, + data::{GrantDesc, Map, PtraceEvent, SetSighandlerData, SigAction, Stat}, error::*, flag::*, usercopy::{UserSliceRo, UserSliceWo}, @@ -37,13 +34,9 @@ use spinning_top::RwSpinlock; use super::{CallerCtx, GlobalSchemes, KernelSchemes, OpenResult}; -fn read_from(dst: UserSliceWo, src: &[u8], offset: &mut usize) -> Result { - let avail_src = src.get(*offset..).unwrap_or(&[]); - let bytes_copied = dst.copy_common_bytes_from_slice(avail_src)?; - *offset = offset - .checked_add(bytes_copied) - .ok_or(Error::new(EOVERFLOW))?; - Ok(bytes_copied) +fn read_from(dst: UserSliceWo, src: &[u8], offset: u64) -> Result { + let avail_src = usize::try_from(offset).ok().and_then(|o| src.get(o..)).unwrap_or(&[]); + dst.copy_common_bytes_from_slice(avail_src) } fn with_context(pid: ContextId, callback: F) -> Result @@ -198,17 +191,16 @@ struct TraceData { } struct StaticData { buf: Box<[u8]>, - offset: usize, } impl StaticData { fn new(buf: Box<[u8]>) -> Self { - Self { buf, offset: 0 } + Self { buf } } } enum OperationData { Trace(TraceData), Static(StaticData), - Offset(usize), + Offset, Other, } impl OperationData { @@ -264,10 +256,10 @@ static NEXT_ID: AtomicUsize = AtomicUsize::new(1); // Using BTreeMap as hashbrown doesn't have a const constructor. static HANDLES: RwLock> = RwLock::new(BTreeMap::new()); -fn new_handle(handle: Handle) -> Result { +fn new_handle((handle, fl): (Handle, InternalFlags)) -> Result<(usize, InternalFlags)> { let id = NEXT_ID.fetch_add(1, Ordering::Relaxed); let _ = HANDLES.write().insert(id, handle); - Ok(id) + Ok((id, fl)) } fn get_context(id: ContextId) -> Result>> { @@ -285,46 +277,46 @@ impl ProcScheme { flags: usize, uid: u32, gid: u32, - ) -> Result { - let operation = match operation_str { - Some("addrspace") => Operation::AddrSpace { + ) -> Result<(usize, InternalFlags)> { + let (operation, positioned) = match operation_str { + Some("addrspace") => (Operation::AddrSpace { addrspace: Arc::clone( get_context(pid)? .read() .addr_space() .map_err(|_| Error::new(ENOENT))?, ), - }, - Some("filetable") => Operation::Filetable { + }, true), + Some("filetable") => (Operation::Filetable { filetable: Arc::downgrade(&get_context(pid)?.read().files), - }, - Some("current-addrspace") => Operation::CurrentAddrSpace, - Some("current-filetable") => Operation::CurrentFiletable, - Some("regs/float") => Operation::Regs(RegsKind::Float), - Some("regs/int") => Operation::Regs(RegsKind::Int), - Some("regs/env") => Operation::Regs(RegsKind::Env), - Some("trace") => Operation::Trace, - Some("exe") => Operation::Static("exe"), - Some("name") => Operation::Name, - Some("session_id") => Operation::SessionId, - Some("sighandler") => Operation::Sighandler, - Some("sigprocmask") => Operation::Sigprocmask, - Some("sigignmask") => Operation::Sigignmask, - Some("start") => Operation::Start, - Some("uid") => Operation::Attr(Attr::Uid), - Some("gid") => Operation::Attr(Attr::Gid), - Some("open_via_dup") => Operation::OpenViaDup, + }, true), + Some("current-addrspace") => (Operation::CurrentAddrSpace, false), + Some("current-filetable") => (Operation::CurrentFiletable, false), + Some("regs/float") => (Operation::Regs(RegsKind::Float), false), + Some("regs/int") => (Operation::Regs(RegsKind::Int), false), + Some("regs/env") => (Operation::Regs(RegsKind::Env), false), + Some("trace") => (Operation::Trace, false), + Some("exe") => (Operation::Static("exe"), true), + Some("name") => (Operation::Name, true), + Some("session_id") => (Operation::SessionId, true), + Some("sighandler") => (Operation::Sighandler, false), + Some("sigprocmask") => (Operation::Sigprocmask, false), + Some("sigignmask") => (Operation::Sigignmask, false), + Some("start") => (Operation::Start, false), + Some("uid") => (Operation::Attr(Attr::Uid), true), + Some("gid") => (Operation::Attr(Attr::Gid), true), + Some("open_via_dup") => (Operation::OpenViaDup, false), Some("sigactions") => { - Operation::Sigactions(Arc::clone(&get_context(pid)?.read().actions)) + (Operation::Sigactions(Arc::clone(&get_context(pid)?.read().actions)), false) } - Some("current-sigactions") => Operation::CurrentSigactions, - Some("mmap-min-addr") => Operation::MmapMinAddr(Arc::clone( + Some("current-sigactions") => (Operation::CurrentSigactions, false), + Some("mmap-min-addr") => (Operation::MmapMinAddr(Arc::clone( get_context(pid)? .read() .addr_space() .map_err(|_| Error::new(ENOENT))?, - )), - Some("sched-affinity") => Operation::SchedAffinity, + )), false), + Some("sched-affinity") => (Operation::SchedAffinity, true), _ => return Err(Error::new(EINVAL)), }; @@ -341,7 +333,7 @@ impl ProcScheme { Operation::Static(_) => OperationData::Static(StaticData::new( target.name.clone().into_owned().into_bytes().into(), )), - Operation::AddrSpace { .. } => OperationData::Offset(0), + Operation::AddrSpace { .. } => OperationData::Offset, _ => OperationData::Other, }; @@ -403,14 +395,14 @@ impl ProcScheme { } }; - let id = new_handle(Handle { + let (id, int_fl) = new_handle((Handle { info: Info { flags, pid, operation: operation.clone(), }, data, - })?; + }, if positioned { InternalFlags::POSITIONED } else { InternalFlags::empty() }))?; if let Operation::Trace = operation { if !ptrace::try_new_session(pid, id) { @@ -425,7 +417,7 @@ impl ProcScheme { } } - Ok(id) + Ok((id, int_fl)) } #[cfg(target_arch = "aarch64")] @@ -599,7 +591,7 @@ impl KernelScheme for ProcScheme { }; self.open_inner(pid, parts.next(), flags, ctx.uid, ctx.gid) - .map(OpenResult::SchemeLocal) + .map(|(r, fl)| OpenResult::SchemeLocal(r, fl)) } fn fcntl(&self, id: usize, cmd: usize, arg: usize) -> Result { @@ -762,7 +754,7 @@ impl KernelScheme for ProcScheme { _ => Err(Error::new(EBADF)), } } - fn kread(&self, id: usize, buf: UserSliceWo) -> Result { + fn kreadoff(&self, id: usize, buf: UserSliceWo, offset: u64, read_flags: u32, _stored_flags: u32) -> Result { // Don't hold a global lock during the context switch later on let info = { let handles = HANDLES.read(); @@ -774,12 +766,7 @@ impl KernelScheme for ProcScheme { Operation::Static(_) => { let mut handles = HANDLES.write(); let handle = handles.get_mut(&id).ok_or(Error::new(EBADF))?; - let data = handle.data.static_data().expect("operations can't change"); - let src_buf = data.buf.get(data.offset..).unwrap_or(&[]); - - let len = buf.copy_common_bytes_from_slice(src_buf)?; - data.offset += len; - Ok(len) + read_from(buf, &handle.data.static_data().ok_or(Error::new(EBADFD))?.buf, offset) } Operation::Regs(kind) => { union Output { @@ -877,11 +864,15 @@ impl KernelScheme for ProcScheme { Ok(read * mem::size_of::()) } Operation::AddrSpace { ref addrspace } => { - let OperationData::Offset(orig_offset) = + let OperationData::Offset = HANDLES.read().get(&id).ok_or(Error::new(EBADF))?.data else { return Err(Error::new(EBADFD)); }; + let Ok(offset) = usize::try_from(offset) else { + return Ok(0); + }; + let grants_to_skip = offset / mem::size_of::(); // Output a list of grant descriptors, sufficient to allow relibc's fork() // implementation to fmap MAP_SHARED grants. @@ -891,7 +882,7 @@ impl KernelScheme for ProcScheme { for (dst, (grant_base, grant_info)) in dst .iter_mut() - .zip(addrspace.acquire_read().grants.iter().skip(orig_offset)) + .zip(addrspace.acquire_read().grants.iter().skip(grants_to_skip)) { *dst = GrantDesc { base: grant_base.start_address().data(), @@ -911,11 +902,6 @@ impl KernelScheme for ProcScheme { chunk.copy_exactly(src)?; } - match HANDLES.write().get_mut(&id).ok_or(Error::new(EBADF))?.data { - OperationData::Offset(ref mut offset) => *offset += grants_read, - _ => return Err(Error::new(EBADFD)), - }; - Ok(grants_read * mem::size_of::()) } Operation::Name => read_from( @@ -926,7 +912,7 @@ impl KernelScheme for ProcScheme { .read() .name .as_bytes(), - &mut 0, + offset, ), Operation::SessionId => read_from( buf, @@ -937,7 +923,7 @@ impl KernelScheme for ProcScheme { .session_id .get() .to_ne_bytes(), - &mut 0, + offset, ), Operation::Sighandler => { @@ -985,14 +971,14 @@ impl KernelScheme for ProcScheme { } .into_bytes(); - read_from(buf, &src_buf, &mut 0) + read_from(buf, &src_buf, offset) } Operation::Filetable { .. } => { let mut handles = HANDLES.write(); let handle = handles.get_mut(&id).ok_or(Error::new(EBADF))?; let data = handle.data.static_data().expect("operations can't change"); - read_from(buf, &data.buf, &mut data.offset) + read_from(buf, &data.buf, offset) } Operation::MmapMinAddr(ref addrspace) => { buf.write_usize(addrspace.acquire_read().mmap_min)?; @@ -1016,7 +1002,9 @@ impl KernelScheme for ProcScheme { _ => Err(Error::new(EBADF)), } } - fn kwrite(&self, id: usize, buf: UserSliceRo) -> Result { + fn kwriteoff(&self, id: usize, buf: UserSliceRo, _offset: u64, fcntl_flags: u32, _stored_flags: u32) -> Result { + // TODO: offset + // Don't hold a global lock during the context switch later on let info = { let mut handles = HANDLES.write(); @@ -1406,7 +1394,7 @@ impl KernelScheme for ProcScheme { buffer.copy_exactly(&Stat { st_mode: MODE_FILE | 0o666, st_size: match handle.data { - OperationData::Static(ref data) => (data.buf.len() - data.offset) as u64, + OperationData::Static(ref data) => data.buf.len() as u64, _ => 0, }, @@ -1416,6 +1404,14 @@ impl KernelScheme for ProcScheme { Ok(()) } + fn fsize(&self, id: usize) -> Result { + let mut handles = HANDLES.write(); + let handle = handles.get_mut(&id).ok_or(Error::new(EBADF))?; + + // TODO: operation-dependent + Ok(handle.data.static_data().ok_or(Error::new(ESPIPE))?.buf.len() as u64) + } + /// Dup is currently used to implement clone() and execve(). fn kdup(&self, old_id: usize, raw_buf: UserSliceRo, _: CallerCtx) -> Result { let info = { @@ -1425,14 +1421,14 @@ impl KernelScheme for ProcScheme { handle.info.clone() }; - let handle = |operation, data| Handle { + let handle = |operation, data, positioned| (Handle { info: Info { flags: 0, pid: info.pid, operation, }, data, - }; + }, if positioned { InternalFlags::POSITIONED } else { InternalFlags::empty() }); let mut array = [0_u8; 64]; if raw_buf.len() > array.len() { return Err(Error::new(EINVAL)); @@ -1458,7 +1454,7 @@ impl KernelScheme for ProcScheme { uid, gid, ) - .map(OpenResult::SchemeLocal); + .map(|(r, fl)| OpenResult::SchemeLocal(r, fl)) } Operation::Filetable { ref filetable } => { @@ -1477,6 +1473,7 @@ impl KernelScheme for ProcScheme { filetable: new_filetable, }, OperationData::Other, + true, ) } Operation::AddrSpace { ref addrspace } => { @@ -1524,7 +1521,7 @@ impl KernelScheme for ProcScheme { _ => return Err(Error::new(EINVAL)), }; - handle(operation, OperationData::Offset(0)) + handle(operation, OperationData::Offset, true) } Operation::Sigactions(ref sigactions) => { let new = match buf { @@ -1532,11 +1529,11 @@ impl KernelScheme for ProcScheme { b"copy" => Arc::new(RwLock::new(sigactions.read().clone())), _ => return Err(Error::new(EINVAL)), }; - handle(Operation::Sigactions(new), OperationData::Other) + handle(Operation::Sigactions(new), OperationData::Other, false) } _ => return Err(Error::new(EINVAL)), }) - .map(OpenResult::SchemeLocal) + .map(|(r, fl)| OpenResult::SchemeLocal(r, fl)) } } extern "C" fn clone_handler() { diff --git a/src/scheme/root.rs b/src/scheme/root.rs index ae5bc46070..1a13adddf2 100644 --- a/src/scheme/root.rs +++ b/src/scheme/root.rs @@ -1,4 +1,5 @@ use alloc::{boxed::Box, string::ToString, sync::Arc, vec::Vec}; +use syscall::O_FSYNC; use core::{ str, sync::atomic::{AtomicUsize, Ordering}, @@ -7,7 +8,7 @@ use hashbrown::HashMap; use spin::{Mutex, RwLock}; use crate::{ - context, + context::{self, file::InternalFlags}, scheme::{ self, user::{UserInner, UserScheme}, @@ -21,29 +22,16 @@ use crate::{ }, }; -use super::{calc_seek_offset, CallerCtx, KernelScheme, KernelSchemes, OpenResult}; +use super::{CallerCtx, KernelScheme, KernelSchemes, OpenResult}; struct FolderInner { data: Box<[u8]>, - pos: Mutex, } impl FolderInner { - fn read(&self, buf: UserSliceWo) -> Result { - let mut pos_guard = self.pos.lock(); - - let avail_buf = self.data.get(*pos_guard..).unwrap_or(&[]); - let bytes_read = buf.copy_common_bytes_from_slice(avail_buf)?; - *pos_guard += bytes_read; - - Ok(bytes_read) - } - - fn seek(&self, pos: isize, whence: usize) -> Result { - let mut seek = self.pos.lock(); - let new_offset = calc_seek_offset(*seek, pos, whence, self.data.len())?; - *seek = new_offset as usize; - Ok(new_offset) + fn read(&self, buf: UserSliceWo, offset: u64) -> Result { + let avail_buf = usize::try_from(offset).ok().and_then(|o| self.data.get(o..)).unwrap_or(&[]); + buf.copy_common_bytes_from_slice(avail_buf) } } @@ -103,6 +91,11 @@ impl KernelScheme for RootScheme { let inner = Arc::new(UserInner::new( self.scheme_id, scheme_id, + + // TODO: This is a hack, but eventually the legacy interface will be + // removed. + flags & O_FSYNC == O_FSYNC, + id, path_box, flags, @@ -119,7 +112,7 @@ impl KernelScheme for RootScheme { self.handles.write().insert(id, Handle::Scheme(inner)); - Ok(OpenResult::SchemeLocal(id)) + Ok(OpenResult::SchemeLocal(id, InternalFlags::empty())) } else if path.is_empty() { let scheme_ns = { let contexts = context::contexts(); @@ -139,18 +132,17 @@ impl KernelScheme for RootScheme { let inner = Arc::new(FolderInner { data: data.into_boxed_slice(), - pos: Mutex::new(0), }); let id = self.next_id.fetch_add(1, Ordering::Relaxed); self.handles.write().insert(id, Handle::Folder(inner)); - Ok(OpenResult::SchemeLocal(id)) + Ok(OpenResult::SchemeLocal(id, InternalFlags::POSITIONED)) } else { let inner = Arc::new(path.as_bytes().to_vec().into_boxed_slice()); let id = self.next_id.fetch_add(1, Ordering::Relaxed); self.handles.write().insert(id, Handle::File(inner)); - Ok(OpenResult::SchemeLocal(id)) + Ok(OpenResult::SchemeLocal(id, InternalFlags::POSITIONED)) } } @@ -181,7 +173,7 @@ impl KernelScheme for RootScheme { inner.unmount() } - fn seek(&self, file: usize, pos: isize, whence: usize) -> Result { + fn fsize(&self, file: usize) -> Result { let handle = { let handles = self.handles.read(); let handle = handles.get(&file).ok_or(Error::new(EBADF))?; @@ -191,7 +183,7 @@ impl KernelScheme for RootScheme { match handle { Handle::Scheme(_) => Err(Error::new(EBADF)), Handle::File(_) => Err(Error::new(EBADF)), - Handle::Folder(inner) => inner.seek(pos, whence), + Handle::Folder(inner) => Ok(inner.data.len() as u64), } } @@ -260,7 +252,7 @@ impl KernelScheme for RootScheme { } Ok(()) } - fn kread(&self, file: usize, buf: UserSliceWo) -> Result { + fn kreadoff(&self, file: usize, buf: UserSliceWo, offset: u64, flags: u32, _stored_flags: u32) -> Result { let handle = { let handles = self.handles.read(); let handle = handles.get(&file).ok_or(Error::new(EBADF))?; @@ -268,13 +260,13 @@ impl KernelScheme for RootScheme { }; match handle { - Handle::Scheme(inner) => inner.read(buf), + Handle::Scheme(inner) => inner.read(buf, flags), Handle::File(_) => Err(Error::new(EBADF)), - Handle::Folder(inner) => inner.read(buf), + Handle::Folder(inner) => inner.read(buf, offset), } } - fn kwrite(&self, file: usize, buf: UserSliceRo) -> Result { + fn kwrite(&self, file: usize, buf: UserSliceRo, _flags: u32, _stored_flags: u32) -> Result { let handle = { let handles = self.handles.read(); let handle = handles.get(&file).ok_or(Error::new(EBADF))?; diff --git a/src/scheme/serio.rs b/src/scheme/serio.rs index bd240330f4..0050e5f87e 100644 --- a/src/scheme/serio.rs +++ b/src/scheme/serio.rs @@ -25,7 +25,6 @@ static INPUT: [WaitQueue; 2] = [WaitQueue::new(), WaitQueue::new()]; #[derive(Clone, Copy)] struct Handle { index: usize, - flags: usize, } // Using BTreeMap as hashbrown doesn't have a const constructor. @@ -46,7 +45,7 @@ pub fn serio_input(index: usize, data: u8) { pub struct SerioScheme; impl KernelScheme for SerioScheme { - fn kopen(&self, path: &str, flags: usize, ctx: CallerCtx) -> Result { + fn kopen(&self, path: &str, _flags: usize, ctx: CallerCtx) -> Result { if ctx.uid != 0 { return Err(Error::new(EPERM)); } @@ -61,27 +60,10 @@ impl KernelScheme for SerioScheme { id, Handle { index, - flags: flags & !O_ACCMODE, }, ); - Ok(OpenResult::SchemeLocal(id)) - } - - fn fcntl(&self, id: usize, cmd: usize, arg: usize) -> Result { - let mut handles = HANDLES.write(); - if let Some(handle) = handles.get_mut(&id) { - match cmd { - F_GETFL => Ok(handle.flags), - F_SETFL => { - handle.flags = arg & !O_ACCMODE; - Ok(0) - } - _ => Err(Error::new(EINVAL)), - } - } else { - Err(Error::new(EBADF)) - } + Ok(OpenResult::SchemeLocal(id, InternalFlags::empty())) } fn fevent(&self, id: usize, _flags: EventFlags) -> Result { @@ -111,7 +93,7 @@ impl KernelScheme for SerioScheme { Ok(()) } - fn kread(&self, id: usize, buf: UserSliceWo) -> Result { + fn kread(&self, id: usize, buf: UserSliceWo, flags: u32, _stored_flags: u32) -> Result { let handle = { let handles = HANDLES.read(); *handles.get(&id).ok_or(Error::new(EBADF))? @@ -119,7 +101,7 @@ impl KernelScheme for SerioScheme { INPUT[handle.index].receive_into_user( buf, - handle.flags & O_NONBLOCK != O_NONBLOCK, + flags & O_NONBLOCK as u32 == 0, "SerioScheme::read", ) } diff --git a/src/scheme/sys/mod.rs b/src/scheme/sys/mod.rs index e1f8d879c8..c0f3026232 100644 --- a/src/scheme/sys/mod.rs +++ b/src/scheme/sys/mod.rs @@ -6,16 +6,15 @@ use core::{ use spin::RwLock; use crate::{ - arch::interrupt, - syscall::{ + arch::interrupt, context::file::InternalFlags, syscall::{ data::Stat, error::{Error, Result, EBADF, ENOENT}, flag::{MODE_DIR, MODE_FILE}, usercopy::UserSliceWo, - }, + } }; -use super::{calc_seek_offset, CallerCtx, KernelScheme, OpenResult}; +use super::{CallerCtx, KernelScheme, OpenResult}; mod block; mod context; @@ -33,7 +32,6 @@ struct Handle { path: &'static str, data: Vec, mode: u16, - seek: usize, } type SysFn = fn() -> Result>; @@ -88,10 +86,9 @@ impl KernelScheme for SysScheme { path: "", data, mode: MODE_DIR | 0o444, - seek: 0, }, ); - return Ok(OpenResult::SchemeLocal(id)); + return Ok(OpenResult::SchemeLocal(id, InternalFlags::POSITIONED)); } else { //Have to iterate to get the path without allocation for entry in FILES.iter() { @@ -104,10 +101,9 @@ impl KernelScheme for SysScheme { path: entry.0, data, mode: MODE_FILE | 0o444, - seek: 0, }, ); - return Ok(OpenResult::SchemeLocal(id)); + return Ok(OpenResult::SchemeLocal(id, InternalFlags::POSITIONED)); } } } @@ -115,13 +111,11 @@ impl KernelScheme for SysScheme { Err(Error::new(ENOENT)) } - fn seek(&self, id: usize, pos: isize, whence: usize) -> Result { + fn fsize(&self, id: usize) -> Result { let mut handles = HANDLES.write(); let handle = handles.get_mut(&id).ok_or(Error::new(EBADF))?; - let new_offset = calc_seek_offset(handle.seek, pos, whence, handle.data.len())?; - handle.seek = new_offset; - Ok(new_offset) + Ok(handle.data.len() as u64) } fn fsync(&self, _id: usize) -> Result<()> { @@ -148,16 +142,17 @@ impl KernelScheme for SysScheme { Ok(bytes_read) } - fn kread(&self, id: usize, buffer: UserSliceWo) -> Result { + fn kreadoff(&self, id: usize, buffer: UserSliceWo, pos: u64, _flags: u32, _stored_flags: u32) -> Result { + let Ok(pos) = usize::try_from(pos) else { + return Ok(0); + }; + let mut handles = HANDLES.write(); let handle = handles.get_mut(&id).ok_or(Error::new(EBADF))?; - let avail_buf = handle.data.get(handle.seek..).unwrap_or(&[]); + let avail_buf = handle.data.get(pos..).unwrap_or(&[]); - let byte_count = buffer.copy_common_bytes_from_slice(avail_buf)?; - - handle.seek = handle.seek.saturating_add(byte_count); - Ok(byte_count) + buffer.copy_common_bytes_from_slice(avail_buf) } fn kfstat(&self, id: usize, buf: UserSliceWo) -> Result<()> { diff --git a/src/scheme/time.rs b/src/scheme/time.rs index 237c5916e6..8a3eec213b 100644 --- a/src/scheme/time.rs +++ b/src/scheme/time.rs @@ -6,7 +6,7 @@ use core::{ use spin::RwLock; use crate::{ - context::timeout, + context::{file::InternalFlags, timeout}, syscall::{ data::TimeSpec, error::*, @@ -37,7 +37,7 @@ impl KernelScheme for TimeScheme { let id = NEXT_ID.fetch_add(1, Ordering::Relaxed); HANDLES.write().insert(id, clock); - Ok(OpenResult::SchemeLocal(id)) + Ok(OpenResult::SchemeLocal(id, InternalFlags::empty())) } fn fcntl(&self, _id: usize, _cmd: usize, _arg: usize) -> Result { @@ -64,7 +64,7 @@ impl KernelScheme for TimeScheme { .ok_or(Error::new(EBADF)) .and(Ok(())) } - fn kread(&self, id: usize, buf: UserSliceWo) -> Result { + fn kread(&self, id: usize, buf: UserSliceWo, _flags: u32, _stored_flags: u32) -> Result { let clock = *HANDLES.read().get(&id).ok_or(Error::new(EBADF))?; let mut bytes_read = 0; @@ -87,7 +87,7 @@ impl KernelScheme for TimeScheme { Ok(bytes_read) } - fn kwrite(&self, id: usize, buf: UserSliceRo) -> Result { + fn kwrite(&self, id: usize, buf: UserSliceRo, _flags: u32, _stored_flags: u32) -> Result { let clock = *HANDLES.read().get(&id).ok_or(Error::new(EBADF))?; let mut bytes_written = 0; diff --git a/src/scheme/user.rs b/src/scheme/user.rs index 664e500fbd..1ae6c67df6 100644 --- a/src/scheme/user.rs +++ b/src/scheme/user.rs @@ -3,6 +3,7 @@ use alloc::{ sync::{Arc, Weak}, vec::Vec, }; +use slab::Slab; use core::{ mem, mem::size_of, @@ -14,19 +15,14 @@ use hashbrown::hash_map::{Entry, HashMap}; use spin::{Mutex, RwLock}; use spinning_top::RwSpinlock; use syscall::{ - FobtainFdFlags, MunmapFlags, SendFdFlags, MAP_FIXED_NOREPLACE, SKMSG_FOBTAINFD, - SKMSG_FRETURNFD, SKMSG_PROVIDE_MMAP, KSMSG_CANCEL, SIGKILL, + schemev2::{Cqe, CqeOpcode, Opcode, Sqe, SqeFlags}, FobtainFdFlags, MunmapFlags, SendFdFlags, F_SETFL, KSMSG_CANCEL, MAP_FIXED_NOREPLACE, SIGKILL, SKMSG_FOBTAINFD, SKMSG_FRETURNFD, SKMSG_PROVIDE_MMAP }; use crate::{ context::{ - self, - context::HardBlockedReason, - file::{FileDescription, FileDescriptor}, - memory::{ - AddrSpace, BorrowedFmapSource, Grant, GrantFileRef, MmapMode, PageSpan, DANGLING, AddrSpaceWrapper, - }, - BorrowedHtBuf, Context, Status, + self, context::HardBlockedReason, file::{FileDescription, FileDescriptor, InternalFlags}, memory::{ + AddrSpace, AddrSpaceWrapper, BorrowedFmapSource, Grant, GrantFileRef, MmapMode, PageSpan, DANGLING + }, BorrowedHtBuf, Context, ContextId, Status }, event, memory::Frame, @@ -48,12 +44,14 @@ pub struct UserInner { root_id: SchemeId, handle_id: usize, pub name: Box, - pub flags: usize, pub scheme_id: SchemeId, - next_id: Mutex, + v2: bool, context: Weak>, - todo: WaitQueue, - states: Mutex>, + todo: WaitQueue, + + // TODO: custom packed radix tree data structure + states: Mutex>, + unmounting: AtomicBool, } @@ -70,7 +68,7 @@ enum State { #[derive(Debug)] pub enum Response { - Regular(usize), + Regular(usize, u8), Fd(Arc>), } @@ -79,26 +77,81 @@ const ONE: NonZeroUsize = match NonZeroUsize::new(1) { None => unreachable!(), }; +enum ParsedCqe { + TriggerFevent { number: usize, flags: EventFlags }, + RegularResponse { tag: u32, code: usize, extra0: u8 }, + ResponseWithFd { tag: u32, fd: usize }, + ObtainFd { tag: u32, flags: FobtainFdFlags, dst_fd_or_ptr: usize }, + ProvideMmap { tag: u32, offset: u64, base_addr: VirtualAddress, page_count: usize } +} +impl ParsedCqe { + fn parse_packet(packet: &Packet) -> Result { + Ok(if packet.id == 0 { + match packet.a { + SYS_FEVENT => Self::TriggerFevent { + number: packet.b, + flags: EventFlags::from_bits_truncate(packet.c), + }, + _ => { + log::warn!("Unknown scheme -> kernel message {} from {}", packet.a, context::current().unwrap().read().name); + return Err(Error::new(EINVAL)); + } + } + } else if Error::demux(packet.a) == Err(Error::new(ESKMSG)) { + // The reason why the new ESKMSG mechanism was introduced, is that passing packet IDs + // in packet.id is much cleaner than having to convert it into 1 or 2 usizes etc. + match packet.b { + SKMSG_FRETURNFD => Self::ResponseWithFd { + tag: (packet.id - 1) as u32, + fd: packet.d, + }, + SKMSG_FOBTAINFD => Self::ObtainFd { + tag: (packet.id - 1) as u32, + flags: FobtainFdFlags::from_bits(packet.d).ok_or(Error::new(EINVAL))?, + dst_fd_or_ptr: packet.c, + }, + SKMSG_PROVIDE_MMAP => Self::ProvideMmap { + tag: (packet.id - 1) as u32, + offset: u64::from(packet.uid) | (u64::from(packet.gid) << 32), + base_addr: VirtualAddress::new(packet.c), + page_count: packet.d, + }, + _ => return Err(Error::new(EINVAL)), + } + } else { + ParsedCqe::RegularResponse { tag: (packet.id - 1) as u32, code: packet.a, extra0: 0 } + }) + } + fn parse_cqe(cqe: &Cqe) -> Result { + Ok(match CqeOpcode::try_from_raw(cqe.flags & 0b11).ok_or(Error::new(EINVAL))? { + CqeOpcode::RespondRegular => Self::RegularResponse { tag: cqe.tag, code: cqe.result as usize, extra0: cqe.extra_raw[0] }, + CqeOpcode::RespondWithFd => Self::ResponseWithFd { tag: cqe.tag, fd: cqe.result as usize }, + CqeOpcode::SendFevent => Self::TriggerFevent { number: cqe.result as usize, flags: EventFlags::from_bits(cqe.tag as usize).ok_or(Error::new(EINVAL))? }, + CqeOpcode::ObtainFd => Self::ObtainFd { tag: cqe.tag, flags: FobtainFdFlags::from_bits(cqe.extra() as usize).ok_or(Error::new(EINVAL))?, dst_fd_or_ptr: cqe.result as usize }, + }) + } +} + impl UserInner { pub fn new( root_id: SchemeId, scheme_id: SchemeId, + v2: bool, handle_id: usize, name: Box, - flags: usize, + _flags: usize, context: Weak>, ) -> UserInner { UserInner { root_id, handle_id, name, - flags, + v2, scheme_id, - next_id: Mutex::new(1), context, todo: WaitQueue::new(), unmounting: AtomicBool::new(false), - states: Mutex::new(HashMap::new()), + states: Mutex::new(Slab::with_capacity(32)), } } @@ -116,24 +169,19 @@ impl UserInner { Ok(()) } - fn next_id(&self) -> u64 { - let mut guard = self.next_id.lock(); - let id = *guard; - *guard += 1; - id + fn next_id(&self) -> Result { + let mut states = self.states.lock(); + let idx = states.insert(State::Placeholder); + + // TODO: implement blocking? + u32::try_from(idx).map_err(|_| Error::new(EAGAIN)) } - pub fn call(&self, a: usize, b: usize, c: usize, d: usize) -> Result { + pub fn call(&self, opcode: Opcode, args: impl Args) -> Result { let ctx = context::current()?.read().caller_ctx(); - match self.call_extended(ctx, None, [a, b, c, d])? { - Response::Regular(code) => Error::demux(code), - Response::Fd(_) => { - if a & SYS_RET_FILE == SYS_RET_FILE { - log::warn!("Kernel code using UserScheme::call wrongly, as an external file descriptor was returned."); - } - - Err(Error::new(EIO)) - } + match self.call_extended(ctx, None, opcode, args)? { + Response::Regular(code, _) => Error::demux(code), + Response::Fd(_) => Err(Error::new(EIO)), } } @@ -141,19 +189,22 @@ impl UserInner { &self, ctx: CallerCtx, fd: Option>>, - [a, b, c, d]: [usize; 4], + opcode: Opcode, + args: impl Args, ) -> Result { self.call_extended_inner( fd, - Packet { - id: self.next_id(), - pid: ctx.pid, - uid: ctx.uid, - gid: ctx.gid, - a, - b, - c, - d, + Sqe { + opcode: opcode as u8, + sqe_flags: SqeFlags::empty(), + _rsvd: 0, + tag: self.next_id()?, + caller: ctx.pid as u64, + args: { + let mut a = args.args(); + a[5] = uid_gid_hack_merge([ctx.uid, ctx.gid]); + a + } }, ) } @@ -161,30 +212,25 @@ impl UserInner { fn call_extended_inner( &self, fd: Option>>, - packet: Packet, + sqe: Sqe, ) -> Result { if self.unmounting.load(Ordering::SeqCst) { return Err(Error::new(ENODEV)); } - let id = packet.id; - let current_context = context::current()?; { let mut states = self.states.lock(); current_context.write().block("UserScheme::call"); - states.insert( - id, - State::Waiting { - context: Arc::downgrade(¤t_context), - fd, - canceling: false, - }, - ); + states[sqe.tag as usize] = State::Waiting { + context: Arc::downgrade(¤t_context), + fd, + canceling: false, + }; } - self.todo.send(packet); + self.todo.send(sqe); event::trigger(self.root_id, self.handle_id, EVENT_READ); loop { @@ -199,31 +245,31 @@ impl UserInner { }; let mut states = self.states.lock(); - match states.entry(id) { + + match states.get_mut(sqe.tag as usize) { // invalid state - Entry::Vacant(_) => return Err(Error::new(EBADFD)), - Entry::Occupied(mut o) => match mem::replace(o.get_mut(), State::Placeholder) { + None => return Err(Error::new(EBADFD)), + Some(o) => match mem::replace(o, State::Placeholder) { // signal wakeup while awaiting cancelation old_state @ State::Waiting { canceling: true, .. } => { - *o.get_mut() = old_state; + *o = old_state; drop(states); eintr_if_sigkill()?; context::current()?.write().block("UserInner::call"); } // spurious wakeup State::Waiting { canceling: false, fd, context } => { - *o.get_mut() = State::Waiting { canceling: true, fd, context }; + *o = State::Waiting { canceling: true, fd, context }; drop(states); eintr_if_sigkill()?; // TODO: Is this too dangerous when the states lock is held? - self.todo.send(Packet { - id: 0, - a: KSMSG_CANCEL, - b: packet.id as usize, - c: (packet.id >> 32) as usize, - ..packet + self.todo.send(Sqe { + opcode: Opcode::Cancel as u8, + sqe_flags: SqeFlags::ONEWAY, + tag: sqe.tag, + ..Default::default() }); event::trigger(self.root_id, self.handle_id, EVENT_READ); context::current()?.write().block("UserInner::call"); @@ -231,12 +277,12 @@ impl UserInner { // invalid state old_state @ (State::Placeholder | State::Fmap(_)) => { - *o.get_mut() = old_state; + *o = old_state; return Err(Error::new(EBADFD)); } State::Responded(response) => { - o.remove(); + states.remove(sqe.tag as usize); return Ok(response); } }, @@ -540,35 +586,148 @@ impl UserInner { }) } - pub fn read(&self, buf: UserSliceWo) -> Result { + pub fn read(&self, buf: UserSliceWo, flags: u32) -> Result { // If O_NONBLOCK is used, do not block - let nonblock = self.flags & O_NONBLOCK == O_NONBLOCK; + let nonblock = flags & O_NONBLOCK as u32 != 0; + // If unmounting, do not block so that EOF can be returned immediately let block = !(nonblock || self.unmounting.load(Ordering::SeqCst)); - match self.todo.receive_into_user(buf, block, "UserInner::read") { - // If we received requests, return them to the scheme handler - Ok(byte_count) => Ok(byte_count), - // If there were no requests and we were unmounting, return EOF - Err(Error { errno: EAGAIN }) if self.unmounting.load(Ordering::SeqCst) => Ok(0), - // If there were no requests and O_NONBLOCK was used (EAGAIN), or some other error - // occurred, return that. - Err(error) => Err(error), + if self.v2 { + return match self.todo.receive_into_user(buf, block, "UserInner::read (v2)") { + // If we received requests, return them to the scheme handler + Ok(byte_count) => Ok(byte_count), + // If there were no requests and we were unmounting, return EOF + Err(Error { errno: EAGAIN }) if self.unmounting.load(Ordering::SeqCst) => Ok(0), + // If there were no requests and O_NONBLOCK was used (EAGAIN), or some other error + // occurred, return that. + Err(error) => Err(error), + }; + } else { + let mut bytes_read = 0; + + for dst in buf.in_exact_chunks(size_of::()) { + match self.todo.receive(block && bytes_read == 0, "UserInner::read (legacy)") { + Ok(sqe) => { + dst.copy_exactly(&self.translate_sqe_to_packet(&sqe)?)?; + bytes_read += size_of::(); + } + Err(error) if bytes_read > 0 => return Ok(bytes_read), + Err(Error { errno: EAGAIN }) if self.unmounting.load(Ordering::SeqCst) => return Ok(bytes_read), + Err(error) => return Err(error), + } + } + Ok(bytes_read) } + + } + fn translate_sqe_to_packet(&self, sqe: &Sqe) -> Result { + let opc = Opcode::try_from_raw(sqe.opcode).expect("passed scheme opcode not internally recognized by kernel"); + + let uid = sqe.args[5] as u32; + let gid = (sqe.args[5] >> 32) as u32; + + Ok(Packet { + id: u64::from(sqe.tag) + 1, + pid: sqe.caller as usize, + a: match opc { + Opcode::Open => SYS_OPEN, + Opcode::Rmdir => SYS_RMDIR, + Opcode::Unlink => SYS_UNLINK, + Opcode::Close => SYS_CLOSE, + Opcode::Dup => SYS_DUP, + Opcode::Read => SYS_READ, + Opcode::Write => SYS_WRITE, + Opcode::Fsize => SYS_LSEEK, // lseek reuses the fsize "opcode", must be !v2 + Opcode::Fchmod => SYS_FCHMOD, + Opcode::Fchown => SYS_FCHOWN, + Opcode::Fcntl => SYS_FCNTL, + Opcode::Fevent => SYS_FEVENT, + Opcode::Sendfd => SYS_SENDFD, + Opcode::Fpath => SYS_FPATH, + Opcode::Frename => SYS_FRENAME, + Opcode::Fstat => SYS_FSTAT, + Opcode::Fstatvfs => SYS_FSTATVFS, + Opcode::Fsync => SYS_FSYNC, + Opcode::Ftruncate => SYS_FTRUNCATE, + Opcode::Futimens => SYS_FUTIMENS, + + Opcode::MmapPrep => return Ok(Packet { + id: u64::from(sqe.tag) + 1, + pid: sqe.caller as usize, + a: KSMSG_MMAP_PREP, + b: sqe.args[0] as usize, + c: sqe.args[1] as usize, + d: sqe.args[2] as usize, + uid: sqe.args[3] as u32, + gid: (sqe.args[3] >> 32) as u32, + }), + Opcode::RequestMmap => return Ok(Packet { + id: u64::from(sqe.tag) + 1, + pid: sqe.caller as usize, + a: KSMSG_MMAP, + b: sqe.args[0] as usize, + c: sqe.args[1] as usize, + d: sqe.args[2] as usize, + uid: sqe.args[3] as u32, + gid: (sqe.args[3] >> 32) as u32, + }), + Opcode::Munmap => return Ok(Packet { + id: u64::from(sqe.tag) + 1, + pid: sqe.caller as usize, + a: KSMSG_MUNMAP, + b: sqe.args[0] as usize, // fd + c: sqe.args[1] as usize, // size + d: sqe.args[2] as usize, // flags + uid: sqe.args[3] as u32, // offset lo + gid: (sqe.args[3] >> 32) as u32, // offset hi + }), + + Opcode::Mremap => SYS_MREMAP, + Opcode::Msync => KSMSG_MSYNC, + + Opcode::Cancel => return Ok(Packet { + id: 0, + a: KSMSG_CANCEL, + b: sqe.tag as usize, + c: 0, + d: 0, + pid: sqe.caller as usize, + uid, + gid, + }), + + _ => return Err(Error::new(EOPNOTSUPP)), + }, + b: sqe.args[0] as usize, + c: sqe.args[1] as usize, + d: sqe.args[2] as usize, + + uid, + gid, + }) } pub fn write(&self, buf: UserSliceRo) -> Result { - let mut packets_read = 0; - - for chunk in buf.in_exact_chunks(size_of::()) { - match self.handle_packet(&unsafe { chunk.read_exact::()? }) { - Ok(()) => packets_read += 1, - Err(_) if packets_read > 0 => break, - Err(error) => return Err(error), + let mut bytes_read = 0; + if self.v2 { + for chunk in buf.in_exact_chunks(size_of::()) { + match ParsedCqe::parse_cqe(&unsafe { chunk.read_exact::()? }).and_then(|p| self.handle_parsed(&p)) { + Ok(()) => bytes_read += size_of::(), + Err(_) if bytes_read > 0 => break, + Err(error) => return Err(error), + } + } + } else { + for chunk in buf.in_exact_chunks(size_of::()) { + match ParsedCqe::parse_packet(&unsafe { chunk.read_exact::()? }).and_then(|p| self.handle_parsed(&p)) { + Ok(()) => bytes_read += size_of::(), + Err(_) if bytes_read > 0 => break, + Err(error) => return Err(error), + } } } - - Ok(packets_read * size_of::()) + Ok(bytes_read) } pub fn request_fmap( &self, @@ -579,11 +738,11 @@ impl UserInner { ) -> Result<()> { log::info!("REQUEST FMAP"); - let packet_id = self.next_id(); + let tag = self.next_id()?; let mut states = self.states.lock(); - states.insert(packet_id, State::Fmap(Arc::downgrade(&context::current()?))); + states[tag as usize] = State::Fmap(Arc::downgrade(&context::current()?)); - self.todo.send(Packet { + /*self.todo.send(Packet { id: packet_id, pid: context::context_id().into(), a: KSMSG_MMAP, @@ -592,151 +751,144 @@ impl UserInner { d: required_page_count, uid: offset as u32, gid: (offset >> 32) as u32, + });*/ + self.todo.send(Sqe { + opcode: Opcode::RequestMmap as u8, + sqe_flags: SqeFlags::empty(), + _rsvd: 0, + tag, + args: [ + id as u64, + flags.bits() as u64, + required_page_count as u64, + 0, + 0, + uid_gid_hack_merge(current_uid_gid()?), + ], + caller: context::context_id().get() as u64, }); event::trigger(self.root_id, self.handle_id, EVENT_READ); Ok(()) } - fn handle_packet(&self, packet: &Packet) -> Result<()> { - if packet.id == 0 { - // TODO: Simplify logic by using SKMSG with packet.id being ignored? - match packet.a { - SYS_FEVENT => event::trigger( - self.scheme_id, - packet.b, - EventFlags::from_bits_truncate(packet.c), - ), - _ => log::warn!("Unknown scheme -> kernel message {} from {}", packet.a, context::current().unwrap().read().name), - } - } else if Error::demux(packet.a) == Err(Error::new(ESKMSG)) { - // The reason why the new ESKMSG mechanism was introduced, is that passing packet IDs - // in packet.id is much cleaner than having to convert it into 1 or 2 usizes etc. - match packet.b { - SKMSG_FRETURNFD => { - let fd = packet.c; + fn handle_parsed(&self, cqe: &ParsedCqe) -> Result<()> { + match *cqe { + ParsedCqe::RegularResponse { tag, code, extra0 } => self.respond(tag, Response::Regular(code, extra0))?, + ParsedCqe::ResponseWithFd { tag, fd } => self.respond(tag, Response::Fd( + context::current()? + .read() + .remove_file(FileHandle::from(fd)) + .ok_or(Error::new(EINVAL))? + .description, + ))?, + ParsedCqe::ObtainFd { tag, flags, dst_fd_or_ptr } => { + let description = match self + .states + .lock() + .get_mut(tag as usize) + .ok_or(Error::new(EINVAL))? + { + State::Waiting { ref mut fd, .. } => fd.take().ok_or(Error::new(ENOENT))?, + _ => return Err(Error::new(ENOENT)), + }; - let desc = context::current()? + // FIXME: Description can leak if context::current() fails, or if there is no + // additional file table space. + if flags.contains(FobtainFdFlags::MANUAL_FD) { + context::current()?.read().insert_file( + FileHandle::from(dst_fd_or_ptr), + FileDescriptor { + description, + cloexec: true, + }, + ); + } else { + let fd = context::current()? .read() - .remove_file(FileHandle::from(fd)) - .ok_or(Error::new(EINVAL))? - .description; - - self.respond(packet.id, Response::Fd(desc))?; + .add_file(FileDescriptor { + description, + cloexec: true, + }) + .ok_or(Error::new(EMFILE))?; + UserSlice::wo(dst_fd_or_ptr, size_of::())?.write_usize(fd.get())?; } - SKMSG_FOBTAINFD => { - let flags = FobtainFdFlags::from_bits(packet.d).ok_or(Error::new(EINVAL))?; - let description = match self - .states - .lock() - .get_mut(&packet.id) - .ok_or(Error::new(EINVAL))? - { - State::Waiting { ref mut fd, .. } => fd.take().ok_or(Error::new(ENOENT))?, - _ => return Err(Error::new(ENOENT)), - }; + } + ParsedCqe::ProvideMmap { tag, offset, base_addr, page_count } => { + log::info!("PROVIDE_MAP {:x} {:x} {:?} {:x}", tag, offset, base_addr, page_count); - // FIXME: Description can leak if context::current() fails, or if there is no - // additional file table space. - if flags.contains(FobtainFdFlags::MANUAL_FD) { - context::current()?.read().insert_file( - FileHandle::from(packet.c), - FileDescriptor { - description, - cloexec: true, - }, - ); - } else { - let fd = context::current()? - .read() - .add_file(FileDescriptor { - description, - cloexec: true, - }) - .ok_or(Error::new(EMFILE))?; - UserSlice::wo(packet.c, size_of::())?.write_usize(fd.get())?; - } + if offset % PAGE_SIZE as u64 != 0 { + return Err(Error::new(EINVAL)); } - SKMSG_PROVIDE_MMAP => { - log::info!("PROVIDE_MAP {:?}", packet); - let offset = u64::from(packet.uid) | (u64::from(packet.gid) << 32); - if offset % PAGE_SIZE as u64 != 0 { - return Err(Error::new(EINVAL)); - } + if base_addr.data() % PAGE_SIZE != 0 { + return Err(Error::new(EINVAL)); + } - let base_addr = VirtualAddress::new(packet.c); - if base_addr.data() % PAGE_SIZE != 0 { - return Err(Error::new(EINVAL)); - } + if page_count != 1 { + return Err(Error::new(EINVAL)); + } - let page_count = packet.d; + let context = { + let mut states = self.states.lock(); - if page_count != 1 { - return Err(Error::new(EINVAL)); - } - - let context = match self.states.lock().entry(packet.id) { - Entry::Occupied(mut o) => { - match mem::replace(o.get_mut(), State::Placeholder) { - // invalid state - State::Placeholder => { - return Err(Error::new(EBADFD)); - } - // invalid kernel to scheme call - old_state @ (State::Waiting { .. } | State::Responded(_)) => { - *o.get_mut() = old_state; - return Err(Error::new(EINVAL)); - } - State::Fmap(context) => { - o.remove(); - context - } + match states.get_mut(tag as usize) { + Some(o) => match mem::replace(o, State::Placeholder) { + // invalid state + State::Placeholder => { + return Err(Error::new(EBADFD)); + } + // invalid kernel to scheme call + old_state @ (State::Waiting { .. } | State::Responded(_)) => { + *o = old_state; + return Err(Error::new(EINVAL)); + } + State::Fmap(context) => { + states.remove(tag as usize); + context } } - Entry::Vacant(_) => return Err(Error::new(EINVAL)), - }; - - let context = context.upgrade().ok_or(Error::new(ESRCH))?; - - let (frame, _) = AddrSpace::current()? - .acquire_read() - .table - .utable - .translate(base_addr) - .ok_or(Error::new(EFAULT))?; - - let mut context = context.write(); - match context.status { - Status::HardBlocked { - reason: HardBlockedReason::AwaitingMmap { .. }, - } => context.status = Status::Runnable, - _ => (), + None => return Err(Error::new(EINVAL)), } - context.fmap_ret = Some(Frame::containing_address(frame)); - } - _ => return Err(Error::new(EINVAL)), - } - } else { - self.respond(packet.id, Response::Regular(packet.a))?; - } + }; + let context = context.upgrade().ok_or(Error::new(ESRCH))?; + + let (frame, _) = AddrSpace::current()? + .acquire_read() + .table + .utable + .translate(base_addr) + .ok_or(Error::new(EFAULT))?; + + let mut context = context.write(); + match context.status { + Status::HardBlocked { + reason: HardBlockedReason::AwaitingMmap { .. }, + } => context.status = Status::Runnable, + _ => (), + } + context.fmap_ret = Some(Frame::containing_address(frame)); + } + ParsedCqe::TriggerFevent { number, flags } => event::trigger(self.scheme_id, number, flags), + } Ok(()) } - fn respond(&self, id: u64, mut response: Response) -> Result<()> { + fn respond(&self, tag: u32, mut response: Response) -> Result<()> { let to_close; - match self.states.lock().entry(id) { - Entry::Occupied(mut o) => match mem::replace(o.get_mut(), State::Placeholder) { + let mut states = self.states.lock(); + match states.get_mut(tag as usize) { + Some(o) => match mem::replace(o, State::Placeholder) { // invalid state State::Placeholder => return Err(Error::new(EBADFD)), // invalid scheme to kernel call old_state @ (State::Responded(_) | State::Fmap(_)) => { - *o.get_mut() = old_state; + *o = old_state; return Err(Error::new(EINVAL)); } State::Waiting { context, fd, canceling } => { - if let Response::Regular(ref mut code) = response && !canceling && *code == Error::mux(Err(Error::new(EINTR))) { + if let Response::Regular(ref mut code, _) = response && !canceling && *code == Error::mux(Err(Error::new(EINTR))) { // EINTR is valid after cancelation has been requested, but not otherwise. // This is because the kernel-assisted signal trampoline will be invoked // after a syscall returns EINTR. @@ -751,14 +903,14 @@ impl UserInner { if let Some(context) = context.upgrade() { context.write().unblock(); - *o.get_mut() = State::Responded(response); + *o = State::Responded(response); } else { - o.remove(); + states.remove(tag as usize); } } }, // invalid state - Entry::Vacant(_) => return Err(Error::new(EBADFD)), + None => return Err(Error::new(EBADFD)), } if let Some(to_close) = to_close { @@ -831,6 +983,7 @@ impl UserInner { let response = self.call_extended_inner( None, + /* Packet { id: self.next_id(), pid: pid.into(), @@ -845,6 +998,22 @@ impl UserInner { #[cfg(target_pointer_width = "32")] gid: 0, }, + */ + Sqe { + opcode: Opcode::MmapPrep as u8, + sqe_flags: SqeFlags::empty(), + _rsvd: 0, + tag: self.next_id()?, + args: [ + file as u64, + unaligned_size as u64, + map.flags.bits() as u64, + map.offset as u64, + 0, + uid_gid_hack_merge(current_uid_gid()?), + ], + caller: pid.get() as u64, + }, )?; // TODO: I've previously tested that this works, but because the scheme trait all of @@ -855,7 +1024,7 @@ impl UserInner { let mapping_is_lazy = false; let base_page_opt = match response { - Response::Regular(code) => (!mapping_is_lazy).then_some(Error::demux(code)?), + Response::Regular(code, _) => (!mapping_is_lazy).then_some(Error::demux(code)?), Response::Fd(_) => { log::debug!("Scheme incorrectly returned an fd for fmap."); @@ -1014,40 +1183,45 @@ impl KernelScheme for UserScheme { fn kopen(&self, path: &str, flags: usize, ctx: CallerCtx) -> Result { let inner = self.inner.upgrade().ok_or(Error::new(ENODEV))?; let address = inner.copy_and_capture_tail(path.as_bytes())?; - match inner.call_extended(ctx, None, [SYS_OPEN, address.base(), address.len(), flags])? { - Response::Regular(code) => Error::demux(code).map(OpenResult::SchemeLocal), + match inner.call_extended(ctx, None, Opcode::Open, [address.base(), address.len(), flags])? { + Response::Regular(code, fl) => Ok({ + let fd = Error::demux(code)?; + OpenResult::SchemeLocal(code, 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 address = inner.copy_and_capture_tail(path.as_bytes())?; - inner.call(SYS_RMDIR, address.base(), address.len(), 0)?; + inner.call(Opcode::Rmdir, [address.base(), address.len()])?; Ok(()) } fn unlink(&self, path: &str, _ctx: CallerCtx) -> Result<()> { let inner = self.inner.upgrade().ok_or(Error::new(ENODEV))?; let address = inner.copy_and_capture_tail(path.as_bytes())?; - inner.call(SYS_UNLINK, address.base(), address.len(), 0)?; + inner.call(Opcode::Unlink, [address.base(), address.len()])?; Ok(()) } - fn seek(&self, file: usize, position: isize, whence: usize) -> Result { + fn fsize(&self, file: usize) -> Result { let inner = self.inner.upgrade().ok_or(Error::new(ENODEV))?; - inner.call(SYS_LSEEK, file, position as usize, whence) + if !inner.v2 { + return Err(Error::new(ESPIPE)); + } + inner.call(Opcode::Fsize, [file]).map(|o| o as u64) } fn fchmod(&self, file: usize, mode: u16) -> Result<()> { let inner = self.inner.upgrade().ok_or(Error::new(ENODEV))?; - inner.call(SYS_FCHMOD, file, mode as usize, 0)?; + inner.call(Opcode::Fchmod, [file, mode as usize])?; Ok(()) } fn fchown(&self, file: usize, uid: u32, gid: u32) -> Result<()> { { - let contexts = context::contexts(); - let context_lock = contexts.current().ok_or(Error::new(ESRCH))?; + let context_lock = context::current()?; let context = context_lock.read(); if context.euid != 0 { if uid != context.euid || gid != context.egid { @@ -1057,144 +1231,127 @@ impl KernelScheme for UserScheme { } let inner = self.inner.upgrade().ok_or(Error::new(ENODEV))?; - inner.call(SYS_FCHOWN, file, uid as usize, gid as usize)?; + inner.call(Opcode::Fchown, [file, uid as usize, gid as usize])?; Ok(()) } fn fcntl(&self, file: usize, cmd: usize, arg: usize) -> Result { let inner = self.inner.upgrade().ok_or(Error::new(ENODEV))?; - inner.call(SYS_FCNTL, file, cmd, arg) + inner.call(Opcode::Fcntl, [file, cmd, arg]) } fn fevent(&self, file: usize, flags: EventFlags) -> Result { let inner = self.inner.upgrade().ok_or(Error::new(ENODEV))?; inner - .call(SYS_FEVENT, file, flags.bits(), 0) + .call(Opcode::Fevent, [file, flags.bits()]) .map(EventFlags::from_bits_truncate) } - /* - fn funmap(&self, grant_address: usize, size: usize) -> Result { - let requested_span = PageSpan::validate_nonempty(VirtualAddress::new(grant_address), size).ok_or(Error::new(EINVAL))?; - - let inner = self.inner.upgrade().ok_or(Error::new(ENODEV))?; - - let address_opt = { - let context_lock = context::current()?; - let context = context_lock.read(); - - let mut addr_space = context.addr_space()?.write(); - let funmap = &mut addr_space.grants.funmap; - let entry = funmap.range(..=Page::containing_address(VirtualAddress::new(grant_address))).next_back(); - - if let Some((&grant_page, &(page_count, user_page))) = entry { - if requested_span.base.next_by(requested_span.count) > grant_page.next_by(page_count) { - return Err(Error::new(EINVAL)); - } - - funmap.remove(&grant_page); - - let grant_span = PageSpan::new(grant_page, page_count); - let user_span = PageSpan::new(user_page, page_count); - - if let Some(before) = grant_span.before(requested_span) { - funmap.insert(before.base, (before.count, user_page)); - } - if let Some(after) = grant_span.after(requested_span) { - let start = grant_span.rebase(user_span, after.base); - funmap.insert(after.base, (after.count, start)); - } - - Some(grant_span.rebase(user_span,grant_span.base).start_address().data()) - } else { - None - } - }; - if let Some(user_address) = address_opt { - inner.call(SYS_FUNMAP, user_address, size, 0) - } else { - Err(Error::new(EINVAL)) - } - } - */ - fn frename(&self, file: usize, path: &str, _ctx: CallerCtx) -> Result<()> { let inner = self.inner.upgrade().ok_or(Error::new(ENODEV))?; let address = inner.copy_and_capture_tail(path.as_bytes())?; - inner.call(SYS_FRENAME, file, address.base(), address.len())?; + inner.call(Opcode::Frename, [file, address.base(), address.len()])?; Ok(()) } fn fsync(&self, file: usize) -> Result<()> { let inner = self.inner.upgrade().ok_or(Error::new(ENODEV))?; - inner.call(SYS_FSYNC, file, 0, 0)?; + inner.call(Opcode::Fsync, [file])?; Ok(()) } fn ftruncate(&self, file: usize, len: usize) -> Result<()> { let inner = self.inner.upgrade().ok_or(Error::new(ENODEV))?; - inner.call(SYS_FTRUNCATE, file, len, 0)?; + inner.call(Opcode::Ftruncate, [file, len])?; Ok(()) } fn close(&self, file: usize) -> Result<()> { let inner = self.inner.upgrade().ok_or(Error::new(ENODEV))?; - inner.call(SYS_CLOSE, file, 0, 0)?; + inner.call(Opcode::Close, [file])?; Ok(()) } fn kdup(&self, file: usize, buf: UserSliceRo, ctx: CallerCtx) -> Result { let inner = self.inner.upgrade().ok_or(Error::new(ENODEV))?; let address = inner.capture_user(buf)?; - let result = inner.call_extended(ctx, None, [SYS_DUP, file, address.base(), address.len()]); + let result = inner.call_extended(ctx, None, Opcode::Dup, [file, address.base(), address.len()]); address.release()?; match result? { - Response::Regular(code) => Error::demux(code).map(OpenResult::SchemeLocal), + 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 kfpath(&self, file: usize, buf: UserSliceWo) -> Result { let inner = self.inner.upgrade().ok_or(Error::new(ENODEV))?; let address = inner.capture_user(buf)?; - let result = inner.call(SYS_FPATH, file, address.base(), address.len()); + let result = inner.call(Opcode::Fpath, [file, address.base(), address.len()]); address.release()?; result } - fn kread(&self, file: usize, buf: UserSliceWo) -> Result { + fn kreadoff(&self, file: usize, buf: UserSliceWo, offset: u64, call_flags: u32, stored_flags: u32) -> Result { + if call_flags != stored_flags { + self.fcntl(file, F_SETFL, call_flags as usize)?; + } + let inner = self.inner.upgrade().ok_or(Error::new(ENODEV))?; let address = inner.capture_user(buf)?; - let result = inner.call(SYS_READ, file, address.base(), address.len()); + let result = inner.call(Opcode::Read, [file as u64, address.base() as u64, address.len() as u64, offset, u64::from(call_flags)]); address.release()?; + + if call_flags != stored_flags { + self.fcntl(file, F_SETFL, stored_flags as usize)?; + } + result } - fn kwrite(&self, file: usize, buf: UserSliceRo) -> Result { + fn kwriteoff(&self, file: usize, buf: UserSliceRo, offset: u64, call_flags: u32, stored_flags: u32) -> Result { + if call_flags != stored_flags { + self.fcntl(file, F_SETFL, call_flags as usize)?; + } + let inner = self.inner.upgrade().ok_or(Error::new(ENODEV))?; let address = inner.capture_user(buf)?; - let result = inner.call(SYS_WRITE, file, address.base(), address.len()); + let result = inner.call(Opcode::Write, [file as u64, address.base() as u64, address.len() as u64, offset, u64::from(call_flags)]); address.release()?; + + if call_flags != stored_flags { + self.fcntl(file, F_SETFL, stored_flags as usize)?; + } + result } + fn legacy_seek(&self, id: usize, pos: isize, whence: usize) -> Option> { + let inner = self.inner.upgrade()?; + if inner.v2 { + return None; + } + Some(inner.call(Opcode::Fsize, [id, pos as usize, whence])) + } fn kfutimens(&self, file: usize, buf: UserSliceRo) -> Result { let inner = self.inner.upgrade().ok_or(Error::new(ENODEV))?; let address = inner.capture_user(buf)?; - let result = inner.call(SYS_FUTIMENS, file, address.base(), address.len()); + let result = inner.call(Opcode::Futimens, [file, address.base(), address.len()]); address.release()?; result } fn kfstat(&self, file: usize, stat: UserSliceWo) -> Result<()> { let inner = self.inner.upgrade().ok_or(Error::new(ENODEV))?; let address = inner.capture_user(stat)?; - let result = inner.call(SYS_FSTAT, file, address.base(), address.len()); + let result = inner.call(Opcode::Fstat, [file, address.base(), address.len()]); address.release()?; result.map(|_| ()) } fn kfstatvfs(&self, file: usize, stat: UserSliceWo) -> Result<()> { let inner = self.inner.upgrade().ok_or(Error::new(ENODEV))?; let address = inner.capture_user(stat)?; - let result = inner.call(SYS_FSTATVFS, file, address.base(), address.len()); + let result = inner.call(Opcode::Fstatvfs, [file, address.base(), address.len()]); address.release()?; result.map(|_| ()) } @@ -1212,23 +1369,15 @@ impl KernelScheme for UserScheme { fn kfunmap(&self, number: usize, offset: usize, size: usize, flags: MunmapFlags) -> Result<()> { let inner = self.inner.upgrade().ok_or(Error::new(ENODEV))?; + let ctx = context::current()?.read().caller_ctx(); let res = inner.call_extended( - CallerCtx { - pid: context::context_id().into(), - uid: offset as u32, - #[cfg(target_pointer_width = "64")] - gid: (offset >> 32) as u32, - - // TODO: saturating_shr? - #[cfg(not(target_pointer_width = "64"))] - gid: 0, - }, + ctx, None, - [KSMSG_MUNMAP, number, size, flags.bits()], + Opcode::Munmap, [number, size, flags.bits(), offset], )?; match res { - Response::Regular(_) => Ok(()), + Response::Regular(_, _) => Ok(()), Response::Fd(_) => Err(Error::new(EIO)), } } @@ -1241,18 +1390,15 @@ impl KernelScheme for UserScheme { ) -> Result { let inner = self.inner.upgrade().ok_or(Error::new(ENODEV))?; + let ctx = context::current()?.read().caller_ctx(); let res = inner.call_extended( - CallerCtx { - pid: context::context_id().into(), - uid: arg as u32, - gid: (arg >> 32) as u32, - }, + ctx, Some(desc), - [SYS_SENDFD, number, flags.bits(), 0], + Opcode::Sendfd, [number, flags.bits()], )?; match res { - Response::Regular(res) => Ok(res), + Response::Regular(res, _) => Ok(res), Response::Fd(_) => Err(Error::new(EIO)), } } @@ -1263,3 +1409,30 @@ pub enum Mode { Ro, Wo, } + +pub trait Args: Copy { + fn args(self) -> [u64; 6]; +} +impl Args for [u64; N] { + fn args(self) -> [u64; 6] { + assert!(self.len() <= N); + core::array::from_fn(|i| self.get(i).copied().unwrap_or(0)) + } +} +impl Args for [usize; N] { + fn args(self) -> [u64; 6] { + self.map(|s| s as u64).args() + } +} + +// TODO: Find a better way to do authentication. No scheme call currently uses arg 5 but this will +// likely change. Ideally this mechanism would also allow the scheme to query the supplementary +// group list. +fn uid_gid_hack_merge([uid, gid]: [u32; 2]) -> u64 { + u64::from(uid) | (u64::from(gid) << 32) +} +fn current_uid_gid() -> Result<[u32; 2]> { + Ok(match context::current()?.read() { + ref ctx => [ctx.euid, ctx.egid], + }) +} diff --git a/src/sync/wait_queue.rs b/src/sync/wait_queue.rs index 8e626cb4b3..c7b91f1191 100644 --- a/src/sync/wait_queue.rs +++ b/src/sync/wait_queue.rs @@ -24,6 +24,23 @@ impl WaitQueue { } } + pub fn receive(&self, block: bool, reason: &'static str) -> Result { + loop { + let mut inner = self.inner.lock(); + + if let Some(t) = inner.pop_front() { + return Ok(t); + } else if block { + if !self.condition.wait(inner, reason) { + return Err(Error::new(EINTR)); + } + continue; + } else { + return Err(Error::new(EAGAIN)); + } + } + } + pub fn receive_into_user( &self, buf: UserSliceWo, diff --git a/src/syscall/fs.rs b/src/syscall/fs.rs index 0f283fbaf4..4fc47196da 100644 --- a/src/syscall/fs.rs +++ b/src/syscall/fs.rs @@ -4,11 +4,7 @@ use redox_path::RedoxPath; use spin::RwLock; use crate::{ - context, - context::{ - file::{FileDescription, FileDescriptor}, - memory::{AddrSpace, PageSpan}, - }, + context::{self, file::{FileDescription, FileDescriptor, InternalFlags}, memory::{AddrSpace, PageSpan}}, paging::{Page, VirtualAddress, PAGE_SIZE}, scheme::{self, CallerCtx, FileHandle, KernelScheme, OpenResult, SchemeId}, syscall::{data::Stat, error::*, flag::*}, @@ -20,28 +16,24 @@ pub fn file_op_generic( fd: FileHandle, op: impl FnOnce(&dyn KernelScheme, usize) -> Result, ) -> Result { - file_op_generic_ext(fd, |s, _, no| op(s, no)) + file_op_generic_ext(fd, |s, _, desc| op(s, desc.number)) } pub fn file_op_generic_ext( fd: FileHandle, - op: impl FnOnce(&dyn KernelScheme, SchemeId, usize) -> Result, + op: impl FnOnce(&dyn KernelScheme, Arc>, FileDescription) -> Result, ) -> Result { let file = context::current()? .read() .get_file(fd) .ok_or(Error::new(EBADF))?; - let FileDescription { - scheme: scheme_id, - number, - .. - } = *file.description.read(); + let desc = *file.description.read(); let scheme = scheme::schemes() - .get(scheme_id) + .get(desc.scheme) .ok_or(Error::new(EBADF))? .clone(); - op(&*scheme, scheme_id, number) + op(&*scheme, file.description, desc) } pub fn copy_path_to_buf(raw_path: UserSliceRo, max_len: usize) -> Result { let mut path_buf = vec![0_u8; max_len]; @@ -90,11 +82,12 @@ pub fn open(raw_path: UserSliceRo, flags: usize) -> Result { }; match scheme.kopen(reference.as_ref(), flags, CallerCtx { uid, gid, pid })? { - OpenResult::SchemeLocal(number) => Arc::new(RwLock::new(FileDescription { - namespace: scheme_ns, + OpenResult::SchemeLocal(number, internal_flags) => Arc::new(RwLock::new(FileDescription { scheme: scheme_id, number, - flags: flags & !O_CLOEXEC, + offset: 0, + flags: (flags & !O_CLOEXEC) as u32, + internal_flags, })), OpenResult::External(desc) => desc, } @@ -192,8 +185,9 @@ fn duplicate_file(fd: FileHandle, user_buf: UserSliceRo) -> Result Arc::new(RwLock::new(FileDescription { - namespace: description.namespace, + OpenResult::SchemeLocal(number, internal_flags) => Arc::new(RwLock::new(FileDescription { + offset: 0, + internal_flags, scheme: description.scheme, number, flags: description.flags, @@ -286,17 +280,26 @@ pub fn sendfd(socket: FileHandle, fd: FileHandle, flags_raw: usize, arg: u64) -> /// File descriptor controls pub fn fcntl(fd: FileHandle, cmd: usize, arg: usize) -> Result { - let file = { - let contexts = context::contexts(); - let context_lock = contexts.current().ok_or(Error::new(ESRCH))?; - let context = context_lock.read(); - context.get_file(fd).ok_or(Error::new(EBADF))? - }; + let file = context::current()?.read().get_file(fd).ok_or(Error::new(EBADF))?; let description = file.description.read(); + if cmd == F_DUPFD { + // Not in match because 'files' cannot be locked + let new_file = duplicate_file(fd, UserSlice::empty())?; + + let contexts = context::contexts(); + let context_lock = contexts.current().ok_or(Error::new(ESRCH))?; + let context = context_lock.read(); + + return context + .add_file_min(new_file, arg) + .ok_or(Error::new(EMFILE)) + .map(FileHandle::into); + } + // Communicate fcntl with scheme - if cmd != F_DUPFD && cmd != F_GETFD && cmd != F_SETFD { + if cmd != F_GETFD && cmd != F_SETFD { let scheme = scheme::schemes() .get(description.scheme) .ok_or(Error::new(EBADF))? @@ -307,19 +310,6 @@ pub fn fcntl(fd: FileHandle, cmd: usize, arg: usize) -> Result { // Perform kernel operation if scheme agrees { - if cmd == F_DUPFD { - // Not in match because 'files' cannot be locked - let new_file = duplicate_file(fd, UserSlice::empty())?; - - let contexts = context::contexts(); - let context_lock = contexts.current().ok_or(Error::new(ESRCH))?; - let context = context_lock.read(); - - return context - .add_file_min(new_file, arg) - .ok_or(Error::new(EMFILE)) - .map(FileHandle::into); - } let contexts = context::contexts(); let context_lock = contexts.current().ok_or(Error::new(ESRCH))?; @@ -339,9 +329,9 @@ pub fn fcntl(fd: FileHandle, cmd: usize, arg: usize) -> Result { file.cloexec = arg & O_CLOEXEC == O_CLOEXEC; Ok(0) } - F_GETFL => Ok(description.flags), + F_GETFL => Ok(description.flags as usize), F_SETFL => { - let new_flags = (description.flags & O_ACCMODE) | (arg & !O_ACCMODE); + let new_flags = (description.flags & O_ACCMODE as u32) | (arg as u32 & !O_ACCMODE as u32); drop(description); file.description.write().flags = new_flags; Ok(0) @@ -393,13 +383,13 @@ pub fn frename(fd: FileHandle, raw_path: UserSliceRo) -> Result<()> { /// File status pub fn fstat(fd: FileHandle, user_buf: UserSliceWo) -> Result<()> { - file_op_generic_ext(fd, |scheme, scheme_id, number| { - scheme.kfstat(number, user_buf)?; + file_op_generic_ext(fd, |scheme, _, desc| { + scheme.kfstat(desc.number, user_buf)?; // TODO: Ensure only the kernel can access the stat when st_dev is set, or use another API // for retrieving the scheme ID from a file descriptor. // TODO: Less hacky method. - let st_dev = scheme_id + let st_dev = desc.scheme .get() .try_into() .map_err(|_| Error::new(EOVERFLOW))?; @@ -491,3 +481,59 @@ pub fn mremap( Ok(base.start_address().data()) } + +pub fn lseek(fd: FileHandle, pos: i64, whence: usize) -> Result { + enum Ret { + Legacy(usize), + Fsize((Option, Arc>)), + } + let fsize_or_legacy = file_op_generic_ext(fd, |scheme, desc_arc, desc| { + Ok(if let Some(new_off) = scheme.legacy_seek(desc.number, pos as isize, whence) { + Ret::Legacy(new_off?) + } else if whence == SEEK_END { + Ret::Fsize((Some(scheme.fsize(desc.number)?), desc_arc)) + } else { + Ret::Fsize((None, desc_arc)) + }) + })?; + let (fsize, desc) = match fsize_or_legacy { + Ret::Fsize(fsize) => fsize, + Ret::Legacy(new_pos) => return Ok(new_pos), + }; + + let mut guard = desc.write(); + + let new_pos = match whence { + SEEK_SET => pos, + SEEK_CUR => pos.checked_add_unsigned(guard.offset).ok_or(Error::new(EOVERFLOW))?, + SEEK_END => pos.checked_add_unsigned(fsize.unwrap()).ok_or(Error::new(EOVERFLOW))?, + _ => return Err(Error::new(EINVAL)), + }; + guard.offset = new_pos.try_into().map_err(|_| Error::new(EINVAL))?; + + Ok(guard.offset as usize) +} +pub fn sys_read(fd: FileHandle, buf: UserSliceWo) -> Result { + let (bytes_read, desc_arc, desc) = file_op_generic_ext(fd, |scheme, desc_arc, desc| { + let offset = if desc.internal_flags.contains(InternalFlags::POSITIONED) { desc.offset } else { u64::MAX }; + Ok((scheme.kreadoff(desc.number, buf, offset, desc.flags, desc.flags)?, desc_arc, desc)) + })?; + if desc.internal_flags.contains(InternalFlags::POSITIONED) { + match desc_arc.write().offset { + ref mut offset => *offset = offset.saturating_add(bytes_read as u64), + } + } + Ok(bytes_read) +} +pub fn sys_write(fd: FileHandle, buf: UserSliceRo) -> Result { + let (bytes_written, desc_arc, desc) = file_op_generic_ext(fd, |scheme, desc_arc, desc| { + let offset = if desc.internal_flags.contains(InternalFlags::POSITIONED) { desc.offset } else { u64::MAX }; + Ok((scheme.kwriteoff(desc.number, buf, offset, desc.flags, desc.flags)?, desc_arc, desc)) + })?; + if desc.internal_flags.contains(InternalFlags::POSITIONED) { + match desc_arc.write().offset { + ref mut offset => *offset = offset.saturating_add(bytes_written as u64), + } + } + Ok(bytes_written) +} diff --git a/src/syscall/mod.rs b/src/syscall/mod.rs index 0bcd551893..32ee529414 100644 --- a/src/syscall/mod.rs +++ b/src/syscall/mod.rs @@ -4,6 +4,8 @@ extern crate syscall; +use syscall::{RwFlags, EINVAL}; + pub use self::syscall::{ data, error, flag, io, number, ptrace_event, EnvRegisters, FloatRegisters, IntRegisters, }; @@ -81,9 +83,11 @@ pub fn syscall( let fd = FileHandle::from(b); match a & SYS_ARG { SYS_ARG_SLICE => match a { - SYS_WRITE => file_op_generic(fd, |scheme, number| { - scheme.kwrite(number, UserSlice::ro(c, d)?) + SYS_WRITE2 => file_op_generic_ext(fd, |scheme, _, desc| { + let flags = u32::try_from(f).ok().and_then(RwFlags::from_bits).ok_or(Error::new(EINVAL))?; + scheme.kwriteoff(desc.number, UserSlice::ro(c, d)?, e as u64, desc.rw_flags(flags), desc.flags) }), + SYS_WRITE => sys_write(fd, UserSlice::ro(c, d)?), SYS_FMAP => { let addrspace = AddrSpace::current()?; let map = unsafe { UserSlice::ro(c, d)?.read_exact::()? }; @@ -103,9 +107,11 @@ pub fn syscall( _ => return Err(Error::new(ENOSYS)), }, SYS_ARG_MSLICE => match a { - SYS_READ => file_op_generic(fd, |scheme, number| { - scheme.kread(number, UserSlice::wo(c, d)?) + SYS_READ2 => file_op_generic_ext(fd, |scheme, _, desc| { + let flags = u32::try_from(f).ok().and_then(RwFlags::from_bits).ok_or(Error::new(EINVAL))?; + scheme.kreadoff(desc.number, UserSlice::wo(c, d)?, e as u64, desc.rw_flags(flags), desc.flags) }), + SYS_READ => sys_read(fd, UserSlice::wo(c, d)?), SYS_FPATH => file_op_generic(fd, |scheme, number| { scheme.kfpath(number, UserSlice::wo(c, d)?) }), @@ -129,9 +135,7 @@ pub fn syscall( #[cfg(target_pointer_width = "64")] SYS_SENDFD => sendfd(fd, FileHandle::from(c), d, e as u64), - SYS_LSEEK => { - file_op_generic(fd, |scheme, number| scheme.seek(number, c as isize, d)) - } + SYS_LSEEK => lseek(fd, c as i64, d), SYS_FCHMOD => file_op_generic(fd, |scheme, number| { scheme.fchmod(number, c as u16).map(|()| 0) }), diff --git a/syscall b/syscall index 3e9d68c6b2..ef099bdd2e 160000 --- a/syscall +++ b/syscall @@ -1 +1 @@ -Subproject commit 3e9d68c6b2fbacfe6f6f1e16e93231aa333bcd8f +Subproject commit ef099bdd2e8578133c33aaf6055db29c383029e2