mirror of
https://github.com/pmmp/PocketMine-MP.git
synced 2025-07-09 11:31:49 +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;
|
namespace pocketmine\utils;
|
||||||
|
|
||||||
|
use function copy;
|
||||||
|
use function dirname;
|
||||||
use function fclose;
|
use function fclose;
|
||||||
use function fflush;
|
use function fflush;
|
||||||
|
use function file_exists;
|
||||||
use function flock;
|
use function flock;
|
||||||
use function fopen;
|
use function fopen;
|
||||||
use function ftruncate;
|
use function ftruncate;
|
||||||
@ -33,6 +36,7 @@ use function getmypid;
|
|||||||
use function is_dir;
|
use function is_dir;
|
||||||
use function is_file;
|
use function is_file;
|
||||||
use function ltrim;
|
use function ltrim;
|
||||||
|
use function mkdir;
|
||||||
use function preg_match;
|
use function preg_match;
|
||||||
use function realpath;
|
use function realpath;
|
||||||
use function rmdir;
|
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{
|
public static function addCleanedPath(string $path, string $replacement) : void{
|
||||||
self::$cleanedPaths[$path] = $replacement;
|
self::$cleanedPaths[$path] = $replacement;
|
||||||
uksort(self::$cleanedPaths, function(string $str1, string $str2) : int{
|
uksort(self::$cleanedPaths, function(string $str1, string $str2) : int{
|
||||||
|
@ -91,8 +91,17 @@ class FormatConverter{
|
|||||||
$new->close();
|
$new->close();
|
||||||
|
|
||||||
$this->logger->info("Backing up pre-conversion world to " . $this->backupPath);
|
$this->logger->info("Backing up pre-conversion world to " . $this->backupPath);
|
||||||
rename($path, $this->backupPath);
|
if(!@rename($path, $this->backupPath)){
|
||||||
rename($new->getPath(), $path);
|
$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");
|
$this->logger->info("Conversion completed");
|
||||||
/**
|
/**
|
||||||
|
Loading…
x
Reference in New Issue
Block a user