@@ -54,7 +54,7 @@ use libc::{c_int, mode_t};
5454#[ cfg( target_os = "android" ) ]
5555use libc:: {
5656 dirent as dirent64, fstat as fstat64, fstatat as fstatat64, ftruncate64, lseek64,
57- lstat as lstat64, off64_t, open as open64, stat as stat64,
57+ lstat as lstat64, off64_t, open as open64, openat as openat64 , stat as stat64,
5858} ;
5959#[ cfg( not( any(
6060 all( target_os = "linux" , not( target_env = "musl" ) ) ,
@@ -64,14 +64,14 @@ use libc::{
6464) ) ) ]
6565use libc:: {
6666 dirent as dirent64, fstat as fstat64, ftruncate as ftruncate64, lseek as lseek64,
67- lstat as lstat64, off_t as off64_t, open as open64, stat as stat64,
67+ lstat as lstat64, off_t as off64_t, open as open64, openat as openat64 , stat as stat64,
6868} ;
6969#[ cfg( any(
7070 all( target_os = "linux" , not( target_env = "musl" ) ) ,
7171 target_os = "l4re" ,
7272 target_os = "hurd"
7373) ) ]
74- use libc:: { dirent64, fstat64, ftruncate64, lseek64, lstat64, off64_t, open64, stat64} ;
74+ use libc:: { dirent64, fstat64, ftruncate64, lseek64, lstat64, off64_t, open64, openat64 , stat64} ;
7575
7676use crate :: ffi:: { CStr , OsStr , OsString } ;
7777use crate :: fmt:: { self , Write as _} ;
@@ -264,7 +264,154 @@ impl ReadDir {
264264 }
265265}
266266
267- struct Dir ( * mut libc:: DIR ) ;
267+ pub struct Dir ( * mut libc:: DIR ) ;
268+
269+ // dirfd isn't supported everywhere
270+ #[ cfg( not( any(
271+ miri,
272+ target_os = "redox" ,
273+ target_os = "nto" ,
274+ target_os = "vita" ,
275+ target_os = "hurd" ,
276+ target_os = "espidf" ,
277+ target_os = "horizon" ,
278+ target_os = "vxworks" ,
279+ target_os = "rtems" ,
280+ target_os = "nuttx" ,
281+ ) ) ) ]
282+ impl Dir {
283+ pub fn open < P : AsRef < Path > > ( & self , path : P ) -> io:: Result < File > {
284+ let mut opts = OpenOptions :: new ( ) ;
285+ opts. read ( true ) ;
286+ run_path_with_cstr ( path. as_ref ( ) , & |path| self . open_c ( path, & opts) )
287+ }
288+
289+ pub fn open_with < P : AsRef < Path > > ( & self , path : P , opts : & OpenOptions ) -> io:: Result < File > {
290+ run_path_with_cstr ( path. as_ref ( ) , & |path| self . open_c ( path, opts) )
291+ }
292+
293+ pub fn open_c ( & self , path : & CStr , opts : & OpenOptions ) -> io:: Result < File > {
294+ let flags = libc:: O_CLOEXEC
295+ | opts. get_access_mode ( ) ?
296+ | opts. get_creation_mode ( ) ?
297+ | ( opts. custom_flags as c_int & !libc:: O_ACCMODE ) ;
298+ let fd = cvt_r ( || unsafe {
299+ openat64 ( libc:: dirfd ( self . 0 ) , path. as_ptr ( ) , flags, opts. mode as c_int )
300+ } ) ?;
301+ Ok ( File ( unsafe { FileDesc :: from_raw_fd ( fd) } ) )
302+ }
303+
304+ // pub fn create_dir<P: AsRef<Path>>(&self, path: P) -> Result<()>
305+ // pub fn rename<P: AsRef<Path>, Q: AsRef<Path>>(&self, from: P, to_dir: &Self, to: Q) -> Result<()>
306+ // pub fn remove_file<P: AsRef<Path>>(&self, path: P) -> Result<()>
307+ // pub fn remove_dir<P: AsRef<Path>>(&self, path: P) -> Result<()>
308+ // pub fn symlink<P: AsRef<Path>, Q: AsRef<Path>>(&self, original: P, link: Q)
309+ }
310+
311+ fn get_path_from_fd ( fd : c_int ) -> Option < PathBuf > {
312+ #[ cfg( any( target_os = "linux" , target_os = "illumos" , target_os = "solaris" ) ) ]
313+ fn get_path ( fd : c_int ) -> Option < PathBuf > {
314+ let mut p = PathBuf :: from ( "/proc/self/fd" ) ;
315+ p. push ( & fd. to_string ( ) ) ;
316+ run_path_with_cstr ( & p, & readlink) . ok ( )
317+ }
318+
319+ #[ cfg( any( target_vendor = "apple" , target_os = "netbsd" ) ) ]
320+ fn get_path ( fd : c_int ) -> Option < PathBuf > {
321+ // FIXME: The use of PATH_MAX is generally not encouraged, but it
322+ // is inevitable in this case because Apple targets and NetBSD define `fcntl`
323+ // with `F_GETPATH` in terms of `MAXPATHLEN`, and there are no
324+ // alternatives. If a better method is invented, it should be used
325+ // instead.
326+ let mut buf = vec ! [ 0 ; libc:: PATH_MAX as usize ] ;
327+ let n = unsafe { libc:: fcntl ( fd, libc:: F_GETPATH , buf. as_ptr ( ) ) } ;
328+ if n == -1 {
329+ cfg_if:: cfg_if! {
330+ if #[ cfg( target_os = "netbsd" ) ] {
331+ // fallback to procfs as last resort
332+ let mut p = PathBuf :: from( "/proc/self/fd" ) ;
333+ p. push( & fd. to_string( ) ) ;
334+ return run_path_with_cstr( & p, & readlink) . ok( )
335+ } else {
336+ return None ;
337+ }
338+ }
339+ }
340+ let l = buf. iter ( ) . position ( |& c| c == 0 ) . unwrap ( ) ;
341+ buf. truncate ( l as usize ) ;
342+ buf. shrink_to_fit ( ) ;
343+ Some ( PathBuf :: from ( OsString :: from_vec ( buf) ) )
344+ }
345+
346+ #[ cfg( target_os = "freebsd" ) ]
347+ fn get_path ( fd : c_int ) -> Option < PathBuf > {
348+ let info = Box :: < libc:: kinfo_file > :: new_zeroed ( ) ;
349+ let mut info = unsafe { info. assume_init ( ) } ;
350+ info. kf_structsize = size_of :: < libc:: kinfo_file > ( ) as libc:: c_int ;
351+ let n = unsafe { libc:: fcntl ( fd, libc:: F_KINFO , & mut * info) } ;
352+ if n == -1 {
353+ return None ;
354+ }
355+ let buf = unsafe { CStr :: from_ptr ( info. kf_path . as_mut_ptr ( ) ) . to_bytes ( ) . to_vec ( ) } ;
356+ Some ( PathBuf :: from ( OsString :: from_vec ( buf) ) )
357+ }
358+
359+ #[ cfg( target_os = "vxworks" ) ]
360+ fn get_path ( fd : c_int ) -> Option < PathBuf > {
361+ let mut buf = vec ! [ 0 ; libc:: PATH_MAX as usize ] ;
362+ let n = unsafe { libc:: ioctl ( fd, libc:: FIOGETNAME , buf. as_ptr ( ) ) } ;
363+ if n == -1 {
364+ return None ;
365+ }
366+ let l = buf. iter ( ) . position ( |& c| c == 0 ) . unwrap ( ) ;
367+ buf. truncate ( l as usize ) ;
368+ Some ( PathBuf :: from ( OsString :: from_vec ( buf) ) )
369+ }
370+
371+ #[ cfg( not( any(
372+ target_os = "linux" ,
373+ target_os = "vxworks" ,
374+ target_os = "freebsd" ,
375+ target_os = "netbsd" ,
376+ target_os = "illumos" ,
377+ target_os = "solaris" ,
378+ target_vendor = "apple" ,
379+ ) ) ) ]
380+ fn get_path ( _fd : c_int ) -> Option < PathBuf > {
381+ // FIXME(#24570): implement this for other Unix platforms
382+ None
383+ }
384+
385+ get_path ( fd)
386+ }
387+
388+ impl fmt:: Debug for Dir {
389+ fn fmt ( & self , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
390+ fn get_mode ( fd : c_int ) -> Option < ( bool , bool ) > {
391+ let mode = unsafe { libc:: fcntl ( fd, libc:: F_GETFL ) } ;
392+ if mode == -1 {
393+ return None ;
394+ }
395+ match mode & libc:: O_ACCMODE {
396+ libc:: O_RDONLY => Some ( ( true , false ) ) ,
397+ libc:: O_RDWR => Some ( ( true , true ) ) ,
398+ libc:: O_WRONLY => Some ( ( false , true ) ) ,
399+ _ => None ,
400+ }
401+ }
402+
403+ let fd = unsafe { dirfd ( self . 0 ) } ;
404+ let mut b = f. debug_struct ( "Dir" ) ;
405+ b. field ( "fd" , & fd) ;
406+ if let Some ( path) = get_path_from_fd ( fd) {
407+ b. field ( "path" , & path) ;
408+ }
409+ if let Some ( ( read, write) ) = get_mode ( fd) {
410+ b. field ( "read" , & read) . field ( "write" , & write) ;
411+ }
412+ b. finish ( )
413+ }
414+ }
268415
269416unsafe impl Send for Dir { }
270417unsafe 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