@@ -44,6 +44,84 @@ pub struct File(FileDesc);
4444#[ derive( Clone ) ]
4545pub struct FileAttr {
4646 stat : stat64 ,
47+ #[ cfg( target_os = "linux" ) ]
48+ statx_extra_fields : Option < StatxExtraFields > ,
49+ }
50+
51+ #[ cfg( target_os = "linux" ) ]
52+ #[ derive( Clone ) ]
53+ struct StatxExtraFields {
54+ // This is needed to check if btime is supported by the filesystem.
55+ stx_mask : u32 ,
56+ stx_btime : libc:: statx_timestamp ,
57+ }
58+
59+ // We prefer `statx` on Linux if available, which contains file creation time.
60+ // Default `stat64` contains no creation time.
61+ #[ cfg( target_os = "linux" ) ]
62+ unsafe fn try_statx (
63+ fd : c_int ,
64+ path : * const libc:: c_char ,
65+ flags : i32 ,
66+ mask : u32 ,
67+ ) -> Option < io:: Result < FileAttr > > {
68+ use crate :: sync:: atomic:: { AtomicBool , Ordering } ;
69+
70+ // Linux kernel prior to 4.11 or glibc prior to glibc 2.28 don't support `statx`
71+ // We store the availability in a global to avoid unnecessary syscalls
72+ static HAS_STATX : AtomicBool = AtomicBool :: new ( true ) ;
73+ syscall ! {
74+ fn statx(
75+ fd: c_int,
76+ pathname: * const libc:: c_char,
77+ flags: c_int,
78+ mask: libc:: c_uint,
79+ statxbuf: * mut libc:: statx
80+ ) -> c_int
81+ }
82+
83+ if !HAS_STATX . load ( Ordering :: Relaxed ) {
84+ return None ;
85+ }
86+
87+ let mut buf: libc:: statx = mem:: zeroed ( ) ;
88+ let ret = cvt ( statx ( fd, path, flags, mask, & mut buf) ) ;
89+ match ret {
90+ Err ( err) => match err. raw_os_error ( ) {
91+ Some ( libc:: ENOSYS ) => {
92+ HAS_STATX . store ( false , Ordering :: Relaxed ) ;
93+ return None ;
94+ }
95+ _ => return Some ( Err ( err) ) ,
96+ }
97+ Ok ( _) => {
98+ // We cannot fill `stat64` exhaustively because of private padding fields.
99+ let mut stat: stat64 = mem:: zeroed ( ) ;
100+ stat. st_dev = libc:: makedev ( buf. stx_dev_major , buf. stx_dev_minor ) ;
101+ stat. st_ino = buf. stx_ino as libc:: ino64_t ;
102+ stat. st_nlink = buf. stx_nlink as libc:: nlink_t ;
103+ stat. st_mode = buf. stx_mode as libc:: mode_t ;
104+ stat. st_uid = buf. stx_uid as libc:: uid_t ;
105+ stat. st_gid = buf. stx_gid as libc:: gid_t ;
106+ stat. st_rdev = libc:: makedev ( buf. stx_rdev_major , buf. stx_rdev_minor ) ;
107+ stat. st_size = buf. stx_size as off64_t ;
108+ stat. st_blksize = buf. stx_blksize as libc:: blksize_t ;
109+ stat. st_blocks = buf. stx_blocks as libc:: blkcnt64_t ;
110+ stat. st_atime = buf. stx_atime . tv_sec as libc:: time_t ;
111+ stat. st_atime_nsec = buf. stx_atime . tv_nsec as libc:: c_long ;
112+ stat. st_mtime = buf. stx_mtime . tv_sec as libc:: time_t ;
113+ stat. st_mtime_nsec = buf. stx_mtime . tv_nsec as libc:: c_long ;
114+ stat. st_ctime = buf. stx_ctime . tv_sec as libc:: time_t ;
115+ stat. st_ctime_nsec = buf. stx_ctime . tv_nsec as libc:: c_long ;
116+
117+ let extra = StatxExtraFields {
118+ stx_mask : buf. stx_mask ,
119+ stx_btime : buf. stx_btime ,
120+ } ;
121+
122+ Some ( Ok ( FileAttr { stat, statx_extra_fields : Some ( extra) } ) )
123+ }
124+ }
47125}
48126
49127// all DirEntry's will have a reference to this struct
@@ -98,6 +176,14 @@ pub struct FileType { mode: mode_t }
98176pub struct DirBuilder { mode : mode_t }
99177
100178impl FileAttr {
179+ fn from_stat64 ( stat : stat64 ) -> Self {
180+ Self {
181+ stat,
182+ #[ cfg( target_os = "linux" ) ]
183+ statx_extra_fields : None ,
184+ }
185+ }
186+
101187 pub fn size ( & self ) -> u64 { self . stat . st_size as u64 }
102188 pub fn perm ( & self ) -> FilePermissions {
103189 FilePermissions { mode : ( self . stat . st_mode as mode_t ) }
@@ -164,6 +250,23 @@ impl FileAttr {
164250 target_os = "macos" ,
165251 target_os = "ios" ) ) ) ]
166252 pub fn created ( & self ) -> io:: Result < SystemTime > {
253+ #[ cfg( target_os = "linux" ) ]
254+ {
255+ if let Some ( ext) = & self . statx_extra_fields {
256+ return if ( ext. stx_mask & libc:: STATX_BTIME ) != 0 {
257+ Ok ( SystemTime :: from ( libc:: timespec {
258+ tv_sec : ext. stx_btime . tv_sec as libc:: time_t ,
259+ tv_nsec : ext. stx_btime . tv_nsec as libc:: c_long ,
260+ } ) )
261+ } else {
262+ Err ( io:: Error :: new (
263+ io:: ErrorKind :: Other ,
264+ "creation time is not available for the filesystem" ,
265+ ) )
266+ } ;
267+ }
268+ }
269+
167270 Err ( io:: Error :: new ( io:: ErrorKind :: Other ,
168271 "creation time is not available on this platform \
169272 currently") )
@@ -306,12 +409,26 @@ impl DirEntry {
306409
307410 #[ cfg( any( target_os = "linux" , target_os = "emscripten" , target_os = "android" ) ) ]
308411 pub fn metadata ( & self ) -> io:: Result < FileAttr > {
309- let fd = cvt ( unsafe { dirfd ( self . dir . inner . dirp . 0 ) } ) ?;
412+ let fd = cvt ( unsafe { dirfd ( self . dir . inner . dirp . 0 ) } ) ?;
413+ let name = self . entry . d_name . as_ptr ( ) ;
414+
415+ #[ cfg( target_os = "linux" ) ]
416+ {
417+ if let Some ( ret) = unsafe { try_statx (
418+ fd,
419+ name,
420+ libc:: AT_SYMLINK_NOFOLLOW | libc:: AT_STATX_SYNC_AS_STAT ,
421+ libc:: STATX_ALL ,
422+ ) } {
423+ return ret;
424+ }
425+ }
426+
310427 let mut stat: stat64 = unsafe { mem:: zeroed ( ) } ;
311428 cvt ( unsafe {
312- fstatat64 ( fd, self . entry . d_name . as_ptr ( ) , & mut stat, libc:: AT_SYMLINK_NOFOLLOW )
429+ fstatat64 ( fd, name , & mut stat, libc:: AT_SYMLINK_NOFOLLOW )
313430 } ) ?;
314- Ok ( FileAttr { stat } )
431+ Ok ( FileAttr :: from_stat64 ( stat) )
315432 }
316433
317434 #[ cfg( not( any( target_os = "linux" , target_os = "emscripten" , target_os = "android" ) ) ) ]
@@ -517,11 +634,25 @@ impl File {
517634 }
518635
519636 pub fn file_attr ( & self ) -> io:: Result < FileAttr > {
637+ let fd = self . 0 . raw ( ) ;
638+
639+ #[ cfg( target_os = "linux" ) ]
640+ {
641+ if let Some ( ret) = unsafe { try_statx (
642+ fd,
643+ b"\0 " as * const _ as * const libc:: c_char ,
644+ libc:: AT_EMPTY_PATH | libc:: AT_STATX_SYNC_AS_STAT ,
645+ libc:: STATX_ALL ,
646+ ) } {
647+ return ret;
648+ }
649+ }
650+
520651 let mut stat: stat64 = unsafe { mem:: zeroed ( ) } ;
521652 cvt ( unsafe {
522- fstat64 ( self . 0 . raw ( ) , & mut stat)
653+ fstat64 ( fd , & mut stat)
523654 } ) ?;
524- Ok ( FileAttr { stat } )
655+ Ok ( FileAttr :: from_stat64 ( stat) )
525656 }
526657
527658 pub fn fsync ( & self ) -> io:: Result < ( ) > {
@@ -798,20 +929,46 @@ pub fn link(src: &Path, dst: &Path) -> io::Result<()> {
798929
799930pub fn stat ( p : & Path ) -> io:: Result < FileAttr > {
800931 let p = cstr ( p) ?;
932+
933+ #[ cfg( target_os = "linux" ) ]
934+ {
935+ if let Some ( ret) = unsafe { try_statx (
936+ libc:: AT_FDCWD ,
937+ p. as_ptr ( ) ,
938+ libc:: AT_STATX_SYNC_AS_STAT ,
939+ libc:: STATX_ALL ,
940+ ) } {
941+ return ret;
942+ }
943+ }
944+
801945 let mut stat: stat64 = unsafe { mem:: zeroed ( ) } ;
802946 cvt ( unsafe {
803947 stat64 ( p. as_ptr ( ) , & mut stat)
804948 } ) ?;
805- Ok ( FileAttr { stat } )
949+ Ok ( FileAttr :: from_stat64 ( stat) )
806950}
807951
808952pub fn lstat ( p : & Path ) -> io:: Result < FileAttr > {
809953 let p = cstr ( p) ?;
954+
955+ #[ cfg( target_os = "linux" ) ]
956+ {
957+ if let Some ( ret) = unsafe { try_statx (
958+ libc:: AT_FDCWD ,
959+ p. as_ptr ( ) ,
960+ libc:: AT_SYMLINK_NOFOLLOW | libc:: AT_STATX_SYNC_AS_STAT ,
961+ libc:: STATX_ALL ,
962+ ) } {
963+ return ret;
964+ }
965+ }
966+
810967 let mut stat: stat64 = unsafe { mem:: zeroed ( ) } ;
811968 cvt ( unsafe {
812969 lstat64 ( p. as_ptr ( ) , & mut stat)
813970 } ) ?;
814- Ok ( FileAttr { stat } )
971+ Ok ( FileAttr :: from_stat64 ( stat) )
815972}
816973
817974pub fn canonicalize ( p : & Path ) -> io:: Result < PathBuf > {
0 commit comments