772 lines
21 KiB
Rust
772 lines
21 KiB
Rust
//! # Schemes
|
|
//! A scheme is a primitive for handling filesystem syscalls in Redox.
|
|
//! Schemes accept paths from the kernel for `open`, and file descriptors that they generate
|
|
//! are then passed for operations like `close`, `read`, `write`, etc.
|
|
//!
|
|
//! The kernel validates paths and file descriptors before they are passed to schemes,
|
|
//! also stripping the scheme identifier of paths if necessary.
|
|
|
|
// TODO: Move handling of the global namespace to userspace.
|
|
|
|
use alloc::{
|
|
sync::{Arc, Weak},
|
|
vec::Vec,
|
|
};
|
|
use core::{
|
|
str,
|
|
sync::atomic::{AtomicUsize, Ordering},
|
|
};
|
|
use hashbrown::hash_map::{DefaultHashBuilder, HashMap};
|
|
use spin::Once;
|
|
use syscall::{
|
|
data::{GlobalSchemes, NewFdParams, StdFsCallMeta},
|
|
error::*,
|
|
CallFlags, EventFlags, MunmapFlags, StdFsCallKind,
|
|
};
|
|
|
|
use crate::{
|
|
context::{
|
|
self,
|
|
file::{FileDescription, InternalFlags, LockedFileDescription},
|
|
memory::AddrSpaceWrapper,
|
|
ContextLock,
|
|
},
|
|
sync::{CleanLockToken, LockToken, RwLock, L0, L1},
|
|
syscall::usercopy::{UserSliceRo, UserSliceRw, UserSliceWo},
|
|
};
|
|
|
|
#[cfg(feature = "acpi")]
|
|
use self::acpi::AcpiScheme;
|
|
#[cfg(dtb)]
|
|
use self::dtb::DtbScheme;
|
|
|
|
use self::{
|
|
debug::DebugScheme,
|
|
event::EventScheme,
|
|
irq::IrqScheme,
|
|
memory::MemoryScheme,
|
|
pipe::PipeScheme,
|
|
proc::ProcScheme,
|
|
serio::SerioScheme,
|
|
sys::SysScheme,
|
|
time::TimeScheme,
|
|
user::{UserInner, UserScheme},
|
|
};
|
|
|
|
/// When compiled with the "acpi" feature - `acpi:` - allows drivers to read a limited set of ACPI tables.
|
|
#[cfg(feature = "acpi")]
|
|
pub mod acpi;
|
|
#[cfg(dtb)]
|
|
pub mod dtb;
|
|
|
|
/// `debug:` - provides access to serial console
|
|
pub mod debug;
|
|
|
|
/// `event:` - allows reading of `Event`s which are registered using `fevent`
|
|
pub mod event;
|
|
|
|
/// `irq:` - allows userspace handling of IRQs
|
|
pub mod irq;
|
|
|
|
/// `memory:` - a scheme for accessing physical memory
|
|
pub mod memory;
|
|
|
|
/// `pipe:` - used internally by the kernel to implement `pipe`
|
|
pub mod pipe;
|
|
|
|
/// `proc:` - allows tracing processes and reading/writing their memory
|
|
pub mod proc;
|
|
|
|
/// `serio:` - provides access to ps/2 devices
|
|
pub mod serio;
|
|
|
|
/// `sys:` - system information, such as the context list and scheme list
|
|
pub mod sys;
|
|
|
|
/// `time:` - allows reading time, setting timeouts and getting events when they are met
|
|
pub mod time;
|
|
|
|
/// A wrapper around userspace schemes, tightly dependent on `root`
|
|
pub mod user;
|
|
|
|
/// Limit on number of schemes
|
|
pub const SCHEME_MAX_SCHEMES: usize = 65_536;
|
|
|
|
// Unique identifier for a scheme.
|
|
int_like!(SchemeId, usize);
|
|
|
|
// Unique identifier for a file descriptor.
|
|
int_like!(FileHandle, AtomicFileHandle, usize, AtomicUsize);
|
|
|
|
#[allow(dead_code)]
|
|
pub enum StrOrBytes<'a> {
|
|
Str(&'a str),
|
|
Bytes(&'a [u8]),
|
|
}
|
|
|
|
#[allow(dead_code)]
|
|
impl<'a> StrOrBytes<'a> {
|
|
pub fn as_str(&self) -> Result<&str, core::str::Utf8Error> {
|
|
match self {
|
|
StrOrBytes::Str(path) => Ok(path),
|
|
StrOrBytes::Bytes(slice) => core::str::from_utf8(slice),
|
|
}
|
|
}
|
|
|
|
pub fn as_bytes(&self) -> &[u8] {
|
|
match self {
|
|
StrOrBytes::Str(path) => path.as_bytes(),
|
|
StrOrBytes::Bytes(slice) => slice,
|
|
}
|
|
}
|
|
|
|
pub fn from_str(path: &'a str) -> Self {
|
|
StrOrBytes::Str(path)
|
|
}
|
|
|
|
pub fn from_bytes(slice: &'a [u8]) -> Self {
|
|
StrOrBytes::Bytes(slice)
|
|
}
|
|
}
|
|
|
|
enum Handle {
|
|
SchemeCreationCapability,
|
|
Scheme(KernelSchemes),
|
|
}
|
|
|
|
/// Schemes list
|
|
static HANDLES: Once<RwLock<L1, HashMap<SchemeId, Handle>>> = Once::new();
|
|
static SCHEME_LIST_NEXT_ID: AtomicUsize = AtomicUsize::new(MAX_GLOBAL_SCHEMES);
|
|
static SCHEME_LIST_ID: AtomicUsize = AtomicUsize::new(0);
|
|
|
|
/// Initialize schemes, called if needed
|
|
fn init_schemes() -> RwLock<L1, HashMap<SchemeId, Handle>> {
|
|
let mut handles = HashMap::new();
|
|
let mut insert_globals = |globals: &[GlobalSchemes]| {
|
|
for &g in globals {
|
|
handles.insert(
|
|
SchemeId::from(g as usize),
|
|
Handle::Scheme(KernelSchemes::Global(g)),
|
|
);
|
|
}
|
|
};
|
|
|
|
// TODO: impl TryFrom<SchemeId> and bypass map for global schemes?
|
|
{
|
|
use GlobalSchemes::*;
|
|
insert_globals(&[Debug, Event, Memory, Pipe, Serio, Irq, Time, Sys, Proc]);
|
|
|
|
#[cfg(feature = "acpi")]
|
|
insert_globals(&[Acpi]);
|
|
|
|
#[cfg(dtb)]
|
|
insert_globals(&[Dtb]);
|
|
}
|
|
let next_id = SCHEME_LIST_NEXT_ID.fetch_add(1, Ordering::Relaxed);
|
|
handles.insert(SchemeId(next_id), Handle::Scheme(KernelSchemes::SchemeMgr));
|
|
SCHEME_LIST_ID.store(next_id, Ordering::Relaxed);
|
|
|
|
RwLock::new(handles)
|
|
}
|
|
|
|
/// Get a handle to a scheme.
|
|
pub fn get_scheme(token: LockToken<'_, L0>, scheme_id: SchemeId) -> Result<KernelSchemes> {
|
|
match handles().read(token).get(&scheme_id) {
|
|
Some(Handle::Scheme(scheme)) => Ok(scheme.clone()),
|
|
_ => Err(Error::new(ENODEV)),
|
|
}
|
|
}
|
|
|
|
fn handles<'a>() -> &'a RwLock<L1, HashMap<SchemeId, Handle>> {
|
|
HANDLES.call_once(init_schemes)
|
|
}
|
|
|
|
/// Scheme list type
|
|
pub struct SchemeList;
|
|
|
|
impl SchemeList {
|
|
/// Get the id of the scheme list
|
|
pub fn id(&self) -> SchemeId {
|
|
SchemeId(SCHEME_LIST_ID.load(Ordering::Relaxed))
|
|
}
|
|
|
|
/// Get the UserInner
|
|
fn get_user_inner(&self, id: usize, token: &mut CleanLockToken) -> Option<Arc<UserInner>> {
|
|
match handles().read(token.token()).get(&SchemeId(id)) {
|
|
Some(Handle::Scheme(KernelSchemes::User(UserScheme { inner }))) => Some(inner.clone()),
|
|
_ => None,
|
|
}
|
|
}
|
|
|
|
/// Create a new scheme.
|
|
fn insert(&self, context: Weak<ContextLock>, token: &mut CleanLockToken) -> Result<SchemeId> {
|
|
let mut handles = handles().write(token.token());
|
|
let id = loop {
|
|
let mut id = SCHEME_LIST_NEXT_ID.fetch_add(1, Ordering::Relaxed);
|
|
|
|
if id >= SCHEME_MAX_SCHEMES {
|
|
id = 1;
|
|
SCHEME_LIST_NEXT_ID.store(id, Ordering::Relaxed);
|
|
}
|
|
|
|
let id = SchemeId(id);
|
|
|
|
if !handles.contains_key(&id) {
|
|
break id;
|
|
}
|
|
};
|
|
|
|
let root_id = SchemeId(SCHEME_LIST_ID.load(Ordering::Relaxed));
|
|
let inner = Arc::new(UserInner::new(root_id, id, context));
|
|
let new_scheme = Handle::Scheme(KernelSchemes::User(UserScheme::new(inner)));
|
|
assert!(handles.insert(id, new_scheme).is_none());
|
|
Ok(id)
|
|
}
|
|
|
|
/// Remove a scheme
|
|
fn remove(&self, id: usize, token: &mut CleanLockToken) {
|
|
let scheme = handles().write(token.token()).remove(&SchemeId(id));
|
|
|
|
assert!(scheme.is_some());
|
|
match scheme {
|
|
Some(Handle::Scheme(KernelSchemes::User(user))) => {
|
|
if let Some(user) = Arc::into_inner(user.inner) {
|
|
user.into_drop(token);
|
|
}
|
|
}
|
|
_ => {}
|
|
}
|
|
}
|
|
}
|
|
|
|
impl KernelScheme for SchemeList {
|
|
fn scheme_root(&self, token: &mut CleanLockToken) -> Result<usize> {
|
|
let id = SchemeId(0);
|
|
handles()
|
|
.write(token.token())
|
|
.insert(id, Handle::SchemeCreationCapability);
|
|
Ok(id.get())
|
|
}
|
|
fn kdup(
|
|
&self,
|
|
scheme_id: usize,
|
|
user_buf: UserSliceRo,
|
|
caller: CallerCtx,
|
|
token: &mut CleanLockToken,
|
|
) -> Result<OpenResult> {
|
|
let scheme_id = SchemeId(scheme_id);
|
|
match handles()
|
|
.read(token.token())
|
|
.get(&scheme_id)
|
|
.ok_or(Error::new(EBADF))?
|
|
{
|
|
Handle::Scheme(KernelSchemes::User(UserScheme { inner })) => {
|
|
let inner = inner.clone();
|
|
assert!(scheme_id == inner.scheme_id);
|
|
let scheme = scheme_id;
|
|
let params = unsafe { user_buf.read_exact::<NewFdParams>()? };
|
|
|
|
return Ok(OpenResult::External(Arc::new(RwLock::new(
|
|
FileDescription {
|
|
scheme,
|
|
number: params.number,
|
|
offset: params.offset,
|
|
flags: params.flags as u32,
|
|
internal_flags: InternalFlags::from_extra0(params.internal_flags)
|
|
.ok_or(Error::new(EINVAL))?,
|
|
},
|
|
))));
|
|
}
|
|
Handle::SchemeCreationCapability => (),
|
|
_ => return Err(Error::new(EBADF)),
|
|
};
|
|
|
|
const EXPECTED: &[u8] = b"create-scheme";
|
|
let mut buf = [0u8; EXPECTED.len()];
|
|
|
|
if user_buf.copy_common_bytes_to_slice(&mut buf)? < EXPECTED.len() || buf != *EXPECTED {
|
|
return Err(Error::new(EINVAL));
|
|
}
|
|
|
|
if caller.uid != 0 {
|
|
return Err(Error::new(EACCES));
|
|
};
|
|
|
|
let context = Arc::downgrade(&context::current());
|
|
|
|
let scheme_id = self.insert(context, token)?;
|
|
Ok(OpenResult::SchemeLocal(
|
|
scheme_id.get(),
|
|
InternalFlags::empty(),
|
|
))
|
|
}
|
|
|
|
fn kfpath(&self, _id: usize, buf: UserSliceWo, _token: &mut CleanLockToken) -> Result<usize> {
|
|
buf.copy_common_bytes_from_slice("/scheme".as_bytes())
|
|
}
|
|
|
|
fn fevent(
|
|
&self,
|
|
id: usize,
|
|
flags: EventFlags,
|
|
token: &mut CleanLockToken,
|
|
) -> Result<EventFlags> {
|
|
match self.get_user_inner(id, token) {
|
|
Some(inner) => inner.fevent(flags, token),
|
|
_ => Err(Error::new(EBADF)),
|
|
}
|
|
}
|
|
|
|
fn fsync(&self, id: usize, token: &mut CleanLockToken) -> Result<()> {
|
|
match self.get_user_inner(id, token) {
|
|
Some(inner) => inner.fsync(),
|
|
None => Err(Error::new(EBADF)),
|
|
}
|
|
}
|
|
|
|
fn close(&self, id: usize, token: &mut CleanLockToken) -> Result<()> {
|
|
self.remove(id, token);
|
|
Ok(())
|
|
}
|
|
|
|
fn kreadoff(
|
|
&self,
|
|
id: usize,
|
|
buf: UserSliceWo,
|
|
_offset: u64,
|
|
flags: u32,
|
|
_stored_flags: u32,
|
|
token: &mut CleanLockToken,
|
|
) -> Result<usize> {
|
|
match self.get_user_inner(id, token) {
|
|
Some(inner) => inner.read(buf, flags, token),
|
|
None => Err(Error::new(EBADF)),
|
|
}
|
|
}
|
|
|
|
fn kwrite(
|
|
&self,
|
|
id: usize,
|
|
buf: UserSliceRo,
|
|
_flags: u32,
|
|
_stored_flags: u32,
|
|
token: &mut CleanLockToken,
|
|
) -> Result<usize> {
|
|
match self.get_user_inner(id, token) {
|
|
Some(inner) => inner.write(buf, token),
|
|
None => Err(Error::new(EBADF)),
|
|
}
|
|
}
|
|
|
|
fn kfdwrite(
|
|
&self,
|
|
id: usize,
|
|
descs: Vec<Arc<LockedFileDescription>>,
|
|
flags: CallFlags,
|
|
arg: u64,
|
|
metadata: &[u64],
|
|
token: &mut CleanLockToken,
|
|
) -> Result<usize> {
|
|
match self.get_user_inner(id, token) {
|
|
Some(inner) => inner.call_fdwrite(descs, flags, arg, metadata, token),
|
|
None => Err(Error::new(EBADF)),
|
|
}
|
|
}
|
|
|
|
fn kfdread(
|
|
&self,
|
|
id: usize,
|
|
payload: UserSliceRw,
|
|
flags: CallFlags,
|
|
metadata: &[u64],
|
|
token: &mut CleanLockToken,
|
|
) -> Result<usize> {
|
|
match self.get_user_inner(id, token) {
|
|
Some(inner) => inner.call_fdread(payload, flags, metadata, token),
|
|
None => Err(Error::new(EBADF)),
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Clone)]
|
|
pub enum KernelSchemes {
|
|
SchemeMgr,
|
|
User(UserScheme),
|
|
Global(GlobalSchemes),
|
|
}
|
|
|
|
impl core::ops::Deref for KernelSchemes {
|
|
type Target = dyn KernelScheme;
|
|
|
|
fn deref(&self) -> &Self::Target {
|
|
match self {
|
|
Self::SchemeMgr => &SchemeList,
|
|
Self::User(scheme) => scheme,
|
|
|
|
Self::Global(global) => global.as_scheme(),
|
|
}
|
|
}
|
|
}
|
|
|
|
pub const ALL_KERNEL_SCHEMES: &[GlobalSchemes] = &[
|
|
GlobalSchemes::Debug,
|
|
GlobalSchemes::Event,
|
|
GlobalSchemes::Memory,
|
|
GlobalSchemes::Pipe,
|
|
GlobalSchemes::Serio,
|
|
GlobalSchemes::Irq,
|
|
GlobalSchemes::Time,
|
|
GlobalSchemes::Sys,
|
|
GlobalSchemes::Proc,
|
|
#[cfg(feature = "acpi")]
|
|
GlobalSchemes::Acpi,
|
|
#[cfg(dtb)]
|
|
GlobalSchemes::Dtb,
|
|
];
|
|
|
|
pub const MAX_GLOBAL_SCHEMES: usize = 16;
|
|
pub const KERNEL_SCHEMES_COUNT: usize = ALL_KERNEL_SCHEMES.len();
|
|
const _: () = {
|
|
assert!(1 + KERNEL_SCHEMES_COUNT < MAX_GLOBAL_SCHEMES);
|
|
};
|
|
|
|
pub trait SchemeExt {
|
|
fn as_scheme(&self) -> &dyn KernelScheme;
|
|
fn scheme_id(self) -> SchemeId;
|
|
}
|
|
impl SchemeExt for GlobalSchemes {
|
|
fn as_scheme(&self) -> &dyn KernelScheme {
|
|
match self {
|
|
Self::Debug => &DebugScheme,
|
|
Self::Event => &EventScheme,
|
|
Self::Memory => &MemoryScheme,
|
|
Self::Pipe => &PipeScheme,
|
|
Self::Serio => &SerioScheme,
|
|
Self::Irq => &IrqScheme,
|
|
Self::Time => &TimeScheme,
|
|
Self::Sys => &SysScheme,
|
|
Self::Proc => &ProcScheme,
|
|
#[cfg(feature = "acpi")]
|
|
Self::Acpi => &AcpiScheme,
|
|
#[cfg(dtb)]
|
|
Self::Dtb => &DtbScheme,
|
|
#[cfg(not(all(feature = "acpi", dtb)))]
|
|
_ => panic!("Unknown global scheme"),
|
|
}
|
|
}
|
|
fn scheme_id(self) -> SchemeId {
|
|
SchemeId::new(self as usize)
|
|
}
|
|
}
|
|
|
|
#[cold]
|
|
pub fn init_globals() {
|
|
#[cfg(feature = "acpi")]
|
|
{
|
|
AcpiScheme::init();
|
|
}
|
|
#[cfg(dtb)]
|
|
{
|
|
DtbScheme::init();
|
|
}
|
|
IrqScheme::init();
|
|
}
|
|
|
|
#[allow(unused_variables)]
|
|
pub trait KernelScheme: Send + Sync + 'static {
|
|
fn scheme_root(&self, token: &mut CleanLockToken) -> Result<usize> {
|
|
Err(Error::new(EOPNOTSUPP))
|
|
}
|
|
|
|
fn kopenat(
|
|
&self,
|
|
file: usize,
|
|
path: StrOrBytes,
|
|
flags: usize,
|
|
fcntl_flags: u32,
|
|
_ctx: CallerCtx,
|
|
token: &mut CleanLockToken,
|
|
) -> Result<OpenResult> {
|
|
Err(Error::new(EOPNOTSUPP))
|
|
}
|
|
|
|
fn kfmap(
|
|
&self,
|
|
number: usize,
|
|
addr_space: &Arc<AddrSpaceWrapper>,
|
|
map: &crate::syscall::data::Map,
|
|
consume: bool,
|
|
token: &mut CleanLockToken,
|
|
) -> Result<usize> {
|
|
Err(Error::new(EOPNOTSUPP))
|
|
}
|
|
fn kfunmap(
|
|
&self,
|
|
number: usize,
|
|
offset: usize,
|
|
size: usize,
|
|
flags: MunmapFlags,
|
|
token: &mut CleanLockToken,
|
|
) -> Result<()> {
|
|
Err(Error::new(EOPNOTSUPP))
|
|
}
|
|
|
|
fn kdup(
|
|
&self,
|
|
old_id: usize,
|
|
buf: UserSliceRo,
|
|
_caller: CallerCtx,
|
|
token: &mut CleanLockToken,
|
|
) -> Result<OpenResult> {
|
|
Err(Error::new(EOPNOTSUPP))
|
|
}
|
|
fn kwriteoff(
|
|
&self,
|
|
id: usize,
|
|
buf: UserSliceRo,
|
|
offset: u64,
|
|
flags: u32,
|
|
stored_flags: u32,
|
|
token: &mut CleanLockToken,
|
|
) -> Result<usize> {
|
|
if offset != u64::MAX {
|
|
return Err(Error::new(ESPIPE));
|
|
}
|
|
self.kwrite(id, buf, flags, stored_flags, token)
|
|
}
|
|
fn kreadoff(
|
|
&self,
|
|
id: usize,
|
|
buf: UserSliceWo,
|
|
offset: u64,
|
|
flags: u32,
|
|
stored_flags: u32,
|
|
token: &mut CleanLockToken,
|
|
) -> Result<usize> {
|
|
if offset != u64::MAX {
|
|
return Err(Error::new(ESPIPE));
|
|
}
|
|
self.kread(id, buf, flags, stored_flags, token)
|
|
}
|
|
fn kwrite(
|
|
&self,
|
|
id: usize,
|
|
buf: UserSliceRo,
|
|
flags: u32,
|
|
stored_flags: u32,
|
|
token: &mut CleanLockToken,
|
|
) -> Result<usize> {
|
|
Err(Error::new(EBADF))
|
|
}
|
|
fn kread(
|
|
&self,
|
|
id: usize,
|
|
buf: UserSliceWo,
|
|
flags: u32,
|
|
stored_flags: u32,
|
|
token: &mut CleanLockToken,
|
|
) -> Result<usize> {
|
|
Err(Error::new(EBADF))
|
|
}
|
|
fn kfpath(&self, id: usize, buf: UserSliceWo, token: &mut CleanLockToken) -> Result<usize>;
|
|
fn kfutimens(&self, id: usize, buf: UserSliceRo, token: &mut CleanLockToken) -> Result<usize> {
|
|
Err(Error::new(EBADF))
|
|
}
|
|
fn kfstat(&self, id: usize, buf: UserSliceWo, token: &mut CleanLockToken) -> Result<()> {
|
|
Err(Error::new(EBADF))
|
|
}
|
|
fn kfstatvfs(&self, id: usize, buf: UserSliceWo, token: &mut CleanLockToken) -> Result<()> {
|
|
Err(Error::new(EBADF))
|
|
}
|
|
|
|
fn getdents(
|
|
&self,
|
|
id: usize,
|
|
buf: UserSliceWo,
|
|
header_size: u16,
|
|
opaque_id_first: u64,
|
|
token: &mut CleanLockToken,
|
|
) -> Result<usize> {
|
|
Err(Error::new(EOPNOTSUPP))
|
|
}
|
|
|
|
fn fsync(&self, id: usize, token: &mut CleanLockToken) -> Result<()> {
|
|
Ok(())
|
|
}
|
|
fn ftruncate(&self, id: usize, len: usize, token: &mut CleanLockToken) -> Result<()> {
|
|
Err(Error::new(EBADF))
|
|
}
|
|
fn fsize(&self, id: usize, token: &mut CleanLockToken) -> Result<u64> {
|
|
Err(Error::new(ESPIPE))
|
|
}
|
|
fn fchmod(&self, id: usize, new_mode: u16, token: &mut CleanLockToken) -> Result<()> {
|
|
Err(Error::new(EBADF))
|
|
}
|
|
fn fchown(
|
|
&self,
|
|
id: usize,
|
|
new_uid: u32,
|
|
new_gid: u32,
|
|
token: &mut CleanLockToken,
|
|
) -> Result<()> {
|
|
Err(Error::new(EBADF))
|
|
}
|
|
fn fevent(
|
|
&self,
|
|
id: usize,
|
|
flags: EventFlags,
|
|
token: &mut CleanLockToken,
|
|
) -> Result<EventFlags> {
|
|
Ok(EventFlags::empty())
|
|
}
|
|
fn flink(
|
|
&self,
|
|
id: usize,
|
|
new_path: &str,
|
|
caller_ctx: CallerCtx,
|
|
token: &mut CleanLockToken,
|
|
) -> Result<()> {
|
|
Err(Error::new(EBADF))
|
|
}
|
|
fn frename(
|
|
&self,
|
|
id: usize,
|
|
new_path: &str,
|
|
caller_ctx: CallerCtx,
|
|
token: &mut CleanLockToken,
|
|
) -> Result<()> {
|
|
Err(Error::new(EBADF))
|
|
}
|
|
fn fcntl(
|
|
&self,
|
|
id: usize,
|
|
cmd: usize,
|
|
arg: usize,
|
|
token: &mut CleanLockToken,
|
|
) -> Result<usize> {
|
|
Ok(0)
|
|
}
|
|
fn unlinkat(
|
|
&self,
|
|
file: usize,
|
|
path: &str,
|
|
flags: usize,
|
|
ctx: CallerCtx,
|
|
token: &mut CleanLockToken,
|
|
) -> Result<()> {
|
|
Err(Error::new(ENOENT))
|
|
}
|
|
fn close(&self, id: usize, token: &mut CleanLockToken) -> Result<()> {
|
|
Ok(())
|
|
}
|
|
fn detach(&self, id: usize, token: &mut CleanLockToken) -> Result<()> {
|
|
Ok(())
|
|
}
|
|
fn kcall(
|
|
&self,
|
|
id: usize,
|
|
payload: UserSliceRw,
|
|
flags: CallFlags,
|
|
metadata: &[u64],
|
|
token: &mut CleanLockToken,
|
|
) -> Result<usize> {
|
|
Err(Error::new(EOPNOTSUPP))
|
|
}
|
|
fn kstdfscall(
|
|
&self,
|
|
id: usize,
|
|
kind: StdFsCallKind,
|
|
desc: Arc<LockedFileDescription>,
|
|
payload: UserSliceRw,
|
|
flags: CallFlags,
|
|
metadata: StdFsCallMeta,
|
|
token: &mut CleanLockToken,
|
|
) -> Result<usize> {
|
|
Err(Error::new(EOPNOTSUPP))
|
|
}
|
|
fn kfdwrite(
|
|
&self,
|
|
id: usize,
|
|
descs: Vec<Arc<LockedFileDescription>>,
|
|
flags: CallFlags,
|
|
args: u64,
|
|
metadata: &[u64],
|
|
token: &mut CleanLockToken,
|
|
) -> Result<usize> {
|
|
Err(Error::new(EOPNOTSUPP))
|
|
}
|
|
fn kfdread(
|
|
&self,
|
|
id: usize,
|
|
payload: UserSliceRw,
|
|
flags: CallFlags,
|
|
metadata: &[u64],
|
|
token: &mut CleanLockToken,
|
|
) -> Result<usize> {
|
|
Err(Error::new(EOPNOTSUPP))
|
|
}
|
|
|
|
fn translate_std_fs_call(
|
|
&self,
|
|
id: usize,
|
|
desc: Arc<LockedFileDescription>,
|
|
payload: UserSliceRw,
|
|
flags: CallFlags,
|
|
metadata: &[u64],
|
|
token: &mut CleanLockToken,
|
|
) -> Result<usize> {
|
|
let &[kind, arg1, arg2, ..] = metadata else {
|
|
return Err(Error::new(EINVAL));
|
|
};
|
|
let Some(kind) = StdFsCallKind::try_from_raw(kind as u8) else {
|
|
return Err(Error::new(EOPNOTSUPP));
|
|
};
|
|
let metadata = StdFsCallMeta::new(kind, arg1, arg2);
|
|
use syscall::flag::StdFsCallKind::*;
|
|
match kind {
|
|
Fchmod => self.fchmod(id, metadata.arg1 as u16, token).map(|_| 0),
|
|
Getdents => self.getdents(
|
|
id,
|
|
payload.into_wo()?,
|
|
metadata.arg2 as u16,
|
|
metadata.arg1,
|
|
token,
|
|
),
|
|
Fstat => self.kfstat(id, payload.into_wo()?, token).map(|_| 0),
|
|
Fstatvfs => self.kfstatvfs(id, payload.into_wo()?, token).map(|_| 0),
|
|
Fsync => self.fsync(id, token).map(|_| 0),
|
|
Ftruncate => self.ftruncate(id, metadata.arg1 as usize, token).map(|_| 0),
|
|
Futimens => self.kfutimens(id, payload.into_ro()?, token),
|
|
/* TODO: Support Fchown and Unlinkat using std_fs_call
|
|
Fchown => self.kstdfscall(id, kind, desc, payload, flags, metadata, token),
|
|
Unlinkat => self.kstdfscall(fd, kind, payload, metadata, &caller).map(|_| 0)
|
|
*/
|
|
_ => Err(Error::new(EOPNOTSUPP)),
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
pub enum OpenResult {
|
|
SchemeLocal(usize, InternalFlags),
|
|
External(Arc<LockedFileDescription>),
|
|
}
|
|
pub struct CallerCtx {
|
|
pub pid: usize,
|
|
pub uid: u32,
|
|
pub gid: u32,
|
|
}
|
|
impl CallerCtx {
|
|
pub fn filter_uid_gid(self, euid: u32, egid: u32) -> Self {
|
|
if self.uid == 0 && self.gid == 0 {
|
|
Self {
|
|
pid: self.pid,
|
|
uid: euid,
|
|
gid: egid,
|
|
}
|
|
} else {
|
|
self
|
|
}
|
|
}
|
|
}
|