11// miri has some special hacks here that make things unused.
22#![ cfg_attr( miri, allow( unused) ) ]
33
4+ #[ cfg( test) ]
5+ mod tests;
6+
47use crate :: os:: unix:: prelude:: * ;
58
69use crate :: ffi:: { CStr , OsStr , OsString } ;
7- use crate :: fmt;
10+ use crate :: fmt:: { self , Write as _ } ;
811use crate :: io:: { self , BorrowedCursor , Error , IoSlice , IoSliceMut , SeekFrom } ;
912use crate :: mem;
1013use crate :: os:: unix:: io:: { AsFd , AsRawFd , BorrowedFd , FromRawFd , IntoRawFd } ;
@@ -354,7 +357,7 @@ pub struct DirEntry {
354357 entry : dirent64 ,
355358}
356359
357- #[ derive( Clone , Debug ) ]
360+ #[ derive( Clone ) ]
358361pub struct OpenOptions {
359362 // generic
360363 read : bool ,
@@ -368,7 +371,7 @@ pub struct OpenOptions {
368371 mode : mode_t ,
369372}
370373
371- #[ derive( Clone , PartialEq , Eq , Debug ) ]
374+ #[ derive( Clone , PartialEq , Eq ) ]
372375pub struct FilePermissions {
373376 mode : mode_t ,
374377}
@@ -381,7 +384,7 @@ pub struct FileTimes {
381384 created : Option < SystemTime > ,
382385}
383386
384- #[ derive( Copy , Clone , Eq , Debug ) ]
387+ #[ derive( Copy , Clone , Eq ) ]
385388pub struct FileType {
386389 mode : mode_t ,
387390}
@@ -398,11 +401,13 @@ impl core::hash::Hash for FileType {
398401 }
399402}
400403
401- #[ derive( Debug ) ]
402404pub struct DirBuilder {
403405 mode : mode_t ,
404406}
405407
408+ #[ derive( Copy , Clone ) ]
409+ struct Mode ( mode_t ) ;
410+
406411cfg_has_statx ! { {
407412 impl FileAttr {
408413 fn from_stat64( stat: stat64) -> Self {
@@ -673,12 +678,26 @@ impl FileType {
673678 }
674679}
675680
681+ impl fmt:: Debug for FileType {
682+ fn fmt ( & self , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
683+ let FileType { mode } = self ;
684+ f. debug_struct ( "FileType" ) . field ( "mode" , & Mode ( * mode) ) . finish ( )
685+ }
686+ }
687+
676688impl FromInner < u32 > for FilePermissions {
677689 fn from_inner ( mode : u32 ) -> FilePermissions {
678690 FilePermissions { mode : mode as mode_t }
679691 }
680692}
681693
694+ impl fmt:: Debug for FilePermissions {
695+ fn fmt ( & self , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
696+ let FilePermissions { mode } = self ;
697+ f. debug_struct ( "FilePermissions" ) . field ( "mode" , & Mode ( * mode) ) . finish ( )
698+ }
699+ }
700+
682701impl fmt:: Debug for ReadDir {
683702 fn fmt ( & self , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
684703 // This will only be called from std::fs::ReadDir, which will add a "ReadDir()" frame.
@@ -1116,6 +1135,23 @@ impl OpenOptions {
11161135 }
11171136}
11181137
1138+ impl fmt:: Debug for OpenOptions {
1139+ fn fmt ( & self , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
1140+ let OpenOptions { read, write, append, truncate, create, create_new, custom_flags, mode } =
1141+ self ;
1142+ f. debug_struct ( "OpenOptions" )
1143+ . field ( "read" , read)
1144+ . field ( "write" , write)
1145+ . field ( "append" , append)
1146+ . field ( "truncate" , truncate)
1147+ . field ( "create" , create)
1148+ . field ( "create_new" , create_new)
1149+ . field ( "custom_flags" , custom_flags)
1150+ . field ( "mode" , & Mode ( * mode) )
1151+ . finish ( )
1152+ }
1153+ }
1154+
11191155impl File {
11201156 pub fn open ( path : & Path , opts : & OpenOptions ) -> io:: Result < File > {
11211157 run_path_with_cstr ( path, & |path| File :: open_c ( path, opts) )
@@ -1402,6 +1438,13 @@ impl DirBuilder {
14021438 }
14031439}
14041440
1441+ impl fmt:: Debug for DirBuilder {
1442+ fn fmt ( & self , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
1443+ let DirBuilder { mode } = self ;
1444+ f. debug_struct ( "DirBuilder" ) . field ( "mode" , & Mode ( * mode) ) . finish ( )
1445+ }
1446+ }
1447+
14051448impl AsInner < FileDesc > for File {
14061449 #[ inline]
14071450 fn as_inner ( & self ) -> & FileDesc {
@@ -1574,6 +1617,73 @@ impl fmt::Debug for File {
15741617 }
15751618}
15761619
1620+ // Format in octal, followed by the mode format used in `ls -l`.
1621+ //
1622+ // References:
1623+ // https://pubs.opengroup.org/onlinepubs/009696899/utilities/ls.html
1624+ // https://www.gnu.org/software/libc/manual/html_node/Testing-File-Type.html
1625+ // https://www.gnu.org/software/libc/manual/html_node/Permission-Bits.html
1626+ //
1627+ // Example:
1628+ // 0o100664 (-rw-rw-r--)
1629+ impl fmt:: Debug for Mode {
1630+ fn fmt ( & self , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
1631+ let Self ( mode) = * self ;
1632+ write ! ( f, "0o{mode:06o}" ) ?;
1633+
1634+ let entry_type = match mode & libc:: S_IFMT {
1635+ libc:: S_IFDIR => 'd' ,
1636+ libc:: S_IFBLK => 'b' ,
1637+ libc:: S_IFCHR => 'c' ,
1638+ libc:: S_IFLNK => 'l' ,
1639+ libc:: S_IFIFO => 'p' ,
1640+ libc:: S_IFREG => '-' ,
1641+ _ => return Ok ( ( ) ) ,
1642+ } ;
1643+
1644+ f. write_str ( " (" ) ?;
1645+ f. write_char ( entry_type) ?;
1646+
1647+ // Owner permissions
1648+ f. write_char ( if mode & libc:: S_IRUSR != 0 { 'r' } else { '-' } ) ?;
1649+ f. write_char ( if mode & libc:: S_IWUSR != 0 { 'w' } else { '-' } ) ?;
1650+ let owner_executable = mode & libc:: S_IXUSR != 0 ;
1651+ let setuid = mode as c_int & libc:: S_ISUID as c_int != 0 ;
1652+ f. write_char ( match ( owner_executable, setuid) {
1653+ ( true , true ) => 's' , // executable and setuid
1654+ ( false , true ) => 'S' , // setuid
1655+ ( true , false ) => 'x' , // executable
1656+ ( false , false ) => '-' ,
1657+ } ) ?;
1658+
1659+ // Group permissions
1660+ f. write_char ( if mode & libc:: S_IRGRP != 0 { 'r' } else { '-' } ) ?;
1661+ f. write_char ( if mode & libc:: S_IWGRP != 0 { 'w' } else { '-' } ) ?;
1662+ let group_executable = mode & libc:: S_IXGRP != 0 ;
1663+ let setgid = mode as c_int & libc:: S_ISGID as c_int != 0 ;
1664+ f. write_char ( match ( group_executable, setgid) {
1665+ ( true , true ) => 's' , // executable and setgid
1666+ ( false , true ) => 'S' , // setgid
1667+ ( true , false ) => 'x' , // executable
1668+ ( false , false ) => '-' ,
1669+ } ) ?;
1670+
1671+ // Other permissions
1672+ f. write_char ( if mode & libc:: S_IROTH != 0 { 'r' } else { '-' } ) ?;
1673+ f. write_char ( if mode & libc:: S_IWOTH != 0 { 'w' } else { '-' } ) ?;
1674+ let other_executable = mode & libc:: S_IXOTH != 0 ;
1675+ let sticky = mode as c_int & libc:: S_ISVTX as c_int != 0 ;
1676+ f. write_char ( match ( entry_type, other_executable, sticky) {
1677+ ( 'd' , true , true ) => 't' , // searchable and restricted deletion
1678+ ( 'd' , false , true ) => 'T' , // restricted deletion
1679+ ( _, true , _) => 'x' , // executable
1680+ ( _, false , _) => '-' ,
1681+ } ) ?;
1682+
1683+ f. write_char ( ')' )
1684+ }
1685+ }
1686+
15771687pub fn readdir ( path : & Path ) -> io:: Result < ReadDir > {
15781688 let ptr = run_path_with_cstr ( path, & |p| unsafe { Ok ( libc:: opendir ( p. as_ptr ( ) ) ) } ) ?;
15791689 if ptr. is_null ( ) {
0 commit comments