@@ -44,6 +44,83 @@ 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+ #[ derive( Clone ) ]
52+ struct StatxExtraFields {
53+ // This is needed to check if btime is supported by the filesystem.
54+ stx_mask : u32 ,
55+ stx_btime : libc:: statx_timestamp ,
56+ }
57+
58+ // We prefer `statx` on Linux if available, which contains file creation time.
59+ // Default `stat64` contains no creation time.
60+ #[ cfg( target_os = "linux" ) ]
61+ unsafe fn try_statx (
62+ fd : c_int ,
63+ path : * const libc:: c_char ,
64+ flags : i32 ,
65+ mask : u32 ,
66+ ) -> Option < io:: Result < FileAttr > > {
67+ use crate :: sync:: atomic:: { AtomicBool , Ordering } ;
68+
69+ // Linux kernel prior to 4.11 or glibc prior to glibc 2.28 don't support `statx`
70+ // We store the availability in a global to avoid unnecessary syscalls
71+ static HAS_STATX : AtomicBool = AtomicBool :: new ( true ) ;
72+ syscall ! {
73+ fn statx(
74+ fd: c_int,
75+ pathname: * const libc:: c_char,
76+ flags: c_int,
77+ mask: libc:: c_uint,
78+ statxbuf: * mut libc:: statx
79+ ) -> c_int
80+ }
81+
82+ if !HAS_STATX . load ( Ordering :: Relaxed ) {
83+ return None ;
84+ }
85+
86+ let mut buf: libc:: statx = mem:: zeroed ( ) ;
87+ let ret = cvt ( statx ( fd, path, flags, mask, & mut buf) ) ;
88+ match ret {
89+ Err ( err) => match err. raw_os_error ( ) {
90+ Some ( libc:: ENOSYS ) => {
91+ HAS_STATX . store ( false , Ordering :: Relaxed ) ;
92+ return None ;
93+ }
94+ _ => return Some ( Err ( err) ) ,
95+ }
96+ Ok ( _) => {
97+ // We cannot fill `stat64` exhaustively because of private padding fields.
98+ let mut stat: stat64 = mem:: zeroed ( ) ;
99+ stat. st_dev = libc:: makedev ( buf. stx_dev_major , buf. stx_dev_minor ) ;
100+ stat. st_ino = buf. stx_ino as libc:: ino64_t ;
101+ stat. st_nlink = buf. stx_nlink as libc:: nlink_t ;
102+ stat. st_mode = buf. stx_mode as libc:: mode_t ;
103+ stat. st_uid = buf. stx_uid as libc:: uid_t ;
104+ stat. st_gid = buf. stx_gid as libc:: gid_t ;
105+ stat. st_rdev = libc:: makedev ( buf. stx_rdev_major , buf. stx_rdev_minor ) ;
106+ stat. st_size = buf. stx_size as off64_t ;
107+ stat. st_blksize = buf. stx_blksize as libc:: blksize_t ;
108+ stat. st_blocks = buf. stx_blocks as libc:: blkcnt64_t ;
109+ stat. st_atime = buf. stx_atime . tv_sec as libc:: time_t ;
110+ stat. st_atime_nsec = buf. stx_atime . tv_nsec as libc:: c_long ;
111+ stat. st_mtime = buf. stx_mtime . tv_sec as libc:: time_t ;
112+ stat. st_mtime_nsec = buf. stx_mtime . tv_nsec as libc:: c_long ;
113+ stat. st_ctime = buf. stx_ctime . tv_sec as libc:: time_t ;
114+ stat. st_ctime_nsec = buf. stx_ctime . tv_nsec as libc:: c_long ;
115+
116+ let extra = StatxExtraFields {
117+ stx_mask : buf. stx_mask ,
118+ stx_btime : buf. stx_btime ,
119+ } ;
120+
121+ Some ( Ok ( FileAttr { stat, statx_extra_fields : Some ( extra) } ) )
122+ }
123+ }
47124}
48125
49126// all DirEntry's will have a reference to this struct
@@ -98,6 +175,14 @@ pub struct FileType { mode: mode_t }
98175pub struct DirBuilder { mode : mode_t }
99176
100177impl FileAttr {
178+ fn from_stat64 ( stat : stat64 ) -> Self {
179+ Self {
180+ stat,
181+ #[ cfg( target_os = "linux" ) ]
182+ statx_extra_fields : None ,
183+ }
184+ }
185+
101186 pub fn size ( & self ) -> u64 { self . stat . st_size as u64 }
102187 pub fn perm ( & self ) -> FilePermissions {
103188 FilePermissions { mode : ( self . stat . st_mode as mode_t ) }
@@ -164,6 +249,23 @@ impl FileAttr {
164249 target_os = "macos" ,
165250 target_os = "ios" ) ) ) ]
166251 pub fn created ( & self ) -> io:: Result < SystemTime > {
252+ #[ cfg( target_os = "linux" ) ]
253+ {
254+ if let Some ( ext) = & self . statx_extra_fields {
255+ return if ( ext. stx_mask & libc:: STATX_BTIME ) != 0 {
256+ Ok ( SystemTime :: from ( libc:: timespec {
257+ tv_sec : ext. stx_btime . tv_sec as libc:: time_t ,
258+ tv_nsec : ext. stx_btime . tv_nsec as libc:: c_long ,
259+ } ) )
260+ } else {
261+ Err ( io:: Error :: new (
262+ io:: ErrorKind :: Other ,
263+ "creation time is not available for the filesystem" ,
264+ ) )
265+ } ;
266+ }
267+ }
268+
167269 Err ( io:: Error :: new ( io:: ErrorKind :: Other ,
168270 "creation time is not available on this platform \
169271 currently") )
@@ -306,12 +408,26 @@ impl DirEntry {
306408
307409 #[ cfg( any( target_os = "linux" , target_os = "emscripten" , target_os = "android" ) ) ]
308410 pub fn metadata ( & self ) -> io:: Result < FileAttr > {
309- let fd = cvt ( unsafe { dirfd ( self . dir . inner . dirp . 0 ) } ) ?;
411+ let fd = cvt ( unsafe { dirfd ( self . dir . inner . dirp . 0 ) } ) ?;
412+ let name = self . entry . d_name . as_ptr ( ) ;
413+
414+ #[ cfg( target_os = "linux" ) ]
415+ {
416+ if let Some ( ret) = unsafe { try_statx (
417+ fd,
418+ name,
419+ libc:: AT_SYMLINK_NOFOLLOW | libc:: AT_STATX_SYNC_AS_STAT ,
420+ libc:: STATX_ALL ,
421+ ) } {
422+ return ret;
423+ }
424+ }
425+
310426 let mut stat: stat64 = unsafe { mem:: zeroed ( ) } ;
311427 cvt ( unsafe {
312- fstatat64 ( fd, self . entry . d_name . as_ptr ( ) , & mut stat, libc:: AT_SYMLINK_NOFOLLOW )
428+ fstatat64 ( fd, name , & mut stat, libc:: AT_SYMLINK_NOFOLLOW )
313429 } ) ?;
314- Ok ( FileAttr { stat } )
430+ Ok ( FileAttr :: from_stat64 ( stat) )
315431 }
316432
317433 #[ cfg( not( any( target_os = "linux" , target_os = "emscripten" , target_os = "android" ) ) ) ]
@@ -517,11 +633,25 @@ impl File {
517633 }
518634
519635 pub fn file_attr ( & self ) -> io:: Result < FileAttr > {
636+ let fd = self . 0 . raw ( ) ;
637+
638+ #[ cfg( target_os = "linux" ) ]
639+ {
640+ if let Some ( ret) = unsafe { try_statx (
641+ fd,
642+ b"\0 " as * const _ as * const libc:: c_char ,
643+ libc:: AT_EMPTY_PATH | libc:: AT_STATX_SYNC_AS_STAT ,
644+ libc:: STATX_ALL ,
645+ ) } {
646+ return ret;
647+ }
648+ }
649+
520650 let mut stat: stat64 = unsafe { mem:: zeroed ( ) } ;
521651 cvt ( unsafe {
522- fstat64 ( self . 0 . raw ( ) , & mut stat)
652+ fstat64 ( fd , & mut stat)
523653 } ) ?;
524- Ok ( FileAttr { stat } )
654+ Ok ( FileAttr :: from_stat64 ( stat) )
525655 }
526656
527657 pub fn fsync ( & self ) -> io:: Result < ( ) > {
@@ -798,20 +928,46 @@ pub fn link(src: &Path, dst: &Path) -> io::Result<()> {
798928
799929pub fn stat ( p : & Path ) -> io:: Result < FileAttr > {
800930 let p = cstr ( p) ?;
931+
932+ #[ cfg( target_os = "linux" ) ]
933+ {
934+ if let Some ( ret) = unsafe { try_statx (
935+ libc:: AT_FDCWD ,
936+ p. as_ptr ( ) ,
937+ libc:: AT_STATX_SYNC_AS_STAT ,
938+ libc:: STATX_ALL ,
939+ ) } {
940+ return ret;
941+ }
942+ }
943+
801944 let mut stat: stat64 = unsafe { mem:: zeroed ( ) } ;
802945 cvt ( unsafe {
803946 stat64 ( p. as_ptr ( ) , & mut stat)
804947 } ) ?;
805- Ok ( FileAttr { stat } )
948+ Ok ( FileAttr :: from_stat64 ( stat) )
806949}
807950
808951pub fn lstat ( p : & Path ) -> io:: Result < FileAttr > {
809952 let p = cstr ( p) ?;
953+
954+ #[ cfg( target_os = "linux" ) ]
955+ {
956+ if let Some ( ret) = unsafe { try_statx (
957+ libc:: AT_FDCWD ,
958+ p. as_ptr ( ) ,
959+ libc:: AT_SYMLINK_NOFOLLOW | libc:: AT_STATX_SYNC_AS_STAT ,
960+ libc:: STATX_ALL ,
961+ ) } {
962+ return ret;
963+ }
964+ }
965+
810966 let mut stat: stat64 = unsafe { mem:: zeroed ( ) } ;
811967 cvt ( unsafe {
812968 lstat64 ( p. as_ptr ( ) , & mut stat)
813969 } ) ?;
814- Ok ( FileAttr { stat } )
970+ Ok ( FileAttr :: from_stat64 ( stat) )
815971}
816972
817973pub fn canonicalize ( p : & Path ) -> io:: Result < PathBuf > {
0 commit comments