@@ -53,7 +53,7 @@ use libc::{c_int, mode_t};
5353#[ cfg( target_os = "android" ) ]
5454use libc:: {
5555 dirent as dirent64, fstat as fstat64, fstatat as fstatat64, ftruncate64, lseek64,
56- lstat as lstat64, off64_t, open as open64, stat as stat64,
56+ lstat as lstat64, off64_t, open as open64, openat as openat64 , stat as stat64,
5757} ;
5858#[ cfg( not( any(
5959 all( target_os = "linux" , not( target_env = "musl" ) ) ,
@@ -63,14 +63,14 @@ use libc::{
6363) ) ) ]
6464use libc:: {
6565 dirent as dirent64, fstat as fstat64, ftruncate as ftruncate64, lseek as lseek64,
66- lstat as lstat64, off_t as off64_t, open as open64, stat as stat64,
66+ lstat as lstat64, off_t as off64_t, open as open64, openat as openat64 , stat as stat64,
6767} ;
6868#[ cfg( any(
6969 all( target_os = "linux" , not( target_env = "musl" ) ) ,
7070 target_os = "l4re" ,
7171 target_os = "hurd"
7272) ) ]
73- use libc:: { dirent64, fstat64, ftruncate64, lseek64, lstat64, off64_t, open64, stat64} ;
73+ use libc:: { dirent64, fstat64, ftruncate64, lseek64, lstat64, off64_t, open64, openat64 , stat64} ;
7474
7575use crate :: ffi:: { CStr , OsStr , OsString } ;
7676use crate :: fmt:: { self , Write as _} ;
@@ -262,7 +262,154 @@ impl ReadDir {
262262 }
263263}
264264
265- struct Dir ( * mut libc:: DIR ) ;
265+ pub struct Dir ( * mut libc:: DIR ) ;
266+
267+ // dirfd isn't supported everywhere
268+ #[ cfg( not( any(
269+ miri,
270+ target_os = "redox" ,
271+ target_os = "nto" ,
272+ target_os = "vita" ,
273+ target_os = "hurd" ,
274+ target_os = "espidf" ,
275+ target_os = "horizon" ,
276+ target_os = "vxworks" ,
277+ target_os = "rtems" ,
278+ target_os = "nuttx" ,
279+ ) ) ) ]
280+ impl Dir {
281+ pub fn open < P : AsRef < Path > > ( & self , path : P ) -> io:: Result < File > {
282+ let mut opts = OpenOptions :: new ( ) ;
283+ opts. read ( true ) ;
284+ run_path_with_cstr ( path. as_ref ( ) , & |path| self . open_c ( path, & opts) )
285+ }
286+
287+ pub fn open_with < P : AsRef < Path > > ( & self , path : P , opts : & OpenOptions ) -> io:: Result < File > {
288+ run_path_with_cstr ( path. as_ref ( ) , & |path| self . open_c ( path, opts) )
289+ }
290+
291+ pub fn open_c ( & self , path : & CStr , opts : & OpenOptions ) -> io:: Result < File > {
292+ let flags = libc:: O_CLOEXEC
293+ | opts. get_access_mode ( ) ?
294+ | opts. get_creation_mode ( ) ?
295+ | ( opts. custom_flags as c_int & !libc:: O_ACCMODE ) ;
296+ let fd = cvt_r ( || unsafe {
297+ openat64 ( libc:: dirfd ( self . 0 ) , path. as_ptr ( ) , flags, opts. mode as c_int )
298+ } ) ?;
299+ Ok ( File ( unsafe { FileDesc :: from_raw_fd ( fd) } ) )
300+ }
301+
302+ // pub fn create_dir<P: AsRef<Path>>(&self, path: P) -> Result<()>
303+ // pub fn rename<P: AsRef<Path>, Q: AsRef<Path>>(&self, from: P, to_dir: &Self, to: Q) -> Result<()>
304+ // pub fn remove_file<P: AsRef<Path>>(&self, path: P) -> Result<()>
305+ // pub fn remove_dir<P: AsRef<Path>>(&self, path: P) -> Result<()>
306+ // pub fn symlink<P: AsRef<Path>, Q: AsRef<Path>>(&self, original: P, link: Q)
307+ }
308+
309+ fn get_path_from_fd ( fd : c_int ) -> Option < PathBuf > {
310+ #[ cfg( any( target_os = "linux" , target_os = "illumos" , target_os = "solaris" ) ) ]
311+ fn get_path ( fd : c_int ) -> Option < PathBuf > {
312+ let mut p = PathBuf :: from ( "/proc/self/fd" ) ;
313+ p. push ( & fd. to_string ( ) ) ;
314+ run_path_with_cstr ( & p, & readlink) . ok ( )
315+ }
316+
317+ #[ cfg( any( target_vendor = "apple" , target_os = "netbsd" ) ) ]
318+ fn get_path ( fd : c_int ) -> Option < PathBuf > {
319+ // FIXME: The use of PATH_MAX is generally not encouraged, but it
320+ // is inevitable in this case because Apple targets and NetBSD define `fcntl`
321+ // with `F_GETPATH` in terms of `MAXPATHLEN`, and there are no
322+ // alternatives. If a better method is invented, it should be used
323+ // instead.
324+ let mut buf = vec ! [ 0 ; libc:: PATH_MAX as usize ] ;
325+ let n = unsafe { libc:: fcntl ( fd, libc:: F_GETPATH , buf. as_ptr ( ) ) } ;
326+ if n == -1 {
327+ cfg_if:: cfg_if! {
328+ if #[ cfg( target_os = "netbsd" ) ] {
329+ // fallback to procfs as last resort
330+ let mut p = PathBuf :: from( "/proc/self/fd" ) ;
331+ p. push( & fd. to_string( ) ) ;
332+ return run_path_with_cstr( & p, & readlink) . ok( )
333+ } else {
334+ return None ;
335+ }
336+ }
337+ }
338+ let l = buf. iter ( ) . position ( |& c| c == 0 ) . unwrap ( ) ;
339+ buf. truncate ( l as usize ) ;
340+ buf. shrink_to_fit ( ) ;
341+ Some ( PathBuf :: from ( OsString :: from_vec ( buf) ) )
342+ }
343+
344+ #[ cfg( target_os = "freebsd" ) ]
345+ fn get_path ( fd : c_int ) -> Option < PathBuf > {
346+ let info = Box :: < libc:: kinfo_file > :: new_zeroed ( ) ;
347+ let mut info = unsafe { info. assume_init ( ) } ;
348+ info. kf_structsize = size_of :: < libc:: kinfo_file > ( ) as libc:: c_int ;
349+ let n = unsafe { libc:: fcntl ( fd, libc:: F_KINFO , & mut * info) } ;
350+ if n == -1 {
351+ return None ;
352+ }
353+ let buf = unsafe { CStr :: from_ptr ( info. kf_path . as_mut_ptr ( ) ) . to_bytes ( ) . to_vec ( ) } ;
354+ Some ( PathBuf :: from ( OsString :: from_vec ( buf) ) )
355+ }
356+
357+ #[ cfg( target_os = "vxworks" ) ]
358+ fn get_path ( fd : c_int ) -> Option < PathBuf > {
359+ let mut buf = vec ! [ 0 ; libc:: PATH_MAX as usize ] ;
360+ let n = unsafe { libc:: ioctl ( fd, libc:: FIOGETNAME , buf. as_ptr ( ) ) } ;
361+ if n == -1 {
362+ return None ;
363+ }
364+ let l = buf. iter ( ) . position ( |& c| c == 0 ) . unwrap ( ) ;
365+ buf. truncate ( l as usize ) ;
366+ Some ( PathBuf :: from ( OsString :: from_vec ( buf) ) )
367+ }
368+
369+ #[ cfg( not( any(
370+ target_os = "linux" ,
371+ target_os = "vxworks" ,
372+ target_os = "freebsd" ,
373+ target_os = "netbsd" ,
374+ target_os = "illumos" ,
375+ target_os = "solaris" ,
376+ target_vendor = "apple" ,
377+ ) ) ) ]
378+ fn get_path ( _fd : c_int ) -> Option < PathBuf > {
379+ // FIXME(#24570): implement this for other Unix platforms
380+ None
381+ }
382+
383+ get_path ( fd)
384+ }
385+
386+ impl fmt:: Debug for Dir {
387+ fn fmt ( & self , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
388+ fn get_mode ( fd : c_int ) -> Option < ( bool , bool ) > {
389+ let mode = unsafe { libc:: fcntl ( fd, libc:: F_GETFL ) } ;
390+ if mode == -1 {
391+ return None ;
392+ }
393+ match mode & libc:: O_ACCMODE {
394+ libc:: O_RDONLY => Some ( ( true , false ) ) ,
395+ libc:: O_RDWR => Some ( ( true , true ) ) ,
396+ libc:: O_WRONLY => Some ( ( false , true ) ) ,
397+ _ => None ,
398+ }
399+ }
400+
401+ let fd = unsafe { dirfd ( self . 0 ) } ;
402+ let mut b = f. debug_struct ( "Dir" ) ;
403+ b. field ( "fd" , & fd) ;
404+ if let Some ( path) = get_path_from_fd ( fd) {
405+ b. field ( "path" , & path) ;
406+ }
407+ if let Some ( ( read, write) ) = get_mode ( fd) {
408+ b. field ( "read" , & read) . field ( "write" , & write) ;
409+ }
410+ b. finish ( )
411+ }
412+ }
266413
267414unsafe impl Send for Dir { }
268415unsafe impl Sync for Dir { }
@@ -1653,79 +1800,6 @@ impl FromRawFd for File {
16531800
16541801impl fmt:: Debug for File {
16551802 fn fmt ( & self , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
1656- #[ cfg( any( target_os = "linux" , target_os = "illumos" , target_os = "solaris" ) ) ]
1657- fn get_path ( fd : c_int ) -> Option < PathBuf > {
1658- let mut p = PathBuf :: from ( "/proc/self/fd" ) ;
1659- p. push ( & fd. to_string ( ) ) ;
1660- run_path_with_cstr ( & p, & readlink) . ok ( )
1661- }
1662-
1663- #[ cfg( any( target_vendor = "apple" , target_os = "netbsd" ) ) ]
1664- fn get_path ( fd : c_int ) -> Option < PathBuf > {
1665- // FIXME: The use of PATH_MAX is generally not encouraged, but it
1666- // is inevitable in this case because Apple targets and NetBSD define `fcntl`
1667- // with `F_GETPATH` in terms of `MAXPATHLEN`, and there are no
1668- // alternatives. If a better method is invented, it should be used
1669- // instead.
1670- let mut buf = vec ! [ 0 ; libc:: PATH_MAX as usize ] ;
1671- let n = unsafe { libc:: fcntl ( fd, libc:: F_GETPATH , buf. as_ptr ( ) ) } ;
1672- if n == -1 {
1673- cfg_if:: cfg_if! {
1674- if #[ cfg( target_os = "netbsd" ) ] {
1675- // fallback to procfs as last resort
1676- let mut p = PathBuf :: from( "/proc/self/fd" ) ;
1677- p. push( & fd. to_string( ) ) ;
1678- return run_path_with_cstr( & p, & readlink) . ok( )
1679- } else {
1680- return None ;
1681- }
1682- }
1683- }
1684- let l = buf. iter ( ) . position ( |& c| c == 0 ) . unwrap ( ) ;
1685- buf. truncate ( l as usize ) ;
1686- buf. shrink_to_fit ( ) ;
1687- Some ( PathBuf :: from ( OsString :: from_vec ( buf) ) )
1688- }
1689-
1690- #[ cfg( target_os = "freebsd" ) ]
1691- fn get_path ( fd : c_int ) -> Option < PathBuf > {
1692- let info = Box :: < libc:: kinfo_file > :: new_zeroed ( ) ;
1693- let mut info = unsafe { info. assume_init ( ) } ;
1694- info. kf_structsize = size_of :: < libc:: kinfo_file > ( ) as libc:: c_int ;
1695- let n = unsafe { libc:: fcntl ( fd, libc:: F_KINFO , & mut * info) } ;
1696- if n == -1 {
1697- return None ;
1698- }
1699- let buf = unsafe { CStr :: from_ptr ( info. kf_path . as_mut_ptr ( ) ) . to_bytes ( ) . to_vec ( ) } ;
1700- Some ( PathBuf :: from ( OsString :: from_vec ( buf) ) )
1701- }
1702-
1703- #[ cfg( target_os = "vxworks" ) ]
1704- fn get_path ( fd : c_int ) -> Option < PathBuf > {
1705- let mut buf = vec ! [ 0 ; libc:: PATH_MAX as usize ] ;
1706- let n = unsafe { libc:: ioctl ( fd, libc:: FIOGETNAME , buf. as_ptr ( ) ) } ;
1707- if n == -1 {
1708- return None ;
1709- }
1710- let l = buf. iter ( ) . position ( |& c| c == 0 ) . unwrap ( ) ;
1711- buf. truncate ( l as usize ) ;
1712- Some ( PathBuf :: from ( OsString :: from_vec ( buf) ) )
1713- }
1714-
1715- #[ cfg( not( any(
1716- target_os = "linux" ,
1717- target_os = "vxworks" ,
1718- target_os = "freebsd" ,
1719- target_os = "netbsd" ,
1720- target_os = "illumos" ,
1721- target_os = "solaris" ,
1722- target_vendor = "apple" ,
1723- ) ) ) ]
1724- fn get_path ( _fd : c_int ) -> Option < PathBuf > {
1725- // FIXME(#24570): implement this for other Unix platforms
1726- None
1727- }
1728-
17291803 fn get_mode ( fd : c_int ) -> Option < ( bool , bool ) > {
17301804 let mode = unsafe { libc:: fcntl ( fd, libc:: F_GETFL ) } ;
17311805 if mode == -1 {
@@ -1742,7 +1816,7 @@ impl fmt::Debug for File {
17421816 let fd = self . as_raw_fd ( ) ;
17431817 let mut b = f. debug_struct ( "File" ) ;
17441818 b. field ( "fd" , & fd) ;
1745- if let Some ( path) = get_path ( fd) {
1819+ if let Some ( path) = get_path_from_fd ( fd) {
17461820 b. field ( "path" , & path) ;
17471821 }
17481822 if let Some ( ( read, write) ) = get_mode ( fd) {
0 commit comments