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