@@ -11,7 +11,7 @@ pub mod error;
1111use clap:: builder:: { PossibleValue , ValueParser } ;
1212use clap:: { Arg , ArgAction , ArgGroup , ArgMatches , Command } ;
1313use filetime:: FileTime ;
14- #[ cfg( any( not( unix) , target_os = "redox" ) ) ]
14+ #[ cfg( any( all ( not( unix) , not ( target_os = "wasi" ) ) , target_os = "redox" ) ) ]
1515use filetime:: set_file_times;
1616#[ cfg( not( target_os = "wasi" ) ) ]
1717use filetime:: set_symlink_file_times;
@@ -649,14 +649,53 @@ fn update_times(
649649 set_times_by_path ( path, atime, mtime)
650650 }
651651
652- #[ cfg( not( unix) ) ]
652+ #[ cfg( target_os = "wasi" ) ]
653+ {
654+ set_times_by_path_wasi ( path, atime, mtime)
655+ }
656+
657+ #[ cfg( not( any( unix, target_os = "wasi" ) ) ) ]
653658 {
654659 set_file_times ( path, atime, mtime) . map_err_context (
655660 || translate ! ( "touch-error-setting-times-of-path" , "path" => path. quote( ) ) ,
656661 )
657662 }
658663}
659664
665+ #[ cfg( target_os = "wasi" ) ]
666+ /// Set file times by path on WASI.
667+ ///
668+ /// wasmtime's `utimensat` implementation refuses to set times on a
669+ /// follow-symlink path if it cannot `open()` the target (it returns
670+ /// `ENOTSUP`, since honoring the symlink race-freely would otherwise require
671+ /// an FD), even for files the caller owns but cannot open (e.g. mode 0).
672+ /// Native Unix `utimensat` has no such restriction. When the follow variant
673+ /// fails and the path is not itself a symlink, retry with
674+ /// `SYMLINK_NOFOLLOW`, which resolves the same file without opening it.
675+ fn set_times_by_path_wasi ( path : & Path , atime : FileTime , mtime : FileTime ) -> UResult < ( ) > {
676+ let timestamps = build_timestamps ( atime, mtime) ;
677+ let result = rustix:: fs:: utimensat (
678+ rustix:: fs:: CWD ,
679+ path,
680+ & timestamps,
681+ rustix:: fs:: AtFlags :: empty ( ) ,
682+ ) ;
683+ let result = match result {
684+ Err ( _) if !fs:: symlink_metadata ( path) . is_ok_and ( |m| m. file_type ( ) . is_symlink ( ) ) => {
685+ rustix:: fs:: utimensat (
686+ rustix:: fs:: CWD ,
687+ path,
688+ & timestamps,
689+ rustix:: fs:: AtFlags :: SYMLINK_NOFOLLOW ,
690+ )
691+ }
692+ other => other,
693+ } ;
694+ result
695+ . map_err ( |e| Error :: from_raw_os_error ( e. raw_os_error ( ) ) )
696+ . map_err_context ( || translate ! ( "touch-error-setting-times-of-path" , "path" => path. quote( ) ) )
697+ }
698+
660699#[ cfg( any( unix, target_os = "wasi" ) ) ]
661700/// Build a rustix `Timestamps` from the access and modification `FileTime`s,
662701/// preserving the `UTIME_NOW`/`UTIME_OMIT` sentinels in the nanoseconds field.
0 commit comments