@@ -34,6 +34,7 @@ use common::{self, Confirm};
3434use errors:: * ;
3535use rustup_dist:: dist;
3636use rustup_utils:: utils;
37+ use same_file:: Handle ;
3738use std:: env;
3839use std:: env:: consts:: EXE_SUFFIX ;
3940use std:: path:: { Path , PathBuf , Component } ;
@@ -657,29 +658,76 @@ pub fn install_proxies() -> Result<()> {
657658 let ref bin_path = try!( utils:: cargo_home ( ) ) . join ( "bin" ) ;
658659 let ref rustup_path = bin_path. join ( & format ! ( "rustup{}" , EXE_SUFFIX ) ) ;
659660
660- // Record the size of the known links, then when we get files which may or
661- // may not be links, we compare their size. Same size means probably a link.
662- let mut file_size = 0 ;
661+ let rustup = Handle :: from_path ( rustup_path) ?;
662+
663+ let mut tool_handles = Vec :: new ( ) ;
664+ let mut link_afterwards = Vec :: new ( ) ;
663665
664666 // Try to hardlink all the Rust exes to the rustup exe. Some systems,
665667 // like Android, does not support hardlinks, so we fallback to symlinks.
668+ //
669+ // Note that this function may not be running in the context of a fresh
670+ // self update but rather as part of a normal update to fill in missing
671+ // proxies. In that case our process may actually have the `rustup.exe`
672+ // file open, and on systems like Windows that means that you can't
673+ // even remove other hard links to the same file. Basically if we have
674+ // `rustup.exe` open and running and `cargo.exe` is a hard link to that
675+ // file, we can't remove `cargo.exe`.
676+ //
677+ // To avoid unnecessary errors from being returned here we use the
678+ // `same-file` crate and its `Handle` type to avoid clobbering hard links
679+ // that are already valid. If a hard link already points to the
680+ // `rustup.exe` file then we leave it alone and move to the next one.
681+ //
682+ // As yet one final caveat, when we're looking at handles for files we can't
683+ // actually delete files (they'll say they're deleted but they won't
684+ // actually be on Windows). As a result we manually drop all the
685+ // `tool_handles` later on. This'll allow us, afterwards, to actually
686+ // overwrite all the previous hard links with new ones.
666687 for tool in TOOLS {
667- let ref tool_path = bin_path. join ( & format ! ( "{}{}" , tool, EXE_SUFFIX ) ) ;
668- if tool_path. exists ( ) {
669- file_size = utils:: file_size ( tool_path) ?;
688+ let tool_path = bin_path. join ( & format ! ( "{}{}" , tool, EXE_SUFFIX ) ) ;
689+ if let Ok ( handle) = Handle :: from_path ( & tool_path) {
690+ tool_handles. push ( handle) ;
691+ if rustup == * tool_handles. last ( ) . unwrap ( ) {
692+ continue
693+ }
670694 }
671- try! ( utils :: hard_or_symlink_file ( rustup_path , tool_path) ) ;
695+ link_afterwards . push ( tool_path) ;
672696 }
673697
674698 for tool in DUP_TOOLS {
675699 let ref tool_path = bin_path. join ( & format ! ( "{}{}" , tool, EXE_SUFFIX ) ) ;
676- if tool_path. exists ( ) && ( file_size == 0 || utils:: file_size ( tool_path) ? != file_size) {
677- warn ! ( "tool `{}` is already installed, remove it from `{}`, then run `rustup update` \
678- to have rustup manage this tool.",
679- tool, bin_path. to_string_lossy( ) ) ;
680- } else {
681- try!( utils:: hard_or_symlink_file ( rustup_path, tool_path) ) ;
700+ if let Ok ( handle) = Handle :: from_path ( tool_path) {
701+ // Like above, don't clobber anything that's already hardlinked to
702+ // avoid extraneous errors from being returned.
703+ if rustup == handle {
704+ continue
705+ }
706+
707+ // If this file exists and is *not* equivalent to all other
708+ // preexisting tools we found, then we're going to assume that it
709+ // was preinstalled and actually pointing to a totally different
710+ // binary. This is intended for cases where historically users
711+ // rand `cargo install rustfmt` and so they had custom `rustfmt`
712+ // and `cargo-fmt` executables lying around, but we as rustup have
713+ // since started managing these tools.
714+ //
715+ // If the file is managed by rustup it should be equivalent to some
716+ // previous file, and if it's not equivalent to anything then it's
717+ // pretty likely that it needs to be dealt with manually.
718+ if tool_handles. iter ( ) . all ( |h| * h != handle) {
719+ warn ! ( "tool `{}` is already installed, remove it from `{}`, then run `rustup update` \
720+ to have rustup manage this tool.",
721+ tool, bin_path. to_string_lossy( ) ) ;
722+ continue
723+ }
682724 }
725+ try!( utils:: hard_or_symlink_file ( rustup_path, tool_path) ) ;
726+ }
727+
728+ drop ( tool_handles) ;
729+ for path in link_afterwards {
730+ try!( utils:: hard_or_symlink_file ( rustup_path, & path) ) ;
683731 }
684732
685733 Ok ( ( ) )
0 commit comments