diff --git a/README.md b/README.md index b24f4f0219..730218c200 100644 --- a/README.md +++ b/README.md @@ -714,6 +714,14 @@ currently can define only `default_toolchain`. - `RUSTUP_NO_BACKTRACE` Disables backtraces on non-panic errors even when `RUST_BACKTRACE` is set. +- `RUSTUP_PERMIT_COPY_RENAME` + *unstable* When set, allows rustup to fall-back to copying files if attempts to + `rename` result in an cross-device link errors. These errors occur on OverlayFS, + which is used by [Docker][dc]. This feature sacrifices some transactions protections + and may be removed at any point. Linux only. + + [dc]: (https://docs.docker.com/storage/storagedriver/overlayfs-driver/#modifying-files-or-directories) + ## Other installation methods The primary installation method, as described at https://rustup.rs, differs by platform: diff --git a/src/utils/utils.rs b/src/utils/utils.rs index c08367c23c..b7c197d9af 100644 --- a/src/utils/utils.rs +++ b/src/utils/utils.rs @@ -593,6 +593,30 @@ pub fn toolchain_sort>(v: &mut Vec) { }); } +fn copy_and_delete<'a, N>( + name: &'static str, + src: &'a Path, + dest: &'a Path, + notify_handler: &'a dyn Fn(N), +) -> Result<()> +where + N: From>, +{ + // https://github.com/rust-lang/rustup/issues/1239 + // This uses std::fs::copy() instead of the faster std::fs::rename() to + // avoid cross-device link errors. + if src.is_dir() { + copy_dir(src, dest, notify_handler).and(remove_dir_all::remove_dir_all(src).chain_err( + || ErrorKind::RemovingDirectory { + name, + path: PathBuf::from(src), + }, + )) + } else { + copy_file(src, dest).and(remove_file(name, src)) + } +} + fn rename<'a, N>( name: &'static str, src: &'a Path, @@ -606,6 +630,8 @@ where // 21 fib steps from 1 sums to ~28 seconds, hopefully more than enough // for our previous poor performance that avoided the race condition with // McAfee and Norton. + #[cfg(target_os = "linux")] + use libc::EXDEV; retry( Fibonacci::from_millis(1).map(jitter).take(26), || match fs::rename(src, dest) { @@ -615,6 +641,16 @@ where notify_handler(Notification::RenameInUse(&src, &dest).into()); OperationResult::Retry(e) } + #[cfg(target_os = "linux")] + io::ErrorKind::Other + if process().var_os("RUSTUP_PERMIT_COPY_RENAME").is_some() + && Some(EXDEV) == e.raw_os_error() => + { + match copy_and_delete(name, src, dest, notify_handler) { + Ok(()) => OperationResult::Ok(()), + Err(_) => OperationResult::Err(e), + } + } _ => OperationResult::Err(e), }, },