Skip to content

Commit df179c3

Browse files
authored
Merge pull request #2410 from StarryInternet/issue-1239
Fallback to copy when rename causes cross-link error
2 parents 6399370 + d847b54 commit df179c3

File tree

2 files changed

+44
-0
lines changed

2 files changed

+44
-0
lines changed

README.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -726,6 +726,14 @@ currently can define only `default_toolchain`.
726726
- `RUSTUP_NO_BACKTRACE`
727727
Disables backtraces on non-panic errors even when `RUST_BACKTRACE` is set.
728728

729+
- `RUSTUP_PERMIT_COPY_RENAME`
730+
*unstable* When set, allows rustup to fall-back to copying files if attempts to
731+
`rename` result in an cross-device link errors. These errors occur on OverlayFS,
732+
which is used by [Docker][dc]. This feature sacrifices some transactions protections
733+
and may be removed at any point. Linux only.
734+
735+
[dc]: (https://docs.docker.com/storage/storagedriver/overlayfs-driver/#modifying-files-or-directories)
736+
729737
## Other installation methods
730738

731739
The primary installation method, as described at https://rustup.rs, differs by platform:

src/utils/utils.rs

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -593,6 +593,30 @@ pub fn toolchain_sort<T: AsRef<str>>(v: &mut Vec<T>) {
593593
});
594594
}
595595

596+
fn copy_and_delete<'a, N>(
597+
name: &'static str,
598+
src: &'a Path,
599+
dest: &'a Path,
600+
notify_handler: &'a dyn Fn(N),
601+
) -> Result<()>
602+
where
603+
N: From<Notification<'a>>,
604+
{
605+
// https://github.com/rust-lang/rustup/issues/1239
606+
// This uses std::fs::copy() instead of the faster std::fs::rename() to
607+
// avoid cross-device link errors.
608+
if src.is_dir() {
609+
copy_dir(src, dest, notify_handler).and(remove_dir_all::remove_dir_all(src).chain_err(
610+
|| ErrorKind::RemovingDirectory {
611+
name,
612+
path: PathBuf::from(src),
613+
},
614+
))
615+
} else {
616+
copy_file(src, dest).and(remove_file(name, src))
617+
}
618+
}
619+
596620
fn rename<'a, N>(
597621
name: &'static str,
598622
src: &'a Path,
@@ -606,6 +630,8 @@ where
606630
// 21 fib steps from 1 sums to ~28 seconds, hopefully more than enough
607631
// for our previous poor performance that avoided the race condition with
608632
// McAfee and Norton.
633+
#[cfg(target_os = "linux")]
634+
use libc::EXDEV;
609635
retry(
610636
Fibonacci::from_millis(1).map(jitter).take(26),
611637
|| match fs::rename(src, dest) {
@@ -615,6 +641,16 @@ where
615641
notify_handler(Notification::RenameInUse(&src, &dest).into());
616642
OperationResult::Retry(e)
617643
}
644+
#[cfg(target_os = "linux")]
645+
io::ErrorKind::Other
646+
if process().var_os("RUSTUP_PERMIT_COPY_RENAME").is_some()
647+
&& Some(EXDEV) == e.raw_os_error() =>
648+
{
649+
match copy_and_delete(name, src, dest, notify_handler) {
650+
Ok(()) => OperationResult::Ok(()),
651+
Err(_) => OperationResult::Err(e),
652+
}
653+
}
618654
_ => OperationResult::Err(e),
619655
},
620656
},

0 commit comments

Comments
 (0)