@@ -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,63 @@ 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+ let mut prev_handles = Vec :: new ( ) ;
663663
664664 // Try to hardlink all the Rust exes to the rustup exe. Some systems,
665665 // like Android, does not support hardlinks, so we fallback to symlinks.
666+ //
667+ // Note that this function may not be running in the context of a fresh
668+ // self update but rather as part of a normal update to fill in missing
669+ // proxies. In that case our process may actually have the `rustup.exe`
670+ // file open, and on systems like Windows that means that you can't
671+ // even remove other hard links to the same file. Basically if we have
672+ // `rustup.exe` open and running and `cargo.exe` is a hard link to that
673+ // file, we can't remove `cargo.exe`.
674+ //
675+ // To avoid unnecessary errors from being returned here we use the
676+ // `same-file` crate and its `Handle` type to avoid clobbering hard links
677+ // that are already valid. If a hard link already points to the
678+ // `rustup.exe` file then we leave it alone and move to the next one.
666679 for tool in TOOLS {
667680 let ref tool_path = bin_path. join ( & format ! ( "{}{}" , tool, EXE_SUFFIX ) ) ;
668- if tool_path. exists ( ) {
669- file_size = utils:: file_size ( tool_path) ?;
681+ if let Ok ( handle) = Handle :: from_path ( tool_path) {
682+ prev_handles. push ( handle) ;
683+ if rustup == * prev_handles. last ( ) . unwrap ( ) {
684+ continue
685+ }
670686 }
671687 try!( utils:: hard_or_symlink_file ( rustup_path, tool_path) ) ;
672688 }
673689
674690 for tool in DUP_TOOLS {
675691 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) ) ;
692+ if let Ok ( handle) = Handle :: from_path ( tool_path) {
693+ // Like above, don't clobber anything that's already hardlinked to
694+ // avoid extraneous errors from being returned.
695+ if rustup == handle {
696+ continue
697+ }
698+
699+ // If this file exists and is *not* equivalent to all other
700+ // preexisting tools we found, then we're going to assume that it
701+ // was preinstalled and actually pointing to a totally different
702+ // binary. This is intended for cases where historically users
703+ // rand `cargo install rustfmt` and so they had custom `rustfmt`
704+ // and `cargo-fmt` executables lying around, but we as rustup have
705+ // since started managing these tools.
706+ //
707+ // If the file is managed by rustup it should be equivalent to some
708+ // previous file, and if it's not equivalent to anything then it's
709+ // pretty likely that it needs to be dealt with manually.
710+ if prev_handles. iter ( ) . all ( |h| * h != handle) {
711+ warn ! ( "tool `{}` is already installed, remove it from `{}`, then run `rustup update` \
712+ to have rustup manage this tool.",
713+ tool, bin_path. to_string_lossy( ) ) ;
714+ continue
715+ }
682716 }
717+ try!( utils:: hard_or_symlink_file ( rustup_path, tool_path) ) ;
683718 }
684719
685720 Ok ( ( ) )
0 commit comments