@@ -41,11 +41,138 @@ pub use crate::sys_common::fs::remove_dir_all;
4141
4242pub struct File ( FileDesc ) ;
4343
44- #[ derive( Clone ) ]
45- pub struct FileAttr {
46- stat : stat64 ,
44+ // FIXME: This should be available on Linux with all `target_arch` and `target_env`.
45+ // https://github.com/rust-lang/libc/issues/1545
46+ macro_rules! cfg_has_statx {
47+ ( { $( $then_tt: tt) * } else { $( $else_tt: tt) * } ) => {
48+ cfg_if:: cfg_if! {
49+ if #[ cfg( all( target_os = "linux" , target_env = "gnu" , any(
50+ target_arch = "x86" ,
51+ target_arch = "arm" ,
52+ // target_arch = "mips",
53+ target_arch = "powerpc" ,
54+ target_arch = "x86_64" ,
55+ // target_arch = "aarch64",
56+ target_arch = "powerpc64" ,
57+ // target_arch = "mips64",
58+ // target_arch = "s390x",
59+ target_arch = "sparc64" ,
60+ ) ) ) ] {
61+ $( $then_tt) *
62+ } else {
63+ $( $else_tt) *
64+ }
65+ }
66+ } ;
67+ ( $( $block_inner: tt) * ) => {
68+ #[ cfg( all( target_os = "linux" , target_env = "gnu" , any(
69+ target_arch = "x86" ,
70+ target_arch = "arm" ,
71+ // target_arch = "mips",
72+ target_arch = "powerpc" ,
73+ target_arch = "x86_64" ,
74+ // target_arch = "aarch64",
75+ target_arch = "powerpc64" ,
76+ // target_arch = "mips64",
77+ // target_arch = "s390x",
78+ target_arch = "sparc64" ,
79+ ) ) ) ]
80+ {
81+ $( $block_inner) *
82+ }
83+ } ;
4784}
4885
86+ cfg_has_statx ! { {
87+ #[ derive( Clone ) ]
88+ pub struct FileAttr {
89+ stat: stat64,
90+ statx_extra_fields: Option <StatxExtraFields >,
91+ }
92+
93+ #[ derive( Clone ) ]
94+ struct StatxExtraFields {
95+ // This is needed to check if btime is supported by the filesystem.
96+ stx_mask: u32 ,
97+ stx_btime: libc:: statx_timestamp,
98+ }
99+
100+ // We prefer `statx` on Linux if available, which contains file creation time.
101+ // Default `stat64` contains no creation time.
102+ unsafe fn try_statx(
103+ fd: c_int,
104+ path: * const libc:: c_char,
105+ flags: i32 ,
106+ mask: u32 ,
107+ ) -> Option <io:: Result <FileAttr >> {
108+ use crate :: sync:: atomic:: { AtomicBool , Ordering } ;
109+
110+ // Linux kernel prior to 4.11 or glibc prior to glibc 2.28 don't support `statx`
111+ // We store the availability in a global to avoid unnecessary syscalls
112+ static HAS_STATX : AtomicBool = AtomicBool :: new( true ) ;
113+ syscall! {
114+ fn statx(
115+ fd: c_int,
116+ pathname: * const libc:: c_char,
117+ flags: c_int,
118+ mask: libc:: c_uint,
119+ statxbuf: * mut libc:: statx
120+ ) -> c_int
121+ }
122+
123+ if !HAS_STATX . load( Ordering :: Relaxed ) {
124+ return None ;
125+ }
126+
127+ let mut buf: libc:: statx = mem:: zeroed( ) ;
128+ let ret = cvt( statx( fd, path, flags, mask, & mut buf) ) ;
129+ match ret {
130+ Err ( err) => match err. raw_os_error( ) {
131+ Some ( libc:: ENOSYS ) => {
132+ HAS_STATX . store( false , Ordering :: Relaxed ) ;
133+ return None ;
134+ }
135+ _ => return Some ( Err ( err) ) ,
136+ }
137+ Ok ( _) => {
138+ // We cannot fill `stat64` exhaustively because of private padding fields.
139+ let mut stat: stat64 = mem:: zeroed( ) ;
140+ // `c_ulong` on gnu-mips, `dev_t` otherwise
141+ stat. st_dev = libc:: makedev( buf. stx_dev_major, buf. stx_dev_minor) as _;
142+ stat. st_ino = buf. stx_ino as libc:: ino64_t;
143+ stat. st_nlink = buf. stx_nlink as libc:: nlink_t;
144+ stat. st_mode = buf. stx_mode as libc:: mode_t;
145+ stat. st_uid = buf. stx_uid as libc:: uid_t;
146+ stat. st_gid = buf. stx_gid as libc:: gid_t;
147+ stat. st_rdev = libc:: makedev( buf. stx_rdev_major, buf. stx_rdev_minor) as _;
148+ stat. st_size = buf. stx_size as off64_t;
149+ stat. st_blksize = buf. stx_blksize as libc:: blksize_t;
150+ stat. st_blocks = buf. stx_blocks as libc:: blkcnt64_t;
151+ stat. st_atime = buf. stx_atime. tv_sec as libc:: time_t;
152+ // `i64` on gnu-x86_64-x32, `c_ulong` otherwise.
153+ stat. st_atime_nsec = buf. stx_atime. tv_nsec as _;
154+ stat. st_mtime = buf. stx_mtime. tv_sec as libc:: time_t;
155+ stat. st_mtime_nsec = buf. stx_mtime. tv_nsec as _;
156+ stat. st_ctime = buf. stx_ctime. tv_sec as libc:: time_t;
157+ stat. st_ctime_nsec = buf. stx_ctime. tv_nsec as _;
158+
159+ let extra = StatxExtraFields {
160+ stx_mask: buf. stx_mask,
161+ stx_btime: buf. stx_btime,
162+ } ;
163+
164+ Some ( Ok ( FileAttr { stat, statx_extra_fields: Some ( extra) } ) )
165+ }
166+ }
167+ }
168+
169+ } else {
170+ #[ derive( Clone ) ]
171+ pub struct FileAttr {
172+ stat: stat64,
173+ }
174+ } }
175+
49176// all DirEntry's will have a reference to this struct
50177struct InnerReadDir {
51178 dirp : Dir ,
@@ -97,6 +224,20 @@ pub struct FileType { mode: mode_t }
97224#[ derive( Debug ) ]
98225pub struct DirBuilder { mode : mode_t }
99226
227+ cfg_has_statx ! { {
228+ impl FileAttr {
229+ fn from_stat64( stat: stat64) -> Self {
230+ Self { stat, statx_extra_fields: None }
231+ }
232+ }
233+ } else {
234+ impl FileAttr {
235+ fn from_stat64( stat: stat64) -> Self {
236+ Self { stat }
237+ }
238+ }
239+ } }
240+
100241impl FileAttr {
101242 pub fn size ( & self ) -> u64 { self . stat . st_size as u64 }
102243 pub fn perm ( & self ) -> FilePermissions {
@@ -164,6 +305,22 @@ impl FileAttr {
164305 target_os = "macos" ,
165306 target_os = "ios" ) ) ) ]
166307 pub fn created ( & self ) -> io:: Result < SystemTime > {
308+ cfg_has_statx ! {
309+ if let Some ( ext) = & self . statx_extra_fields {
310+ return if ( ext. stx_mask & libc:: STATX_BTIME ) != 0 {
311+ Ok ( SystemTime :: from( libc:: timespec {
312+ tv_sec: ext. stx_btime. tv_sec as libc:: time_t,
313+ tv_nsec: ext. stx_btime. tv_nsec as _,
314+ } ) )
315+ } else {
316+ Err ( io:: Error :: new(
317+ io:: ErrorKind :: Other ,
318+ "creation time is not available for the filesystem" ,
319+ ) )
320+ } ;
321+ }
322+ }
323+
167324 Err ( io:: Error :: new ( io:: ErrorKind :: Other ,
168325 "creation time is not available on this platform \
169326 currently") )
@@ -306,12 +463,25 @@ impl DirEntry {
306463
307464 #[ cfg( any( target_os = "linux" , target_os = "emscripten" , target_os = "android" ) ) ]
308465 pub fn metadata ( & self ) -> io:: Result < FileAttr > {
309- let fd = cvt ( unsafe { dirfd ( self . dir . inner . dirp . 0 ) } ) ?;
466+ let fd = cvt ( unsafe { dirfd ( self . dir . inner . dirp . 0 ) } ) ?;
467+ let name = self . entry . d_name . as_ptr ( ) ;
468+
469+ cfg_has_statx ! {
470+ if let Some ( ret) = unsafe { try_statx(
471+ fd,
472+ name,
473+ libc:: AT_SYMLINK_NOFOLLOW | libc:: AT_STATX_SYNC_AS_STAT ,
474+ libc:: STATX_ALL ,
475+ ) } {
476+ return ret;
477+ }
478+ }
479+
310480 let mut stat: stat64 = unsafe { mem:: zeroed ( ) } ;
311481 cvt ( unsafe {
312- fstatat64 ( fd, self . entry . d_name . as_ptr ( ) , & mut stat, libc:: AT_SYMLINK_NOFOLLOW )
482+ fstatat64 ( fd, name , & mut stat, libc:: AT_SYMLINK_NOFOLLOW )
313483 } ) ?;
314- Ok ( FileAttr { stat } )
484+ Ok ( FileAttr :: from_stat64 ( stat) )
315485 }
316486
317487 #[ cfg( not( any( target_os = "linux" , target_os = "emscripten" , target_os = "android" ) ) ) ]
@@ -517,11 +687,24 @@ impl File {
517687 }
518688
519689 pub fn file_attr ( & self ) -> io:: Result < FileAttr > {
690+ let fd = self . 0 . raw ( ) ;
691+
692+ cfg_has_statx ! {
693+ if let Some ( ret) = unsafe { try_statx(
694+ fd,
695+ b"\0 " as * const _ as * const libc:: c_char,
696+ libc:: AT_EMPTY_PATH | libc:: AT_STATX_SYNC_AS_STAT ,
697+ libc:: STATX_ALL ,
698+ ) } {
699+ return ret;
700+ }
701+ }
702+
520703 let mut stat: stat64 = unsafe { mem:: zeroed ( ) } ;
521704 cvt ( unsafe {
522- fstat64 ( self . 0 . raw ( ) , & mut stat)
705+ fstat64 ( fd , & mut stat)
523706 } ) ?;
524- Ok ( FileAttr { stat } )
707+ Ok ( FileAttr :: from_stat64 ( stat) )
525708 }
526709
527710 pub fn fsync ( & self ) -> io:: Result < ( ) > {
@@ -798,20 +981,44 @@ pub fn link(src: &Path, dst: &Path) -> io::Result<()> {
798981
799982pub fn stat ( p : & Path ) -> io:: Result < FileAttr > {
800983 let p = cstr ( p) ?;
984+
985+ cfg_has_statx ! {
986+ if let Some ( ret) = unsafe { try_statx(
987+ libc:: AT_FDCWD ,
988+ p. as_ptr( ) ,
989+ libc:: AT_STATX_SYNC_AS_STAT ,
990+ libc:: STATX_ALL ,
991+ ) } {
992+ return ret;
993+ }
994+ }
995+
801996 let mut stat: stat64 = unsafe { mem:: zeroed ( ) } ;
802997 cvt ( unsafe {
803998 stat64 ( p. as_ptr ( ) , & mut stat)
804999 } ) ?;
805- Ok ( FileAttr { stat } )
1000+ Ok ( FileAttr :: from_stat64 ( stat) )
8061001}
8071002
8081003pub fn lstat ( p : & Path ) -> io:: Result < FileAttr > {
8091004 let p = cstr ( p) ?;
1005+
1006+ cfg_has_statx ! {
1007+ if let Some ( ret) = unsafe { try_statx(
1008+ libc:: AT_FDCWD ,
1009+ p. as_ptr( ) ,
1010+ libc:: AT_SYMLINK_NOFOLLOW | libc:: AT_STATX_SYNC_AS_STAT ,
1011+ libc:: STATX_ALL ,
1012+ ) } {
1013+ return ret;
1014+ }
1015+ }
1016+
8101017 let mut stat: stat64 = unsafe { mem:: zeroed ( ) } ;
8111018 cvt ( unsafe {
8121019 lstat64 ( p. as_ptr ( ) , & mut stat)
8131020 } ) ?;
814- Ok ( FileAttr { stat } )
1021+ Ok ( FileAttr :: from_stat64 ( stat) )
8151022}
8161023
8171024pub fn canonicalize ( p : & Path ) -> io:: Result < PathBuf > {
0 commit comments