From 4db00fe229f08b06feeee552ae53af9f49c25048 Mon Sep 17 00:00:00 2001
From: Luca Barbato <lu_zero@gentoo.org>
Date: Fri, 10 May 2024 16:38:19 +0200
Subject: [PATCH] Use an helper to move the files

In case the source is not in the same filesystem.
---
 src/bootstrap/src/core/build_steps/dist.rs |  6 ++++--
 src/bootstrap/src/core/download.rs         |  6 +++---
 src/bootstrap/src/utils/helpers.rs         | 15 +++++++++++++++
 src/bootstrap/src/utils/tarball.rs         |  4 ++--
 4 files changed, 24 insertions(+), 7 deletions(-)

diff --git a/src/bootstrap/src/core/build_steps/dist.rs b/src/bootstrap/src/core/build_steps/dist.rs
index 5bc9d7615e2bc..1f006e1453f4b 100644
--- a/src/bootstrap/src/core/build_steps/dist.rs
+++ b/src/bootstrap/src/core/build_steps/dist.rs
@@ -26,7 +26,9 @@ use crate::core::build_steps::tool::{self, Tool};
 use crate::core::builder::{Builder, Kind, RunConfig, ShouldRun, Step};
 use crate::core::config::TargetSelection;
 use crate::utils::channel::{self, Info};
-use crate::utils::helpers::{exe, is_dylib, output, t, target_supports_cranelift_backend, timeit};
+use crate::utils::helpers::{
+    exe, is_dylib, move_file, output, t, target_supports_cranelift_backend, timeit,
+};
 use crate::utils::tarball::{GeneratedTarball, OverlayKind, Tarball};
 use crate::{Compiler, DependencyType, Mode, LLVM_TOOLS};
 
@@ -2024,7 +2026,7 @@ impl Step for Extended {
             builder.run(&mut cmd);
 
             if !builder.config.dry_run() {
-                t!(fs::rename(exe.join(&filename), distdir(builder).join(&filename)));
+                t!(move_file(exe.join(&filename), distdir(builder).join(&filename)));
             }
         }
     }
diff --git a/src/bootstrap/src/core/download.rs b/src/bootstrap/src/core/download.rs
index a074d53aa36e6..60f48c5923e1c 100644
--- a/src/bootstrap/src/core/download.rs
+++ b/src/bootstrap/src/core/download.rs
@@ -12,7 +12,7 @@ use build_helper::ci::CiEnv;
 use build_helper::stage0_parser::VersionMetadata;
 use xz2::bufread::XzDecoder;
 
-use crate::utils::helpers::{check_run, exe, program_out_of_date};
+use crate::utils::helpers::{check_run, exe, move_file, program_out_of_date};
 use crate::{core::build_steps::llvm::detect_llvm_sha, utils::helpers::hex_encode};
 use crate::{t, Config};
 
@@ -209,7 +209,7 @@ impl Config {
             None => panic!("no protocol in {url}"),
         }
         t!(
-            std::fs::rename(&tempfile, dest_path),
+            move_file(&tempfile, dest_path),
             format!("failed to rename {tempfile:?} to {dest_path:?}")
         );
     }
@@ -313,7 +313,7 @@ impl Config {
             if src_path.is_dir() && dst_path.exists() {
                 continue;
             }
-            t!(fs::rename(src_path, dst_path));
+            t!(move_file(src_path, dst_path));
         }
         let dst_dir = dst.join(directory_prefix);
         if dst_dir.exists() {
diff --git a/src/bootstrap/src/utils/helpers.rs b/src/bootstrap/src/utils/helpers.rs
index 0d2ff4f951b61..278359cb08e39 100644
--- a/src/bootstrap/src/utils/helpers.rs
+++ b/src/bootstrap/src/utils/helpers.rs
@@ -150,6 +150,21 @@ pub fn symlink_dir(config: &Config, original: &Path, link: &Path) -> io::Result<
     }
 }
 
+/// Rename a file if from and to are in the same filesystem or
+/// copy and remove the file otherwise
+pub fn move_file<P: AsRef<Path>, Q: AsRef<Path>>(from: P, to: Q) -> io::Result<()> {
+    match fs::rename(&from, &to) {
+        // FIXME: Once `ErrorKind::CrossesDevices` is stabilized use
+        // if e.kind() == io::ErrorKind::CrossesDevices {
+        #[cfg(unix)]
+        Err(e) if e.raw_os_error() == Some(libc::EXDEV) => {
+            std::fs::copy(&from, &to)?;
+            std::fs::remove_file(&from)
+        }
+        r => r,
+    }
+}
+
 pub fn forcing_clang_based_tests() -> bool {
     if let Some(var) = env::var_os("RUSTBUILD_FORCE_CLANG_BASED_TESTS") {
         match &var.to_string_lossy().to_lowercase()[..] {
diff --git a/src/bootstrap/src/utils/tarball.rs b/src/bootstrap/src/utils/tarball.rs
index 2a950e669b923..57cdf7473a191 100644
--- a/src/bootstrap/src/utils/tarball.rs
+++ b/src/bootstrap/src/utils/tarball.rs
@@ -13,7 +13,7 @@ use std::{
 use crate::core::builder::Builder;
 use crate::core::{build_steps::dist::distdir, builder::Kind};
 use crate::utils::channel;
-use crate::utils::helpers::t;
+use crate::utils::helpers::{move_file, t};
 
 #[derive(Copy, Clone)]
 pub(crate) enum OverlayKind {
@@ -284,7 +284,7 @@ impl<'a> Tarball<'a> {
         // name, not "image". We rename the image directory just before passing
         // into rust-installer.
         let dest = self.temp_dir.join(self.package_name());
-        t!(std::fs::rename(&self.image_dir, &dest));
+        t!(move_file(&self.image_dir, &dest));
 
         self.run(|this, cmd| {
             let distdir = distdir(this.builder);