@@ -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+
596620fn 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