Skip to content

Commit 3564f6d

Browse files
authored
Fix zip archive strip function for cross-device move (#942)
2 parents 553b817 + c6de6e7 commit 3564f6d

File tree

2 files changed

+39
-6
lines changed

2 files changed

+39
-6
lines changed

src/SPC/store/FileSystem.php

Lines changed: 38 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -645,6 +645,36 @@ private static function extractWithType(string $source_type, string $filename, s
645645
};
646646
}
647647

648+
/**
649+
* Move file or directory, handling cross-device scenarios
650+
* Uses rename() if possible, falls back to copy+delete for cross-device moves
651+
*
652+
* @param string $source Source path
653+
* @param string $dest Destination path
654+
*/
655+
private static function moveFileOrDir(string $source, string $dest): void
656+
{
657+
$source = self::convertPath($source);
658+
$dest = self::convertPath($dest);
659+
660+
// Try rename first (fast, atomic)
661+
if (@rename($source, $dest)) {
662+
return;
663+
}
664+
665+
if (is_dir($source)) {
666+
self::copyDir($source, $dest);
667+
self::removeDir($source);
668+
} else {
669+
if (!copy($source, $dest)) {
670+
throw new FileSystemException("Failed to copy file from {$source} to {$dest}");
671+
}
672+
if (!unlink($source)) {
673+
throw new FileSystemException("Failed to remove source file: {$source}");
674+
}
675+
}
676+
}
677+
648678
/**
649679
* Unzip file with stripping top-level directory
650680
*/
@@ -675,10 +705,10 @@ private static function unzipWithStrip(string $zip_file, string $extract_path):
675705
if (is_dir($extract_path)) {
676706
self::removeDir($extract_path);
677707
}
678-
// if only one dir, move its contents to extract_path using rename
708+
// if only one dir, move its contents to extract_path
679709
$subdir = self::convertPath("{$temp_dir}/{$contents[0]}");
680710
if (count($contents) === 1 && is_dir($subdir)) {
681-
rename($subdir, $extract_path);
711+
self::moveFileOrDir($subdir, $extract_path);
682712
} else {
683713
// else, if it contains only one dir, strip dir and copy other files
684714
$dircount = 0;
@@ -701,17 +731,20 @@ private static function unzipWithStrip(string $zip_file, string $extract_path):
701731
throw new FileSystemException("Cannot scan unzip temp sub-dir: {$dir[0]}");
702732
}
703733
foreach ($sub_contents as $sub_item) {
704-
rename(self::convertPath("{$temp_dir}/{$dir[0]}/{$sub_item}"), self::convertPath("{$extract_path}/{$sub_item}"));
734+
self::moveFileOrDir(self::convertPath("{$temp_dir}/{$dir[0]}/{$sub_item}"), self::convertPath("{$extract_path}/{$sub_item}"));
705735
}
706736
} else {
707737
foreach ($dir as $item) {
708-
rename(self::convertPath("{$temp_dir}/{$item}"), self::convertPath("{$extract_path}/{$item}"));
738+
self::moveFileOrDir(self::convertPath("{$temp_dir}/{$item}"), self::convertPath("{$extract_path}/{$item}"));
709739
}
710740
}
711741
// move top-level files to extract_path
712742
foreach ($top_files as $top_file) {
713-
rename(self::convertPath("{$temp_dir}/{$top_file}"), self::convertPath("{$extract_path}/{$top_file}"));
743+
self::moveFileOrDir(self::convertPath("{$temp_dir}/{$top_file}"), self::convertPath("{$extract_path}/{$top_file}"));
714744
}
715745
}
746+
747+
// Clean up temp directory
748+
self::removeDir($temp_dir);
716749
}
717750
}

src/globals/test-extensions.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@
4949

5050
// If you want to test your added extensions and libs, add below (comma separated, example `bcmath,openssl`).
5151
$extensions = match (PHP_OS_FAMILY) {
52-
'Linux', 'Darwin' => 'pdo_pgsql',
52+
'Linux', 'Darwin' => 'spx',
5353
'Windows' => 'bcmath,bz2,calendar,ctype,curl,dom,exif,fileinfo,filter,ftp,iconv,xml,mbstring,mbregex,mysqlnd,openssl,pdo,pdo_mysql,pdo_sqlite,phar,session,simplexml,soap,sockets,sqlite3,tokenizer,xmlwriter,xmlreader,zlib,zip',
5454
};
5555

0 commit comments

Comments
 (0)