diff options
| author | simonschoening <simon.schoening@rwth-aachen.de> | 2023-06-14 22:51:51 +0200 |
|---|---|---|
| committer | Stefan Lankes <slankes@eonerc.rwth-aachen.de> | 2024-03-01 09:20:36 +0100 |
| commit | aa11bf6f0a1ba4e6eb92f31423feb831fae2b7d2 (patch) | |
| tree | 40e0a98d6cb398f618f05432f88a3824260a3ebf | |
| parent | 384d26fc7e3bdd7687cc17b2662b091f6017ec2a (diff) | |
| download | rust-aa11bf6f0a1ba4e6eb92f31423feb831fae2b7d2.tar.gz rust-aa11bf6f0a1ba4e6eb92f31423feb831fae2b7d2.zip | |
Extending filesystem support for hermit-os
| -rw-r--r-- | library/std/src/sys/pal/hermit/fd.rs | 5 | ||||
| -rw-r--r-- | library/std/src/sys/pal/hermit/fs.rs | 306 | ||||
| -rw-r--r-- | library/std/src/sys/pal/hermit/time.rs | 10 |
3 files changed, 225 insertions, 96 deletions
diff --git a/library/std/src/sys/pal/hermit/fd.rs b/library/std/src/sys/pal/hermit/fd.rs index 5eb828fea1f..962577bb1ed 100644 --- a/library/std/src/sys/pal/hermit/fd.rs +++ b/library/std/src/sys/pal/hermit/fd.rs @@ -48,6 +48,11 @@ impl FileDesc { pub fn set_nonblocking(&self, _nonblocking: bool) -> io::Result<()> { unsupported() } + + pub fn fstat(&self, stat: *mut abi::stat) -> io::Result<()> { + cvt(unsafe { abi::fstat(self.fd.as_raw_fd(), stat) })?; + Ok(()) + } } impl<'a> Read for &'a FileDesc { diff --git a/library/std/src/sys/pal/hermit/fs.rs b/library/std/src/sys/pal/hermit/fs.rs index d4da53fd3df..306e4d2210a 100644 --- a/library/std/src/sys/pal/hermit/fs.rs +++ b/library/std/src/sys/pal/hermit/fs.rs @@ -2,11 +2,14 @@ use super::abi::{self, O_APPEND, O_CREAT, O_EXCL, O_RDONLY, O_RDWR, O_TRUNC, O_W use super::fd::FileDesc; use crate::ffi::{CStr, OsString}; use crate::fmt; -use crate::hash::{Hash, Hasher}; use crate::io::{self, Error, ErrorKind}; use crate::io::{BorrowedCursor, IoSlice, IoSliceMut, SeekFrom}; +use crate::mem; +use crate::os::hermit::ffi::OsStringExt; use crate::os::hermit::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, RawFd}; use crate::path::{Path, PathBuf}; +use crate::ptr; +use crate::sync::Arc; use crate::sys::common::small_c_string::run_path_with_cstr; use crate::sys::cvt; use crate::sys::time::SystemTime; @@ -18,12 +21,44 @@ pub use crate::sys_common::fs::{copy, try_exists}; #[derive(Debug)] pub struct File(FileDesc); +#[derive(Clone)] +pub struct FileAttr { + stat_val: stat_struct, +} -pub struct FileAttr(!); +impl FileAttr { + fn from_stat(stat_val: stat_struct) -> Self { + Self { stat_val } + } +} -pub struct ReadDir(!); +// all DirEntry's will have a reference to this struct +struct InnerReadDir { + dirp: FileDesc, + root: PathBuf, +} + +pub struct ReadDir { + inner: Arc<InnerReadDir>, + end_of_stream: bool, +} -pub struct DirEntry(!); +impl ReadDir { + fn new(inner: InnerReadDir) -> Self { + Self { inner: Arc::new(inner), end_of_stream: false } + } +} + +pub struct DirEntry { + dir: Arc<InnerReadDir>, + entry: dirent_min, + name: OsString, +} + +struct dirent_min { + d_ino: u64, + d_type: u32, +} #[derive(Clone, Debug)] pub struct OpenOptions { @@ -41,72 +76,78 @@ pub struct OpenOptions { #[derive(Copy, Clone, Debug, Default)] pub struct FileTimes {} -pub struct FilePermissions(!); - -pub struct FileType(!); +#[derive(Clone, PartialEq, Eq, Debug)] +pub struct FilePermissions { + mode: u32, +} -#[derive(Debug)] -pub struct DirBuilder {} +#[derive(Copy, Clone, Eq, Debug)] +pub struct FileType { + mode: u32, +} -impl FileAttr { - pub fn size(&self) -> u64 { - self.0 +impl PartialEq for FileType { + fn eq(&self, other: &Self) -> bool { + self.mode == other.mode } +} - pub fn perm(&self) -> FilePermissions { - self.0 +impl core::hash::Hash for FileType { + fn hash<H: core::hash::Hasher>(&self, state: &mut H) { + self.mode.hash(state); } +} - pub fn file_type(&self) -> FileType { - self.0 - } +#[derive(Debug)] +pub struct DirBuilder { + mode: u32, +} +impl FileAttr { pub fn modified(&self) -> io::Result<SystemTime> { - self.0 + Ok(SystemTime::new(self.stat_val.st_mtime, self.stat_val.st_mtime_nsec)) } pub fn accessed(&self) -> io::Result<SystemTime> { - self.0 + Ok(SystemTime::new(self.stat_val.st_atime, self.stat_val.st_atime_nsec)) } pub fn created(&self) -> io::Result<SystemTime> { - self.0 + Ok(SystemTime::new(self.stat_val.st_ctime, self.stat_val.st_ctime_nsec)) } -} -impl Clone for FileAttr { - fn clone(&self) -> FileAttr { - self.0 + pub fn size(&self) -> u64 { + self.stat_val.st_size as u64 } -} - -impl FilePermissions { - pub fn readonly(&self) -> bool { - self.0 + pub fn perm(&self) -> FilePermissions { + FilePermissions { mode: (self.stat_val.st_mode) } } - pub fn set_readonly(&mut self, _readonly: bool) { - self.0 + pub fn file_type(&self) -> FileType { + let masked_mode = self.stat_val.st_mode & S_IFMT; + let mode = match masked_mode { + S_IFDIR => DT_DIR, + S_IFLNK => DT_LNK, + S_IFREG => DT_REG, + _ => DT_UNKNOWN, + }; + FileType { mode: mode } } } -impl Clone for FilePermissions { - fn clone(&self) -> FilePermissions { - self.0 +impl FilePermissions { + pub fn readonly(&self) -> bool { + // check if any class (owner, group, others) has write permission + self.mode & 0o222 == 0 } -} -impl PartialEq for FilePermissions { - fn eq(&self, _other: &FilePermissions) -> bool { - self.0 + pub fn set_readonly(&mut self, _readonly: bool) { + unimplemented!() } -} - -impl Eq for FilePermissions {} -impl fmt::Debug for FilePermissions { - fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.0 + #[allow(dead_code)] + pub fn mode(&self) -> u32 { + self.mode as u32 } } @@ -117,75 +158,127 @@ impl FileTimes { impl FileType { pub fn is_dir(&self) -> bool { - self.0 + self.mode == DT_DIR } - pub fn is_file(&self) -> bool { - self.0 + self.mode == DT_REG } - pub fn is_symlink(&self) -> bool { - self.0 + self.mode == DT_LNK } } -impl Clone for FileType { - fn clone(&self) -> FileType { - self.0 +impl fmt::Debug for ReadDir { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + // This will only be called from std::fs::ReadDir, which will add a "ReadDir()" frame. + // Thus the result will be e g 'ReadDir("/home")' + fmt::Debug::fmt(&*self.inner.root, f) } } -impl Copy for FileType {} +impl Iterator for ReadDir { + type Item = io::Result<DirEntry>; -impl PartialEq for FileType { - fn eq(&self, _other: &FileType) -> bool { - self.0 - } -} + fn next(&mut self) -> Option<io::Result<DirEntry>> { + if self.end_of_stream { + return None; + } -impl Eq for FileType {} + unsafe { + loop { + // As of POSIX.1-2017, readdir() is not required to be thread safe; only + // readdir_r() is. However, readdir_r() cannot correctly handle platforms + // with unlimited or variable NAME_MAX. Many modern platforms guarantee + // thread safety for readdir() as long an individual DIR* is not accessed + // concurrently, which is sufficient for Rust. + let entry_ptr = match abi::readdir(self.inner.dirp.as_raw_fd()) { + abi::DirectoryEntry::Invalid(e) => { + // We either encountered an error, or reached the end. Either way, + // the next call to next() should return None. + self.end_of_stream = true; + + return Some(Err(Error::from_raw_os_error(e))); + } + abi::DirectoryEntry::Valid(ptr) => { + if ptr.is_null() { + return None; + } + + ptr + } + }; + + macro_rules! offset_ptr { + ($entry_ptr:expr, $field:ident) => {{ + const OFFSET: isize = { + let delusion = MaybeUninit::<dirent>::uninit(); + let entry_ptr = delusion.as_ptr(); + unsafe { + ptr::addr_of!((*entry_ptr).$field) + .cast::<u8>() + .offset_from(entry_ptr.cast::<u8>()) + } + }; + if true { + // Cast to the same type determined by the else branch. + $entry_ptr.byte_offset(OFFSET).cast::<_>() + } else { + #[allow(deref_nullptr)] + { + ptr::addr_of!((*ptr::null::<dirent>()).$field) + } + } + }}; + } -impl Hash for FileType { - fn hash<H: Hasher>(&self, _h: &mut H) { - self.0 - } -} + // d_name is NOT guaranteed to be null-terminated. + let name_bytes = core::slice::from_raw_parts( + offset_ptr!(entry_ptr, d_name) as *const u8, + *offset_ptr!(entry_ptr, d_namelen) as usize, + ) + .to_vec(); -impl fmt::Debug for FileType { - fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.0 - } -} + if name_bytes == b"." || name_bytes == b".." { + continue; + } -impl fmt::Debug for ReadDir { - fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.0 - } -} + let name = OsString::from_vec(name_bytes); -impl Iterator for ReadDir { - type Item = io::Result<DirEntry>; + let entry = dirent_min { + d_ino: *offset_ptr!(entry_ptr, d_ino), + d_type: *offset_ptr!(entry_ptr, d_type), + }; - fn next(&mut self) -> Option<io::Result<DirEntry>> { - self.0 + return Some(Ok(DirEntry { entry, name: name, dir: Arc::clone(&self.inner) })); + } + } } } impl DirEntry { pub fn path(&self) -> PathBuf { - self.0 + self.dir.root.join(self.file_name_os_str()) } pub fn file_name(&self) -> OsString { - self.0 + self.file_name_os_str().to_os_string() } pub fn metadata(&self) -> io::Result<FileAttr> { - self.0 + lstat(&self.path()) } pub fn file_type(&self) -> io::Result<FileType> { - self.0 + Ok(FileType { mode: self.entry.d_type }) + } + + #[allow(dead_code)] + pub fn ino(&self) -> u64 { + self.entry.d_ino + } + + pub fn file_name_os_str(&self) -> &OsStr { + self.name.as_os_str() } } @@ -288,7 +381,9 @@ impl File { } pub fn file_attr(&self) -> io::Result<FileAttr> { - Err(Error::from_raw_os_error(22)) + let mut stat_val: stat_struct = unsafe { mem::zeroed() }; + self.0.fstat(&mut stat_val)?; + Ok(FileAttr::from_stat(stat_val)) } pub fn fsync(&self) -> io::Result<()> { @@ -357,11 +452,18 @@ impl File { impl DirBuilder { pub fn new() -> DirBuilder { - DirBuilder {} + DirBuilder { mode: 0o777 } } - pub fn mkdir(&self, _p: &Path) -> io::Result<()> { - unsupported() + pub fn mkdir(&self, path: &Path) -> io::Result<()> { + run_path_with_cstr(path, |path| { + cvt(unsafe { abi::mkdir(path.as_ptr(), self.mode) }).map(|_| ()) + }) + } + + #[allow(dead_code)] + pub fn set_mode(&mut self, mode: u32) { + self.mode = mode as u32; } } @@ -416,8 +518,12 @@ impl FromRawFd for File { } } -pub fn readdir(_p: &Path) -> io::Result<ReadDir> { - unsupported() +pub fn readdir(path: &Path) -> io::Result<ReadDir> { + let fd_raw = run_path_with_cstr(path, |path| cvt(unsafe { abi::opendir(path.as_ptr()) }))?; + let fd = unsafe { FileDesc::from_raw_fd(fd_raw as i32) }; + let root = path.to_path_buf(); + let inner = InnerReadDir { dirp: fd, root }; + Ok(ReadDir::new(inner)) } pub fn unlink(path: &Path) -> io::Result<()> { @@ -428,12 +534,12 @@ pub fn rename(_old: &Path, _new: &Path) -> io::Result<()> { unsupported() } -pub fn set_perm(_p: &Path, perm: FilePermissions) -> io::Result<()> { - match perm.0 {} +pub fn set_perm(_p: &Path, _perm: FilePermissions) -> io::Result<()> { + Err(Error::from_raw_os_error(22)) } -pub fn rmdir(_p: &Path) -> io::Result<()> { - unsupported() +pub fn rmdir(path: &Path) -> io::Result<()> { + run_path_with_cstr(path, |path| cvt(unsafe { abi::rmdir(path.as_ptr()) }).map(|_| ())) } pub fn remove_dir_all(_path: &Path) -> io::Result<()> { @@ -453,12 +559,20 @@ pub fn link(_original: &Path, _link: &Path) -> io::Result<()> { unsupported() } -pub fn stat(_p: &Path) -> io::Result<FileAttr> { - unsupported() +pub fn stat(path: &Path) -> io::Result<FileAttr> { + run_path_with_cstr(path, |path| { + let mut stat_val: stat_struct = unsafe { mem::zeroed() }; + cvt(unsafe { abi::stat(path.as_ptr(), &mut stat_val) })?; + Ok(FileAttr::from_stat(stat_val)) + }) } -pub fn lstat(_p: &Path) -> io::Result<FileAttr> { - unsupported() +pub fn lstat(path: &Path) -> io::Result<FileAttr> { + run_path_with_cstr(path, |path| { + let mut stat_val: stat_struct = unsafe { mem::zeroed() }; + cvt(unsafe { abi::lstat(path.as_ptr(), &mut stat_val) })?; + Ok(FileAttr::from_stat(stat_val)) + }) } pub fn canonicalize(_p: &Path) -> io::Result<PathBuf> { diff --git a/library/std/src/sys/pal/hermit/time.rs b/library/std/src/sys/pal/hermit/time.rs index f289dafd8bc..fa10b7638ec 100644 --- a/library/std/src/sys/pal/hermit/time.rs +++ b/library/std/src/sys/pal/hermit/time.rs @@ -18,6 +18,12 @@ impl Timespec { Timespec { t: timespec { tv_sec: 0, tv_nsec: 0 } } } + const fn new(tv_sec: i64, tv_nsec: i64) -> Timespec { + assert!(tv_nsec >= 0 && tv_nsec < NSEC_PER_SEC as i64); + // SAFETY: The assert above checks tv_nsec is within the valid range + Timespec { t: timespec { tv_sec: tv_sec, tv_nsec: tv_nsec } } + } + fn sub_timespec(&self, other: &Timespec) -> Result<Duration, Duration> { if self >= other { Ok(if self.t.tv_nsec >= other.t.tv_nsec { @@ -195,6 +201,10 @@ pub struct SystemTime(Timespec); pub const UNIX_EPOCH: SystemTime = SystemTime(Timespec::zero()); impl SystemTime { + pub fn new(tv_sec: i64, tv_nsec: i64) -> SystemTime { + SystemTime(Timespec::new(tv_sec, tv_nsec)) + } + pub fn now() -> SystemTime { let mut time: Timespec = Timespec::zero(); let _ = unsafe { abi::clock_gettime(CLOCK_REALTIME, core::ptr::addr_of_mut!(time.t)) }; |
