Skip to content

Commit abe2bde

Browse files
committed
Auto merge of #1326 - alexcrichton:fix-errors, r=alexcrichton
Fix self update errors filling in missing proxies The previous logic had some subtle bugs for a number of reasons, and hopefully this iteration irons them out. Closes #1316
2 parents 17aa5b5 + f94daed commit abe2bde

File tree

9 files changed

+129
-29
lines changed

9 files changed

+129
-29
lines changed

Cargo.lock

+32
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ markdown = "0.2"
4242
rand = "0.3.11"
4343
regex = "0.2"
4444
remove_dir_all = "0.2.0"
45+
same-file = "1.0"
4546
scopeguard = "0.3"
4647
serde = "1.0"
4748
serde_derive = "1.0"

src/rustup-cli/errors.rs

+2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
#![allow(dead_code)]
22

3+
use std::io;
34
use std::path::PathBuf;
45

56
use rustup;
@@ -15,6 +16,7 @@ error_chain! {
1516

1617
foreign_links {
1718
Temp(temp::Error);
19+
Io(io::Error);
1820
}
1921

2022
errors {

src/rustup-cli/main.rs

+1
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ extern crate term;
2626
extern crate itertools;
2727
extern crate time;
2828
extern crate rand;
29+
extern crate same_file;
2930
extern crate scopeguard;
3031
extern crate tempdir;
3132
extern crate sha2;

src/rustup-cli/self_update.rs

+61-13
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ use common::{self, Confirm};
3434
use errors::*;
3535
use rustup_dist::dist;
3636
use rustup_utils::utils;
37+
use same_file::Handle;
3738
use std::env;
3839
use std::env::consts::EXE_SUFFIX;
3940
use 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(())

src/rustup-mock/src/clitools.rs

+8-2
Original file line numberDiff line numberDiff line change
@@ -322,13 +322,19 @@ pub fn run(config: &Config, name: &str, args: &[&str], env: &[(&str, &str)]) ->
322322
for env in env {
323323
cmd.env(env.0, env.1);
324324
}
325+
326+
println!("running {:?}", cmd);
325327
let out = cmd.output().expect("failed to run test command");
326328

327-
SanitizedOutput {
329+
let output = SanitizedOutput {
328330
ok: out.status.success(),
329331
stdout: String::from_utf8(out.stdout).unwrap(),
330332
stderr: String::from_utf8(out.stderr).unwrap(),
331-
}
333+
};
334+
println!("status: {}", out.status);
335+
println!("----- stdout\n{}", output.stdout);
336+
println!("----- stderr\n{}", output.stderr);
337+
return output
332338
}
333339

334340
// Creates a mock dist server populated with some test data

src/rustup-utils/src/raw.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,7 @@ pub fn append_file(dest: &Path, line: &str) -> io::Result<()> {
130130
Ok(())
131131
}
132132

133-
pub fn tee_file<W: io::Write>(path: &Path, mut w: &mut W) -> io::Result<()> {
133+
pub fn tee_file<W: io::Write>(path: &Path, w: &mut W) -> io::Result<()> {
134134
let mut file = try!(fs::OpenOptions::new()
135135
.read(true)
136136
.open(path));
@@ -213,7 +213,7 @@ fn symlink_junction_inner(target: &Path, junction: &Path) -> io::Result<()> {
213213
ptr::null_mut());
214214

215215
let mut data = [0u8; MAXIMUM_REPARSE_DATA_BUFFER_SIZE];
216-
let mut db = data.as_mut_ptr()
216+
let db = data.as_mut_ptr()
217217
as *mut REPARSE_MOUNTPOINT_DATA_BUFFER;
218218
let buf = &mut (*db).ReparseTarget as *mut _;
219219
let mut i = 0;

tests/cli-inst-interactive.rs

+2
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,8 @@ fn blank_lines_around_stderr_log_output_update() {
9696
setup(&|config| {
9797
run_input(config, &["rustup-init"], "\n\n");
9898
let out = run_input(config, &["rustup-init"], "\n\n");
99+
println!("-- stdout --\n {}", out.stdout);
100+
println!("-- stderr --\n {}", out.stderr);
99101

100102
assert!(out.stdout.contains(r"
101103
3) Cancel installation

tests/cli-self-upd.rs

+20-12
Original file line numberDiff line numberDiff line change
@@ -1250,28 +1250,36 @@ fn rls_proxy_set_up_after_update() {
12501250

12511251
#[test]
12521252
fn update_does_not_overwrite_rustfmt() {
1253-
update_setup(&|config, _| {
1253+
update_setup(&|config, self_dist| {
12541254
expect_ok(config, &["rustup-init", "-y"]);
1255+
let version = env!("CARGO_PKG_VERSION");
1256+
output_release_file(self_dist, "1", version);
1257+
1258+
// Since we just did a fresh install rustfmt will exist. Let's emulate
1259+
// it not existing in this test though by removing it just after our
1260+
// installation.
12551261
let ref rustfmt_path = config.cargodir.join(format!("bin/rustfmt{}", EXE_SUFFIX));
1262+
assert!(rustfmt_path.exists());
1263+
fs::remove_file(rustfmt_path).unwrap();
12561264
raw::write_file(rustfmt_path, "").unwrap();
12571265
assert_eq!(utils::file_size(rustfmt_path).unwrap(), 0);
12581266

1267+
// Ok, now a self-update should complain about `rustfmt` not looking
1268+
// like rustup and the user should take some action.
12591269
expect_stderr_ok(config, &["rustup", "self", "update"],
12601270
"`rustfmt` is already installed");
1261-
expect_ok(config, &["rustup", "self", "update"]);
12621271
assert!(rustfmt_path.exists());
12631272
assert_eq!(utils::file_size(rustfmt_path).unwrap(), 0);
12641273

1265-
1266-
// We run the test twice because the first time around none of the shims
1267-
// exist, and we want to check that out check for rustfmt works if there
1268-
// are shims or not.
1269-
let ref rustdoc_path = config.cargodir.join(format!("bin/rustdoc{}", EXE_SUFFIX));
1270-
assert!(rustdoc_path.exists());
1271-
1272-
expect_stderr_ok(config, &["rustup", "self", "update"],
1273-
"`rustfmt` is already installed");
1274+
// Now simluate us removing the rustfmt executable and rerunning a self
1275+
// update, this should install the rustup shim. Note that we don't run
1276+
// `rustup` here but rather the rustup we've actually installed, this'll
1277+
// help reproduce bugs related to having that file being opened by the
1278+
// current process.
1279+
fs::remove_file(rustfmt_path).unwrap();
1280+
let installed_rustup = config.cargodir.join("bin/rustup");
1281+
expect_ok(config, &[installed_rustup.to_str().unwrap(), "self", "update"]);
12741282
assert!(rustfmt_path.exists());
1275-
assert_eq!(utils::file_size(rustfmt_path).unwrap(), 0);
1283+
assert!(utils::file_size(rustfmt_path).unwrap() > 0);
12761284
});
12771285
}

0 commit comments

Comments
 (0)