@@ -20,6 +20,10 @@ use libc::{stat64, fstat64, lstat64, off64_t, ftruncate64, lseek64, dirent64, re
20
20
use libc:: fstatat64;
21
21
#[ cfg( any( target_os = "linux" , target_os = "emscripten" , target_os = "android" ) ) ]
22
22
use libc:: dirfd;
23
+ // We only use struct `statx`, not the function `statx`.
24
+ // Instead, use `syscall` to check if it is available at runtime.
25
+ #[ cfg( target_os = "linux" ) ]
26
+ use libc:: { statx, makedev} ;
23
27
#[ cfg( target_os = "android" ) ]
24
28
use libc:: { stat as stat64, fstat as fstat64, fstatat as fstatat64, lstat as lstat64, lseek64,
25
29
dirent as dirent64, open as open64} ;
@@ -44,6 +48,82 @@ pub struct File(FileDesc);
44
48
#[ derive( Clone ) ]
45
49
pub struct FileAttr {
46
50
stat : stat64 ,
51
+ #[ cfg( target_os = "linux" ) ]
52
+ statx_extra_fields : Option < StatxExtraFields > ,
53
+ }
54
+
55
+ #[ derive( Clone ) ]
56
+ struct StatxExtraFields {
57
+ // This is needed to check if btime is supported by the filesystem.
58
+ stx_mask : u32 ,
59
+ stx_btime : libc:: statx_timestamp ,
60
+ }
61
+
62
+ // We prefer `statx` if available, which contains file creation time.
63
+ #[ cfg( target_os = "linux" ) ]
64
+ unsafe fn try_statx (
65
+ fd : c_int ,
66
+ path : * const libc:: c_char ,
67
+ flags : i32 ,
68
+ mask : u32 ,
69
+ ) -> Option < io:: Result < FileAttr > > {
70
+ use crate :: sync:: atomic:: { AtomicBool , Ordering } ;
71
+
72
+ // Linux kernel prior to 4.11 or glibc prior to glibc 2.28 don't support `statx`
73
+ // We store the availability in a global to avoid unnecessary syscalls
74
+ static HAS_STATX : AtomicBool = AtomicBool :: new ( true ) ;
75
+ syscall ! {
76
+ fn statx(
77
+ fd: c_int,
78
+ pathname: * const libc:: c_char,
79
+ flags: c_int,
80
+ mask: libc:: c_uint,
81
+ statxbuf: * mut statx
82
+ ) -> c_int
83
+ }
84
+
85
+ if !HAS_STATX . load ( Ordering :: Relaxed ) {
86
+ return None ;
87
+ }
88
+
89
+ let mut buf: statx = mem:: zeroed ( ) ;
90
+ let ret = cvt ( statx ( fd, path, flags, mask, & mut buf) ) ;
91
+ match ret {
92
+ Err ( err) => match err. raw_os_error ( ) {
93
+ Some ( libc:: ENOSYS ) => {
94
+ HAS_STATX . store ( false , Ordering :: Relaxed ) ;
95
+ return None ;
96
+ }
97
+ _ => return Some ( Err ( err) ) ,
98
+ }
99
+ Ok ( _) => {
100
+ // We cannot fill `stat64` exhaustively because of private padding fields.
101
+ let mut stat: stat64 = mem:: zeroed ( ) ;
102
+ stat. st_dev = makedev ( buf. stx_dev_major , buf. stx_dev_minor ) ;
103
+ stat. st_ino = buf. stx_ino ;
104
+ stat. st_nlink = buf. stx_nlink as u64 ;
105
+ stat. st_mode = buf. stx_mode as u32 ;
106
+ stat. st_uid = buf. stx_uid ;
107
+ stat. st_gid = buf. stx_gid ;
108
+ stat. st_rdev = makedev ( buf. stx_rdev_major , buf. stx_rdev_minor ) ;
109
+ stat. st_size = buf. stx_size as i64 ;
110
+ stat. st_blksize = buf. stx_blksize as i64 ;
111
+ stat. st_blocks = buf. stx_blocks as i64 ;
112
+ stat. st_atime = buf. stx_atime . tv_sec ;
113
+ stat. st_atime_nsec = buf. stx_atime . tv_nsec as i64 ;
114
+ stat. st_mtime = buf. stx_mtime . tv_sec ;
115
+ stat. st_mtime_nsec = buf. stx_mtime . tv_nsec as i64 ;
116
+ stat. st_ctime = buf. stx_ctime . tv_sec ;
117
+ stat. st_ctime_nsec = buf. stx_ctime . tv_nsec as i64 ;
118
+
119
+ let extra = StatxExtraFields {
120
+ stx_mask : buf. stx_mask ,
121
+ stx_btime : buf. stx_btime ,
122
+ } ;
123
+
124
+ Some ( Ok ( FileAttr { stat, statx_extra_fields : Some ( extra) } ) )
125
+ }
126
+ }
47
127
}
48
128
49
129
// all DirEntry's will have a reference to this struct
@@ -148,6 +228,26 @@ impl FileAttr {
148
228
} ) )
149
229
}
150
230
231
+ #[ cfg( target_os = "linux" ) ]
232
+ pub fn created ( & self ) -> io:: Result < SystemTime > {
233
+ match & self . statx_extra_fields {
234
+ Some ( ext) if ( ext. stx_mask & libc:: STATX_BTIME ) != 0 => {
235
+ Ok ( SystemTime :: from ( libc:: timespec {
236
+ tv_sec : ext. stx_btime . tv_sec as libc:: time_t ,
237
+ tv_nsec : ext. stx_btime . tv_nsec as libc:: c_long ,
238
+ } ) )
239
+ }
240
+ Some ( _) => Err ( io:: Error :: new (
241
+ io:: ErrorKind :: Other ,
242
+ "creation time is not available for the filesystam" ,
243
+ ) ) ,
244
+ None => Err ( io:: Error :: new (
245
+ io:: ErrorKind :: Other ,
246
+ "creation time is not available on this platform currently" ,
247
+ ) ) ,
248
+ }
249
+ }
250
+
151
251
#[ cfg( any( target_os = "freebsd" ,
152
252
target_os = "openbsd" ,
153
253
target_os = "macos" ,
@@ -159,7 +259,8 @@ impl FileAttr {
159
259
} ) )
160
260
}
161
261
162
- #[ cfg( not( any( target_os = "freebsd" ,
262
+ #[ cfg( not( any( target_os = "linux" ,
263
+ target_os = "freebsd" ,
163
264
target_os = "openbsd" ,
164
265
target_os = "macos" ,
165
266
target_os = "ios" ) ) ) ]
@@ -304,7 +405,23 @@ impl DirEntry {
304
405
OsStr :: from_bytes ( self . name_bytes ( ) ) . to_os_string ( )
305
406
}
306
407
307
- #[ cfg( any( target_os = "linux" , target_os = "emscripten" , target_os = "android" ) ) ]
408
+ #[ cfg( target_os = "linux" ) ]
409
+ pub fn metadata ( & self ) -> io:: Result < FileAttr > {
410
+ let dir_fd = cvt ( unsafe { dirfd ( self . dir . inner . dirp . 0 ) } ) ?;
411
+ let pathname = self . entry . d_name . as_ptr ( ) ;
412
+ unsafe { try_statx (
413
+ dir_fd,
414
+ pathname,
415
+ libc:: AT_SYMLINK_NOFOLLOW | libc:: AT_STATX_SYNC_AS_STAT ,
416
+ libc:: STATX_ALL ,
417
+ ) } . unwrap_or_else ( || {
418
+ let mut stat = unsafe { mem:: zeroed ( ) } ;
419
+ cvt ( unsafe { fstatat64 ( dir_fd, pathname, & mut stat, libc:: AT_SYMLINK_NOFOLLOW ) } ) ?;
420
+ Ok ( FileAttr { stat, statx_extra_fields : None } )
421
+ } )
422
+ }
423
+
424
+ #[ cfg( any( target_os = "emscripten" , target_os = "android" ) ) ]
308
425
pub fn metadata ( & self ) -> io:: Result < FileAttr > {
309
426
let fd = cvt ( unsafe { dirfd ( self . dir . inner . dirp . 0 ) } ) ?;
310
427
let mut stat: stat64 = unsafe { mem:: zeroed ( ) } ;
@@ -516,6 +633,22 @@ impl File {
516
633
Ok ( File ( fd) )
517
634
}
518
635
636
+ #[ cfg( target_os = "linux" ) ]
637
+ pub fn file_attr ( & self ) -> io:: Result < FileAttr > {
638
+ let fd = self . 0 . raw ( ) ;
639
+ unsafe { try_statx (
640
+ fd,
641
+ b"\0 " as * const _ as * const libc:: c_char ,
642
+ libc:: AT_EMPTY_PATH | libc:: AT_STATX_SYNC_AS_STAT ,
643
+ libc:: STATX_ALL ,
644
+ ) } . unwrap_or_else ( || {
645
+ let mut stat = unsafe { mem:: zeroed ( ) } ;
646
+ cvt ( unsafe { fstat64 ( fd, & mut stat) } ) ?;
647
+ Ok ( FileAttr { stat, statx_extra_fields : None } )
648
+ } )
649
+ }
650
+
651
+ #[ cfg( not( target_os = "linux" ) ) ]
519
652
pub fn file_attr ( & self ) -> io:: Result < FileAttr > {
520
653
let mut stat: stat64 = unsafe { mem:: zeroed ( ) } ;
521
654
cvt ( unsafe {
@@ -796,6 +929,23 @@ pub fn link(src: &Path, dst: &Path) -> io::Result<()> {
796
929
Ok ( ( ) )
797
930
}
798
931
932
+ #[ cfg( target_os = "linux" ) ]
933
+ pub fn stat ( p : & Path ) -> io:: Result < FileAttr > {
934
+ let p = cstr ( p) ?;
935
+ let p = p. as_ptr ( ) ;
936
+ unsafe { try_statx (
937
+ libc:: AT_FDCWD ,
938
+ p,
939
+ libc:: AT_STATX_SYNC_AS_STAT ,
940
+ libc:: STATX_ALL ,
941
+ ) } . unwrap_or_else ( || {
942
+ let mut stat = unsafe { mem:: zeroed ( ) } ;
943
+ cvt ( unsafe { stat64 ( p, & mut stat) } ) ?;
944
+ Ok ( FileAttr { stat, statx_extra_fields : None } )
945
+ } )
946
+ }
947
+
948
+ #[ cfg( not( target_os = "linux" ) ) ]
799
949
pub fn stat ( p : & Path ) -> io:: Result < FileAttr > {
800
950
let p = cstr ( p) ?;
801
951
let mut stat: stat64 = unsafe { mem:: zeroed ( ) } ;
@@ -805,6 +955,23 @@ pub fn stat(p: &Path) -> io::Result<FileAttr> {
805
955
Ok ( FileAttr { stat } )
806
956
}
807
957
958
+ #[ cfg( target_os = "linux" ) ]
959
+ pub fn lstat ( p : & Path ) -> io:: Result < FileAttr > {
960
+ let p = cstr ( p) ?;
961
+ let p = p. as_ptr ( ) ;
962
+ unsafe { try_statx (
963
+ libc:: AT_FDCWD ,
964
+ p,
965
+ libc:: AT_SYMLINK_NOFOLLOW | libc:: AT_STATX_SYNC_AS_STAT ,
966
+ libc:: STATX_ALL ,
967
+ ) } . unwrap_or_else ( || {
968
+ let mut stat = unsafe { mem:: zeroed ( ) } ;
969
+ cvt ( unsafe { lstat64 ( p, & mut stat) } ) ?;
970
+ Ok ( FileAttr { stat, statx_extra_fields : None } )
971
+ } )
972
+ }
973
+
974
+ #[ cfg( not( target_os = "linux" ) ) ]
808
975
pub fn lstat ( p : & Path ) -> io:: Result < FileAttr > {
809
976
let p = cstr ( p) ?;
810
977
let mut stat: stat64 = unsafe { mem:: zeroed ( ) } ;
0 commit comments