@@ -13,9 +13,12 @@ use filetime::{set_file_times, FileTime};
1313use std:: error:: Error ;
1414use std:: fmt:: { Debug , Display } ;
1515use std:: fs;
16+ #[ cfg( not( unix) ) ]
1617use std:: fs:: File ;
1718use std:: os:: unix:: fs:: MetadataExt ;
1819#[ cfg( unix) ]
20+ use std:: os:: unix:: fs:: OpenOptionsExt ;
21+ #[ cfg( unix) ]
1922use std:: os:: unix:: prelude:: OsStrExt ;
2023use std:: path:: { Path , PathBuf , MAIN_SEPARATOR } ;
2124use std:: process;
@@ -750,27 +753,52 @@ fn perform_backup(to: &Path, b: &Behavior) -> UResult<Option<PathBuf>> {
750753fn copy_file ( from : & Path , to : & Path ) -> UResult < ( ) > {
751754 // fs::copy fails if destination is a invalid symlink.
752755 // so lets just remove all existing files at destination before copy.
753- if let Err ( e) = fs:: remove_file ( to) {
754- if e. kind ( ) != std:: io:: ErrorKind :: NotFound {
755- show_error ! (
756- "Failed to remove existing file {}. Error: {:?}" ,
757- to. display( ) ,
758- e
759- ) ;
756+ let remove_destination = || {
757+ if let Err ( e) = fs:: remove_file ( to) {
758+ if e. kind ( ) != std:: io:: ErrorKind :: NotFound {
759+ show_error ! (
760+ "Failed to remove existing file {}. Error: {:?}" ,
761+ to. display( ) ,
762+ e
763+ ) ;
764+ }
760765 }
766+ } ;
767+ remove_destination ( ) ;
768+
769+ // create the destination file first. Using safer mode on unix to avoid
770+ // potential unsafe mode between time-of-creation and time-of-chmod.
771+ #[ cfg( unix) ]
772+ let creation = fs:: OpenOptions :: new ( )
773+ . write ( true )
774+ . create_new ( true )
775+ . mode ( 0o600 )
776+ . open ( to) ;
777+ #[ cfg( not( unix) ) ]
778+ let creation = File :: create ( to) ;
779+
780+ if let Err ( e) = creation {
781+ show_error ! (
782+ "Failed to create destination file {}. Error: {:?}" ,
783+ to. display( ) ,
784+ e
785+ ) ;
786+ return Err ( InstallError :: InstallFailed ( from. to_path_buf ( ) , to. to_path_buf ( ) , e) . into ( ) ) ;
761787 }
762788
763- if from. as_os_str ( ) == "/dev/null" {
764- /* workaround a limitation of fs::copy
765- * https://github.com/rust-lang/rust/issues/79390
766- */
767- if let Err ( err) = File :: create ( to) {
789+ // drop the file to close the fd of creation
790+ drop ( creation) ;
791+
792+ /* workaround a limitation of fs::copy: skip copy if source is /dev/null
793+ * https://github.com/rust-lang/rust/issues/79390
794+ */
795+ if from. as_os_str ( ) != "/dev/null" {
796+ if let Err ( err) = fs:: copy ( from, to) {
797+ remove_destination ( ) ;
768798 return Err (
769799 InstallError :: InstallFailed ( from. to_path_buf ( ) , to. to_path_buf ( ) , err) . into ( ) ,
770800 ) ;
771801 }
772- } else if let Err ( err) = fs:: copy ( from, to) {
773- return Err ( InstallError :: InstallFailed ( from. to_path_buf ( ) , to. to_path_buf ( ) , err) . into ( ) ) ;
774802 }
775803 Ok ( ( ) )
776804}
0 commit comments