mirror of
https://github.com/obhq/obliteration.git
synced 2024-06-12 09:37:15 -04:00
Initializes stat, read and write syscalls (#589)
Co-authored-by: Marcin Mikołajczyk <marcinmikolajcz@gmail.com> Co-authored-by: Putta Khunchalee <ultimaweapon@outlook.com>
This commit is contained in:
parent
b441e29feb
commit
6324c4936a
|
@ -1,5 +1,5 @@
|
|||
use crate::errno::{Errno, ENOTTY};
|
||||
use crate::fs::{FileBackend, IoCmd, VFile};
|
||||
use crate::fs::{FileBackend, IoCmd, Stat, VFile};
|
||||
use crate::process::VThread;
|
||||
use macros::Errno;
|
||||
use std::sync::Arc;
|
||||
|
@ -23,6 +23,15 @@ impl FileBackend for BlockPool {
|
|||
_ => Err(IoctlError::InvalidCommand(cmd).into()),
|
||||
}
|
||||
}
|
||||
|
||||
fn stat(self: &Arc<Self>, _: &VFile, _: Option<&VThread>) -> Result<Stat, Box<dyn Errno>> {
|
||||
let mut stat = Stat::zeroed();
|
||||
|
||||
stat.block_size = 0x10000;
|
||||
stat.mode = 0o130000;
|
||||
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Error, Errno)]
|
||||
|
|
|
@ -2,11 +2,11 @@ use super::dirent::Dirent;
|
|||
use crate::errno::Errno;
|
||||
use crate::fs::{Mode, OpenFlags, VFile};
|
||||
use crate::process::VThread;
|
||||
use crate::time::TimeSpec;
|
||||
use crate::ucred::{Gid, Ucred, Uid};
|
||||
use bitflags::bitflags;
|
||||
use gmtx::{Gutex, GutexGroup, GutexReadGuard, GutexWriteGuard};
|
||||
use std::sync::{Arc, Weak};
|
||||
use std::time::SystemTime;
|
||||
|
||||
/// An implementation of `cdev` and `cdev_priv` structures.
|
||||
#[derive(Debug)]
|
||||
|
@ -17,9 +17,9 @@ pub struct Cdev {
|
|||
uid: Uid, // si_uid
|
||||
gid: Gid, // si_gid
|
||||
mode: Mode, // si_mode
|
||||
ctime: SystemTime, // si_ctime
|
||||
atime: SystemTime, // si_atime
|
||||
mtime: SystemTime, // si_mtime
|
||||
ctime: TimeSpec, // si_ctime
|
||||
atime: TimeSpec, // si_atime
|
||||
mtime: TimeSpec, // si_mtime
|
||||
cred: Option<Arc<Ucred>>, // si_cred
|
||||
max_io: usize, // si_iosize_max
|
||||
flags: DeviceFlags, // si_flags
|
||||
|
@ -41,7 +41,7 @@ impl Cdev {
|
|||
inode: i32,
|
||||
) -> Self {
|
||||
let gg = GutexGroup::new();
|
||||
let now = SystemTime::now();
|
||||
let now = TimeSpec::now();
|
||||
|
||||
Self {
|
||||
sw: sw.clone(),
|
||||
|
|
|
@ -62,7 +62,7 @@ impl crate::fs::VnodeBackend for VnodeBackend {
|
|||
};
|
||||
|
||||
// Check access.
|
||||
let err = match check_access(cred, fuid, fgid, fmode.into(), mode, is_dir) {
|
||||
let err = match check_access(cred, fuid, fgid, fmode, mode, is_dir) {
|
||||
Ok(_) => return Ok(()),
|
||||
Err(e) => e,
|
||||
};
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use super::{IoCmd, Vnode};
|
||||
use super::{IoCmd, Offset, Stat, Uio, UioMut, Vnode};
|
||||
use crate::dmem::BlockPool;
|
||||
use crate::errno::Errno;
|
||||
use crate::errno::{ENOTTY, ENXIO};
|
||||
|
@ -34,22 +34,64 @@ impl VFile {
|
|||
&mut self.flags
|
||||
}
|
||||
|
||||
pub fn read(&self, data: &mut [u8], td: Option<&VThread>) -> Result<usize, Box<dyn Errno>> {
|
||||
/// See `dofileread` on the PS4 for a reference.
|
||||
pub fn do_read(
|
||||
&self,
|
||||
mut uio: UioMut,
|
||||
off: Offset,
|
||||
td: Option<&VThread>,
|
||||
) -> Result<usize, Box<dyn Errno>> {
|
||||
if uio.bytes_left == 0 {
|
||||
return Ok(0);
|
||||
}
|
||||
|
||||
// TODO: consider implementing ktrace.
|
||||
|
||||
let res = self.read(&mut uio, td);
|
||||
|
||||
if let Err(ref e) = res {
|
||||
todo!()
|
||||
}
|
||||
|
||||
res
|
||||
}
|
||||
|
||||
/// See `dofilewrite` on the PS4 for a reference.
|
||||
pub fn do_write(
|
||||
&self,
|
||||
mut uio: Uio,
|
||||
off: Offset,
|
||||
td: Option<&VThread>,
|
||||
) -> Result<usize, Box<dyn Errno>> {
|
||||
// TODO: consider implementing ktrace.
|
||||
// TODO: implement bwillwrite.
|
||||
|
||||
let res = self.write(&mut uio, td);
|
||||
|
||||
if let Err(ref e) = res {
|
||||
todo!()
|
||||
}
|
||||
|
||||
res
|
||||
}
|
||||
|
||||
fn read(&self, buf: &mut UioMut, td: Option<&VThread>) -> Result<usize, Box<dyn Errno>> {
|
||||
match self.backend {
|
||||
VFileType::Vnode(ref vn) => vn.read(self, data, td),
|
||||
VFileType::KernelQueue(ref kq) => kq.read(self, data, td),
|
||||
VFileType::Blockpool(ref bp) => bp.read(self, data, td),
|
||||
VFileType::Vnode(ref vn) => vn.read(self, buf, td),
|
||||
VFileType::KernelQueue(ref kq) => kq.read(self, buf, td),
|
||||
VFileType::Blockpool(ref bp) => bp.read(self, buf, td),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn write(&self, data: &[u8], td: Option<&VThread>) -> Result<usize, Box<dyn Errno>> {
|
||||
fn write(&self, buf: &mut Uio, td: Option<&VThread>) -> Result<usize, Box<dyn Errno>> {
|
||||
match self.backend {
|
||||
VFileType::Vnode(ref vn) => vn.write(self, data, td),
|
||||
VFileType::KernelQueue(ref kq) => kq.write(self, data, td),
|
||||
VFileType::Blockpool(ref bp) => bp.write(self, data, td),
|
||||
VFileType::Vnode(ref vn) => vn.write(self, buf, td),
|
||||
VFileType::KernelQueue(ref kq) => kq.write(self, buf, td),
|
||||
VFileType::Blockpool(ref bp) => bp.write(self, buf, td),
|
||||
}
|
||||
}
|
||||
|
||||
/// See `fo_ioctl` on the PS4 for a reference.
|
||||
pub fn ioctl(
|
||||
&self,
|
||||
cmd: IoCmd,
|
||||
|
@ -62,6 +104,22 @@ impl VFile {
|
|||
VFileType::Blockpool(ref bp) => bp.ioctl(self, cmd, data, td),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn stat(&self, td: Option<&VThread>) -> Result<Stat, Box<dyn Errno>> {
|
||||
match self.backend {
|
||||
VFileType::Vnode(ref vn) => vn.stat(self, td),
|
||||
VFileType::KernelQueue(ref kq) => kq.stat(self, td),
|
||||
VFileType::Blockpool(ref bp) => bp.stat(self, td),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn op_flags(&self) -> VFileOpsFlags {
|
||||
match self.backend {
|
||||
VFileType::Vnode(ref vn) => vn.flags(),
|
||||
VFileType::KernelQueue(ref kq) => kq.flags(),
|
||||
VFileType::Blockpool(ref bp) => bp.flags(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Seek for VFile {
|
||||
|
@ -71,7 +129,7 @@ impl Seek for VFile {
|
|||
}
|
||||
|
||||
impl Read for VFile {
|
||||
fn read(&mut self, _buf: &mut [u8]) -> std::io::Result<usize> {
|
||||
fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
@ -88,6 +146,7 @@ impl Write for VFile {
|
|||
|
||||
/// Type of [`VFile`].
|
||||
#[derive(Debug)]
|
||||
#[rustfmt::skip]
|
||||
pub enum VFileType {
|
||||
Vnode(Arc<Vnode>), // DTYPE_VNODE = 1
|
||||
KernelQueue(Arc<KernelQueue>), // DTYPE_KQUEUE = 5,
|
||||
|
@ -98,8 +157,16 @@ bitflags! {
|
|||
/// Flags for [`VFile`].
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct VFileFlags: u32 {
|
||||
const FREAD = 0x00000001;
|
||||
const FWRITE = 0x00000002;
|
||||
const READ = 0x00000001; // FREAD
|
||||
const WRITE = 0x00000002; // FWRITE
|
||||
}
|
||||
}
|
||||
|
||||
bitflags! {
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct VFileOpsFlags: u32 {
|
||||
const PASSABLE = 0x00000001; // DFLAG_PASSABLE
|
||||
const SEEKABLE = 0x00000002; // DFLAG_SEEKABLE
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -109,7 +176,7 @@ pub trait FileBackend: Debug + Send + Sync + 'static {
|
|||
fn read(
|
||||
self: &Arc<Self>,
|
||||
file: &VFile,
|
||||
buf: &mut [u8],
|
||||
buf: &mut UioMut,
|
||||
td: Option<&VThread>,
|
||||
) -> Result<usize, Box<dyn Errno>> {
|
||||
Err(Box::new(DefaultError::ReadNotSupported))
|
||||
|
@ -119,7 +186,7 @@ pub trait FileBackend: Debug + Send + Sync + 'static {
|
|||
fn write(
|
||||
self: &Arc<Self>,
|
||||
file: &VFile,
|
||||
buf: &[u8],
|
||||
buf: &mut Uio,
|
||||
td: Option<&VThread>,
|
||||
) -> Result<usize, Box<dyn Errno>> {
|
||||
Err(Box::new(DefaultError::WriteNotSupported))
|
||||
|
@ -135,6 +202,13 @@ pub trait FileBackend: Debug + Send + Sync + 'static {
|
|||
) -> Result<(), Box<dyn Errno>> {
|
||||
Err(Box::new(DefaultError::IoctlNotSupported))
|
||||
}
|
||||
|
||||
#[allow(unused_variables)]
|
||||
fn stat(self: &Arc<Self>, file: &VFile, td: Option<&VThread>) -> Result<Stat, Box<dyn Errno>>;
|
||||
|
||||
fn flags(&self) -> VFileOpsFlags {
|
||||
VFileOpsFlags::empty()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Error, Errno)]
|
||||
|
|
|
@ -166,15 +166,14 @@ enum MountSource {
|
|||
Bind(VPathBuf),
|
||||
}
|
||||
|
||||
/// Represents an error when [`mount()`] was failed.
|
||||
/// Represents an error when [`mount()`] fails.
|
||||
#[derive(Debug, Error, Errno)]
|
||||
enum MountError {
|
||||
#[error("cannot create {0}")]
|
||||
#[errno(EIO)]
|
||||
CreateDirectoryFailed(PathBuf, #[source] std::io::Error),
|
||||
}
|
||||
|
||||
/// Represents an error when [`get_vnode()`] was failed.
|
||||
/// Represents an error when [`get_vnode()`] fails.
|
||||
#[derive(Debug, Error)]
|
||||
enum GetVnodeError {
|
||||
#[error("cannot open the specified file")]
|
||||
|
|
|
@ -121,7 +121,7 @@ impl crate::fs::VnodeBackend for VnodeBackend {
|
|||
}
|
||||
}
|
||||
|
||||
/// Represents an error when [`getattr()`] was failed.
|
||||
/// Represents an error when [`getattr()`] fails.
|
||||
#[derive(Debug, Error, Errno)]
|
||||
enum GetAttrError {
|
||||
#[error("cannot get file size")]
|
||||
|
@ -129,7 +129,7 @@ enum GetAttrError {
|
|||
GetSizeFailed(#[source] std::io::Error),
|
||||
}
|
||||
|
||||
/// Represents an error when [`lookup()`] was failed.
|
||||
/// Represents an error when [`lookup()`] fails.
|
||||
#[derive(Debug, Error)]
|
||||
enum LookupError {
|
||||
#[error("current file is not a directory")]
|
||||
|
|
|
@ -1,3 +1,20 @@
|
|||
use crate::errno::{Errno, EBADF, EBUSY, EINVAL, ENAMETOOLONG, ENODEV, ENOENT, ESPIPE};
|
||||
use crate::info;
|
||||
use crate::process::{GetFileError, VThread};
|
||||
use crate::syscalls::{SysArg, SysErr, SysIn, SysOut, Syscalls};
|
||||
use crate::ucred::PrivilegeError;
|
||||
use crate::ucred::{Privilege, Ucred};
|
||||
use bitflags::bitflags;
|
||||
use gmtx::{Gutex, GutexGroup};
|
||||
use macros::vpath;
|
||||
use macros::Errno;
|
||||
use param::Param;
|
||||
use std::fmt::{Display, Formatter};
|
||||
use std::num::{NonZeroI32, TryFromIntError};
|
||||
use std::path::PathBuf;
|
||||
use std::sync::{Arc, Weak};
|
||||
use thiserror::Error;
|
||||
|
||||
pub use self::dev::{make_dev, Cdev, CdevSw, DriverFlags, MakeDev, MakeDevError};
|
||||
pub use self::dirent::*;
|
||||
pub use self::file::*;
|
||||
|
@ -5,22 +22,8 @@ pub use self::ioctl::*;
|
|||
pub use self::mount::*;
|
||||
pub use self::path::*;
|
||||
pub use self::perm::*;
|
||||
pub use self::stat::*;
|
||||
pub use self::vnode::*;
|
||||
use crate::errno::{Errno, EBADF, EBUSY, EINVAL, ENAMETOOLONG, ENODEV, ENOENT};
|
||||
use crate::info;
|
||||
use crate::process::VThread;
|
||||
use crate::syscalls::{SysArg, SysErr, SysIn, SysOut, Syscalls};
|
||||
use crate::ucred::PrivilegeError;
|
||||
use crate::ucred::{Privilege, Ucred};
|
||||
use bitflags::bitflags;
|
||||
use gmtx::{Gutex, GutexGroup};
|
||||
use macros::vpath;
|
||||
use param::Param;
|
||||
use std::fmt::{Display, Formatter};
|
||||
use std::num::{NonZeroI32, TryFromIntError};
|
||||
use std::path::PathBuf;
|
||||
use std::sync::{Arc, Weak};
|
||||
use thiserror::Error;
|
||||
|
||||
mod dev;
|
||||
mod dirent;
|
||||
|
@ -31,6 +34,7 @@ mod mount;
|
|||
mod null;
|
||||
mod path;
|
||||
mod perm;
|
||||
mod stat;
|
||||
mod tmp;
|
||||
mod vnode;
|
||||
|
||||
|
@ -125,13 +129,24 @@ impl Fs {
|
|||
}
|
||||
|
||||
// Install syscall handlers.
|
||||
sys.register(3, &fs, Self::sys_read);
|
||||
sys.register(4, &fs, Self::sys_write);
|
||||
sys.register(5, &fs, Self::sys_open);
|
||||
sys.register(6, &fs, Self::sys_close);
|
||||
sys.register(54, &fs, Self::sys_ioctl);
|
||||
sys.register(56, &fs, Self::sys_revoke);
|
||||
sys.register(120, &fs, Self::sys_readv);
|
||||
sys.register(121, &fs, Self::sys_writev);
|
||||
sys.register(136, &fs, Self::sys_mkdir);
|
||||
sys.register(188, &fs, Self::sys_stat);
|
||||
sys.register(189, &fs, Self::sys_fstat);
|
||||
sys.register(190, &fs, Self::sys_lstat);
|
||||
sys.register(191, &fs, Self::sys_pread);
|
||||
sys.register(209, &fs, Self::sys_poll);
|
||||
sys.register(289, &fs, Self::sys_preadv);
|
||||
sys.register(290, &fs, Self::sys_pwritev);
|
||||
sys.register(476, &fs, Self::sys_pwrite);
|
||||
sys.register(493, &fs, Self::sys_fstatat);
|
||||
sys.register(496, &fs, Self::sys_mkdirat);
|
||||
|
||||
Ok(fs)
|
||||
|
@ -247,6 +262,21 @@ impl Fs {
|
|||
Ok(vn)
|
||||
}
|
||||
|
||||
fn sys_read(self: &Arc<Self>, i: &SysIn) -> Result<SysOut, SysErr> {
|
||||
let fd: i32 = i.args[0].try_into().unwrap();
|
||||
let ptr: *mut u8 = i.args[1].into();
|
||||
let len: usize = i.args[2].try_into().unwrap();
|
||||
|
||||
let iovec = unsafe { IoVec::try_from_raw_parts(ptr, len) }?;
|
||||
|
||||
let uio = UioMut {
|
||||
vecs: &mut [iovec],
|
||||
bytes_left: len,
|
||||
};
|
||||
|
||||
self.readv(fd, uio)
|
||||
}
|
||||
|
||||
/// See `vfs_donmount` on the PS4 for a reference.
|
||||
pub fn mount(
|
||||
self: &Arc<Self>,
|
||||
|
@ -347,16 +377,14 @@ impl Fs {
|
|||
let ptr: *const u8 = i.args[1].into();
|
||||
let len: usize = i.args[2].into();
|
||||
|
||||
if len > 0x7fffffff {
|
||||
return Err(SysErr::Raw(EINVAL));
|
||||
}
|
||||
let iovec = unsafe { IoVec::try_from_raw_parts(ptr, len) }?;
|
||||
|
||||
let td = VThread::current().unwrap();
|
||||
let file = td.proc().files().get(fd).ok_or(SysErr::Raw(EBADF))?;
|
||||
let buf = unsafe { std::slice::from_raw_parts(ptr, len) };
|
||||
let written = file.write(buf, Some(&td))?;
|
||||
let uio = Uio {
|
||||
vecs: &[iovec],
|
||||
bytes_left: len,
|
||||
};
|
||||
|
||||
Ok(written.into())
|
||||
self.writev(fd, uio)
|
||||
}
|
||||
|
||||
fn sys_open(self: &Arc<Self>, i: &SysIn) -> Result<SysOut, SysErr> {
|
||||
|
@ -415,64 +443,56 @@ impl Fs {
|
|||
}
|
||||
|
||||
fn sys_ioctl(self: &Arc<Self>, i: &SysIn) -> Result<SysOut, SysErr> {
|
||||
let fd: i32 = i.args[0].try_into().unwrap();
|
||||
let cmd: IoCmd = i.args[1].try_into()?;
|
||||
let data_arg: *mut u8 = i.args[2].into();
|
||||
|
||||
// Get data.
|
||||
let data = unsafe { std::slice::from_raw_parts_mut(data_arg, cmd.size()) };
|
||||
|
||||
// Get target file.
|
||||
let td = VThread::current().unwrap();
|
||||
|
||||
// Execute the operation.
|
||||
info!("Executing ioctl({cmd}) on file descriptor {fd}.");
|
||||
|
||||
self.ioctl(fd, cmd, data, &td)?;
|
||||
|
||||
Ok(SysOut::ZERO)
|
||||
}
|
||||
|
||||
/// See `kern_ioctl` on the PS4 for a reference.
|
||||
fn ioctl(
|
||||
self: &Arc<Self>,
|
||||
fd: i32,
|
||||
cmd: IoCmd,
|
||||
data: &mut [u8],
|
||||
td: &VThread,
|
||||
) -> Result<SysOut, IoctlError> {
|
||||
const FIOCLEX: IoCmd = IoCmd::io(b'f', 1);
|
||||
const FIONCLEX: IoCmd = IoCmd::io(b'f', 2);
|
||||
const FIONBIO: IoCmd = IoCmd::iowint(b'f', 0x7e);
|
||||
const FIOASYNC: IoCmd = IoCmd::iowint(b'f', 0x7d);
|
||||
|
||||
let fd: i32 = i.args[0].try_into().unwrap();
|
||||
let com: IoCmd = i.args[1].try_into()?;
|
||||
let data_arg: *mut u8 = i.args[2].into();
|
||||
|
||||
let size: usize = com.size();
|
||||
let mut vec = vec![0u8; size];
|
||||
|
||||
// Get data.
|
||||
let data = if size == 0 {
|
||||
&mut []
|
||||
} else {
|
||||
if com.is_void() {
|
||||
todo!("ioctl with com & IOC_VOID != 0");
|
||||
} else {
|
||||
&mut vec[..]
|
||||
}
|
||||
};
|
||||
|
||||
if com.is_in() {
|
||||
todo!("ioctl with IOC_IN & != 0");
|
||||
} else if com.is_out() {
|
||||
data.fill(0);
|
||||
}
|
||||
|
||||
// Get target file.
|
||||
let td = VThread::current().unwrap();
|
||||
let file = td.proc().files().get(fd).ok_or(SysErr::Raw(EBADF))?;
|
||||
let file = td.proc().files().get(fd)?;
|
||||
|
||||
if !file
|
||||
.flags()
|
||||
.intersects(VFileFlags::FREAD | VFileFlags::FWRITE)
|
||||
.intersects(VFileFlags::READ | VFileFlags::WRITE)
|
||||
{
|
||||
return Err(SysErr::Raw(EBADF));
|
||||
return Err(IoctlError::BadFileFlags(file.flags()));
|
||||
}
|
||||
|
||||
// Execute the operation.
|
||||
info!("Executing ioctl({com}) on file descriptor {fd}.");
|
||||
|
||||
match com {
|
||||
FIOCLEX => todo!("ioctl with com = FIOCLEX"),
|
||||
FIONCLEX => todo!("ioctl with com = FIONCLEX"),
|
||||
FIONBIO => todo!("ioctl with com = FIONBIO"),
|
||||
FIOASYNC => todo!("ioctl with com = FIOASYNC"),
|
||||
match cmd {
|
||||
FIOCLEX => todo!("ioctl with cmd = FIOCLEX"),
|
||||
FIONCLEX => todo!("ioctl with cmd = FIONCLEX"),
|
||||
FIONBIO => todo!("ioctl with cmd = FIONBIO"),
|
||||
FIOASYNC => todo!("ioctl with cmd = FIOASYNC"),
|
||||
_ => {}
|
||||
}
|
||||
|
||||
file.ioctl(com, data, Some(&td))?;
|
||||
|
||||
if com.is_void() {
|
||||
unsafe {
|
||||
std::ptr::copy_nonoverlapping(data.as_ptr(), data_arg, size);
|
||||
}
|
||||
}
|
||||
file.ioctl(cmd, data, Some(&td))
|
||||
.map_err(IoctlError::FileIoctlFailed)?;
|
||||
|
||||
Ok(SysOut::ZERO)
|
||||
}
|
||||
|
@ -501,6 +521,227 @@ impl Fs {
|
|||
Ok(SysOut::ZERO)
|
||||
}
|
||||
|
||||
fn sys_readv(self: &Arc<Self>, i: &SysIn) -> Result<SysOut, SysErr> {
|
||||
let fd: i32 = i.args[0].try_into().unwrap();
|
||||
let iovec: *mut IoVec = i.args[1].into();
|
||||
let count: u32 = i.args[2].try_into().unwrap();
|
||||
|
||||
let uio = unsafe { UioMut::copyin(iovec, count) }?;
|
||||
|
||||
self.readv(fd, uio)
|
||||
}
|
||||
|
||||
fn readv(&self, fd: i32, uio: UioMut) -> Result<SysOut, SysErr> {
|
||||
let td = VThread::current().unwrap();
|
||||
|
||||
let file = td.proc().files().get_for_read(fd)?;
|
||||
|
||||
let read = file.do_read(uio, Offset::Current, Some(&td))?;
|
||||
|
||||
Ok(read.into())
|
||||
}
|
||||
|
||||
fn sys_writev(self: &Arc<Self>, i: &SysIn) -> Result<SysOut, SysErr> {
|
||||
let fd: i32 = i.args[0].try_into().unwrap();
|
||||
let iovec: *const IoVec = i.args[1].into();
|
||||
let iovcnt: u32 = i.args[2].try_into().unwrap();
|
||||
|
||||
let uio = unsafe { Uio::copyin(iovec, iovcnt) }?;
|
||||
|
||||
self.writev(fd, uio)
|
||||
}
|
||||
|
||||
fn writev(&self, fd: i32, uio: Uio) -> Result<SysOut, SysErr> {
|
||||
let td = VThread::current().unwrap();
|
||||
|
||||
let file = td.proc().files().get_for_write(fd)?;
|
||||
|
||||
let written = file.do_write(uio, Offset::Current, Some(&td))?;
|
||||
|
||||
Ok(written.into())
|
||||
}
|
||||
|
||||
fn sys_stat(self: &Arc<Self>, i: &SysIn) -> Result<SysOut, SysErr> {
|
||||
let path = unsafe { i.args[0].to_path() }?.unwrap();
|
||||
let stat_out: *mut Stat = i.args[1].into();
|
||||
|
||||
let td = VThread::current().unwrap();
|
||||
|
||||
let stat = self.stat(path, &td)?;
|
||||
|
||||
unsafe {
|
||||
*stat_out = stat;
|
||||
}
|
||||
|
||||
Ok(SysOut::ZERO)
|
||||
}
|
||||
|
||||
/// This function is inlined on the PS4, but corresponds to `kern_stat` in FreeBSD.
|
||||
fn stat(self: &Arc<Self>, path: impl AsRef<VPath>, td: &VThread) -> Result<Stat, StatError> {
|
||||
self.statat(AtFlags::empty(), At::Cwd, path, td)
|
||||
}
|
||||
|
||||
fn sys_fstat(self: &Arc<Self>, i: &SysIn) -> Result<SysOut, SysErr> {
|
||||
let fd: i32 = i.args[0].try_into().unwrap();
|
||||
let stat_out: *mut Stat = i.args[1].into();
|
||||
|
||||
let td = VThread::current().unwrap();
|
||||
|
||||
let stat = self.fstat(fd, &td)?;
|
||||
|
||||
unsafe {
|
||||
*stat_out = stat;
|
||||
}
|
||||
|
||||
Ok(SysOut::ZERO)
|
||||
}
|
||||
|
||||
/// See `kern_fstat` on the PS4 for a reference.
|
||||
#[allow(unused_variables)] // Remove this when it is being implemented
|
||||
fn fstat(self: &Arc<Self>, fd: i32, td: &VThread) -> Result<Stat, StatError> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn sys_lstat(self: &Arc<Self>, i: &SysIn) -> Result<SysOut, SysErr> {
|
||||
let path = unsafe { i.args[0].to_path() }?.unwrap();
|
||||
let stat_out: *mut Stat = i.args[1].into();
|
||||
|
||||
let td = VThread::current().unwrap();
|
||||
|
||||
td.priv_check(Privilege::SCE683)?;
|
||||
|
||||
let stat = self.lstat(path, &td)?;
|
||||
|
||||
unsafe {
|
||||
*stat_out = stat;
|
||||
}
|
||||
|
||||
Ok(SysOut::ZERO)
|
||||
}
|
||||
|
||||
/// See `kern_lstat` in FreeBSD for a reference. (This function is inlined on the PS4)
|
||||
fn lstat(self: &Arc<Self>, path: impl AsRef<VPath>, td: &VThread) -> Result<Stat, StatError> {
|
||||
self.statat(AtFlags::SYMLINK_NOFOLLOW, At::Cwd, path, td)
|
||||
}
|
||||
|
||||
fn sys_pread(self: &Arc<Self>, i: &SysIn) -> Result<SysOut, SysErr> {
|
||||
let fd: i32 = i.args[0].try_into().unwrap();
|
||||
let ptr: *mut u8 = i.args[1].into();
|
||||
let len: usize = i.args[2].try_into().unwrap();
|
||||
let offset: u64 = i.args[3].try_into().unwrap();
|
||||
|
||||
let iovec = unsafe { IoVec::try_from_raw_parts(ptr, len) }?;
|
||||
|
||||
let uio = UioMut {
|
||||
vecs: &mut [iovec],
|
||||
bytes_left: len,
|
||||
};
|
||||
|
||||
self.preadv(fd, uio, offset)
|
||||
}
|
||||
|
||||
fn sys_pwrite(self: &Arc<Self>, i: &SysIn) -> Result<SysOut, SysErr> {
|
||||
let fd: i32 = i.args[0].try_into().unwrap();
|
||||
let ptr: *mut u8 = i.args[1].into();
|
||||
let len: usize = i.args[2].try_into().unwrap();
|
||||
let offset: u64 = i.args[3].try_into().unwrap();
|
||||
|
||||
let iovec = unsafe { IoVec::try_from_raw_parts(ptr, len) }?;
|
||||
|
||||
let uio = Uio {
|
||||
vecs: &[iovec],
|
||||
bytes_left: len,
|
||||
};
|
||||
|
||||
self.pwritev(fd, uio, offset)
|
||||
}
|
||||
|
||||
fn sys_preadv(self: &Arc<Self>, i: &SysIn) -> Result<SysOut, SysErr> {
|
||||
let fd: i32 = i.args[0].try_into().unwrap();
|
||||
let iovec: *mut IoVec = i.args[1].into();
|
||||
let count: u32 = i.args[2].try_into().unwrap();
|
||||
let offset: u64 = i.args[3].try_into().unwrap();
|
||||
|
||||
let uio = unsafe { UioMut::copyin(iovec, count) }?;
|
||||
|
||||
self.preadv(fd, uio, offset)
|
||||
}
|
||||
|
||||
fn preadv(&self, fd: i32, uio: UioMut, off: u64) -> Result<SysOut, SysErr> {
|
||||
let td = VThread::current().unwrap();
|
||||
|
||||
let file = td.proc().files().get_for_read(fd)?;
|
||||
|
||||
if !file.op_flags().intersects(VFileOpsFlags::SEEKABLE) {
|
||||
return Err(SysErr::Raw(ESPIPE));
|
||||
}
|
||||
|
||||
// TODO: check vnode type
|
||||
|
||||
let read = file.do_read(uio, Offset::Provided(off), Some(&td))?;
|
||||
|
||||
Ok(read.into())
|
||||
}
|
||||
|
||||
fn sys_pwritev(self: &Arc<Self>, i: &SysIn) -> Result<SysOut, SysErr> {
|
||||
let fd: i32 = i.args[0].try_into().unwrap();
|
||||
let iovec: *const IoVec = i.args[1].into();
|
||||
let count: u32 = i.args[2].try_into().unwrap();
|
||||
let offset: u64 = i.args[3].try_into().unwrap();
|
||||
|
||||
let uio = unsafe { Uio::copyin(iovec, count) }?;
|
||||
|
||||
self.pwritev(fd, uio, offset)
|
||||
}
|
||||
|
||||
fn pwritev(&self, fd: i32, uio: Uio, off: u64) -> Result<SysOut, SysErr> {
|
||||
let td = VThread::current().unwrap();
|
||||
|
||||
let file = td.proc().files().get_for_write(fd)?;
|
||||
|
||||
if !file.op_flags().intersects(VFileOpsFlags::SEEKABLE) {
|
||||
return Err(SysErr::Raw(ESPIPE));
|
||||
}
|
||||
|
||||
// TODO: check vnode type
|
||||
|
||||
let written = file.do_write(uio, Offset::Provided(off), Some(&td))?;
|
||||
|
||||
Ok(written.into())
|
||||
}
|
||||
|
||||
fn sys_fstatat(self: &Arc<Self>, i: &SysIn) -> Result<SysOut, SysErr> {
|
||||
let dirfd: i32 = i.args[0].try_into().unwrap();
|
||||
let path = unsafe { i.args[1].to_path() }?.unwrap();
|
||||
let stat_out: *mut Stat = i.args[2].into();
|
||||
let flags: AtFlags = i.args[3].try_into().unwrap();
|
||||
|
||||
let td = VThread::current().unwrap();
|
||||
|
||||
td.priv_check(Privilege::SCE683)?;
|
||||
|
||||
let stat = self.statat(flags, At::Fd(dirfd), path, &td)?;
|
||||
|
||||
unsafe {
|
||||
*stat_out = stat;
|
||||
}
|
||||
|
||||
Ok(SysOut::ZERO)
|
||||
}
|
||||
|
||||
/// See `kern_statat_vnhook` on the PS4 for a reference. Not that we ignore the hook argument for now.
|
||||
#[allow(unused_variables)] // Remove this when statat is being implemented
|
||||
fn statat(
|
||||
self: &Arc<Self>,
|
||||
flags: AtFlags,
|
||||
dirat: At,
|
||||
path: impl AsRef<VPath>,
|
||||
td: &VThread,
|
||||
) -> Result<Stat, StatError> {
|
||||
// TODO: this will need lookup from a start dir
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn revoke(&self, vn: Arc<Vnode>, td: &VThread) -> Result<(), RevokeError> {
|
||||
let vattr = vn.getattr().map_err(RevokeError::GetAttrError)?;
|
||||
|
||||
|
@ -544,6 +785,7 @@ impl Fs {
|
|||
}
|
||||
|
||||
/// See `kern_mkdirat` on the PS4 for a reference.
|
||||
#[allow(unused_variables)] // Remove this when mkdirat is being implemented.
|
||||
fn mkdirat(
|
||||
&self,
|
||||
at: At,
|
||||
|
@ -632,18 +874,123 @@ pub struct FsConfig {
|
|||
) -> Result<Mount, Box<dyn Errno>>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
/// Represents the fd arg for
|
||||
enum Offset {
|
||||
Current,
|
||||
Provided(u64),
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
/// Represents the fd arg for
|
||||
enum At {
|
||||
Cwd,
|
||||
Fd(i32),
|
||||
}
|
||||
|
||||
pub struct IoVec {
|
||||
base: *const u8,
|
||||
len: usize,
|
||||
}
|
||||
|
||||
impl IoVec {
|
||||
pub unsafe fn try_from_raw_parts(base: *const u8, len: usize) -> Result<Self, IoVecError> {
|
||||
Ok(Self { base, len })
|
||||
}
|
||||
}
|
||||
|
||||
const UIO_MAXIOV: u32 = 1024;
|
||||
const IOSIZE_MAX: usize = 0x7fffffff;
|
||||
|
||||
pub struct Uio<'a> {
|
||||
vecs: &'a [IoVec], // uio_iov + uio_iovcnt
|
||||
bytes_left: usize, // uio_resid
|
||||
}
|
||||
|
||||
impl<'a> Uio<'a> {
|
||||
/// See `copyinuio` on the PS4 for a reference.
|
||||
pub unsafe fn copyin(first: *const IoVec, count: u32) -> Result<Self, CopyInUioError> {
|
||||
if count > UIO_MAXIOV {
|
||||
return Err(CopyInUioError::TooManyVecs);
|
||||
}
|
||||
|
||||
let vecs = std::slice::from_raw_parts(first, count as usize);
|
||||
let bytes_left = vecs.iter().map(|v| v.len).try_fold(0, |acc, len| {
|
||||
if acc > IOSIZE_MAX - len {
|
||||
Err(CopyInUioError::MaxLenExceeded)
|
||||
} else {
|
||||
Ok(acc + len)
|
||||
}
|
||||
})?;
|
||||
|
||||
Ok(Self { vecs, bytes_left })
|
||||
}
|
||||
}
|
||||
|
||||
pub struct UioMut<'a> {
|
||||
vecs: &'a mut [IoVec], // uio_iov + uio_iovcnt
|
||||
bytes_left: usize, // uio_resid
|
||||
}
|
||||
|
||||
impl<'a> UioMut<'a> {
|
||||
/// See `copyinuio` on the PS4 for a reference.
|
||||
pub unsafe fn copyin(first: *mut IoVec, count: u32) -> Result<Self, CopyInUioError> {
|
||||
if count > UIO_MAXIOV {
|
||||
return Err(CopyInUioError::TooManyVecs);
|
||||
}
|
||||
|
||||
let vecs = std::slice::from_raw_parts_mut(first, count as usize);
|
||||
let bytes_left = vecs.iter().map(|v| v.len).try_fold(0, |acc, len| {
|
||||
if acc > IOSIZE_MAX - len {
|
||||
Err(CopyInUioError::MaxLenExceeded)
|
||||
} else {
|
||||
Ok(acc + len)
|
||||
}
|
||||
})?;
|
||||
|
||||
Ok(Self { vecs, bytes_left })
|
||||
}
|
||||
}
|
||||
|
||||
bitflags! {
|
||||
/// Flags for *at() syscalls.
|
||||
struct AtFlags: i32 {
|
||||
const SYMLINK_NOFOLLOW = 0x200;
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<SysArg> for AtFlags {
|
||||
type Error = TryFromIntError;
|
||||
|
||||
fn try_from(value: SysArg) -> Result<Self, Self::Error> {
|
||||
Ok(Self::from_bits_retain(value.get().try_into()?))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Error, Errno)]
|
||||
pub enum IoVecError {
|
||||
#[error("len exceed the maximum value")]
|
||||
#[errno(EINVAL)]
|
||||
MaxLenExceeded,
|
||||
}
|
||||
|
||||
#[derive(Debug, Error, Errno)]
|
||||
pub enum CopyInUioError {
|
||||
#[error("too many iovecs")]
|
||||
#[errno(EINVAL)]
|
||||
TooManyVecs,
|
||||
|
||||
#[error("the sum of iovec lengths is too large")]
|
||||
#[errno(EINVAL)]
|
||||
MaxLenExceeded,
|
||||
}
|
||||
|
||||
bitflags! {
|
||||
pub struct RevokeFlags: i32 {
|
||||
const REVOKE_ALL = 0x0001;
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum At {
|
||||
Cwd,
|
||||
Fd(i32),
|
||||
}
|
||||
|
||||
struct PollFd {
|
||||
fd: i32,
|
||||
events: i16, // TODO: this probably deserves its own type
|
||||
|
@ -663,7 +1010,7 @@ pub enum FsError {
|
|||
LookupDevFailed(#[source] LookupError),
|
||||
}
|
||||
|
||||
/// Represents an error when FS mounting is failed.
|
||||
/// Represents an error when FS mounting fails.
|
||||
#[derive(Debug, Error)]
|
||||
pub enum MountError {
|
||||
#[error("fstype is too long")]
|
||||
|
@ -697,7 +1044,7 @@ impl Errno for MountError {
|
|||
}
|
||||
}
|
||||
|
||||
/// Represents an error when [`Fs::open()`] was failed.
|
||||
/// Represents an error when [`Fs::open()`] fails.
|
||||
#[derive(Debug, Error)]
|
||||
pub enum OpenError {
|
||||
#[error("cannot lookup the file")]
|
||||
|
@ -712,7 +1059,38 @@ impl Errno for OpenError {
|
|||
}
|
||||
}
|
||||
|
||||
/// Represents an error when [`Fs::lookup()`] was failed.
|
||||
#[derive(Debug, Error)]
|
||||
pub enum WriteError {}
|
||||
|
||||
impl Errno for WriteError {
|
||||
fn errno(&self) -> NonZeroI32 {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum IoctlError {
|
||||
#[error("Couldn't get file")]
|
||||
FailedToGetFile(#[from] GetFileError),
|
||||
|
||||
#[error("Bad file flags {0:?}")]
|
||||
BadFileFlags(VFileFlags),
|
||||
|
||||
#[error(transparent)]
|
||||
FileIoctlFailed(Box<dyn Errno>),
|
||||
}
|
||||
|
||||
impl Errno for IoctlError {
|
||||
fn errno(&self) -> NonZeroI32 {
|
||||
match self {
|
||||
Self::FailedToGetFile(e) => e.errno(),
|
||||
Self::BadFileFlags(_) => EBADF,
|
||||
Self::FileIoctlFailed(e) => e.errno(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents an error when [`Fs::lookup()`] fails.
|
||||
#[derive(Debug, Error)]
|
||||
pub enum LookupError {
|
||||
#[error("no such file or directory")]
|
||||
|
@ -752,6 +1130,24 @@ impl Errno for RevokeError {
|
|||
}
|
||||
}
|
||||
}
|
||||
/// Represents an error when one of the stat syscalls fails
|
||||
#[derive(Debug, Error)]
|
||||
pub enum StatError {
|
||||
#[error("failed to get file")]
|
||||
FailedToGetFile(#[from] GetFileError),
|
||||
|
||||
#[error("failed to get file attr")]
|
||||
GetAttrError(#[from] Box<dyn Errno>),
|
||||
}
|
||||
|
||||
impl Errno for StatError {
|
||||
fn errno(&self) -> NonZeroI32 {
|
||||
match self {
|
||||
Self::FailedToGetFile(e) => e.errno(),
|
||||
Self::GetAttrError(e) => e.errno(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static HOST: FsConfig = FsConfig {
|
||||
name: "exfatfs",
|
||||
|
|
30
src/kernel/src/fs/stat.rs
Normal file
30
src/kernel/src/fs/stat.rs
Normal file
|
@ -0,0 +1,30 @@
|
|||
use crate::time::TimeSpec;
|
||||
|
||||
/// An implementation of the `stat` structure.
|
||||
#[repr(C)]
|
||||
pub struct Stat {
|
||||
dev: i32,
|
||||
ino: u32,
|
||||
pub mode: u16,
|
||||
nlink: u16,
|
||||
uid: u32,
|
||||
gid: u32,
|
||||
rdev: i32,
|
||||
atime: TimeSpec,
|
||||
mtime: TimeSpec,
|
||||
ctime: TimeSpec,
|
||||
size: i64,
|
||||
block_count: i64,
|
||||
pub block_size: u32,
|
||||
flags: u32,
|
||||
gen: u32,
|
||||
_spare: i32,
|
||||
birthtime: TimeSpec,
|
||||
}
|
||||
|
||||
impl Stat {
|
||||
/// This is what would happen when calling `bzero` on a `stat` structure.
|
||||
pub fn zeroed() -> Self {
|
||||
unsafe { std::mem::zeroed() }
|
||||
}
|
||||
}
|
|
@ -118,7 +118,7 @@ impl Filesystem for TempFs {
|
|||
}
|
||||
}
|
||||
|
||||
/// Represents an error when [`mount()`] was failed.
|
||||
/// Represents an error when [`mount()`] fails.
|
||||
#[derive(Debug, Error)]
|
||||
enum MountError {
|
||||
#[error("update is not supported")]
|
||||
|
|
|
@ -41,7 +41,7 @@ impl Nodes {
|
|||
#[derive(Debug)]
|
||||
pub struct Node {}
|
||||
|
||||
/// Represents an error when [`Nodes::alloc()`] was failed.
|
||||
/// Represents an error when [`Nodes::alloc()`] fails.
|
||||
#[derive(Debug, Error, Errno)]
|
||||
pub enum AllocNodeError {
|
||||
#[error("maximum number of nodes has been reached")]
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
use super::{
|
||||
unixify_access, Access, FileBackend, IoCmd, Mode, Mount, OpenFlags, RevokeFlags, VFile,
|
||||
unixify_access, Access, FileBackend, IoCmd, Mode, Mount, OpenFlags, RevokeFlags, Stat, Uio,
|
||||
UioMut, VFile, VFileOpsFlags,
|
||||
};
|
||||
use crate::errno::{Errno, ENOTDIR, ENOTTY, EOPNOTSUPP, EPERM};
|
||||
use crate::process::VThread;
|
||||
|
@ -127,7 +128,7 @@ impl FileBackend for Vnode {
|
|||
fn read(
|
||||
self: &Arc<Self>,
|
||||
file: &VFile,
|
||||
buf: &mut [u8],
|
||||
buf: &mut UioMut,
|
||||
td: Option<&VThread>,
|
||||
) -> Result<usize, Box<dyn Errno>> {
|
||||
todo!()
|
||||
|
@ -137,7 +138,7 @@ impl FileBackend for Vnode {
|
|||
fn write(
|
||||
self: &Arc<Self>,
|
||||
file: &VFile,
|
||||
buf: &[u8],
|
||||
buf: &mut Uio,
|
||||
td: Option<&VThread>,
|
||||
) -> Result<usize, Box<dyn Errno>> {
|
||||
todo!()
|
||||
|
@ -153,6 +154,14 @@ impl FileBackend for Vnode {
|
|||
) -> Result<(), Box<dyn Errno>> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn stat(self: &Arc<Self>, file: &VFile, td: Option<&VThread>) -> Result<Stat, Box<dyn Errno>> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn flags(&self) -> VFileOpsFlags {
|
||||
VFileOpsFlags::SEEKABLE | VFileOpsFlags::PASSABLE
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Vnode {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use crate::{
|
||||
fs::{FileBackend, VFileFlags, VFileType},
|
||||
fs::{FileBackend, Stat, VFile, VFileFlags, VFileType},
|
||||
process::{FileDesc, VThread},
|
||||
syscalls::{SysErr, SysIn, SysOut, Syscalls},
|
||||
};
|
||||
|
@ -34,7 +34,7 @@ impl KernelQueueManager {
|
|||
|
||||
Ok(VFileType::KernelQueue(kq))
|
||||
},
|
||||
VFileFlags::FREAD | VFileFlags::FWRITE,
|
||||
VFileFlags::READ | VFileFlags::WRITE,
|
||||
)?;
|
||||
|
||||
Ok(fd.into())
|
||||
|
@ -54,4 +54,16 @@ impl KernelQueue {
|
|||
}
|
||||
}
|
||||
|
||||
impl FileBackend for KernelQueue {}
|
||||
impl FileBackend for KernelQueue {
|
||||
fn stat(
|
||||
self: &Arc<Self>,
|
||||
_: &VFile,
|
||||
_: Option<&VThread>,
|
||||
) -> Result<Stat, Box<dyn crate::errno::Errno>> {
|
||||
let mut stat = Stat::zeroed();
|
||||
|
||||
stat.mode = 0o10000;
|
||||
|
||||
Ok(stat)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -127,7 +127,7 @@ impl MemoryManager {
|
|||
todo!("mmap with len = 0");
|
||||
}
|
||||
|
||||
if flags.intersects(MappingFlags::MAP_NOEXTEND | MappingFlags::MAP_ANON) {
|
||||
if flags.intersects(MappingFlags::MAP_VOID | MappingFlags::MAP_ANON) {
|
||||
if offset != 0 {
|
||||
return Err(MmapError::NonZeroOffset);
|
||||
} else if fd != -1 {
|
||||
|
@ -162,12 +162,12 @@ impl MemoryManager {
|
|||
todo!("mmap with addr = 0x880000000");
|
||||
}
|
||||
|
||||
if flags.contains(MappingFlags::MAP_NOEXTEND) {
|
||||
flags = flags | MappingFlags::MAP_ANON;
|
||||
td.as_ref().and_then(|t| {
|
||||
t.set_fpop(None);
|
||||
Some(t)
|
||||
});
|
||||
if flags.contains(MappingFlags::MAP_VOID) {
|
||||
flags |= MappingFlags::MAP_ANON;
|
||||
|
||||
if let Some(ref td) = td {
|
||||
td.set_fpop(None);
|
||||
}
|
||||
} else if !flags.contains(MappingFlags::MAP_ANON) {
|
||||
todo!("mmap with flags & 0x1000 = 0");
|
||||
}
|
||||
|
@ -611,7 +611,7 @@ impl MemoryManager {
|
|||
let addr = (addr & 0xffffffffffffc000) as *mut u8;
|
||||
|
||||
if let Err(e) = self.mname(addr, len, name) {
|
||||
warn!(e, "mname({addr:p}, {len:#x}, {name}) was failed");
|
||||
warn!(e, "mname({addr:p}, {len:#x}, {name}) failed");
|
||||
}
|
||||
|
||||
Ok(SysOut::ZERO)
|
||||
|
@ -754,7 +754,7 @@ bitflags! {
|
|||
pub struct MappingFlags: u32 {
|
||||
const MAP_PRIVATE = 0x00000002;
|
||||
const MAP_FIXED = 0x00000010;
|
||||
const MAP_NOEXTEND = 0x00000100;
|
||||
const MAP_VOID = 0x00000100;
|
||||
const MAP_STACK = 0x00000400;
|
||||
const MAP_ANON = 0x00001000;
|
||||
const MAP_GUARD = 0x00002000;
|
||||
|
|
|
@ -73,14 +73,16 @@ impl FileDesc {
|
|||
panic!("Too many files has been opened.");
|
||||
}
|
||||
|
||||
/// See `fget` on the PS4 for a reference.
|
||||
pub fn get(&self, fd: i32) -> Option<Arc<VFile>> {
|
||||
// TODO: Check what we have missed here.
|
||||
let fd: usize = fd.try_into().ok()?;
|
||||
pub fn get(&self, fd: i32) -> Result<Arc<VFile>, GetFileError> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
let files = self.files.read();
|
||||
pub fn get_for_write(&self, fd: i32) -> Result<Arc<VFile>, GetFileError> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
files.get(fd)?.clone()
|
||||
pub fn get_for_read(&self, fd: i32) -> Result<Arc<VFile>, GetFileError> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
pub fn free(&self, fd: i32) -> Result<(), FreeError> {
|
||||
|
@ -98,6 +100,15 @@ impl FileDesc {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum GetFileError {}
|
||||
|
||||
impl Errno for GetFileError {
|
||||
fn errno(&self) -> NonZeroI32 {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum FileAllocError<E: Errno> {
|
||||
#[error(transparent)]
|
||||
|
|
178
src/kernel/src/process/filedesc.rs
Normal file
178
src/kernel/src/process/filedesc.rs
Normal file
|
@ -0,0 +1,178 @@
|
|||
use crate::errno::{Errno, EBADF};
|
||||
use crate::fs::{VFile, VFileFlags, VFileType, Vnode};
|
||||
use crate::kqueue::KernelQueue;
|
||||
use gmtx::{Gutex, GutexGroup};
|
||||
use macros::Errno;
|
||||
use std::collections::VecDeque;
|
||||
use std::convert::Infallible;
|
||||
use std::num::NonZeroI32;
|
||||
use std::sync::Arc;
|
||||
use thiserror::Error;
|
||||
|
||||
/// An implementation of `filedesc` structure.
|
||||
#[derive(Debug)]
|
||||
pub struct FileDesc {
|
||||
files: Gutex<Vec<Option<Arc<VFile>>>>, // fd_ofiles + fd_nfiles
|
||||
cwd: Gutex<Arc<Vnode>>, // fd_cdir
|
||||
root: Gutex<Arc<Vnode>>, // fd_rdir
|
||||
kqueue_list: Gutex<VecDeque<Arc<KernelQueue>>>, // fd_kqlist
|
||||
}
|
||||
|
||||
impl FileDesc {
|
||||
pub(super) fn new(root: Arc<Vnode>) -> Arc<Self> {
|
||||
let gg = GutexGroup::new();
|
||||
|
||||
let filedesc = Self {
|
||||
// TODO: these aren't none on the PS4
|
||||
files: gg.spawn(vec![None, None, None]),
|
||||
cwd: gg.spawn(root.clone()),
|
||||
root: gg.spawn(root),
|
||||
kqueue_list: gg.spawn(VecDeque::new()),
|
||||
};
|
||||
|
||||
Arc::new(filedesc)
|
||||
}
|
||||
|
||||
pub fn cwd(&self) -> Arc<Vnode> {
|
||||
self.cwd.read().clone()
|
||||
}
|
||||
|
||||
pub fn root(&self) -> Arc<Vnode> {
|
||||
self.root.read().clone()
|
||||
}
|
||||
|
||||
pub fn insert_kqueue(&self, kq: Arc<KernelQueue>) {
|
||||
self.kqueue_list.write().push_front(kq);
|
||||
}
|
||||
|
||||
#[allow(unused_variables)] // TODO: remove when implementing; add budget argument
|
||||
pub fn alloc_with_budget<E: Errno>(
|
||||
&self,
|
||||
constructor: impl FnOnce(i32) -> Result<VFileType, E>,
|
||||
flags: VFileFlags,
|
||||
) -> Result<i32, FileAllocError<E>> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
/// See `finstall` on the PS4 for a reference.
|
||||
pub fn alloc(&self, file: Arc<VFile>) -> i32 {
|
||||
// TODO: Implement fdalloc.
|
||||
let mut files = self.files.write();
|
||||
|
||||
for i in 3..=i32::MAX {
|
||||
let i: usize = i.try_into().unwrap();
|
||||
|
||||
if i == files.len() {
|
||||
files.push(Some(file));
|
||||
} else if files[i].is_none() {
|
||||
files[i] = Some(file);
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
|
||||
return i as i32;
|
||||
}
|
||||
|
||||
// This should never happened.
|
||||
panic!("Too many files has been opened.");
|
||||
}
|
||||
|
||||
// TODO: implement capabilities
|
||||
|
||||
/// See `fget` on the PS4 for a reference.
|
||||
pub fn get(&self, fd: i32) -> Result<Arc<VFile>, GetFileError> {
|
||||
self.get_internal(fd, VFileFlags::empty())
|
||||
}
|
||||
|
||||
/// See `fget_write` on the PS4 for a reference.
|
||||
pub fn get_for_write(&self, fd: i32) -> Result<Arc<VFile>, GetFileError> {
|
||||
self.get_internal(fd, VFileFlags::WRITE)
|
||||
}
|
||||
|
||||
/// See `fget_read` on the PS4 for a reference.
|
||||
pub fn get_for_read(&self, fd: i32) -> Result<Arc<VFile>, GetFileError> {
|
||||
self.get_internal(fd, VFileFlags::READ)
|
||||
}
|
||||
|
||||
/// See `_fget` and `fget_unlocked` on the PS4 for a reference.
|
||||
fn get_internal(&self, fd: i32, flags: VFileFlags) -> Result<Arc<VFile>, GetFileError> {
|
||||
if fd < 0 {
|
||||
return Err(GetFileError::NegativeFd(fd));
|
||||
}
|
||||
|
||||
let files = self.files.write();
|
||||
|
||||
if fd as usize >= files.len() {
|
||||
return Err(GetFileError::FdOutOfRange(fd));
|
||||
}
|
||||
|
||||
loop {
|
||||
if let None = files.get(fd as usize) {
|
||||
return Err(GetFileError::NoFile(fd));
|
||||
}
|
||||
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn free(&self, fd: i32) -> Result<(), FreeError> {
|
||||
let fd: usize = fd.try_into().map_err(|_| FreeError::NegativeFd)?;
|
||||
|
||||
let mut files = self.files.write();
|
||||
|
||||
if let Some(file) = files.get_mut(fd) {
|
||||
*file = None;
|
||||
|
||||
Ok(())
|
||||
} else {
|
||||
Err(FreeError::NoFile)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum GetFileError {
|
||||
#[error("got negative file descriptor {0}")]
|
||||
NegativeFd(i32),
|
||||
|
||||
#[error("file descriptor {0} out of range")]
|
||||
FdOutOfRange(i32),
|
||||
|
||||
#[error("no file assoiated with file descriptor {0}")]
|
||||
NoFile(i32),
|
||||
}
|
||||
|
||||
impl Errno for GetFileError {
|
||||
fn errno(&self) -> NonZeroI32 {
|
||||
match self {
|
||||
Self::NegativeFd(_) => EBADF,
|
||||
Self::FdOutOfRange(_) => EBADF,
|
||||
Self::NoFile(_) => EBADF,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum FileAllocError<E: Errno = Infallible> {
|
||||
#[error(transparent)]
|
||||
Inner(E),
|
||||
}
|
||||
|
||||
impl<E: Errno> Errno for FileAllocError<E> {
|
||||
fn errno(&self) -> NonZeroI32 {
|
||||
match self {
|
||||
Self::Inner(e) => e.errno(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Error, Errno)]
|
||||
pub enum FreeError {
|
||||
#[error("negative file descriptor provided")]
|
||||
#[errno(EBADF)]
|
||||
NegativeFd,
|
||||
|
||||
#[error("no file associated with file descriptor")]
|
||||
#[errno(EBADF)]
|
||||
NoFile,
|
||||
}
|
|
@ -1,11 +1,12 @@
|
|||
pub use self::appinfo::*;
|
||||
pub use self::cpuset::*;
|
||||
pub use self::file::*;
|
||||
pub use self::filedesc::*;
|
||||
pub use self::group::*;
|
||||
pub use self::rlimit::*;
|
||||
pub use self::session::*;
|
||||
pub use self::signal::*;
|
||||
pub use self::thread::*;
|
||||
|
||||
use crate::budget::ProcType;
|
||||
use crate::errno::{EINVAL, ENAMETOOLONG, EPERM, ERANGE, ESRCH};
|
||||
use crate::fs::Vnode;
|
||||
|
@ -31,7 +32,7 @@ use thiserror::Error;
|
|||
|
||||
mod appinfo;
|
||||
mod cpuset;
|
||||
mod file;
|
||||
mod filedesc;
|
||||
mod group;
|
||||
mod rlimit;
|
||||
mod session;
|
||||
|
|
|
@ -203,7 +203,7 @@ impl<E: ExecutionEngine> RuntimeLinker<E> {
|
|||
|
||||
// Get file.
|
||||
let td = VThread::current();
|
||||
let file = match self.fs.open(path, td.as_ref().map(|v| v.deref().as_ref())) {
|
||||
let file = match self.fs.open(path, td.as_deref().map(|v| v.deref())) {
|
||||
Ok(v) => v,
|
||||
Err(e) => return Err(LoadError::OpenFileFailed(e)),
|
||||
};
|
||||
|
@ -223,7 +223,7 @@ impl<E: ExecutionEngine> RuntimeLinker<E> {
|
|||
// Search for TLS free slot.
|
||||
let names = vec![name];
|
||||
let tls = elf.tls().map(|i| &elf.programs()[i]);
|
||||
let tls = if tls.map(|p| p.memory_size()).unwrap_or(0) == 0 {
|
||||
let tls = if tls.map_or(0, |p| p.memory_size()) == 0 {
|
||||
0
|
||||
} else {
|
||||
let mut alloc = self.tls.write();
|
||||
|
|
|
@ -422,7 +422,7 @@ impl Sysctl {
|
|||
req: &mut SysctlReq,
|
||||
) -> Result<(), SysErr> {
|
||||
let mut buf = [0; 256];
|
||||
let len = min(req.old.as_ref().map(|b| b.len()).unwrap_or(0), 256);
|
||||
let len = min(256, req.old.as_ref().map_or(0, |b| b.len()));
|
||||
|
||||
rand_bytes(&mut buf[..len]);
|
||||
|
||||
|
|
|
@ -40,6 +40,20 @@ impl TimeManager {
|
|||
}
|
||||
}
|
||||
|
||||
/// An implementation of the `timespec` structure.
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
#[repr(C)]
|
||||
pub struct TimeSpec {
|
||||
sec: i64,
|
||||
nsec: i64,
|
||||
}
|
||||
|
||||
impl TimeSpec {
|
||||
pub fn now() -> Self {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
struct TimeVal {
|
||||
sec: i64, // tv_sec
|
||||
|
|
|
@ -62,7 +62,10 @@ impl Ucred {
|
|||
|
||||
/// See `sceSblACMgrIsDiskplayeruiProcess` on the PS4 for a reference.
|
||||
pub fn is_diskplayerui_process(&self) -> bool {
|
||||
self.auth.paid.get() == 0x380000001000000f || self.auth.paid.get() == 0x3800000010000013
|
||||
matches!(
|
||||
self.auth.paid.get(),
|
||||
0x380000001000000f | 0x3800000010000013
|
||||
)
|
||||
}
|
||||
|
||||
/// See `sceSblACMgrIsNongameUcred` on the PS4 for a reference.
|
||||
|
|
|
@ -113,14 +113,14 @@ impl<T> Tls<T> {
|
|||
let err = libc::pthread_key_delete(storage);
|
||||
|
||||
if err != 0 {
|
||||
panic!("pthread_key_delete was failed: {err}");
|
||||
panic!("pthread_key_delete failed: {err}");
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
unsafe fn free_storage(storage: Storage) {
|
||||
if windows_sys::Win32::System::Threading::FlsFree(storage) == 0 {
|
||||
panic!("FlsFree was failed: {}", Error::last_os_error());
|
||||
panic!("FlsFree failed: {}", Error::last_os_error());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -139,14 +139,14 @@ impl<T> Tls<T> {
|
|||
let err = libc::pthread_setspecific(storage, value as _);
|
||||
|
||||
if err != 0 {
|
||||
panic!("pthread_setspecific was failed: {err}");
|
||||
panic!("pthread_setspecific failed: {err}");
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
unsafe fn set_raw(storage: Storage, value: *mut T) {
|
||||
if windows_sys::Win32::System::Threading::FlsSetValue(storage, value as _) == 0 {
|
||||
panic!("FlsSetValue was failed: {}", Error::last_os_error());
|
||||
panic!("FlsSetValue failed: {}", Error::last_os_error());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue