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:
SuchAFuriousDeath 2024-02-15 14:54:19 +01:00 committed by GitHub
parent b441e29feb
commit 6324c4936a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
21 changed files with 871 additions and 135 deletions

View file

@ -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)]

View file

@ -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(),

View file

@ -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,
};

View file

@ -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)]

View file

@ -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")]

View 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")]

View file

@ -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
View 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() }
}
}

View file

@ -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")]

View file

@ -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")]

View file

@ -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 {

View file

@ -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)
}
}

View file

@ -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;

View file

@ -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)]

View 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,
}

View file

@ -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;

View file

@ -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();

View file

@ -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]);

View file

@ -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

View file

@ -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.

View file

@ -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());
}
}
}