@@ -13,9 +13,12 @@ use filetime::{set_file_times, FileTime};
13
13
use std:: error:: Error ;
14
14
use std:: fmt:: { Debug , Display } ;
15
15
use std:: fs;
16
+ #[ cfg( not( unix) ) ]
16
17
use std:: fs:: File ;
17
18
use std:: os:: unix:: fs:: MetadataExt ;
18
19
#[ cfg( unix) ]
20
+ use std:: os:: unix:: fs:: OpenOptionsExt ;
21
+ #[ cfg( unix) ]
19
22
use std:: os:: unix:: prelude:: OsStrExt ;
20
23
use std:: path:: { Path , PathBuf , MAIN_SEPARATOR } ;
21
24
use std:: process;
@@ -750,27 +753,52 @@ fn perform_backup(to: &Path, b: &Behavior) -> UResult<Option<PathBuf>> {
750
753
fn copy_file ( from : & Path , to : & Path ) -> UResult < ( ) > {
751
754
// fs::copy fails if destination is a invalid symlink.
752
755
// 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
+ }
760
765
}
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 ( ) ) ;
761
787
}
762
788
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 ( ) ;
768
798
return Err (
769
799
InstallError :: InstallFailed ( from. to_path_buf ( ) , to. to_path_buf ( ) , err) . into ( ) ,
770
800
) ;
771
801
}
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 ( ) ) ;
774
802
}
775
803
Ok ( ( ) )
776
804
}
0 commit comments