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