@@ -571,18 +571,30 @@ protected function unzipWithStrip(string $zip_file, string $extract_path): void
571571 }
572572
573573 /**
574- * Move file or directory to destination.
574+ * Move file or directory, handling cross-device scenarios
575+ * Uses rename() if possible, falls back to copy+delete for cross-device moves
576+ *
577+ * @param string $source Source path
578+ * @param string $dest Destination path
575579 */
576- protected function moveFileOrDir (string $ source , string $ dest ): void
580+ private static function moveFileOrDir (string $ source , string $ dest ): void
577581 {
578582 $ source = FileSystem::convertPath ($ source );
579583 $ dest = FileSystem::convertPath ($ dest );
580584
581- // Try rename first (fast, atomic)
582- if (@rename ($ source , $ dest )) {
583- return ;
585+ // Check if source and dest are on the same device to avoid cross-device rename errors
586+ $ source_stat = @stat ($ source );
587+ $ dest_parent = dirname ($ dest );
588+ $ dest_stat = @stat ($ dest_parent );
589+
590+ // Only use rename if on same device
591+ if ($ source_stat !== false && $ dest_stat !== false && $ source_stat ['dev ' ] === $ dest_stat ['dev ' ]) {
592+ if (@rename ($ source , $ dest )) {
593+ return ;
594+ }
584595 }
585596
597+ // Fall back to copy + delete for cross-device moves or if rename failed
586598 if (is_dir ($ source )) {
587599 FileSystem::copyDir ($ source , $ dest );
588600 FileSystem::removeDir ($ source );
0 commit comments