Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 29fd33f

Browse files
simonschoeningstlankes
authored andcommittedMar 1, 2024
Extending filesystem support for hermit-os
1 parent 8229824 commit 29fd33f

File tree

3 files changed

+225
-96
lines changed

3 files changed

+225
-96
lines changed
 

‎library/std/src/sys/pal/hermit/fd.rs

+5
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,11 @@ impl FileDesc {
4848
pub fn set_nonblocking(&self, _nonblocking: bool) -> io::Result<()> {
4949
unsupported()
5050
}
51+
52+
pub fn fstat(&self, stat: *mut abi::stat) -> io::Result<()> {
53+
cvt(unsafe { abi::fstat(self.fd.as_raw_fd(), stat) })?;
54+
Ok(())
55+
}
5156
}
5257

5358
impl<'a> Read for &'a FileDesc {

‎library/std/src/sys/pal/hermit/fs.rs

+210-96
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,14 @@ use super::abi::{self, O_APPEND, O_CREAT, O_EXCL, O_RDONLY, O_RDWR, O_TRUNC, O_W
22
use super::fd::FileDesc;
33
use crate::ffi::{CStr, OsString};
44
use crate::fmt;
5-
use crate::hash::{Hash, Hasher};
65
use crate::io::{self, Error, ErrorKind};
76
use crate::io::{BorrowedCursor, IoSlice, IoSliceMut, SeekFrom};
7+
use crate::mem;
8+
use crate::os::hermit::ffi::OsStringExt;
89
use crate::os::hermit::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, RawFd};
910
use crate::path::{Path, PathBuf};
11+
use crate::ptr;
12+
use crate::sync::Arc;
1013
use crate::sys::common::small_c_string::run_path_with_cstr;
1114
use crate::sys::cvt;
1215
use crate::sys::time::SystemTime;
@@ -18,12 +21,44 @@ pub use crate::sys_common::fs::{copy, try_exists};
1821

1922
#[derive(Debug)]
2023
pub struct File(FileDesc);
24+
#[derive(Clone)]
25+
pub struct FileAttr {
26+
stat_val: stat_struct,
27+
}
2128

22-
pub struct FileAttr(!);
29+
impl FileAttr {
30+
fn from_stat(stat_val: stat_struct) -> Self {
31+
Self { stat_val }
32+
}
33+
}
2334

24-
pub struct ReadDir(!);
35+
// all DirEntry's will have a reference to this struct
36+
struct InnerReadDir {
37+
dirp: FileDesc,
38+
root: PathBuf,
39+
}
40+
41+
pub struct ReadDir {
42+
inner: Arc<InnerReadDir>,
43+
end_of_stream: bool,
44+
}
2545

26-
pub struct DirEntry(!);
46+
impl ReadDir {
47+
fn new(inner: InnerReadDir) -> Self {
48+
Self { inner: Arc::new(inner), end_of_stream: false }
49+
}
50+
}
51+
52+
pub struct DirEntry {
53+
dir: Arc<InnerReadDir>,
54+
entry: dirent_min,
55+
name: OsString,
56+
}
57+
58+
struct dirent_min {
59+
d_ino: u64,
60+
d_type: u32,
61+
}
2762

2863
#[derive(Clone, Debug)]
2964
pub struct OpenOptions {
@@ -41,72 +76,78 @@ pub struct OpenOptions {
4176
#[derive(Copy, Clone, Debug, Default)]
4277
pub struct FileTimes {}
4378

44-
pub struct FilePermissions(!);
45-
46-
pub struct FileType(!);
79+
#[derive(Clone, PartialEq, Eq, Debug)]
80+
pub struct FilePermissions {
81+
mode: u32,
82+
}
4783

48-
#[derive(Debug)]
49-
pub struct DirBuilder {}
84+
#[derive(Copy, Clone, Eq, Debug)]
85+
pub struct FileType {
86+
mode: u32,
87+
}
5088

51-
impl FileAttr {
52-
pub fn size(&self) -> u64 {
53-
self.0
89+
impl PartialEq for FileType {
90+
fn eq(&self, other: &Self) -> bool {
91+
self.mode == other.mode
5492
}
93+
}
5594

56-
pub fn perm(&self) -> FilePermissions {
57-
self.0
95+
impl core::hash::Hash for FileType {
96+
fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
97+
self.mode.hash(state);
5898
}
99+
}
59100

60-
pub fn file_type(&self) -> FileType {
61-
self.0
62-
}
101+
#[derive(Debug)]
102+
pub struct DirBuilder {
103+
mode: u32,
104+
}
63105

106+
impl FileAttr {
64107
pub fn modified(&self) -> io::Result<SystemTime> {
65-
self.0
108+
Ok(SystemTime::new(self.stat_val.st_mtime, self.stat_val.st_mtime_nsec))
66109
}
67110

68111
pub fn accessed(&self) -> io::Result<SystemTime> {
69-
self.0
112+
Ok(SystemTime::new(self.stat_val.st_atime, self.stat_val.st_atime_nsec))
70113
}
71114

72115
pub fn created(&self) -> io::Result<SystemTime> {
73-
self.0
116+
Ok(SystemTime::new(self.stat_val.st_ctime, self.stat_val.st_ctime_nsec))
74117
}
75-
}
76118

77-
impl Clone for FileAttr {
78-
fn clone(&self) -> FileAttr {
79-
self.0
119+
pub fn size(&self) -> u64 {
120+
self.stat_val.st_size as u64
80121
}
81-
}
82-
83-
impl FilePermissions {
84-
pub fn readonly(&self) -> bool {
85-
self.0
122+
pub fn perm(&self) -> FilePermissions {
123+
FilePermissions { mode: (self.stat_val.st_mode) }
86124
}
87125

88-
pub fn set_readonly(&mut self, _readonly: bool) {
89-
self.0
126+
pub fn file_type(&self) -> FileType {
127+
let masked_mode = self.stat_val.st_mode & S_IFMT;
128+
let mode = match masked_mode {
129+
S_IFDIR => DT_DIR,
130+
S_IFLNK => DT_LNK,
131+
S_IFREG => DT_REG,
132+
_ => DT_UNKNOWN,
133+
};
134+
FileType { mode: mode }
90135
}
91136
}
92137

93-
impl Clone for FilePermissions {
94-
fn clone(&self) -> FilePermissions {
95-
self.0
138+
impl FilePermissions {
139+
pub fn readonly(&self) -> bool {
140+
// check if any class (owner, group, others) has write permission
141+
self.mode & 0o222 == 0
96142
}
97-
}
98143

99-
impl PartialEq for FilePermissions {
100-
fn eq(&self, _other: &FilePermissions) -> bool {
101-
self.0
144+
pub fn set_readonly(&mut self, _readonly: bool) {
145+
unimplemented!()
102146
}
103-
}
104-
105-
impl Eq for FilePermissions {}
106147

107-
impl fmt::Debug for FilePermissions {
108-
fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result {
109-
self.0
148+
#[allow(dead_code)]
149+
pub fn mode(&self) -> u32 {
150+
self.mode as u32
110151
}
111152
}
112153

@@ -117,75 +158,127 @@ impl FileTimes {
117158

118159
impl FileType {
119160
pub fn is_dir(&self) -> bool {
120-
self.0
161+
self.mode == DT_DIR
121162
}
122-
123163
pub fn is_file(&self) -> bool {
124-
self.0
164+
self.mode == DT_REG
125165
}
126-
127166
pub fn is_symlink(&self) -> bool {
128-
self.0
167+
self.mode == DT_LNK
129168
}
130169
}
131170

132-
impl Clone for FileType {
133-
fn clone(&self) -> FileType {
134-
self.0
171+
impl fmt::Debug for ReadDir {
172+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
173+
// This will only be called from std::fs::ReadDir, which will add a "ReadDir()" frame.
174+
// Thus the result will be e g 'ReadDir("/home")'
175+
fmt::Debug::fmt(&*self.inner.root, f)
135176
}
136177
}
137178

138-
impl Copy for FileType {}
179+
impl Iterator for ReadDir {
180+
type Item = io::Result<DirEntry>;
139181

140-
impl PartialEq for FileType {
141-
fn eq(&self, _other: &FileType) -> bool {
142-
self.0
143-
}
144-
}
182+
fn next(&mut self) -> Option<io::Result<DirEntry>> {
183+
if self.end_of_stream {
184+
return None;
185+
}
145186

146-
impl Eq for FileType {}
187+
unsafe {
188+
loop {
189+
// As of POSIX.1-2017, readdir() is not required to be thread safe; only
190+
// readdir_r() is. However, readdir_r() cannot correctly handle platforms
191+
// with unlimited or variable NAME_MAX. Many modern platforms guarantee
192+
// thread safety for readdir() as long an individual DIR* is not accessed
193+
// concurrently, which is sufficient for Rust.
194+
let entry_ptr = match abi::readdir(self.inner.dirp.as_raw_fd()) {
195+
abi::DirectoryEntry::Invalid(e) => {
196+
// We either encountered an error, or reached the end. Either way,
197+
// the next call to next() should return None.
198+
self.end_of_stream = true;
199+
200+
return Some(Err(Error::from_raw_os_error(e)));
201+
}
202+
abi::DirectoryEntry::Valid(ptr) => {
203+
if ptr.is_null() {
204+
return None;
205+
}
206+
207+
ptr
208+
}
209+
};
210+
211+
macro_rules! offset_ptr {
212+
($entry_ptr:expr, $field:ident) => {{
213+
const OFFSET: isize = {
214+
let delusion = MaybeUninit::<dirent>::uninit();
215+
let entry_ptr = delusion.as_ptr();
216+
unsafe {
217+
ptr::addr_of!((*entry_ptr).$field)
218+
.cast::<u8>()
219+
.offset_from(entry_ptr.cast::<u8>())
220+
}
221+
};
222+
if true {
223+
// Cast to the same type determined by the else branch.
224+
$entry_ptr.byte_offset(OFFSET).cast::<_>()
225+
} else {
226+
#[allow(deref_nullptr)]
227+
{
228+
ptr::addr_of!((*ptr::null::<dirent>()).$field)
229+
}
230+
}
231+
}};
232+
}
147233

148-
impl Hash for FileType {
149-
fn hash<H: Hasher>(&self, _h: &mut H) {
150-
self.0
151-
}
152-
}
234+
// d_name is NOT guaranteed to be null-terminated.
235+
let name_bytes = core::slice::from_raw_parts(
236+
offset_ptr!(entry_ptr, d_name) as *const u8,
237+
*offset_ptr!(entry_ptr, d_namelen) as usize,
238+
)
239+
.to_vec();
153240

154-
impl fmt::Debug for FileType {
155-
fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result {
156-
self.0
157-
}
158-
}
241+
if name_bytes == b"." || name_bytes == b".." {
242+
continue;
243+
}
159244

160-
impl fmt::Debug for ReadDir {
161-
fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result {
162-
self.0
163-
}
164-
}
245+
let name = OsString::from_vec(name_bytes);
165246

166-
impl Iterator for ReadDir {
167-
type Item = io::Result<DirEntry>;
247+
let entry = dirent_min {
248+
d_ino: *offset_ptr!(entry_ptr, d_ino),
249+
d_type: *offset_ptr!(entry_ptr, d_type),
250+
};
168251

169-
fn next(&mut self) -> Option<io::Result<DirEntry>> {
170-
self.0
252+
return Some(Ok(DirEntry { entry, name: name, dir: Arc::clone(&self.inner) }));
253+
}
254+
}
171255
}
172256
}
173257

174258
impl DirEntry {
175259
pub fn path(&self) -> PathBuf {
176-
self.0
260+
self.dir.root.join(self.file_name_os_str())
177261
}
178262

179263
pub fn file_name(&self) -> OsString {
180-
self.0
264+
self.file_name_os_str().to_os_string()
181265
}
182266

183267
pub fn metadata(&self) -> io::Result<FileAttr> {
184-
self.0
268+
lstat(&self.path())
185269
}
186270

187271
pub fn file_type(&self) -> io::Result<FileType> {
188-
self.0
272+
Ok(FileType { mode: self.entry.d_type })
273+
}
274+
275+
#[allow(dead_code)]
276+
pub fn ino(&self) -> u64 {
277+
self.entry.d_ino
278+
}
279+
280+
pub fn file_name_os_str(&self) -> &OsStr {
281+
self.name.as_os_str()
189282
}
190283
}
191284

@@ -288,7 +381,9 @@ impl File {
288381
}
289382

290383
pub fn file_attr(&self) -> io::Result<FileAttr> {
291-
Err(Error::from_raw_os_error(22))
384+
let mut stat_val: stat_struct = unsafe { mem::zeroed() };
385+
self.0.fstat(&mut stat_val)?;
386+
Ok(FileAttr::from_stat(stat_val))
292387
}
293388

294389
pub fn fsync(&self) -> io::Result<()> {
@@ -357,11 +452,18 @@ impl File {
357452

358453
impl DirBuilder {
359454
pub fn new() -> DirBuilder {
360-
DirBuilder {}
455+
DirBuilder { mode: 0o777 }
361456
}
362457

363-
pub fn mkdir(&self, _p: &Path) -> io::Result<()> {
364-
unsupported()
458+
pub fn mkdir(&self, path: &Path) -> io::Result<()> {
459+
run_path_with_cstr(path, |path| {
460+
cvt(unsafe { abi::mkdir(path.as_ptr(), self.mode) }).map(|_| ())
461+
})
462+
}
463+
464+
#[allow(dead_code)]
465+
pub fn set_mode(&mut self, mode: u32) {
466+
self.mode = mode as u32;
365467
}
366468
}
367469

@@ -416,8 +518,12 @@ impl FromRawFd for File {
416518
}
417519
}
418520

419-
pub fn readdir(_p: &Path) -> io::Result<ReadDir> {
420-
unsupported()
521+
pub fn readdir(path: &Path) -> io::Result<ReadDir> {
522+
let fd_raw = run_path_with_cstr(path, |path| cvt(unsafe { abi::opendir(path.as_ptr()) }))?;
523+
let fd = unsafe { FileDesc::from_raw_fd(fd_raw as i32) };
524+
let root = path.to_path_buf();
525+
let inner = InnerReadDir { dirp: fd, root };
526+
Ok(ReadDir::new(inner))
421527
}
422528

423529
pub fn unlink(path: &Path) -> io::Result<()> {
@@ -428,12 +534,12 @@ pub fn rename(_old: &Path, _new: &Path) -> io::Result<()> {
428534
unsupported()
429535
}
430536

431-
pub fn set_perm(_p: &Path, perm: FilePermissions) -> io::Result<()> {
432-
match perm.0 {}
537+
pub fn set_perm(_p: &Path, _perm: FilePermissions) -> io::Result<()> {
538+
Err(Error::from_raw_os_error(22))
433539
}
434540

435-
pub fn rmdir(_p: &Path) -> io::Result<()> {
436-
unsupported()
541+
pub fn rmdir(path: &Path) -> io::Result<()> {
542+
run_path_with_cstr(path, |path| cvt(unsafe { abi::rmdir(path.as_ptr()) }).map(|_| ()))
437543
}
438544

439545
pub fn remove_dir_all(_path: &Path) -> io::Result<()> {
@@ -453,12 +559,20 @@ pub fn link(_original: &Path, _link: &Path) -> io::Result<()> {
453559
unsupported()
454560
}
455561

456-
pub fn stat(_p: &Path) -> io::Result<FileAttr> {
457-
unsupported()
562+
pub fn stat(path: &Path) -> io::Result<FileAttr> {
563+
run_path_with_cstr(path, |path| {
564+
let mut stat_val: stat_struct = unsafe { mem::zeroed() };
565+
cvt(unsafe { abi::stat(path.as_ptr(), &mut stat_val) })?;
566+
Ok(FileAttr::from_stat(stat_val))
567+
})
458568
}
459569

460-
pub fn lstat(_p: &Path) -> io::Result<FileAttr> {
461-
unsupported()
570+
pub fn lstat(path: &Path) -> io::Result<FileAttr> {
571+
run_path_with_cstr(path, |path| {
572+
let mut stat_val: stat_struct = unsafe { mem::zeroed() };
573+
cvt(unsafe { abi::lstat(path.as_ptr(), &mut stat_val) })?;
574+
Ok(FileAttr::from_stat(stat_val))
575+
})
462576
}
463577

464578
pub fn canonicalize(_p: &Path) -> io::Result<PathBuf> {

‎library/std/src/sys/pal/hermit/time.rs

+10
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,12 @@ impl Timespec {
1818
Timespec { t: timespec { tv_sec: 0, tv_nsec: 0 } }
1919
}
2020

21+
const fn new(tv_sec: i64, tv_nsec: i64) -> Timespec {
22+
assert!(tv_nsec >= 0 && tv_nsec < NSEC_PER_SEC as i64);
23+
// SAFETY: The assert above checks tv_nsec is within the valid range
24+
Timespec { t: timespec { tv_sec: tv_sec, tv_nsec: tv_nsec } }
25+
}
26+
2127
fn sub_timespec(&self, other: &Timespec) -> Result<Duration, Duration> {
2228
if self >= other {
2329
Ok(if self.t.tv_nsec >= other.t.tv_nsec {
@@ -195,6 +201,10 @@ pub struct SystemTime(Timespec);
195201
pub const UNIX_EPOCH: SystemTime = SystemTime(Timespec::zero());
196202

197203
impl SystemTime {
204+
pub fn new(tv_sec: i64, tv_nsec: i64) -> SystemTime {
205+
SystemTime(Timespec::new(tv_sec, tv_nsec))
206+
}
207+
198208
pub fn now() -> SystemTime {
199209
let mut time: Timespec = Timespec::zero();
200210
let _ = unsafe { abi::clock_gettime(CLOCK_REALTIME, core::ptr::addr_of_mut!(time.t)) };

0 commit comments

Comments
 (0)
Please sign in to comment.