@@ -1195,6 +1195,55 @@ impl fmt::Debug for OpenOptions {
1195
1195
}
1196
1196
}
1197
1197
1198
+ #[ cfg( not( any(
1199
+ target_os = "redox" ,
1200
+ target_os = "espidf" ,
1201
+ target_os = "horizon" ,
1202
+ target_os = "nuttx" ,
1203
+ ) ) ) ]
1204
+ fn to_timespec ( time : Option < SystemTime > ) -> io:: Result < libc:: timespec > {
1205
+ match time {
1206
+ Some ( time) if let Some ( ts) = time. t . to_timespec ( ) => Ok ( ts) ,
1207
+ Some ( time) if time > crate :: sys:: time:: UNIX_EPOCH => Err ( io:: const_error!(
1208
+ io:: ErrorKind :: InvalidInput ,
1209
+ "timestamp is too large to set as a file time" ,
1210
+ ) ) ,
1211
+ Some ( _) => Err ( io:: const_error!(
1212
+ io:: ErrorKind :: InvalidInput ,
1213
+ "timestamp is too small to set as a file time" ,
1214
+ ) ) ,
1215
+ None => Ok ( libc:: timespec { tv_sec : 0 , tv_nsec : libc:: UTIME_OMIT as _ } ) ,
1216
+ }
1217
+ }
1218
+
1219
+ #[ cfg( target_vendor = "apple" ) ]
1220
+ fn set_attrlist_with_times (
1221
+ times : & FileTimes ,
1222
+ ) -> io:: Result < ( libc:: attrlist , [ mem:: MaybeUninit < libc:: timespec > ; 3 ] , usize ) > {
1223
+ let mut buf = [ mem:: MaybeUninit :: < libc:: timespec > :: uninit ( ) ; 3 ] ;
1224
+ let mut num_times = 0 ;
1225
+ let mut attrlist: libc:: attrlist = unsafe { mem:: zeroed ( ) } ;
1226
+ attrlist. bitmapcount = libc:: ATTR_BIT_MAP_COUNT ;
1227
+
1228
+ if times. created . is_some ( ) {
1229
+ buf[ num_times] . write ( to_timespec ( times. created ) ?) ;
1230
+ num_times += 1 ;
1231
+ attrlist. commonattr |= libc:: ATTR_CMN_CRTIME ;
1232
+ }
1233
+ if times. modified . is_some ( ) {
1234
+ buf[ num_times] . write ( to_timespec ( times. modified ) ?) ;
1235
+ num_times += 1 ;
1236
+ attrlist. commonattr |= libc:: ATTR_CMN_MODTIME ;
1237
+ }
1238
+ if times. accessed . is_some ( ) {
1239
+ buf[ num_times] . write ( to_timespec ( times. accessed ) ?) ;
1240
+ num_times += 1 ;
1241
+ attrlist. commonattr |= libc:: ATTR_CMN_ACCTIME ;
1242
+ }
1243
+
1244
+ Ok ( ( attrlist, buf, num_times) )
1245
+ }
1246
+
1198
1247
impl File {
1199
1248
pub fn open ( path : & Path , opts : & OpenOptions ) -> io:: Result < File > {
1200
1249
run_path_with_cstr ( path, & |path| File :: open_c ( path, opts) )
@@ -1604,24 +1653,6 @@ impl File {
1604
1653
}
1605
1654
1606
1655
pub fn set_times ( & self , times : FileTimes ) -> io:: Result < ( ) > {
1607
- #[ cfg( not( any(
1608
- target_os = "redox" ,
1609
- target_os = "espidf" ,
1610
- target_os = "horizon" ,
1611
- target_os = "nuttx" ,
1612
- ) ) ) ]
1613
- let to_timespec = |time : Option < SystemTime > | match time {
1614
- Some ( time) if let Some ( ts) = time. t . to_timespec ( ) => Ok ( ts) ,
1615
- Some ( time) if time > crate :: sys:: time:: UNIX_EPOCH => Err ( io:: const_error!(
1616
- io:: ErrorKind :: InvalidInput ,
1617
- "timestamp is too large to set as a file time" ,
1618
- ) ) ,
1619
- Some ( _) => Err ( io:: const_error!(
1620
- io:: ErrorKind :: InvalidInput ,
1621
- "timestamp is too small to set as a file time" ,
1622
- ) ) ,
1623
- None => Ok ( libc:: timespec { tv_sec : 0 , tv_nsec : libc:: UTIME_OMIT as _ } ) ,
1624
- } ;
1625
1656
cfg_select ! {
1626
1657
any( target_os = "redox" , target_os = "espidf" , target_os = "horizon" , target_os = "nuttx" ) => {
1627
1658
// Redox doesn't appear to support `UTIME_OMIT`.
@@ -1634,25 +1665,7 @@ impl File {
1634
1665
) )
1635
1666
}
1636
1667
target_vendor = "apple" => {
1637
- let mut buf = [ mem:: MaybeUninit :: <libc:: timespec>:: uninit( ) ; 3 ] ;
1638
- let mut num_times = 0 ;
1639
- let mut attrlist: libc:: attrlist = unsafe { mem:: zeroed( ) } ;
1640
- attrlist. bitmapcount = libc:: ATTR_BIT_MAP_COUNT ;
1641
- if times. created. is_some( ) {
1642
- buf[ num_times] . write( to_timespec( times. created) ?) ;
1643
- num_times += 1 ;
1644
- attrlist. commonattr |= libc:: ATTR_CMN_CRTIME ;
1645
- }
1646
- if times. modified. is_some( ) {
1647
- buf[ num_times] . write( to_timespec( times. modified) ?) ;
1648
- num_times += 1 ;
1649
- attrlist. commonattr |= libc:: ATTR_CMN_MODTIME ;
1650
- }
1651
- if times. accessed. is_some( ) {
1652
- buf[ num_times] . write( to_timespec( times. accessed) ?) ;
1653
- num_times += 1 ;
1654
- attrlist. commonattr |= libc:: ATTR_CMN_ACCTIME ;
1655
- }
1668
+ let ( attrlist, buf, num_times) = set_attrlist_with_times( & times) ?;
1656
1669
cvt( unsafe { libc:: fsetattrlist(
1657
1670
self . as_raw_fd( ) ,
1658
1671
( & raw const attrlist) . cast:: <libc:: c_void>( ) . cast_mut( ) ,
@@ -2081,6 +2094,84 @@ fn open_from(from: &Path) -> io::Result<(crate::fs::File, crate::fs::Metadata)>
2081
2094
Ok ( ( reader, metadata) )
2082
2095
}
2083
2096
2097
+ fn set_times_impl ( p : & CStr , times : FileTimes , flags : c_int ) -> io:: Result < ( ) > {
2098
+ cfg_select ! {
2099
+ any( target_os = "redox" , target_os = "espidf" , target_os = "horizon" , target_os = "nuttx" ) => {
2100
+ let _ = ( p, times, flags) ;
2101
+ Err ( io:: const_error!(
2102
+ io:: ErrorKind :: Unsupported ,
2103
+ "setting file times not supported" ,
2104
+ ) )
2105
+ }
2106
+ target_vendor = "apple" => {
2107
+ // Apple platforms use setattrlist which supports setting times on symlinks
2108
+ let ( attrlist, buf, num_times) = set_attrlist_with_times( & times) ?;
2109
+ let options = if flags == libc:: AT_SYMLINK_NOFOLLOW {
2110
+ libc:: FSOPT_NOFOLLOW
2111
+ } else {
2112
+ 0
2113
+ } ;
2114
+
2115
+ cvt( unsafe { libc:: setattrlist(
2116
+ p. as_ptr( ) ,
2117
+ ( & raw const attrlist) . cast:: <libc:: c_void>( ) . cast_mut( ) ,
2118
+ buf. as_ptr( ) . cast:: <libc:: c_void>( ) . cast_mut( ) ,
2119
+ num_times * size_of:: <libc:: timespec>( ) ,
2120
+ options as u32
2121
+ ) } ) ?;
2122
+ Ok ( ( ) )
2123
+ }
2124
+ target_os = "android" => {
2125
+ let times = [ to_timespec( times. accessed) ?, to_timespec( times. modified) ?] ;
2126
+ // utimensat requires Android API level 19
2127
+ cvt( unsafe {
2128
+ weak!(
2129
+ fn utimensat( dirfd: c_int, path: * const c_char, times: * const libc:: timespec, flags: c_int) -> c_int;
2130
+ ) ;
2131
+ match utimensat. get( ) {
2132
+ Some ( utimensat) => utimensat( libc:: AT_FDCWD , p. as_ptr( ) , times. as_ptr( ) , flags) ,
2133
+ None => return Err ( io:: const_error!(
2134
+ io:: ErrorKind :: Unsupported ,
2135
+ "setting file times requires Android API level >= 19" ,
2136
+ ) ) ,
2137
+ }
2138
+ } ) ?;
2139
+ Ok ( ( ) )
2140
+ }
2141
+ _ => {
2142
+ #[ cfg( all( target_os = "linux" , target_env = "gnu" , target_pointer_width = "32" , not( target_arch = "riscv32" ) ) ) ]
2143
+ {
2144
+ use crate :: sys:: { time:: __timespec64, weak:: weak} ;
2145
+
2146
+ // Added in glibc 2.34
2147
+ weak!(
2148
+ fn __utimensat64( dirfd: c_int, path: * const c_char, times: * const __timespec64, flags: c_int) -> c_int;
2149
+ ) ;
2150
+
2151
+ if let Some ( utimensat64) = __utimensat64. get( ) {
2152
+ let to_timespec = |time: Option <SystemTime >| time. map( |time| time. t. to_timespec64( ) )
2153
+ . unwrap_or( __timespec64:: new( 0 , libc:: UTIME_OMIT as _) ) ;
2154
+ let times = [ to_timespec( times. accessed) , to_timespec( times. modified) ] ;
2155
+ cvt( unsafe { utimensat64( libc:: AT_FDCWD , p. as_ptr( ) , times. as_ptr( ) , flags) } ) ?;
2156
+ return Ok ( ( ) ) ;
2157
+ }
2158
+ }
2159
+ let times = [ to_timespec( times. accessed) ?, to_timespec( times. modified) ?] ;
2160
+ cvt( unsafe { libc:: utimensat( libc:: AT_FDCWD , p. as_ptr( ) , times. as_ptr( ) , flags) } ) ?;
2161
+ Ok ( ( ) )
2162
+ }
2163
+ }
2164
+ }
2165
+
2166
+ pub fn set_times ( p : & CStr , times : FileTimes ) -> io:: Result < ( ) > {
2167
+ // flags = 0 means follow symlinks
2168
+ set_times_impl ( p, times, 0 )
2169
+ }
2170
+
2171
+ pub fn set_times_nofollow ( p : & CStr , times : FileTimes ) -> io:: Result < ( ) > {
2172
+ set_times_impl ( p, times, libc:: AT_SYMLINK_NOFOLLOW )
2173
+ }
2174
+
2084
2175
#[ cfg( target_os = "espidf" ) ]
2085
2176
fn open_to_and_set_permissions (
2086
2177
to : & Path ,
0 commit comments