@@ -2,11 +2,14 @@ use super::abi::{self, O_APPEND, O_CREAT, O_EXCL, O_RDONLY, O_RDWR, O_TRUNC, O_W
2
2
use super :: fd:: FileDesc ;
3
3
use crate :: ffi:: { CStr , OsString } ;
4
4
use crate :: fmt;
5
- use crate :: hash:: { Hash , Hasher } ;
6
5
use crate :: io:: { self , Error , ErrorKind } ;
7
6
use crate :: io:: { BorrowedCursor , IoSlice , IoSliceMut , SeekFrom } ;
7
+ use crate :: mem;
8
+ use crate :: os:: hermit:: ffi:: OsStringExt ;
8
9
use crate :: os:: hermit:: io:: { AsFd , AsRawFd , BorrowedFd , FromRawFd , IntoRawFd , RawFd } ;
9
10
use crate :: path:: { Path , PathBuf } ;
11
+ use crate :: ptr;
12
+ use crate :: sync:: Arc ;
10
13
use crate :: sys:: common:: small_c_string:: run_path_with_cstr;
11
14
use crate :: sys:: cvt;
12
15
use crate :: sys:: time:: SystemTime ;
@@ -18,12 +21,44 @@ pub use crate::sys_common::fs::{copy, try_exists};
18
21
19
22
#[ derive( Debug ) ]
20
23
pub struct File ( FileDesc ) ;
24
+ #[ derive( Clone ) ]
25
+ pub struct FileAttr {
26
+ stat_val : stat_struct ,
27
+ }
21
28
22
- pub struct FileAttr ( !) ;
29
+ impl FileAttr {
30
+ fn from_stat ( stat_val : stat_struct ) -> Self {
31
+ Self { stat_val }
32
+ }
33
+ }
23
34
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
+ }
25
45
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
+ }
27
62
28
63
#[ derive( Clone , Debug ) ]
29
64
pub struct OpenOptions {
@@ -41,72 +76,78 @@ pub struct OpenOptions {
41
76
#[ derive( Copy , Clone , Debug , Default ) ]
42
77
pub struct FileTimes { }
43
78
44
- pub struct FilePermissions ( !) ;
45
-
46
- pub struct FileType ( !) ;
79
+ #[ derive( Clone , PartialEq , Eq , Debug ) ]
80
+ pub struct FilePermissions {
81
+ mode : u32 ,
82
+ }
47
83
48
- #[ derive( Debug ) ]
49
- pub struct DirBuilder { }
84
+ #[ derive( Copy , Clone , Eq , Debug ) ]
85
+ pub struct FileType {
86
+ mode : u32 ,
87
+ }
50
88
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
54
92
}
93
+ }
55
94
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) ;
58
98
}
99
+ }
59
100
60
- pub fn file_type ( & self ) -> FileType {
61
- self . 0
62
- }
101
+ #[ derive( Debug ) ]
102
+ pub struct DirBuilder {
103
+ mode : u32 ,
104
+ }
63
105
106
+ impl FileAttr {
64
107
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 ) )
66
109
}
67
110
68
111
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 ) )
70
113
}
71
114
72
115
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 ) )
74
117
}
75
- }
76
118
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
80
121
}
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 ) }
86
124
}
87
125
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 }
90
135
}
91
136
}
92
137
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
96
142
}
97
- }
98
143
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 ! ( )
102
146
}
103
- }
104
-
105
- impl Eq for FilePermissions { }
106
147
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
110
151
}
111
152
}
112
153
@@ -117,75 +158,127 @@ impl FileTimes {
117
158
118
159
impl FileType {
119
160
pub fn is_dir ( & self ) -> bool {
120
- self . 0
161
+ self . mode == DT_DIR
121
162
}
122
-
123
163
pub fn is_file ( & self ) -> bool {
124
- self . 0
164
+ self . mode == DT_REG
125
165
}
126
-
127
166
pub fn is_symlink ( & self ) -> bool {
128
- self . 0
167
+ self . mode == DT_LNK
129
168
}
130
169
}
131
170
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)
135
176
}
136
177
}
137
178
138
- impl Copy for FileType { }
179
+ impl Iterator for ReadDir {
180
+ type Item = io:: Result < DirEntry > ;
139
181
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
+ }
145
186
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
+ }
147
233
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 ( ) ;
153
240
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
+ }
159
244
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) ;
165
246
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
+ } ;
168
251
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
+ }
171
255
}
172
256
}
173
257
174
258
impl DirEntry {
175
259
pub fn path ( & self ) -> PathBuf {
176
- self . 0
260
+ self . dir . root . join ( self . file_name_os_str ( ) )
177
261
}
178
262
179
263
pub fn file_name ( & self ) -> OsString {
180
- self . 0
264
+ self . file_name_os_str ( ) . to_os_string ( )
181
265
}
182
266
183
267
pub fn metadata ( & self ) -> io:: Result < FileAttr > {
184
- self . 0
268
+ lstat ( & self . path ( ) )
185
269
}
186
270
187
271
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 ( )
189
282
}
190
283
}
191
284
@@ -288,7 +381,9 @@ impl File {
288
381
}
289
382
290
383
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) )
292
387
}
293
388
294
389
pub fn fsync ( & self ) -> io:: Result < ( ) > {
@@ -357,11 +452,18 @@ impl File {
357
452
358
453
impl DirBuilder {
359
454
pub fn new ( ) -> DirBuilder {
360
- DirBuilder { }
455
+ DirBuilder { mode : 0o777 }
361
456
}
362
457
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 ;
365
467
}
366
468
}
367
469
@@ -416,8 +518,12 @@ impl FromRawFd for File {
416
518
}
417
519
}
418
520
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) )
421
527
}
422
528
423
529
pub fn unlink ( path : & Path ) -> io:: Result < ( ) > {
@@ -428,12 +534,12 @@ pub fn rename(_old: &Path, _new: &Path) -> io::Result<()> {
428
534
unsupported ( )
429
535
}
430
536
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 ) )
433
539
}
434
540
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 ( |_| ( ) ) )
437
543
}
438
544
439
545
pub fn remove_dir_all ( _path : & Path ) -> io:: Result < ( ) > {
@@ -453,12 +559,20 @@ pub fn link(_original: &Path, _link: &Path) -> io::Result<()> {
453
559
unsupported ( )
454
560
}
455
561
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
+ } )
458
568
}
459
569
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
+ } )
462
576
}
463
577
464
578
pub fn canonicalize ( _p : & Path ) -> io:: Result < PathBuf > {
0 commit comments