From 0c02b1545233e54a6fd7e0a3af7b8c79b9b66bbf Mon Sep 17 00:00:00 2001 From: SuchAFuriousDeath <48620541+SuchAFuriousDeath@users.noreply.github.com> Date: Sat, 17 Feb 2024 01:00:17 +0100 Subject: [PATCH] Implements ftruncate and truncate (#654) --- src/kernel/src/fs/file.rs | 39 +++++++++++- src/kernel/src/fs/mod.rs | 116 +++++++++++++++++++++++++++++++++++ src/kernel/src/fs/vnode.rs | 15 ++++- src/kernel/src/kqueue/mod.rs | 18 ++++-- src/kernel/src/net/socket.rs | 12 +++- src/kernel/src/shm/mod.rs | 30 +++++++-- src/kernel/src/time/mod.rs | 11 +++- 7 files changed, 224 insertions(+), 17 deletions(-) diff --git a/src/kernel/src/fs/file.rs b/src/kernel/src/fs/file.rs index b0e04d078..b826dfee5 100644 --- a/src/kernel/src/fs/file.rs +++ b/src/kernel/src/fs/file.rs @@ -1,7 +1,7 @@ -use super::{IoCmd, Offset, Stat, Uio, UioMut, Vnode}; +use super::{IoCmd, Offset, Stat, TruncateLength, Uio, UioMut, Vnode}; use crate::dmem::BlockPool; use crate::errno::Errno; -use crate::errno::{ENOTTY, ENXIO, EOPNOTSUPP}; +use crate::errno::{EINVAL, ENOTTY, ENXIO, EOPNOTSUPP}; use crate::kqueue::KernelQueue; use crate::net::Socket; use crate::process::VThread; @@ -122,6 +122,20 @@ impl VFile { } } + pub fn truncate( + &self, + length: TruncateLength, + td: Option<&VThread>, + ) -> Result<(), Box> { + match &self.backend { + VFileType::Vnode(vn) => vn.truncate(self, length, td), + VFileType::Socket(so) | VFileType::IpcSocket(so) => so.truncate(self, length, td), + VFileType::KernelQueue(kq) => kq.truncate(self, length, td), + VFileType::SharedMemory(shm) => shm.truncate(self, length, td), + VFileType::Blockpool(bp) => bp.truncate(self, length, td), + } + } + pub fn is_seekable(&self) -> bool { matches!(self.backend, VFileType::Vnode(_)) } @@ -203,6 +217,16 @@ pub trait FileBackend: Debug + Send + Sync + 'static { #[allow(unused_variables)] fn stat(self: &Arc, file: &VFile, td: Option<&VThread>) -> Result>; + + #[allow(unused_variables)] + fn truncate( + self: &Arc, + file: &VFile, + length: TruncateLength, + td: Option<&VThread>, + ) -> Result<(), Box> { + Err(Box::new(DefaultError::TruncateNotSupported)) + } } #[derive(Debug, Error, Errno)] @@ -215,10 +239,19 @@ pub enum DefaultError { #[errno(ENXIO)] WriteNotSupported, - #[error("iocll is not supported")] + #[error("ioctl is not supported")] #[errno(ENOTTY)] IoctlNotSupported, + #[error("truncating is not supported")] + #[errno(ENXIO)] + TruncateNotSupported, + + /// This is used by some file backends to indicate that the operation is not supported. + #[error("invalid value provided")] + #[errno(EINVAL)] + InvalidValue, + #[error("operation is not supported")] #[errno(EOPNOTSUPP)] OperationNotSupported, diff --git a/src/kernel/src/fs/mod.rs b/src/kernel/src/fs/mod.rs index cdbf4b830..a64233928 100644 --- a/src/kernel/src/fs/mod.rs +++ b/src/kernel/src/fs/mod.rs @@ -147,6 +147,8 @@ impl Fs { sys.register(290, &fs, Self::sys_pwritev); sys.register(476, &fs, Self::sys_pwrite); sys.register(478, &fs, Self::sys_lseek); + sys.register(479, &fs, Self::sys_truncate); + sys.register(480, &fs, Self::sys_ftruncate); sys.register(493, &fs, Self::sys_fstatat); sys.register(496, &fs, Self::sys_mkdirat); @@ -797,6 +799,50 @@ impl Fs { todo!() } + fn sys_truncate(self: &Arc, i: &SysIn) -> Result { + let path = unsafe { i.args[0].to_path() }?.unwrap(); + let length = i.args[1].into(); + + let td = VThread::current().unwrap(); + + self.truncate(path, length, &td)?; + + Ok(SysOut::ZERO) + } + + fn truncate(&self, path: &VPath, length: i64, td: &VThread) -> Result<(), TruncateError> { + let _length: TruncateLength = length.try_into()?; + + let _vn = self.lookup(path, Some(&td))?; + + todo!() + } + + fn sys_ftruncate(self: &Arc, i: &SysIn) -> Result { + let fd = i.args[0].try_into().unwrap(); + let length = i.args[1].into(); + + let td = VThread::current().unwrap(); + + self.ftruncate(fd, length, &td)?; + + Ok(SysOut::ZERO) + } + + fn ftruncate(&self, fd: i32, length: i64, td: &VThread) -> Result<(), FileTruncateError> { + let length = length.try_into()?; + + let file = td.proc().files().get(fd)?; + + if !file.flags().contains(VFileFlags::WRITE) { + return Err(FileTruncateError::FileNotWritable); + } + + file.truncate(length, Some(&td))?; + + Ok(()) + } + fn sys_mkdirat(self: &Arc, i: &SysIn) -> Result { let td = VThread::current().unwrap(); @@ -1048,6 +1094,20 @@ struct PollFd { revents: i16, // likewise } +pub struct TruncateLength(i64); + +impl TryFrom for TruncateLength { + type Error = TruncateLengthError; + fn try_from(value: i64) -> Result { + if value < 0 { + Err(TruncateLengthError(())) + } else { + Ok(Self(value)) + } + } +} +pub struct TruncateLengthError(()); + /// Represents an error when FS was failed to initialized. #[derive(Debug, Error)] pub enum FsError { @@ -1200,6 +1260,62 @@ impl Errno for StatError { } } +#[derive(Debug, Error)] +pub enum TruncateError { + #[error("the provided length is invalid")] + InvalidLength, + + #[error("failed to get file")] + FailedToLookupFile(#[from] LookupError), +} + +impl Errno for TruncateError { + fn errno(&self) -> NonZeroI32 { + match self { + Self::InvalidLength => EINVAL, + Self::FailedToLookupFile(e) => e.errno(), + } + } +} + +impl From for TruncateError { + fn from(_: TruncateLengthError) -> Self { + Self::InvalidLength + } +} + +#[derive(Debug, Error)] +pub enum FileTruncateError { + #[error("the provided length is invalid")] + InvalidLength, + + #[error("failed to get file")] + FailedToGetFile(#[from] GetFileError), + + #[error("file is not writable")] + FileNotWritable, + + #[error(transparent)] + TruncateError(#[from] Box), +} + +impl Errno for FileTruncateError { + fn errno(&self) -> NonZeroI32 { + match self { + Self::InvalidLength => EINVAL, + Self::FailedToGetFile(e) => e.errno(), + Self::FileNotWritable => EINVAL, + Self::TruncateError(e) => e.errno(), + } + } +} + +impl From for FileTruncateError { + fn from(_: TruncateLengthError) -> Self { + Self::InvalidLength + } +} + static HOST: FsConfig = FsConfig { name: "exfatfs", ty: 0x2C, diff --git a/src/kernel/src/fs/vnode.rs b/src/kernel/src/fs/vnode.rs index f67d91b85..95eefb64f 100644 --- a/src/kernel/src/fs/vnode.rs +++ b/src/kernel/src/fs/vnode.rs @@ -1,6 +1,6 @@ use super::{ - unixify_access, Access, FileBackend, IoCmd, Mode, Mount, OpenFlags, RevokeFlags, Stat, Uio, - UioMut, VFile, + unixify_access, Access, FileBackend, IoCmd, Mode, Mount, OpenFlags, RevokeFlags, Stat, + TruncateLength, Uio, UioMut, VFile, }; use crate::errno::{Errno, ENOTDIR, ENOTTY, EOPNOTSUPP, EPERM}; use crate::process::VThread; @@ -154,9 +154,20 @@ impl FileBackend for Vnode { todo!() } + #[allow(unused_variables)] // TODO: remove when implementing fn stat(self: &Arc, file: &VFile, td: Option<&VThread>) -> Result> { todo!() } + + #[allow(unused_variables)] // TODO: remove when implementing + fn truncate( + self: &Arc, + file: &VFile, + length: TruncateLength, + td: Option<&VThread>, + ) -> Result<(), Box> { + todo!() + } } impl Drop for Vnode { diff --git a/src/kernel/src/kqueue/mod.rs b/src/kernel/src/kqueue/mod.rs index 9861b9cc9..d6c83c1ab 100644 --- a/src/kernel/src/kqueue/mod.rs +++ b/src/kernel/src/kqueue/mod.rs @@ -1,6 +1,7 @@ use crate::{ budget::BudgetType, - fs::{FileBackend, Stat, VFile, VFileFlags, VFileType}, + errno::Errno, + fs::{DefaultError, FileBackend, Stat, TruncateLength, VFile, VFileFlags, VFileType}, process::{FileDesc, VThread}, syscalls::{SysErr, SysIn, SysOut, Syscalls}, }; @@ -57,15 +58,20 @@ impl KernelQueue { } impl FileBackend for KernelQueue { - fn stat( - self: &Arc, - _: &VFile, - _: Option<&VThread>, - ) -> Result> { + fn stat(self: &Arc, _: &VFile, _: Option<&VThread>) -> Result> { let mut stat = Stat::zeroed(); stat.mode = 0o10000; Ok(stat) } + + fn truncate( + self: &Arc, + _: &VFile, + _: TruncateLength, + _: Option<&VThread>, + ) -> Result<(), Box> { + Err(DefaultError::InvalidValue.into()) + } } diff --git a/src/kernel/src/net/socket.rs b/src/kernel/src/net/socket.rs index 5db2dc3b7..09c4c68a4 100644 --- a/src/kernel/src/net/socket.rs +++ b/src/kernel/src/net/socket.rs @@ -1,4 +1,4 @@ -use crate::fs::{FileBackend, IoCmd, Stat, Uio, UioMut, VFile}; +use crate::fs::{DefaultError, FileBackend, IoCmd, Stat, TruncateLength, Uio, UioMut, VFile}; use crate::ucred::Ucred; use crate::{ errno::{Errno, EPIPE}, @@ -101,9 +101,19 @@ impl FileBackend for Socket { todo!() } + #[allow(unused_variables)] // TODO: remove when implementing fn stat(self: &Arc, file: &VFile, td: Option<&VThread>) -> Result> { todo!() } + + fn truncate( + self: &Arc, + _: &VFile, + _: TruncateLength, + _: Option<&VThread>, + ) -> Result<(), Box> { + Err(DefaultError::InvalidValue.into()) + } } #[derive(Debug, Error)] diff --git a/src/kernel/src/shm/mod.rs b/src/kernel/src/shm/mod.rs index 407e355f2..20de5a156 100644 --- a/src/kernel/src/shm/mod.rs +++ b/src/kernel/src/shm/mod.rs @@ -1,15 +1,17 @@ +use thiserror::Error; + use crate::{ errno::{Errno, EINVAL}, fs::{ - check_access, Access, DefaultError, FileBackend, IoCmd, Mode, OpenFlags, Stat, Uio, UioMut, - VFile, VFileFlags, VPathBuf, + check_access, Access, DefaultError, FileBackend, IoCmd, Mode, OpenFlags, Stat, + TruncateLength, Uio, UioMut, VFile, VFileFlags, VPathBuf, }, memory::MemoryManager, process::VThread, syscalls::{SysErr, SysIn, SysOut, Syscalls}, ucred::{Gid, Ucred, Uid}, }; -use std::{convert::Infallible, sync::Arc}; +use std::{convert::Infallible, num::NonZeroI32, sync::Arc}; pub struct SharedMemoryManager { mm: Arc, @@ -85,7 +87,7 @@ pub struct Shm { impl Shm { /// See `shm_do_truncate` on the PS4 for a reference. - fn truncate(&self, size: usize) { + fn do_truncate(&self, length: TruncateLength) -> Result<(), TruncateError> { todo!() } @@ -146,4 +148,24 @@ impl FileBackend for Shm { todo!() } + + fn truncate( + self: &Arc, + _: &VFile, + length: TruncateLength, + _: Option<&VThread>, + ) -> Result<(), Box> { + self.do_truncate(length)?; + + Ok(()) + } +} + +#[derive(Debug, Error)] +pub enum TruncateError {} + +impl Errno for TruncateError { + fn errno(&self) -> NonZeroI32 { + match *self {} + } } diff --git a/src/kernel/src/time/mod.rs b/src/kernel/src/time/mod.rs index b382f37c8..0197c1d00 100644 --- a/src/kernel/src/time/mod.rs +++ b/src/kernel/src/time/mod.rs @@ -50,7 +50,16 @@ pub struct TimeSpec { impl TimeSpec { pub fn now() -> Self { - todo!() + TimeVal::microtime().expect("Couldn't get time").into() + } +} + +impl From for TimeSpec { + fn from(tv: TimeVal) -> Self { + Self { + sec: tv.sec, + nsec: tv.usec * 1000, + } } }