@@ -5,16 +5,10 @@ use crate::fcntl::{self, OFlag};
55use  crate :: sys; 
66use  crate :: { NixPath ,  Result } ; 
77use  cfg_if:: cfg_if; 
8- use  std:: ffi; 
8+ use  std:: ffi:: { CStr ,   CString } ; 
99use  std:: os:: unix:: io:: { AsRawFd ,  IntoRawFd ,  RawFd } ; 
1010use  std:: ptr; 
1111
12- #[ cfg( target_os = "linux" ) ]  
13- use  libc:: { dirent64 as  dirent,  readdir64_r as  readdir_r} ; 
14- 
15- #[ cfg( not( target_os = "linux" ) ) ]  
16- use  libc:: { dirent,  readdir_r} ; 
17- 
1812/// An open directory. 
1913/// 
2014/// This is a lower-level interface than [`std::fs::ReadDir`]. Notable differences: 
@@ -26,7 +20,7 @@ use libc::{dirent, readdir_r};
2620///    * can be iterated through multiple times without closing and reopening the file 
2721///      descriptor. Each iteration rewinds when finished. 
2822///    * returns entries for `.` (current directory) and `..` (parent directory). 
29- ///    * returns entries' names as a [`CStr`][cstr]  (no allocation or conversion beyond whatever libc 
23+ ///    * returns entries' names as a [`CStr`] (no allocation or conversion beyond whatever libc 
3024///      does). 
3125/// 
3226/// [AsFd]: std::os::fd::AsFd 
@@ -124,10 +118,7 @@ impl Dir {
124118    } 
125119} 
126120
127- // `Dir` is not `Sync`. With the current implementation, it could be, but according to 
128- // https://www.gnu.org/software/libc/manual/html_node/Reading_002fClosing-Directory.html, 
129- // future versions of POSIX are likely to obsolete `readdir_r` and specify that it's unsafe to 
130- // call `readdir` simultaneously from multiple threads. 
121+ // `Dir` is not `Sync` because it's unsafe to call `readdir` simultaneously from multiple threads. 
131122// 
132123// `Dir` is safe to pass from one thread to another, as it's not reference-counted. 
133124unsafe  impl  Send  for  Dir  { } 
@@ -160,29 +151,22 @@ impl Drop for Dir {
160151} 
161152
162153// The pass by mut is technically needless only because the inner NonNull is 
163- // Copy.  But philosophically we're  mutating the Dir, so we pass by mut. 
154+ // Copy.  But we are actually  mutating the Dir, so we pass by mut. 
164155#[ allow( clippy:: needless_pass_by_ref_mut) ]  
165- fn  next ( dir :  & mut  Dir )  -> Option < Result < Entry > >  { 
156+ fn  readdir ( dir :  & mut  Dir )  -> Option < Result < Entry > >  { 
157+     Errno :: clear ( ) ; 
166158    unsafe  { 
167-         // Note: POSIX specifies that portable applications should dynamically allocate a 
168-         // buffer with room for a `d_name` field of size `pathconf(..., _PC_NAME_MAX)` plus 1 
169-         // for the NUL byte. It doesn't look like the std library does this; it just uses 
170-         // fixed-sized buffers (and libc's dirent seems to be sized so this is appropriate). 
171-         // Probably fine here too then. 
172-         let  mut  ent = std:: mem:: MaybeUninit :: < dirent > :: uninit ( ) ; 
173-         let  mut  result = ptr:: null_mut ( ) ; 
174-         if  let  Err ( e)  = Errno :: result ( readdir_r ( 
175-             dir. 0 . as_ptr ( ) , 
176-             ent. as_mut_ptr ( ) , 
177-             & mut  result, 
178-         ) )  { 
179-             return  Some ( Err ( e) ) ; 
180-         } 
181-         if  result. is_null ( )  { 
182-             return  None ; 
159+         let  de = libc:: readdir ( dir. 0 . as_ptr ( ) ) ; 
160+         if  de. is_null ( )  { 
161+             if  Errno :: last_raw ( )  == 0  { 
162+                 // EOF 
163+                 None 
164+             }  else  { 
165+                 Some ( Err ( Errno :: last ( ) ) ) 
166+             } 
167+         }  else  { 
168+             Some ( Ok ( Entry :: from_raw ( & * de) ) ) 
183169        } 
184-         assert_eq ! ( result,  ent. as_mut_ptr( ) ) ; 
185-         Some ( Ok ( Entry ( ent. assume_init ( ) ) ) ) 
186170    } 
187171} 
188172
@@ -194,7 +178,7 @@ impl Iterator for Iter<'_> {
194178    type  Item  = Result < Entry > ; 
195179
196180    fn  next ( & mut  self )  -> Option < Self :: Item >  { 
197-         next ( self . 0 ) 
181+         readdir ( self . 0 ) 
198182    } 
199183} 
200184
@@ -212,7 +196,7 @@ impl Iterator for OwningIter {
212196    type  Item  = Result < Entry > ; 
213197
214198    fn  next ( & mut  self )  -> Option < Self :: Item >  { 
215-         next ( & mut  self . 0 ) 
199+         readdir ( & mut  self . 0 ) 
216200    } 
217201} 
218202
@@ -252,9 +236,18 @@ impl IntoIterator for Dir {
252236/// A directory entry, similar to `std::fs::DirEntry`. 
253237/// 
254238/// Note that unlike the std version, this may represent the `.` or `..` entries. 
255- #[ derive( Copy ,  Clone ,  Debug ,  Eq ,  Hash ,  PartialEq ) ]  
256- #[ repr( transparent) ]  
257- pub  struct  Entry ( dirent ) ; 
239+ #[ derive( Clone ,  Debug ,  Eq ,  Hash ,  PartialEq ) ]  
240+ pub  struct  Entry  { 
241+     ino :  u64 , 
242+     type_ :  Option < Type > , 
243+     // On some platforms libc::dirent is a "flexible-length structure", where there may be 
244+     // data beyond the end of the struct definition.  So it isn't possible to copy it and subsequently 
245+     // dereference the copy's d_name field.  Nor is it possible for Entry to wrap a &libc::dirent. 
246+     // At least, not if it is to work with the Iterator trait.  Because the Entry would need to 
247+     // maintain a mutable reference to the Dir, which would conflict with the iterator's mutable 
248+     // reference to the same Dir.  So we're forced to copy the name here. 
249+     name :  CString , 
250+ } 
258251
259252/// Type of file referenced by a directory entry 
260253#[ derive( Copy ,  Clone ,  Debug ,  Eq ,  Hash ,  PartialEq ) ]  
@@ -277,10 +270,28 @@ pub enum Type {
277270
278271impl  Entry  { 
279272    /// Returns the inode number (`d_ino`) of the underlying `dirent`. 
273+      pub  fn  ino ( & self )  -> u64  { 
274+         self . ino 
275+     } 
276+ 
277+     /// Returns the bare file name of this directory entry without any other leading path component. 
278+      pub  fn  file_name ( & self )  -> & CStr  { 
279+         self . name . as_c_str ( ) 
280+     } 
281+ 
282+     /// Returns the type of this directory entry, if known. 
283+      /// 
284+      /// See platform `readdir(3)` or `dirent(5)` manpage for when the file type is known; 
285+      /// notably, some Linux filesystems don't implement this. The caller should use `stat` or 
286+      /// `fstat` if this returns `None`. 
287+      pub  fn  file_type ( & self )  -> Option < Type >  { 
288+         self . type_ 
289+     } 
290+ 
280291    #[ allow( clippy:: useless_conversion) ]   // Not useless on all OSes 
281292    // The cast is not unnecessary on all platforms. 
282293    #[ allow( clippy:: unnecessary_cast) ]  
283-     pub   fn  ino ( & self )  -> u64  { 
294+     fn  from_raw ( de :   & libc :: dirent )  -> Self  { 
284295        cfg_if !  { 
285296            if  #[ cfg( any( target_os = "aix" , 
286297                         target_os = "emscripten" , 
@@ -291,38 +302,34 @@ impl Entry {
291302                         solarish, 
292303                         linux_android, 
293304                         apple_targets) ) ]  { 
294-                 self . 0 . d_ino as  u64 
305+                 let  ino = de . d_ino as  u64 ; 
295306            }  else { 
296-                 u64 :: from( self . 0 . d_fileno) 
307+                 let  ino =  u64 :: from( de . d_fileno) ; 
297308            } 
298309        } 
299-     } 
300- 
301-     /// Returns the bare file name of this directory entry without any other leading path component. 
302-      pub  fn  file_name ( & self )  -> & ffi:: CStr  { 
303-         unsafe  {  ffi:: CStr :: from_ptr ( self . 0 . d_name . as_ptr ( ) )  } 
304-     } 
305- 
306-     /// Returns the type of this directory entry, if known. 
307-      /// 
308-      /// See platform `readdir(3)` or `dirent(5)` manpage for when the file type is known; 
309-      /// notably, some Linux filesystems don't implement this. The caller should use `stat` or 
310-      /// `fstat` if this returns `None`. 
311-      pub  fn  file_type ( & self )  -> Option < Type >  { 
312-         #[ cfg( not( any( solarish,  target_os = "aix" ,  target_os = "haiku" ) ) ) ]  
313-         match  self . 0 . d_type  { 
314-             libc:: DT_FIFO  => Some ( Type :: Fifo ) , 
315-             libc:: DT_CHR  => Some ( Type :: CharacterDevice ) , 
316-             libc:: DT_DIR  => Some ( Type :: Directory ) , 
317-             libc:: DT_BLK  => Some ( Type :: BlockDevice ) , 
318-             libc:: DT_REG  => Some ( Type :: File ) , 
319-             libc:: DT_LNK  => Some ( Type :: Symlink ) , 
320-             libc:: DT_SOCK  => Some ( Type :: Socket ) , 
321-             /* libc::DT_UNKNOWN | */  _ => None , 
310+         cfg_if !  { 
311+             if  #[ cfg( not( any( solarish,  target_os = "aix" ,  target_os = "haiku" ) ) ) ]  { 
312+                 let  type_ = match  de. d_type { 
313+                     libc:: DT_FIFO  => Some ( Type :: Fifo ) , 
314+                     libc:: DT_CHR  => Some ( Type :: CharacterDevice ) , 
315+                     libc:: DT_DIR  => Some ( Type :: Directory ) , 
316+                     libc:: DT_BLK  => Some ( Type :: BlockDevice ) , 
317+                     libc:: DT_REG  => Some ( Type :: File ) , 
318+                     libc:: DT_LNK  => Some ( Type :: Symlink ) , 
319+                     libc:: DT_SOCK  => Some ( Type :: Socket ) , 
320+                     /* libc::DT_UNKNOWN | */  _ => None , 
321+                 } ; 
322+             }  else { 
323+                 // illumos, Solaris, and Haiku systems do not have the d_type member at all: 
324+                 #[ cfg( any( solarish,  target_os = "aix" ,  target_os = "haiku" ) ) ] 
325+                 let  type_ = None ; 
326+             } 
322327        } 
328+         // Annoyingly, since libc::dirent is open-ended, the easiest way to read the name field in 
329+         // Rust is to treat it like a pointer. 
330+         // Safety: safe because we knod that de.d_name is in valid memory. 
331+         let  name = unsafe  {  CStr :: from_ptr ( de. d_name . as_ptr ( ) )  } . to_owned ( ) ; 
323332
324-         // illumos, Solaris, and Haiku systems do not have the d_type member at all: 
325-         #[ cfg( any( solarish,  target_os = "aix" ,  target_os = "haiku" ) ) ]  
326-         None 
333+         Entry  {  ino,  type_,  name } 
327334    } 
328335} 
0 commit comments