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 d577cb6

Browse files
simonschoeningstlankes
authored andcommittedSep 26, 2023
Extending filesystem support for hermit-os
1 parent 15ca08a commit d577cb6

File tree

3 files changed

+232
-98
lines changed

3 files changed

+232
-98
lines changed
 

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

Lines changed: 5 additions & 0 deletions
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/hermit/fs.rs

Lines changed: 217 additions & 98 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,22 @@
1-
use crate::ffi::{CStr, OsString};
1+
use core::mem::MaybeUninit;
2+
3+
use abi::DT_UNKNOWN;
4+
5+
use crate::ffi::{CStr, OsStr, OsString};
26
use crate::fmt;
3-
use crate::hash::{Hash, Hasher};
47
use crate::io::{self, Error, ErrorKind};
58
use crate::io::{BorrowedCursor, IoSlice, IoSliceMut, SeekFrom};
9+
use crate::mem;
10+
use crate::os::hermit::ffi::OsStringExt;
611
use crate::os::hermit::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, RawFd};
712
use crate::path::{Path, PathBuf};
13+
use crate::ptr;
14+
use crate::sync::Arc;
815
use crate::sys::common::small_c_string::run_path_with_cstr;
916
use crate::sys::cvt;
1017
use crate::sys::hermit::abi::{
11-
self, O_APPEND, O_CREAT, O_EXCL, O_RDONLY, O_RDWR, O_TRUNC, O_WRONLY,
18+
self, dirent, stat as stat_struct, DT_DIR, DT_LNK, DT_REG, O_APPEND, O_CREAT, O_EXCL, O_RDONLY,
19+
O_RDWR, O_TRUNC, O_WRONLY, S_IFDIR, S_IFLNK, S_IFMT, S_IFREG,
1220
};
1321
use crate::sys::hermit::fd::FileDesc;
1422
use crate::sys::time::SystemTime;
@@ -20,12 +28,44 @@ pub use crate::sys_common::fs::{copy, try_exists};
2028

2129
#[derive(Debug)]
2230
pub struct File(FileDesc);
31+
#[derive(Clone)]
32+
pub struct FileAttr {
33+
stat_val: stat_struct,
34+
}
35+
36+
impl FileAttr {
37+
fn from_stat(stat_val: stat_struct) -> Self {
38+
Self { stat_val }
39+
}
40+
}
2341

24-
pub struct FileAttr(!);
42+
// all DirEntry's will have a reference to this struct
43+
struct InnerReadDir {
44+
dirp: FileDesc,
45+
root: PathBuf,
46+
}
2547

26-
pub struct ReadDir(!);
48+
pub struct ReadDir {
49+
inner: Arc<InnerReadDir>,
50+
end_of_stream: bool,
51+
}
2752

28-
pub struct DirEntry(!);
53+
impl ReadDir {
54+
fn new(inner: InnerReadDir) -> Self {
55+
Self { inner: Arc::new(inner), end_of_stream: false }
56+
}
57+
}
58+
59+
pub struct DirEntry {
60+
dir: Arc<InnerReadDir>,
61+
entry: dirent_min,
62+
name: OsString,
63+
}
64+
65+
struct dirent_min {
66+
d_ino: u64,
67+
d_type: u32,
68+
}
2969

