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 } ;
2
6
use crate :: fmt;
3
- use crate :: hash:: { Hash , Hasher } ;
4
7
use crate :: io:: { self , Error , ErrorKind } ;
5
8
use crate :: io:: { BorrowedCursor , IoSlice , IoSliceMut , SeekFrom } ;
9
+ use crate :: mem;
10
+ use crate :: os:: hermit:: ffi:: OsStringExt ;
6
11
use crate :: os:: hermit:: io:: { AsFd , AsRawFd , BorrowedFd , FromRawFd , IntoRawFd , RawFd } ;
7
12
use crate :: path:: { Path , PathBuf } ;
13
+ use crate :: ptr;
14
+ use crate :: sync:: Arc ;
8
15
use crate :: sys:: common:: small_c_string:: run_path_with_cstr;
9
16
use crate :: sys:: cvt;
10
17
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 ,
12
20
} ;
13
21
use crate :: sys:: hermit:: fd:: FileDesc ;
14
22
use crate :: sys:: time:: SystemTime ;
@@ -20,12 +28,44 @@ pub use crate::sys_common::fs::{copy, try_exists};
20
28
21
29
#[ derive( Debug ) ]
22
30
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
+ }
23
41
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
+ }
25
47
26
- pub struct ReadDir ( !) ;
48
+ pub struct ReadDir {
49
+ inner : Arc < InnerReadDir > ,
50
+ end_of_stream : bool ,
51
+ }
27
52
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
+ }
29
69
30
70
#[ derive( Clone , Debug ) ]
31
71
pub struct OpenOptions {
@@ -43,72 +83,78 @@ pub struct OpenOptions {
43
83
#[ derive( Copy , Clone , Debug , Default ) ]
44
84
pub struct FileTimes { }
45
85
46
- pub struct FilePermissions ( !) ;
47
-
48
- pub struct FileType ( !) ;
86
+ #[ derive( Clone , PartialEq , Eq , Debug ) ]
87
+ pub struct FilePermissions {
88
+ mode : u32 ,
89
+ }
49
90
50
- #[ derive( Debug ) ]
51
- pub struct DirBuilder { }
91
+ #[ derive( Copy , Clone , Eq , Debug ) ]
92
+ pub struct FileType {
93
+ mode : u32 ,
94
+ }
52
95
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
56
99
}
100
+ }
57
101
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) ;
60
105
}
106
+ }
61
107
62
- pub fn file_type ( & self ) -> FileType {
63
- self . 0
64
- }
108
+ #[ derive( Debug ) ]
109
+ pub struct DirBuilder {
110
+ mode : u32 ,
111
+ }
65
112
113
+ impl FileAttr {
66
114
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 ) )
68
116
}
69
117
70
118
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 ) )
72
120
}
73
121
74
122
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 ) )
76
124
}
77
- }
78
125
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
82
128
}
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 ) }
88
131
}
89
132
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 }
92
142
}
93
143
}
94
144
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
98
149
}
99
- }
100
150
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 ! ( )
104
153
}
105
- }
106
154
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
112
158
}
113
159
}
114
160
@@ -119,75 +165,127 @@ impl FileTimes {
119
165
120
166
impl FileType {
121
167
pub fn is_dir ( & self ) -> bool {
122
- self . 0
168
+ self . mode == DT_DIR
123
169
}
124
-
125
170
pub fn is_file ( & self ) -> bool {
126
- self . 0
171
+ self . mode == DT_REG
127
172
}
128
-
129
173
pub fn is_symlink ( & self ) -> bool {
130
- self . 0
174
+ self . mode == DT_LNK
131
175
}
132
176
}
133
177
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)
137
183
}
138
184
}
139
185
140
- impl Copy for FileType { }
186
+ impl Iterator for ReadDir {
187
+ type Item = io:: Result < DirEntry > ;
141
188
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
+ }
147
193
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
+ }
149
240
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 ( ) ;
155
247
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
+ }
161
251
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) ;
167
253
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
+ } ;
170
258
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
+ }
173
262
}
174
263
}
175
264
176
265
impl DirEntry {
177
266
pub fn path ( & self ) -> PathBuf {
178
- self . 0
267
+ self . dir . root . join ( self . file_name_os_str ( ) )
179
268
}
180
269
181
270
pub fn file_name ( & self ) -> OsString {
182
- self . 0
271
+ self . file_name_os_str ( ) . to_os_string ( )
183
272
}
184
273
185
274
pub fn metadata ( & self ) -> io:: Result < FileAttr > {
186
- self . 0
275
+ lstat ( & self . path ( ) )
187
276
}
188
277
189
278
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 ( )
191
289
}
192
290
}
193
291
@@ -290,7 +388,9 @@ impl File {
290
388
}
291
389
292
390
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) )
294
394
}
295
395
296
396
pub fn fsync ( & self ) -> io:: Result < ( ) > {
@@ -359,11 +459,18 @@ impl File {
359
459
360
460
impl DirBuilder {
361
461
pub fn new ( ) -> DirBuilder {
362
- DirBuilder { }
462
+ DirBuilder { mode : 0o777 }
363
463
}
364
464
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 ;
367
474
}
368
475
}
369
476
@@ -418,8 +525,12 @@ impl FromRawFd for File {
418
525
}
419
526
}
420
527
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) )
423
534
}
424
535
425
536
pub fn unlink ( path : & Path ) -> io:: Result < ( ) > {
@@ -430,12 +541,12 @@ pub fn rename(_old: &Path, _new: &Path) -> io::Result<()> {
430
541
unsupported ( )
431
542
}
432
543
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 ) )
435
546
}
436
547
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 ( |_| ( ) ) )
439
550
}
440
551
441
552
pub fn remove_dir_all ( _path : & Path ) -> io:: Result < ( ) > {
@@ -455,12 +566,20 @@ pub fn link(_original: &Path, _link: &Path) -> io::Result<()> {
455
566
unsupported ( )
456
567
}
457
568
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
+ } )
460
575
}
461
576
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
+ } )
464
583
}
465
584
466
585
pub fn canonicalize ( _p : & Path ) -> io:: Result < PathBuf > {
0 commit comments