mirror of
https://github.com/pmmp/PocketMine-MP.git
synced 2025-04-21 16:24:05 +00:00
FormatConverter: Copy worlds for backup if rename fails
this can fail if the backups directory points to a different drive than the original worlds location. In this case, we have to copy and delete the files instead, which is much slower, but works. I REALLY advise against putting backups on a different mount point than worlds if you plan to convert large worlds.
This commit is contained in:
parent
ec6103d61e
commit
43f71d0d63
@ -23,8 +23,11 @@ declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\utils;
|
||||
|
||||
use function copy;
|
||||
use function dirname;
|
||||
use function fclose;
|
||||
use function fflush;
|
||||
use function file_exists;
|
||||
use function flock;
|
||||
use function fopen;
|
||||
use function ftruncate;
|
||||
@ -33,6 +36,7 @@ use function getmypid;
|
||||
use function is_dir;
|
||||
use function is_file;
|
||||
use function ltrim;
|
||||
use function mkdir;
|
||||
use function preg_match;
|
||||
use function realpath;
|
||||
use function rmdir;
|
||||
@ -88,6 +92,53 @@ final class Filesystem{
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Recursively copies a directory to a new location. The parent directories for the destination must exist.
|
||||
*/
|
||||
public static function recursiveCopy(string $origin, string $destination) : void{
|
||||
if(!is_dir($origin)){
|
||||
throw new \RuntimeException("$origin does not exist, or is not a directory");
|
||||
}
|
||||
if(!is_dir($destination)){
|
||||
if(file_exists($destination)){
|
||||
throw new \RuntimeException("$destination already exists, and is not a directory");
|
||||
}
|
||||
if(!is_dir(dirname($destination))){
|
||||
//if the parent dir doesn't exist, the user most likely made a mistake
|
||||
throw new \RuntimeException("The parent directory of $destination does not exist, or is not a directory");
|
||||
}
|
||||
if(!@mkdir($destination) && !is_dir($destination)){
|
||||
throw new \RuntimeException("Failed to create output directory $destination (permission denied?)");
|
||||
}
|
||||
}
|
||||
self::recursiveCopyInternal($origin, $destination);
|
||||
}
|
||||
|
||||
private static function recursiveCopyInternal(string $origin, string $destination) : void{
|
||||
if(is_dir($origin)){
|
||||
if(!is_dir($destination)){
|
||||
if(file_exists($destination)){
|
||||
throw new \RuntimeException("Path $destination does not exist, or is not a directory");
|
||||
}
|
||||
mkdir($destination); //TODO: access permissions?
|
||||
}
|
||||
$objects = scandir($origin, SCANDIR_SORT_NONE);
|
||||
if($objects === false) throw new AssumptionFailedError("scandir() shouldn't return false when is_dir() returns true");
|
||||
foreach($objects as $object){
|
||||
if($object === "." || $object === ".."){
|
||||
continue;
|
||||
}
|
||||
self::recursiveCopyInternal($origin . "/" . $object, $destination . "/" . $object);
|
||||
}
|
||||
}else{
|
||||
$dirName = dirname($destination);
|
||||
if(!is_dir($dirName)){ //the destination folder should already exist
|
||||
throw new AssumptionFailedError("The destination folder should have been created in the parent call");
|
||||
}
|
||||
copy($origin, $destination);
|
||||
}
|
||||
}
|
||||
|
||||
public static function addCleanedPath(string $path, string $replacement) : void{
|
||||
self::$cleanedPaths[$path] = $replacement;
|
||||
uksort(self::$cleanedPaths, function(string $str1, string $str2) : int{
|
||||
|
@ -91,8 +91,17 @@ class FormatConverter{
|
||||
$new->close();
|
||||
|
||||
$this->logger->info("Backing up pre-conversion world to " . $this->backupPath);
|
||||
rename($path, $this->backupPath);
|
||||
rename($new->getPath(), $path);
|
||||
if(!@rename($path, $this->backupPath)){
|
||||
$this->logger->warning("Moving old world files for backup failed, attempting copy instead. This might take a long time.");
|
||||
Filesystem::recursiveCopy($path, $this->backupPath);
|
||||
Filesystem::recursiveUnlink($path);
|
||||
}
|
||||
if(!@rename($new->getPath(), $path)){
|
||||
//we don't expect this to happen because worlds/ should most likely be all on the same FS, but just in case...
|
||||
$this->logger->debug("Relocation of new world files to location failed, attempting copy and delete instead");
|
||||
Filesystem::recursiveCopy($new->getPath(), $path);
|
||||
Filesystem::recursiveUnlink($new->getPath());
|
||||
}
|
||||
|
||||
$this->logger->info("Conversion completed");
|
||||
/**
|
||||
|
Loading…
x
Reference in New Issue
Block a user