From c91f514a4f3cdbfa7a2a22a73570f7bcd614f261 Mon Sep 17 00:00:00 2001 From: Stefan Lankes Date: Sat, 6 Jan 2024 13:05:44 +0000 Subject: [PATCH] extend Metadata to determine last access and modification time --- src/fs/fuse.rs | 14 ++-- src/fs/mem.rs | 162 +++++++++++++++++++++++++++++------------- src/fs/mod.rs | 29 ++++++-- src/lib.rs | 1 + src/syscalls/futex.rs | 4 +- src/syscalls/tasks.rs | 2 +- src/syscalls/timer.rs | 45 ++---------- src/time.rs | 80 +++++++++++++++++++++ tests/thread.rs | 3 +- 9 files changed, 232 insertions(+), 108 deletions(-) create mode 100644 src/time.rs diff --git a/src/fs/fuse.rs b/src/fs/fuse.rs index a994a21363..5b3777a201 100644 --- a/src/fs/fuse.rs +++ b/src/fs/fuse.rs @@ -226,15 +226,15 @@ impl From for FileAttr { st_uid: attr.uid, st_gid: attr.gid, st_rdev: attr.rdev as u64, - st_size: attr.size.try_into().unwrap(), + st_size: attr.size, st_blksize: attr.blksize as i64, st_blocks: attr.blocks.try_into().unwrap(), - st_atime: attr.atime.try_into().unwrap(), - st_atime_nsec: attr.atimensec as i64, - st_mtime: attr.mtime.try_into().unwrap(), - st_mtime_nsec: attr.atimensec as i64, - st_ctime: attr.ctime.try_into().unwrap(), - st_ctime_nsec: attr.ctimensec as i64, + st_atime: attr.atime, + st_atime_nsec: attr.atimensec as u64, + st_mtime: attr.mtime, + st_mtime_nsec: attr.atimensec as u64, + st_ctime: attr.ctime, + st_ctime_nsec: attr.ctimensec as u64, ..Default::default() } } diff --git a/src/fs/mem.rs b/src/fs/mem.rs index dc23653f21..e43a147366 100644 --- a/src/fs/mem.rs +++ b/src/fs/mem.rs @@ -14,25 +14,47 @@ use alloc::collections::BTreeMap; use alloc::string::{String, ToString}; use alloc::sync::Arc; use alloc::vec::Vec; -use core::ops::{Deref, DerefMut}; use core::slice; use hermit_sync::{RwSpinLock, SpinMutex}; +use crate::arch; use crate::fd::{AccessPermission, IoError, ObjectInterface, OpenOption}; use crate::fs::{DirectoryEntry, FileAttr, NodeKind, VfsNode}; +#[derive(Debug, Clone)] +pub(crate) struct RomFileInner { + pub data: &'static [u8], + pub attr: FileAttr, +} + +impl RomFileInner { + pub unsafe fn new(ptr: *const u8, length: usize, attr: FileAttr) -> Self { + Self { + data: unsafe { slice::from_raw_parts(ptr, length) }, + attr, + } + } +} + #[derive(Debug)] struct RomFileInterface { /// Position within the file pos: SpinMutex, /// File content - data: Arc>, + inner: Arc>, } impl ObjectInterface for RomFileInterface { fn read(&self, buf: &mut [u8]) -> Result { - let vec = self.data.read(); + { + let microseconds = arch::processor::get_timer_ticks() + arch::get_boot_time(); + let mut guard = self.inner.write(); + guard.attr.st_atime = microseconds / 1_000_000; + guard.attr.st_atime_nsec = (microseconds % 1_000_000) * 1000; + } + + let vec = self.inner.read().data; let mut pos_guard = self.pos.lock(); let pos = *pos_guard; @@ -54,16 +76,15 @@ impl ObjectInterface for RomFileInterface { } impl RomFileInterface { - pub fn new(data: Arc>) -> Self { + pub fn new(inner: Arc>) -> Self { Self { pos: SpinMutex::new(0), - data, + inner, } } pub fn len(&self) -> usize { - let guard = self.data.read(); - guard.len() + self.inner.read().data.len() } } @@ -71,7 +92,22 @@ impl Clone for RomFileInterface { fn clone(&self) -> Self { Self { pos: SpinMutex::new(*self.pos.lock()), - data: self.data.clone(), + inner: self.inner.clone(), + } + } +} + +#[derive(Debug, Clone)] +pub(crate) struct RamFileInner { + pub data: Vec, + pub attr: FileAttr, +} + +impl RamFileInner { + pub fn new(attr: FileAttr) -> Self { + Self { + data: Vec::new(), + attr, } } } @@ -81,43 +117,56 @@ pub struct RamFileInterface { /// Position within the file pos: SpinMutex, /// File content - data: Arc>>, + inner: Arc>, } impl ObjectInterface for RamFileInterface { fn read(&self, buf: &mut [u8]) -> Result { - let guard = self.data.read(); - let vec = guard.deref(); + { + let microseconds = arch::processor::get_timer_ticks() + arch::get_boot_time(); + let mut guard = self.inner.write(); + guard.attr.st_atime = microseconds / 1_000_000; + guard.attr.st_atime_nsec = (microseconds % 1_000_000) * 1000; + } + + let guard = self.inner.read(); let mut pos_guard = self.pos.lock(); let pos = *pos_guard; - if pos >= vec.len() { + if pos >= guard.data.len() { return Ok(0); } - let len = if vec.len() - pos < buf.len() { - vec.len() - pos + let len = if guard.data.len() - pos < buf.len() { + guard.data.len() - pos } else { buf.len() }; - buf[0..len].clone_from_slice(&vec[pos..pos + len]); + buf[0..len].clone_from_slice(&guard.data[pos..pos + len]); *pos_guard = pos + len; Ok(len) } fn write(&self, buf: &[u8]) -> Result { - let mut guard = self.data.write(); - let vec = guard.deref_mut(); + let microseconds = arch::processor::get_timer_ticks() + arch::get_boot_time(); + let mut guard = self.inner.write(); let mut pos_guard = self.pos.lock(); let pos = *pos_guard; - if pos + buf.len() > vec.len() { - vec.resize(pos + buf.len(), 0); + if pos + buf.len() > guard.data.len() { + guard.data.resize(pos + buf.len(), 0); + guard.attr.st_size = guard.data.len().try_into().unwrap(); } - - vec[pos..pos + buf.len()].clone_from_slice(buf); + guard.attr.st_atime = microseconds / 1_000_000; + guard.attr.st_atime_nsec = (microseconds % 1_000_000) * 1000; + guard.attr.st_mtime = guard.attr.st_atime; + guard.attr.st_mtime_nsec = guard.attr.st_atime_nsec; + guard.attr.st_ctime = guard.attr.st_atime; + guard.attr.st_ctime_nsec = guard.attr.st_atime_nsec; + + guard.data[pos..pos + buf.len()].clone_from_slice(buf); *pos_guard = pos + buf.len(); Ok(buf.len()) @@ -125,17 +174,15 @@ impl ObjectInterface for RamFileInterface { } impl RamFileInterface { - pub fn new(data: Arc>>) -> Self { + pub fn new(inner: Arc>) -> Self { Self { pos: SpinMutex::new(0), - data, + inner, } } pub fn len(&self) -> usize { - let guard = self.data.read(); - let vec: &Vec = guard.deref(); - vec.len() + self.inner.read().data.len() } } @@ -143,15 +190,14 @@ impl Clone for RamFileInterface { fn clone(&self) -> Self { Self { pos: SpinMutex::new(*self.pos.lock()), - data: self.data.clone(), + inner: self.inner.clone(), } } } #[derive(Debug)] pub(crate) struct RomFile { - data: Arc>, - attr: FileAttr, + data: Arc>, } impl VfsNode for RomFile { @@ -164,9 +210,7 @@ impl VfsNode for RomFile { } fn get_file_attributes(&self) -> Result { - let mut attr = self.attr; - attr.st_size = self.data.read().len().try_into().unwrap(); - Ok(attr) + Ok(self.data.read().attr) } fn traverse_lstat(&self, components: &mut Vec<&str>) -> Result { @@ -188,22 +232,28 @@ impl VfsNode for RomFile { impl RomFile { pub unsafe fn new(ptr: *const u8, length: usize, mode: AccessPermission) -> Self { + let microseconds = arch::processor::get_timer_ticks() + arch::get_boot_time(); + let attr = FileAttr { + st_size: length.try_into().unwrap(), + st_mode: mode | AccessPermission::S_IFREG, + st_atime: microseconds / 1_000_000, + st_atime_nsec: (microseconds % 1_000_000) * 1000, + st_mtime: microseconds / 1_000_000, + st_mtime_nsec: (microseconds % 1_000_000) * 1000, + st_ctime: microseconds / 1_000_000, + st_ctime_nsec: (microseconds % 1_000_000) * 1000, + ..Default::default() + }; + Self { - data: Arc::new(RwSpinLock::new(unsafe { - slice::from_raw_parts(ptr, length) - })), - attr: FileAttr { - st_mode: mode | AccessPermission::S_IFREG, - ..Default::default() - }, + data: unsafe { Arc::new(RwSpinLock::new(RomFileInner::new(ptr, length, attr))) }, } } } #[derive(Debug, Clone)] pub(crate) struct RamFile { - data: Arc>>, - attr: FileAttr, + data: Arc>, } impl VfsNode for RamFile { @@ -216,9 +266,7 @@ impl VfsNode for RamFile { } fn get_file_attributes(&self) -> Result { - let mut attr = self.attr; - attr.st_size = self.data.read().len().try_into().unwrap(); - Ok(attr) + Ok(self.data.read().attr) } fn traverse_lstat(&self, components: &mut Vec<&str>) -> Result { @@ -240,12 +288,20 @@ impl VfsNode for RamFile { impl RamFile { pub fn new(mode: AccessPermission) -> Self { + let microseconds = arch::processor::get_timer_ticks() + arch::get_boot_time(); + let attr = FileAttr { + st_mode: mode | AccessPermission::S_IFREG, + st_atime: microseconds / 1_000_000, + st_atime_nsec: (microseconds % 1_000_000) * 1000, + st_mtime: microseconds / 1_000_000, + st_mtime_nsec: (microseconds % 1_000_000) * 1000, + st_ctime: microseconds / 1_000_000, + st_ctime_nsec: (microseconds % 1_000_000) * 1000, + ..Default::default() + }; + Self { - data: Arc::new(RwSpinLock::new(Vec::new())), - attr: FileAttr { - st_mode: mode | AccessPermission::S_IFREG, - ..Default::default() - }, + data: Arc::new(RwSpinLock::new(RamFileInner::new(attr))), } } } @@ -260,10 +316,18 @@ pub(crate) struct MemDirectory { impl MemDirectory { pub fn new(mode: AccessPermission) -> Self { + let microseconds = arch::processor::get_timer_ticks() + arch::get_boot_time(); + Self { inner: Arc::new(RwSpinLock::new(BTreeMap::new())), attr: FileAttr { st_mode: mode | AccessPermission::S_IFDIR, + st_atime: microseconds / 1_000_000, + st_atime_nsec: (microseconds % 1_000_000) * 1000, + st_mtime: microseconds / 1_000_000, + st_mtime_nsec: (microseconds % 1_000_000) * 1000, + st_ctime: microseconds / 1_000_000, + st_ctime_nsec: (microseconds % 1_000_000) * 1000, ..Default::default() }, } diff --git a/src/fs/mod.rs b/src/fs/mod.rs index cdec07096f..101f7da748 100644 --- a/src/fs/mod.rs +++ b/src/fs/mod.rs @@ -18,6 +18,7 @@ use crate::fd::{ insert_object, AccessPermission, IoError, ObjectInterface, OpenOption, FD_COUNTER, }; use crate::io::Write; +use crate::time::{timespec, SystemTime}; pub(crate) static FILESYSTEM: OnceCell = OnceCell::new(); @@ -316,20 +317,20 @@ pub struct FileAttr { /// device id pub st_rdev: u64, /// size in bytes - pub st_size: i64, + pub st_size: u64, /// block size pub st_blksize: i64, /// size in blocks pub st_blocks: i64, /// time of last access - pub st_atime: i64, - pub st_atime_nsec: i64, + pub st_atime: u64, + pub st_atime_nsec: u64, /// time of last modification - pub st_mtime: i64, - pub st_mtime_nsec: i64, + pub st_mtime: u64, + pub st_mtime_nsec: u64, /// time of last status change - pub st_ctime: i64, - pub st_ctime_nsec: i64, + pub st_ctime: u64, + pub st_ctime_nsec: u64, } #[derive(Debug, FromPrimitive, ToPrimitive)] @@ -439,6 +440,20 @@ impl Metadata { pub fn is_dir(&self) -> bool { self.0.st_mode.contains(AccessPermission::S_IFDIR) } + + /// Returns the last modification time listed in this metadata. + pub fn modified(&self) -> Result { + Ok(SystemTime::from(timespec::from_usec( + self.0.st_mtime * 1_000_000 + self.0.st_mtime_nsec / 1000, + ))) + } + + /// Returns the last modification time listed in this metadata. + pub fn accessed(&self) -> Result { + Ok(SystemTime::from(timespec::from_usec( + self.0.st_atime * 1_000_000 + self.0.st_atime_nsec / 1000, + ))) + } } /// Given a path, query the file system to get information about a file, directory, etc. diff --git a/src/lib.rs b/src/lib.rs index 8752cde908..f4944e4f33 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -91,6 +91,7 @@ mod mm; mod scheduler; mod synch; pub mod syscalls; +pub mod time; #[cfg(target_os = "none")] hermit_entry::define_entry_version!(); diff --git a/src/syscalls/futex.rs b/src/syscalls/futex.rs index db301880fc..649aa46750 100644 --- a/src/syscalls/futex.rs +++ b/src/syscalls/futex.rs @@ -2,7 +2,7 @@ use core::sync::atomic::AtomicU32; use crate::errno::EINVAL; use crate::synch::futex::{self as synch, Flags}; -use crate::{timespec, timespec_to_microseconds}; +use crate::time::timespec; /// Like `synch::futex_wait`, but does extra sanity checks and takes a `timespec`. /// @@ -24,7 +24,7 @@ extern "C" fn __sys_futex_wait( let timeout = if timeout.is_null() { None } else { - match timespec_to_microseconds(unsafe { timeout.read() }) { + match unsafe { timeout.read().into_usec() } { t @ Some(_) => t, None => return -EINVAL, } diff --git a/src/syscalls/tasks.rs b/src/syscalls/tasks.rs index e4962dae15..62ec673715 100644 --- a/src/syscalls/tasks.rs +++ b/src/syscalls/tasks.rs @@ -15,7 +15,7 @@ use crate::errno::*; use crate::mm::{task_heap_end, task_heap_start}; use crate::scheduler::task::{Priority, TaskHandle, TaskId}; use crate::scheduler::PerCoreSchedulerExt; -use crate::syscalls::timer::timespec; +use crate::time::timespec; use crate::{arch, scheduler, syscalls}; #[cfg(feature = "newlib")] diff --git a/src/syscalls/timer.rs b/src/syscalls/timer.rs index 87a2df5d04..75ecf49a65 100644 --- a/src/syscalls/timer.rs +++ b/src/syscalls/timer.rs @@ -1,27 +1,7 @@ use crate::arch; use crate::errno::*; use crate::syscalls::__sys_usleep; - -#[derive(Copy, Clone, Debug)] -#[repr(C)] -pub struct itimerval { - pub it_interval: timeval, - pub it_value: timeval, -} - -#[derive(Copy, Clone, Debug)] -#[repr(C)] -pub struct timespec { - pub tv_sec: i64, - pub tv_nsec: i64, -} - -#[derive(Copy, Clone, Debug)] -#[repr(C)] -pub struct timeval { - pub tv_sec: i64, - pub tv_usec: i64, -} +use crate::time::{itimerval, timespec, timeval}; pub(crate) const CLOCK_REALTIME: u64 = 1; pub(crate) const CLOCK_PROCESS_CPUTIME_ID: u64 = 2; @@ -29,23 +9,6 @@ pub(crate) const CLOCK_THREAD_CPUTIME_ID: u64 = 3; pub(crate) const CLOCK_MONOTONIC: u64 = 4; pub(crate) const TIMER_ABSTIME: i32 = 4; -fn microseconds_to_timespec(microseconds: u64, result: &mut timespec) { - result.tv_sec = (microseconds / 1_000_000) as i64; - result.tv_nsec = ((microseconds % 1_000_000) * 1000) as i64; -} - -fn microseconds_to_timeval(microseconds: u64, result: &mut timeval) { - result.tv_sec = (microseconds / 1_000_000) as i64; - result.tv_usec = (microseconds % 1_000_000) as i64; -} - -pub(crate) fn timespec_to_microseconds(time: timespec) -> Option { - u64::try_from(time.tv_sec) - .ok() - .and_then(|secs| secs.checked_mul(1_000_000)) - .and_then(|millions| millions.checked_add(u64::try_from(time.tv_nsec).ok()? / 1000)) -} - /// Finds the resolution (or precision) of a clock. /// /// This function gets the clock resolution of the clock with `clock_id` and stores it in parameter `res`. @@ -66,7 +29,7 @@ extern "C" fn __sys_clock_getres(clock_id: u64, res: *mut timespec) -> i32 { match clock_id { CLOCK_REALTIME | CLOCK_PROCESS_CPUTIME_ID | CLOCK_THREAD_CPUTIME_ID | CLOCK_MONOTONIC => { // All clocks in Hermit have 1 microsecond resolution. - microseconds_to_timespec(1, result); + *result = timespec::from_usec(1); 0 } _ => { @@ -104,7 +67,7 @@ extern "C" fn __sys_clock_gettime(clock_id: u64, tp: *mut timespec) -> i32 { microseconds += arch::get_boot_time(); } - microseconds_to_timespec(microseconds, result); + *result = timespec::from_usec(microseconds); 0 } _ => { @@ -202,7 +165,7 @@ extern "C" fn __sys_gettimeofday(tp: *mut timeval, tz: usize) -> i32 { // Return the current time based on the wallclock time when we were booted up // plus the current timer ticks. let microseconds = arch::get_boot_time() + arch::processor::get_timer_ticks(); - microseconds_to_timeval(microseconds, result); + *result = timeval::from_usec(microseconds); } if tz > 0 { diff --git a/src/time.rs b/src/time.rs new file mode 100644 index 0000000000..d2633d14b9 --- /dev/null +++ b/src/time.rs @@ -0,0 +1,80 @@ +use crate::arch; + +/// Represent the number of seconds and microseconds since +/// the Epoch (1970-01-01 00:00:00 +0000 (UTC)) +#[derive(Copy, Clone, Debug)] +#[repr(C)] +pub struct timeval { + /// seconds + pub tv_sec: i64, + /// microseconds + pub tv_usec: i64, +} + +impl timeval { + pub fn from_usec(microseconds: u64) -> Self { + Self { + tv_sec: (microseconds / 1_000_000) as i64, + tv_usec: (microseconds % 1_000_000) as i64, + } + } + + pub fn into_usec(&self) -> Option { + u64::try_from(self.tv_sec) + .ok() + .and_then(|secs| secs.checked_mul(1_000_000)) + .and_then(|millions| millions.checked_add(u64::try_from(self.tv_usec).ok()?)) + } +} + +#[derive(Copy, Clone, Debug)] +#[repr(C)] +pub struct itimerval { + pub it_interval: timeval, + pub it_value: timeval, +} + +/// Represent the number of seconds and nanoseconds since +/// the Epoch (1970-01-01 00:00:00 +0000 (UTC)) +#[derive(Copy, Clone, Debug)] +#[repr(C)] +pub struct timespec { + /// seconds + pub tv_sec: i64, + /// nanoseconds + pub tv_nsec: i64, +} + +impl timespec { + pub fn from_usec(microseconds: u64) -> Self { + Self { + tv_sec: (microseconds / 1_000_000) as i64, + tv_nsec: ((microseconds % 1_000_000) * 1000) as i64, + } + } + + pub fn into_usec(&self) -> Option { + u64::try_from(self.tv_sec) + .ok() + .and_then(|secs| secs.checked_mul(1_000_000)) + .and_then(|millions| millions.checked_add(u64::try_from(self.tv_nsec).ok()? / 1000)) + } +} + +#[derive(Copy, Clone, Debug)] +pub struct SystemTime(timespec); + +impl SystemTime { + /// Returns the system time corresponding to "now". + pub fn now() -> Self { + let microseconds = arch::processor::get_timer_ticks() + arch::get_boot_time(); + + Self(timespec::from_usec(microseconds)) + } +} + +impl From for SystemTime { + fn from(t: timespec) -> Self { + Self(t) + } +} diff --git a/tests/thread.rs b/tests/thread.rs index 52ba5f3b1a..9a624b1e9b 100644 --- a/tests/thread.rs +++ b/tests/thread.rs @@ -18,7 +18,8 @@ mod common; use alloc::vec; use hermit::errno::{EAGAIN, ETIMEDOUT}; -use hermit::{sys_futex_wait, sys_futex_wake, sys_join, sys_spawn2, sys_usleep, timespec}; +use hermit::time::timespec; +use hermit::{sys_futex_wait, sys_futex_wake, sys_join, sys_spawn2, sys_usleep}; const USER_STACK_SIZE: usize = 1_048_576; const NORMAL_PRIO: u8 = 2;