3070
#[derive(Clone, Debug)]
3171
pub struct OpenOptions {
@@ -43,72 +83,78 @@ pub struct OpenOptions {
4383
#[derive(Copy, Clone, Debug, Default)]
4484
pub struct FileTimes {}
4585

46-
pub struct FilePermissions(!);
47-
48-
pub struct FileType(!);
86+
#[derive(Clone, PartialEq, Eq, Debug)]
87+
pub struct FilePermissions {
88+
mode: u32,
89+
}
4990

50-
#[derive(Debug)]
51-
pub struct DirBuilder {}
91+
#[derive(Copy, Clone, Eq, Debug)]
92+
pub struct FileType {
93+
mode: u32,
94+
}
5295

53-
impl FileAttr {
54-
pub fn size(&self) -> u64 {
55-
self.0
96+
impl PartialEq for FileType {
97+
fn eq(&self, other: &Self) -> bool {
98+
self.mode == other.mode
5699
}
100+
}
57101

58-
pub fn perm(&self) -> FilePermissions {
59-
self.0
102+
impl core::hash::Hash for FileType {
103+
fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
104+
self.mode.hash(state);
60105
}
106+
}
61107

62-
pub fn file_type(&self) -> FileType {
63-
self.0
64-
}
108+
#[derive(Debug)]
109+
pub struct DirBuilder {
110+
mode: u32,
111+
}
65112

113+
impl FileAttr {
66114
pub fn modified(&self) -> io::Result<SystemTime> {
67-
self.0
115+
Ok(SystemTime::new(self.stat_val.st_mtime, self.stat_val.st_mtime_nsec))
68116
}
69117

70118
pub fn accessed(&self) -> io::Result<SystemTime> {
71-
self.0
119+
Ok(SystemTime::new(self.stat_val.st_atime, self.stat_val.st_atime_nsec))
72120
}
73121

74122
pub fn created(&self) -> io::Result<SystemTime> {
75-
self.0
123+
Ok(SystemTime::new(self.stat_val.st_ctime, self.stat_val.st_ctime_nsec))
76124
}
77-
}
78125

79-
impl Clone for FileAttr {
80-
fn clone(&self) -> FileAttr {
81-
self.0
126+
pub fn size(&self) -> u64 {
127+
self.stat_val.st_size as u64
82128
}
83-
}
84-
85-
impl FilePermissions {
86-
pub fn readonly(&self) -> bool {
87-
self.0
129+
pub fn perm(&self) -> FilePermissions {
130+
FilePermissions { mode: (self.stat_val.st_mode) }
88131
}
89132

90-
pub fn set_readonly(&mut self, _readonly: bool) {
91-
self.0
133+
pub fn file_type(&self) -> FileType {
134+
let masked_mode = self.stat_val.st_mode & S_IFMT;
135+
let mode = match masked_mode {
136+
S_IFDIR => DT_DIR,
137+
S_IFLNK => DT_LNK,
138+
S_IFREG => DT_REG,
139+
_ => DT_UNKNOWN,
140+
};
141+
FileType { mode: mode }
92142
}
93143
}
94144

95-
impl Clone for FilePermissions {
96-
fn clone(&self) -> FilePermissions {
97-
self.0
145+
impl FilePermissions {
146+
pub fn readonly(&self) -> bool {
147+
// check if any class (owner, group, others) has write permission
148+
self.mode & 0o222 == 0
98149
}
99-
}
100150

101-
impl PartialEq for FilePermissions {
102-
fn eq(&self, _other: &FilePermissions) -> bool {
103-
self.0
151+
pub fn set_readonly(&mut self, _readonly: bool) {
152+
unimplemented!()
104153
}
105-
}
106154

107-
impl Eq for FilePermissions {}
108-
109-
impl fmt::Debug for FilePermissions {
110-
fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result {
111-
self.0
155+
#[allow(dead_code)]
156+
pub fn mode(&self) -> u32 {
157+
self.mode as u32
112158
}
113159
}
114160

@@ -119,75 +165,127 @@ impl FileTimes {
119165

120166
impl FileType {
121167
pub fn is_dir(&self) -> bool {
122-
self.0
168+
self.mode == DT_DIR
123169
}
124-
125170
pub fn is_file(&self) -> bool {
126-
self.0
171+
self.mode == DT_REG
127172
}
128-
129173
pub fn is_symlink(&self) -> bool {
130-
self.0
174+
self.mode == DT_LNK
131175
}
132176
}
133177

134-
impl Clone for FileType {
135-
fn clone(&self) -> FileType {
136-
self.0
178+
impl fmt::Debug for ReadDir {
179+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
180+
// This will only be called from std::fs::ReadDir, which will add a "ReadDir()" frame.
181+
// Thus the result will be e g 'ReadDir("/home")'
182+
fmt::Debug::fmt(&*self.inner.root, f)
137183
}
138184
}
139185

140-
impl Copy for FileType {}
186+
impl Iterator for ReadDir {
187+
type Item = io::Result<DirEntry>;
141188

142-
impl PartialEq for FileType {
143-
fn eq(&self, _other: &FileType) -> bool {
144-
self.0
145-
}
146-
}
189+
fn next(&mut self) -> Option<io::Result<DirEntry>> {
190+
if self.end_of_stream {
191+
return None;
192+
}
147193

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

150-
impl Hash for FileType {
151-
fn hash<H: Hasher>(&self, _h: &mut H) {
152-
self.0
153-
}
154-
}
241+
// d_name is NOT guaranteed to be null-terminated.
242+
let name_bytes = core::slice::from_raw_parts(
243+
offset_ptr!(entry_ptr, d_name) as *const u8,
244+
*offset_ptr!(entry_ptr, d_namelen) as usize,
245+
)
246+
.to_vec();
155247

156-
impl fmt::Debug for FileType {
157-
fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result {
158-
self.0
159-
}
160-
}
248+
if name_bytes == b"." || name_bytes == b".." {
249+
continue;
250+
}
161251

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

168-
impl Iterator for ReadDir {
169-
type Item = io::Result<DirEntry>;
254+
let entry = dirent_min {
255+
d_ino: *offset_ptr!(entry_ptr, d_ino),
256+
d_type: *offset_ptr!(entry_ptr, d_type),
257+
};
170258

171-
fn next(&mut self) -> Option<io::Result<DirEntry>> {
172-
self.0
259+
return Some(Ok(DirEntry { entry, name: name, dir: Arc::clone(&self.inner) }));
260+
}
261+
}
173262
}
174263
}
175264

176265
impl DirEntry {
177266
pub fn path(&self) -> PathBuf {
178-
self.0
267+
self.dir.root.join(self.file_name_os_str())
179268
}
180269

181270
pub fn file_name(&self) -> OsString {
182-
self.0
271+
self.file_name_os_str().to_os_string()
183272
}
184273

185274
pub fn metadata(&self) -> io::Result<FileAttr> {
186-
self.0
275+
lstat(&self.path())
187276
}
188277

189278
pub fn file_type(&self) -> io::Result<FileType> {
190-
self.0
279+
Ok(FileType { mode: self.entry.d_type })
280+
}
281+
282+
#[allow(dead_code)]
283+
pub fn ino(&self) -> u64 {
284+
self.entry.d_ino
285+
}
286+
287+
pub fn file_name_os_str(&self) -> &OsStr {
288+
self.name.as_os_str()
191289
}
192290
}
193291

@@ -290,7 +388,9 @@ impl File {
290388
}
291389

292390
pub fn file_attr(&self) -> io::Result<FileAttr> {
293-
Err(Error::from_raw_os_error(22))
391+
let mut stat_val: stat_struct = unsafe { mem::zeroed() };
392+
self.0.fstat(&mut stat_val)?;
393+
Ok(FileAttr::from_stat(stat_val))
294394
}
295395

296396
pub fn fsync(&self) -> io::Result<()> {
@@ -359,11 +459,18 @@ impl File {
359459

360460
impl DirBuilder {
361461
pub fn new() -> DirBuilder {
362-
DirBuilder {}
462+
DirBuilder { mode: 0o777 }
363463
}
364464

365-
pub fn mkdir(&self, _p: &Path) -> io::Result<()> {
366-
unsupported()
465+
pub fn mkdir(&self, path: &Path) -> io::Result<()> {
466+
run_path_with_cstr(path, |path| {
467+
cvt(unsafe { abi::mkdir(path.as_ptr(), self.mode) }).map(|_| ())
468+
})
469+
}
470+
471+
#[allow(dead_code)]
472+
pub fn set_mode(&mut self, mode: u32) {
473+
self.mode = mode as u32;
367474
}
368475
}
369476

@@ -418,8 +525,12 @@ impl FromRawFd for File {
418525
}
419526
}
420527

421-
pub fn readdir(_p: &Path) -> io::Result<ReadDir> {
422-
unsupported()
528+
pub fn readdir(path: &Path) -> io::Result<ReadDir> {
529+
let fd_raw = run_path_with_cstr(path, |path| cvt(unsafe { abi::opendir(path.as_ptr()) }))?;
530+
let fd = unsafe { FileDesc::from_raw_fd(fd_raw as i32) };
531+
let root = path.to_path_buf();
532+
let inner = InnerReadDir { dirp: fd, root };
533+
Ok(ReadDir::new(inner))
423534
}
424535

425536
pub fn unlink(path: &Path) -> io::Result<()> {
@@ -430,12 +541,12 @@ pub fn rename(_old: &Path, _new: &Path) -> io::Result<()> {
430541
unsupported()
431542
}
432543

433-
pub fn set_perm(_p: &Path, perm: FilePermissions) -> io::Result<()> {
434-
match perm.0 {}
544+
pub fn set_perm(_p: &Path, _perm: FilePermissions) -> io::Result<()> {
545+
Err(Error::from_raw_os_error(22))
435546
}
436547

437-
pub fn rmdir(_p: &Path) -> io::Result<()> {
438-
unsupported()
548+
pub fn rmdir(path: &Path) -> io::Result<()> {
549+
run_path_with_cstr(path, |path| cvt(unsafe { abi::rmdir(path.as_ptr()) }).map(|_| ()))
439550
}
440551

441552
pub fn remove_dir_all(_path: &Path) -> io::Result<()> {
@@ -455,12 +566,20 @@ pub fn link(_original: &Path, _link: &Path) -> io::Result<()> {
455566
unsupported()
456567
}
457568

458-
pub fn stat(_p: &Path) -> io::Result<FileAttr> {
459-
unsupported()
569+
pub fn stat(path: &Path) -> io::Result<FileAttr> {
570+
run_path_with_cstr(path, |path| {
571+
let mut stat_val: stat_struct = unsafe { mem::zeroed() };
572+
cvt(unsafe { abi::stat(path.as_ptr(), &mut stat_val) })?;
573+
Ok(FileAttr::from_stat(stat_val))
574+
})
460575
}
461576

462-
pub fn lstat(_p: &Path) -> io::Result<FileAttr> {
463-
unsupported()
577+
pub fn lstat(path: &Path) -> io::Result<FileAttr> {
578+
run_path_with_cstr(path, |path| {
579+
let mut stat_val: stat_struct = unsafe { mem::zeroed() };
580+
cvt(unsafe { abi::lstat(path.as_ptr(), &mut stat_val) })?;
581+
Ok(FileAttr::from_stat(stat_val))
582+
})
464583
}
465584

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

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

Lines changed: 10 additions & 0 deletions
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, &mut time.t as *mut timespec) };

0 commit comments

Comments
 (0)
Please sign in to comment.