@@ -41,88 +41,135 @@ 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+ stat. st_dev = libc:: makedev( buf. stx_dev_major, buf. stx_dev_minor) ;
141+ stat. st_ino = buf. stx_ino as libc:: ino64_t;
142+ stat. st_nlink = buf. stx_nlink as libc:: nlink_t;
143+ stat. st_mode = buf. stx_mode as libc:: mode_t;
144+ stat. st_uid = buf. stx_uid as libc:: uid_t;
145+ stat. st_gid = buf. stx_gid as libc:: gid_t;
146+ stat. st_rdev = libc:: makedev( buf. stx_rdev_major, buf. stx_rdev_minor) ;
147+ stat. st_size = buf. stx_size as off64_t;
148+ stat. st_blksize = buf. stx_blksize as libc:: blksize_t;
149+ stat. st_blocks = buf. stx_blocks as libc:: blkcnt64_t;
150+ stat. st_atime = buf. stx_atime. tv_sec as libc:: time_t;
151+ stat. st_atime_nsec = buf. stx_atime. tv_nsec as libc:: c_long;
152+ stat. st_mtime = buf. stx_mtime. tv_sec as libc:: time_t;
153+ stat. st_mtime_nsec = buf. stx_mtime. tv_nsec as libc:: c_long;
154+ stat. st_ctime = buf. stx_ctime. tv_sec as libc:: time_t;
155+ stat. st_ctime_nsec = buf. stx_ctime. tv_nsec as libc:: c_long;
156+
157+ let extra = StatxExtraFields {
158+ stx_mask: buf. stx_mask,
159+ stx_btime: buf. stx_btime,
160+ } ;
86161
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 ;
162+ Some ( Ok ( FileAttr { stat, statx_extra_fields: Some ( extra) } ) )
94163 }
95- _ => return Some ( Err ( err) ) ,
96164 }
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- } ;
165+ }
121166
122- Some ( Ok ( FileAttr { stat, statx_extra_fields : Some ( extra) } ) )
123- }
167+ } else {
168+ #[ derive( Clone ) ]
169+ pub struct FileAttr {
170+ stat: stat64,
124171 }
125- }
172+ } }
126173
127174// all DirEntry's will have a reference to this struct
128175struct InnerReadDir {
@@ -175,15 +222,21 @@ pub struct FileType { mode: mode_t }
175222#[ derive( Debug ) ]
176223pub struct DirBuilder { mode : mode_t }
177224
178- impl FileAttr {
179- fn from_stat64 ( stat : stat64 ) -> Self {
180- Self {
181- stat,
182- #[ cfg( target_os = "linux" ) ]
183- statx_extra_fields : None ,
225+ cfg_has_statx ! { {
226+ impl FileAttr {
227+ fn from_stat64( stat: stat64) -> Self {
228+ Self { stat, statx_extra_fields: None }
184229 }
185230 }
231+ } else {
232+ impl FileAttr {
233+ fn from_stat64( stat: stat64) -> Self {
234+ Self { stat }
235+ }
236+ }
237+ } }
186238
239+ impl FileAttr {
187240 pub fn size ( & self ) -> u64 { self . stat . st_size as u64 }
188241 pub fn perm ( & self ) -> FilePermissions {
189242 FilePermissions { mode : ( self . stat . st_mode as mode_t ) }
@@ -250,8 +303,7 @@ impl FileAttr {
250303 target_os = "macos" ,
251304 target_os = "ios" ) ) ) ]
252305 pub fn created ( & self ) -> io:: Result < SystemTime > {
253- #[ cfg( target_os = "linux" ) ]
254- {
306+ cfg_has_statx ! {
255307 if let Some ( ext) = & self . statx_extra_fields {
256308 return if ( ext. stx_mask & libc:: STATX_BTIME ) != 0 {
257309 Ok ( SystemTime :: from( libc:: timespec {
@@ -412,8 +464,7 @@ impl DirEntry {
412464 let fd = cvt ( unsafe { dirfd ( self . dir . inner . dirp . 0 ) } ) ?;
413465 let name = self . entry . d_name . as_ptr ( ) ;
414466
415- #[ cfg( target_os = "linux" ) ]
416- {
467+ cfg_has_statx ! {
417468 if let Some ( ret) = unsafe { try_statx(
418469 fd,
419470 name,
@@ -636,8 +687,7 @@ impl File {
636687 pub fn file_attr ( & self ) -> io:: Result < FileAttr > {
637688 let fd = self . 0 . raw ( ) ;
638689
639- #[ cfg( target_os = "linux" ) ]
640- {
690+ cfg_has_statx ! {
641691 if let Some ( ret) = unsafe { try_statx(
642692 fd,
643693 b"\0 " as * const _ as * const libc:: c_char,
@@ -930,8 +980,7 @@ pub fn link(src: &Path, dst: &Path) -> io::Result<()> {
930980pub fn stat ( p : & Path ) -> io:: Result < FileAttr > {
931981 let p = cstr ( p) ?;
932982
933- #[ cfg( target_os = "linux" ) ]
934- {
983+ cfg_has_statx ! {
935984 if let Some ( ret) = unsafe { try_statx(
936985 libc:: AT_FDCWD ,
937986 p. as_ptr( ) ,
@@ -952,8 +1001,7 @@ pub fn stat(p: &Path) -> io::Result<FileAttr> {
9521001pub fn lstat ( p : & Path ) -> io:: Result < FileAttr > {
9531002 let p = cstr ( p) ?;
9541003
955- #[ cfg( target_os = "linux" ) ]
956- {
1004+ cfg_has_statx ! {
9571005 if let Some ( ret) = unsafe { try_statx(
9581006 libc:: AT_FDCWD ,
9591007 p. as_ptr( ) ,
0 commit comments