@@ -20,7 +20,7 @@ use crate::sys_common::{AsInner, AsInnerMut, FromInner, IntoInner};
2020 target_os = "watchos" ,
2121) ) ]
2222use crate :: sys:: weak:: syscall;
23- #[ cfg( target_os = "macos" ) ]
23+ #[ cfg( any ( target_os = "android" , target_os = " macos") ) ]
2424use crate :: sys:: weak:: weak;
2525
2626use libc:: { c_int, mode_t} ;
@@ -313,6 +313,9 @@ pub struct FilePermissions {
313313 mode : mode_t ,
314314}
315315
316+ #[ derive( Copy , Clone ) ]
317+ pub struct FileTimes ( [ libc:: timespec ; 2 ] ) ;
318+
316319#[ derive( Copy , Clone , PartialEq , Eq , Hash , Debug ) ]
317320pub struct FileType {
318321 mode : mode_t ,
@@ -507,6 +510,48 @@ impl FilePermissions {
507510 }
508511}
509512
513+ impl FileTimes {
514+ pub fn set_accessed ( & mut self , t : SystemTime ) {
515+ self . 0 [ 0 ] = t. t . to_timespec ( ) . expect ( "Invalid system time" ) ;
516+ }
517+
518+ pub fn set_modified ( & mut self , t : SystemTime ) {
519+ self . 0 [ 1 ] = t. t . to_timespec ( ) . expect ( "Invalid system time" ) ;
520+ }
521+ }
522+
523+ struct TimespecDebugAdapter < ' a > ( & ' a libc:: timespec ) ;
524+
525+ impl fmt:: Debug for TimespecDebugAdapter < ' _ > {
526+ fn fmt ( & self , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
527+ f. debug_struct ( "timespec" )
528+ . field ( "tv_sec" , & self . 0 . tv_sec )
529+ . field ( "tv_nsec" , & self . 0 . tv_nsec )
530+ . finish ( )
531+ }
532+ }
533+
534+ impl fmt:: Debug for FileTimes {
535+ fn fmt ( & self , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
536+ f. debug_struct ( "FileTimes" )
537+ . field ( "accessed" , & TimespecDebugAdapter ( & self . 0 [ 0 ] ) )
538+ . field ( "modified" , & TimespecDebugAdapter ( & self . 0 [ 1 ] ) )
539+ . finish ( )
540+ }
541+ }
542+
543+ impl Default for FileTimes {
544+ fn default ( ) -> Self {
545+ // Redox doesn't appear to support `UTIME_OMIT`, so we stub it out here, and always return
546+ // an error in `set_times`.
547+ #[ cfg( target_os = "redox" ) ]
548+ let omit = libc:: timespec { tv_sec : 0 , tv_nsec : 0 } ;
549+ #[ cfg( not( target_os = "redox" ) ) ]
550+ let omit = libc:: timespec { tv_sec : 0 , tv_nsec : libc:: UTIME_OMIT as _ } ;
551+ Self ( [ omit; 2 ] )
552+ }
553+ }
554+
510555impl FileType {
511556 pub fn is_dir ( & self ) -> bool {
512557 self . is ( libc:: S_IFDIR )
@@ -1029,6 +1074,48 @@ impl File {
10291074 cvt_r ( || unsafe { libc:: fchmod ( self . as_raw_fd ( ) , perm. mode ) } ) ?;
10301075 Ok ( ( ) )
10311076 }
1077+
1078+ pub fn set_times ( & self , times : FileTimes ) -> io:: Result < ( ) > {
1079+ cfg_if:: cfg_if! {
1080+ if #[ cfg( target_os = "redox" ) ] {
1081+ // Redox doesn't appear to support `UTIME_OMIT`.
1082+ drop( times) ;
1083+ Err ( io:: const_io_error!(
1084+ io:: ErrorKind :: Unsupported ,
1085+ "setting file times not supported" ,
1086+ ) )
1087+ } else if #[ cfg( any( target_os = "android" , target_os = "macos" ) ) ] {
1088+ // futimens requires macOS 10.13, and Android API level 19
1089+ cvt( unsafe {
1090+ weak!( fn futimens( c_int, * const libc:: timespec) -> c_int) ;
1091+ match futimens. get( ) {
1092+ Some ( futimens) => futimens( self . as_raw_fd( ) , times. 0 . as_ptr( ) ) ,
1093+ #[ cfg( target_os = "macos" ) ]
1094+ None => {
1095+ fn ts_to_tv( ts: & libc:: timespec) -> libc:: timeval {
1096+ libc:: timeval {
1097+ tv_sec: ts. tv_sec,
1098+ tv_usec: ( ts. tv_nsec / 1000 ) as _
1099+ }
1100+ }
1101+ let timevals = [ ts_to_tv( & times. 0 [ 0 ] ) , ts_to_tv( & times. 0 [ 1 ] ) ] ;
1102+ libc:: futimes( self . as_raw_fd( ) , timevals. as_ptr( ) )
1103+ }
1104+ // futimes requires even newer Android.
1105+ #[ cfg( target_os = "android" ) ]
1106+ None => return Err ( io:: const_io_error!(
1107+ io:: ErrorKind :: Unsupported ,
1108+ "setting file times requires Android API level >= 19" ,
1109+ ) ) ,
1110+ }
1111+ } ) ?;
1112+ Ok ( ( ) )
1113+ } else {
1114+ cvt( unsafe { libc:: futimens( self . as_raw_fd( ) , times. 0 . as_ptr( ) ) } ) ?;
1115+ Ok ( ( ) )
1116+ }
1117+ }
1118+ }
10321119}
10331120
10341121impl DirBuilder {
0 commit comments