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 } ;
@@ -356,7 +359,7 @@ pub struct DirEntry {
356359 entry : dirent64 ,
357360}
358361
359- #[ derive( Clone , Debug ) ]
362+ #[ derive( Clone ) ]
360363pub struct OpenOptions {
361364 // generic
362365 read : bool ,
@@ -370,7 +373,7 @@ pub struct OpenOptions {
370373 mode : mode_t ,
371374}
372375
373- #[ derive( Clone , PartialEq , Eq , Debug ) ]
376+ #[ derive( Clone , PartialEq , Eq ) ]
374377pub struct FilePermissions {
375378 mode : mode_t ,
376379}
@@ -389,7 +392,7 @@ pub struct FileTimes {
389392 created : Option < SystemTime > ,
390393}
391394
392- #[ derive( Copy , Clone , Eq , Debug ) ]
395+ #[ derive( Copy , Clone , Eq ) ]
393396pub struct FileType {
394397 mode : mode_t ,
395398}
@@ -406,11 +409,13 @@ impl core::hash::Hash for FileType {
406409 }
407410}
408411
409- #[ derive( Debug ) ]
410412pub struct DirBuilder {
411413 mode : mode_t ,
412414}
413415
416+ #[ derive( Copy , Clone ) ]
417+ struct Mode ( mode_t ) ;
418+
414419cfg_has_statx ! { {
415420 impl FileAttr {
416421 fn from_stat64( stat: stat64) -> Self {
@@ -689,12 +694,26 @@ impl FileType {
689694 }
690695}
691696
697+ impl fmt:: Debug for FileType {
698+ fn fmt ( & self , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
699+ let FileType { mode } = self ;
700+ f. debug_struct ( "FileType" ) . field ( "mode" , & Mode ( * mode) ) . finish ( )
701+ }
702+ }
703+
692704impl FromInner < u32 > for FilePermissions {
693705 fn from_inner ( mode : u32 ) -> FilePermissions {
694706 FilePermissions { mode : mode as mode_t }
695707 }
696708}
697709
710+ impl fmt:: Debug for FilePermissions {
711+ fn fmt ( & self , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
712+ let FilePermissions { mode } = self ;
713+ f. debug_struct ( "FilePermissions" ) . field ( "mode" , & Mode ( * mode) ) . finish ( )
714+ }
715+ }
716+
698717impl fmt:: Debug for ReadDir {
699718 fn fmt ( & self , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
700719 // This will only be called from std::fs::ReadDir, which will add a "ReadDir()" frame.
@@ -1135,6 +1154,23 @@ impl OpenOptions {
11351154 }
11361155}
11371156
1157+ impl fmt:: Debug for OpenOptions {
1158+ fn fmt ( & self , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
1159+ let OpenOptions { read, write, append, truncate, create, create_new, custom_flags, mode } =
1160+ self ;
1161+ f. debug_struct ( "OpenOptions" )
1162+ . field ( "read" , read)
1163+ . field ( "write" , write)
1164+ . field ( "append" , append)
1165+ . field ( "truncate" , truncate)
1166+ . field ( "create" , create)
1167+ . field ( "create_new" , create_new)
1168+ . field ( "custom_flags" , custom_flags)
1169+ . field ( "mode" , & Mode ( * mode) )
1170+ . finish ( )
1171+ }
1172+ }
1173+
11381174impl File {
11391175 pub fn open ( path : & Path , opts : & OpenOptions ) -> io:: Result < File > {
11401176 run_path_with_cstr ( path, & |path| File :: open_c ( path, opts) )
@@ -1425,6 +1461,13 @@ impl DirBuilder {
14251461 }
14261462}
14271463
1464+ impl fmt:: Debug for DirBuilder {
1465+ fn fmt ( & self , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
1466+ let DirBuilder { mode } = self ;
1467+ f. debug_struct ( "DirBuilder" ) . field ( "mode" , & Mode ( * mode) ) . finish ( )
1468+ }
1469+ }
1470+
14281471impl AsInner < FileDesc > for File {
14291472 #[ inline]
14301473 fn as_inner ( & self ) -> & FileDesc {
@@ -1597,6 +1640,73 @@ impl fmt::Debug for File {
15971640 }
15981641}
15991642
1643+ // Format in octal, followed by the mode format used in `ls -l`.
1644+ //
1645+ // References:
1646+ // https://pubs.opengroup.org/onlinepubs/009696899/utilities/ls.html
1647+ // https://www.gnu.org/software/libc/manual/html_node/Testing-File-Type.html
1648+ // https://www.gnu.org/software/libc/manual/html_node/Permission-Bits.html
1649+ //
1650+ // Example:
1651+ // 0o100664 (-rw-rw-r--)
1652+ impl fmt:: Debug for Mode {
1653+ fn fmt ( & self , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
1654+ let Self ( mode) = * self ;
1655+ write ! ( f, "0o{mode:06o}" ) ?;
1656+
1657+ let entry_type = match mode & libc:: S_IFMT {
1658+ libc:: S_IFDIR => 'd' ,
1659+ libc:: S_IFBLK => 'b' ,
1660+ libc:: S_IFCHR => 'c' ,
1661+ libc:: S_IFLNK => 'l' ,
1662+ libc:: S_IFIFO => 'p' ,
1663+ libc:: S_IFREG => '-' ,
1664+ _ => return Ok ( ( ) ) ,
1665+ } ;
1666+
1667+ f. write_str ( " (" ) ?;
1668+ f. write_char ( entry_type) ?;
1669+
1670+ // Owner permissions
1671+ f. write_char ( if mode & libc:: S_IRUSR != 0 { 'r' } else { '-' } ) ?;
1672+ f. write_char ( if mode & libc:: S_IWUSR != 0 { 'w' } else { '-' } ) ?;
1673+ let owner_executable = mode & libc:: S_IXUSR != 0 ;
1674+ let setuid = mode as c_int & libc:: S_ISUID as c_int != 0 ;
1675+ f. write_char ( match ( owner_executable, setuid) {
1676+ ( true , true ) => 's' , // executable and setuid
1677+ ( false , true ) => 'S' , // setuid
1678+ ( true , false ) => 'x' , // executable
1679+ ( false , false ) => '-' ,
1680+ } ) ?;
1681+
1682+ // Group permissions
1683+ f. write_char ( if mode & libc:: S_IRGRP != 0 { 'r' } else { '-' } ) ?;
1684+ f. write_char ( if mode & libc:: S_IWGRP != 0 { 'w' } else { '-' } ) ?;
1685+ let group_executable = mode & libc:: S_IXGRP != 0 ;
1686+ let setgid = mode as c_int & libc:: S_ISGID as c_int != 0 ;
1687+ f. write_char ( match ( group_executable, setgid) {
1688+ ( true , true ) => 's' , // executable and setgid
1689+ ( false , true ) => 'S' , // setgid
1690+ ( true , false ) => 'x' , // executable
1691+ ( false , false ) => '-' ,
1692+ } ) ?;
1693+
1694+ // Other permissions
1695+ f. write_char ( if mode & libc:: S_IROTH != 0 { 'r' } else { '-' } ) ?;
1696+ f. write_char ( if mode & libc:: S_IWOTH != 0 { 'w' } else { '-' } ) ?;
1697+ let other_executable = mode & libc:: S_IXOTH != 0 ;
1698+ let sticky = mode as c_int & libc:: S_ISVTX as c_int != 0 ;
1699+ f. write_char ( match ( entry_type, other_executable, sticky) {
1700+ ( 'd' , true , true ) => 't' , // searchable and restricted deletion
1701+ ( 'd' , false , true ) => 'T' , // restricted deletion
1702+ ( _, true , _) => 'x' , // executable
1703+ ( _, false , _) => '-' ,
1704+ } ) ?;
1705+
1706+ f. write_char ( ')' )
1707+ }
1708+ }
1709+
16001710pub fn readdir ( path : & Path ) -> io:: Result < ReadDir > {
16011711 let ptr = run_path_with_cstr ( path, & |p| unsafe { Ok ( libc:: opendir ( p. as_ptr ( ) ) ) } ) ?;
16021712 if ptr. is_null ( ) {
0 commit comments