@@ -44,6 +44,84 @@ pub struct File(FileDesc);
44
44
#[ derive( Clone ) ]
45
45
pub struct FileAttr {
46
46
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
+ }
47
125
}
48
126
49
127
// all DirEntry's will have a reference to this struct
@@ -98,6 +176,14 @@ pub struct FileType { mode: mode_t }
98
176
pub struct DirBuilder { mode : mode_t }
99
177
100
178
impl 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
+
101
187
pub fn size ( & self ) -> u64 { self . stat . st_size as u64 }
102
188
pub fn perm ( & self ) -> FilePermissions {
103
189
FilePermissions { mode : ( self . stat . st_mode as mode_t ) }
@@ -164,6 +250,23 @@ impl FileAttr {
164
250
target_os = "macos" ,
165
251
target_os = "ios" ) ) ) ]
166
252
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
+
167
270
Err ( io:: Error :: new ( io:: ErrorKind :: Other ,
168
271
"creation time is not available on this platform \
169
272
currently") )
@@ -306,12 +409,26 @@ impl DirEntry {
306
409
307
410
#[ cfg( any( target_os = "linux" , target_os = "emscripten" , target_os = "android" ) ) ]
308
411
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
+
310
427
let mut stat: stat64 = unsafe { mem:: zeroed ( ) } ;
311
428
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 )
313
430
} ) ?;
314
- Ok ( FileAttr { stat } )
431
+ Ok ( FileAttr :: from_stat64 ( stat) )
315
432
}
316
433
317
434
#[ cfg( not( any( target_os = "linux" , target_os = "emscripten" , target_os = "android" ) ) ) ]
@@ -517,11 +634,25 @@ impl File {
517
634
}
518
635
519
636
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
+
520
651
let mut stat: stat64 = unsafe { mem:: zeroed ( ) } ;
521
652
cvt ( unsafe {
522
- fstat64 ( self . 0 . raw ( ) , & mut stat)
653
+ fstat64 ( fd , & mut stat)
523
654
} ) ?;
524
- Ok ( FileAttr { stat } )
655
+ Ok ( FileAttr :: from_stat64 ( stat) )
525
656
}
526
657
527
658
pub fn fsync ( & self ) -> io:: Result < ( ) > {
@@ -798,20 +929,46 @@ pub fn link(src: &Path, dst: &Path) -> io::Result<()> {
798
929
799
930
pub fn stat ( p : & Path ) -> io:: Result < FileAttr > {
800
931
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
+
801
945
let mut stat: stat64 = unsafe { mem:: zeroed ( ) } ;
802
946
cvt ( unsafe {
803
947
stat64 ( p. as_ptr ( ) , & mut stat)
804
948
} ) ?;
805
- Ok ( FileAttr { stat } )
949
+ Ok ( FileAttr :: from_stat64 ( stat) )
806
950
}
807
951
808
952
pub fn lstat ( p : & Path ) -> io:: Result < FileAttr > {
809
953
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
+
810
967
let mut stat: stat64 = unsafe { mem:: zeroed ( ) } ;
811
968
cvt ( unsafe {
812
969
lstat64 ( p. as_ptr ( ) , & mut stat)
813
970
} ) ?;
814
- Ok ( FileAttr { stat } )
971
+ Ok ( FileAttr :: from_stat64 ( stat) )
815
972
}
816
973
817
974
pub fn canonicalize ( p : & Path ) -> io:: Result < PathBuf > {
0 commit comments