@@ -41,88 +41,137 @@ 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 ,
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 ,
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+ } ;
5784}
5885
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 } ;
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+ }
69122
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- }
123+ if !HAS_STATX . load( Ordering :: Relaxed ) {
124+ return None ;
125+ }
82126
83- if !HAS_STATX . load ( Ordering :: Relaxed ) {
84- return None ;
85- }
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+ } ;
86163
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 ;
164+ Some ( Ok ( FileAttr { stat, statx_extra_fields: Some ( extra) } ) )
94165 }
95- _ => return Some ( Err ( err) ) ,
96166 }
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- } ;
167+ }
121168
122- Some ( Ok ( FileAttr { stat, statx_extra_fields : Some ( extra) } ) )
123- }
169+ } else {
170+ #[ derive( Clone ) ]
171+ pub struct FileAttr {
172+ stat: stat64,
124173 }
125- }
174+ } }
126175
127176// all DirEntry's will have a reference to this struct
128177struct InnerReadDir {
@@ -175,15 +224,21 @@ pub struct FileType { mode: mode_t }
175224#[ derive( Debug ) ]
176225pub struct DirBuilder { mode : mode_t }
177226
178- impl FileAttr {
179- fn from_stat64 ( stat : stat64 ) -> Self {
180- Self {
181- stat,
182- #[ cfg( target_os = "linux" ) ]
183- statx_extra_fields : None ,
227+ cfg_has_statx ! { {
228+ impl FileAttr {
229+ fn from_stat64( stat: stat64) -> Self {
230+ Self { stat, statx_extra_fields: None }
184231 }
185232 }
233+ } else {
234+ impl FileAttr {
235+ fn from_stat64( stat: stat64) -> Self {
236+ Self { stat }
237+ }
238+ }
239+ } }
186240
241+ impl FileAttr {
187242 pub fn size ( & self ) -> u64 { self . stat . st_size as u64 }
188243 pub fn perm ( & self ) -> FilePermissions {
189244 FilePermissions { mode : ( self . stat . st_mode as mode_t ) }
@@ -250,8 +305,7 @@ impl FileAttr {
250305 target_os = "macos" ,
251306 target_os = "ios" ) ) ) ]
252307 pub fn created ( & self ) -> io:: Result < SystemTime > {
253- #[ cfg( target_os = "linux" ) ]
254- {
308+ cfg_has_statx ! {
255309 if let Some ( ext) = & self . statx_extra_fields {
256310 return if ( ext. stx_mask & libc:: STATX_BTIME ) != 0 {
257311 Ok ( SystemTime :: from( libc:: timespec {
@@ -412,8 +466,7 @@ impl DirEntry {
412466 let fd = cvt ( unsafe { dirfd ( self . dir . inner . dirp . 0 ) } ) ?;
413467 let name = self . entry . d_name . as_ptr ( ) ;
414468
415- #[ cfg( target_os = "linux" ) ]
416- {
469+ cfg_has_statx ! {
417470 if let Some ( ret) = unsafe { try_statx(
418471 fd,
419472 name,
@@ -636,8 +689,7 @@ impl File {
636689 pub fn file_attr ( & self ) -> io:: Result < FileAttr > {
637690 let fd = self . 0 . raw ( ) ;
638691
639- #[ cfg( target_os = "linux" ) ]
640- {
692+ cfg_has_statx ! {
641693 if let Some ( ret) = unsafe { try_statx(
642694 fd,
643695 b"\0 " as * const _ as * const libc:: c_char,
@@ -930,8 +982,7 @@ pub fn link(src: &Path, dst: &Path) -> io::Result<()> {
930982pub fn stat ( p : & Path ) -> io:: Result < FileAttr > {
931983 let p = cstr ( p) ?;
932984
933- #[ cfg( target_os = "linux" ) ]
934- {
985+ cfg_has_statx ! {
935986 if let Some ( ret) = unsafe { try_statx(
936987 libc:: AT_FDCWD ,
937988 p. as_ptr( ) ,
@@ -952,8 +1003,7 @@ pub fn stat(p: &Path) -> io::Result<FileAttr> {
9521003pub fn lstat ( p : & Path ) -> io:: Result < FileAttr > {
9531004 let p = cstr ( p) ?;
9541005
955- #[ cfg( target_os = "linux" ) ]
956- {
1006+ cfg_has_statx ! {
9571007 if let Some ( ret) = unsafe { try_statx(
9581008 libc:: AT_FDCWD ,
9591009 p. as_ptr( ) ,
0 commit comments