Zankaria
1 month ago
1 changed files with 143 additions and 0 deletions
@ -0,0 +1,143 @@ |
|||
<?php |
|||
|
|||
namespace Vichan\Functions; |
|||
|
|||
|
|||
class FsUtils { |
|||
public const ATOMIC_MULTI_ERR_NONE = 0; |
|||
public const ATOMIC_MULTI_ERR_SRC_BAD = 1; |
|||
public const ATOMIC_MULTI_ERR_SRC_NO_PERM = 2; |
|||
public const ATOMIC_MULTI_ERR_TARGET_BAD = 3; |
|||
public const ATOMIC_MULTI_ERR_TARGET_NO_PERM = 4; |
|||
public const ATOMIC_MULTI_ERR_ACTION_ERROR = 5; |
|||
|
|||
|
|||
private static function atomic_multi(array $config, string $src_dir, string $target_dir, iterable $src_file_names, callable $action, callable $action_rollback): int { |
|||
if (!is_dir($src_dir)) { |
|||
return self::ATOMIC_MULTI_ERR_SRC_BAD; |
|||
} |
|||
if (!is_readable($src_dir)) { |
|||
return self::ATOMIC_MULTI_ERR_SRC_NO_PERM; |
|||
} |
|||
|
|||
if (!is_dir($target_dir)) { |
|||
return self::ATOMIC_MULTI_ERR_TARGET_BAD; |
|||
} |
|||
if (!is_writable($target_dir)) { |
|||
return self::ATOMIC_MULTI_ERR_TARGET_NO_PERM; |
|||
} |
|||
|
|||
$src_dir = self::ensure_end_path_separator($src_dir); |
|||
$target_dir = self::ensure_end_path_separator($target_dir); |
|||
|
|||
$linked = []; |
|||
foreach ($src_file_names as $file_name) { |
|||
$src_path = $src_dir . $file_name; |
|||
$target_path = $target_dir . $file_name; |
|||
|
|||
$success = $action($src_path, $target_path); |
|||
|
|||
if ($success) { |
|||
$linked[] = $file_name; |
|||
} |
|||
// Rollback. |
|||
else { |
|||
if ($config['syslog']) { |
|||
$l_count = count($linked); |
|||
_syslog(LOG_ERR, "Failed to link $src_path in $target_dir, rolling back $l_count linked files."); |
|||
} |
|||
|
|||
foreach ($linked as $file_name) { |
|||
$src_path = $src_dir . $file_name; |
|||
$target_path = $target_dir . $file_name; |
|||
|
|||
// Already falling back, ignore eventual errors and just log them. |
|||
$success = $action_rollback($src_path, $target_path); |
|||
if (!$success && $config['syslog']) { |
|||
_syslog(LOG_ERR, "Failed to rollback $target_path in $src_dir."); |
|||
} |
|||
} |
|||
|
|||
return self::ATOMIC_MULTI_ERR_ACTION_ERROR; |
|||
} |
|||
} |
|||
|
|||
return self::ATOMIC_MULTI_ERR_NONE; |
|||
} |
|||
|
|||
|
|||
/** |
|||
* Appends a '/' to the given path if not already present. |
|||
* @return string Returns the directory path terminating with a path separator. |
|||
*/ |
|||
public static function ensure_end_path_separator(string $dir): string { |
|||
if ($dir[strlen($dir) - 1] !== '/') { |
|||
return "$dir/"; |
|||
} |
|||
return $dir; |
|||
} |
|||
|
|||
/** |
|||
* Copies all the files or aborts and rolls back on error. |
|||
* Implemented with file hardlinking. |
|||
* @param array $config The configuration array. |
|||
* @param string $src_dir The directory from which to fetch the files. |
|||
* @param string $target_dir The directory to which the files should be copied to. |
|||
* @param iterable $src_file_names The names of the files in $src_dir to be copied into $target_dir. The file names |
|||
* must NOT begin with a path separator. |
|||
* @return int Returns one of the class's ATOMIC_MULTI_ERR_* constants. |
|||
*/ |
|||
public function atomic_multi_copy(array $config, string $src_dir, string $target_dir, iterable $src_file_names) { |
|||
self::atomic_multi($config, $src_dir, $target_dir, $src_file_names, 'link', function($src_path, $target_path) { |
|||
// Delete the hard links. |
|||
\unlink($target_path); |
|||
}); |
|||
} |
|||
|
|||
/** |
|||
* Copies all the files or aborts and rolls back on error. |
|||
* Implemented via file move. |
|||
* @param array $config The configuration array. |
|||
* @param string $src_dir The directory from which to fetch the files. |
|||
* @param string $target_dir The directory to which the files should be copied to. |
|||
* @param iterable $src_file_names The names of the files in $src_dir to be copied into $target_dir. The file names |
|||
* must NOT begin with a path separator. |
|||
* @return int Returns one of the class's ATOMIC_MULTI_ERR_* constants. |
|||
*/ |
|||
public function atomic_multi_move(array $config, string $src_dir, string $target_dir, iterable $src_file_names) { |
|||
self::atomic_multi($config, $src_dir, $target_dir, $src_file_names, 'rename', function ($src_path, $target_path) { |
|||
// Move the files back into the source directory. |
|||
\rename($target_path, $src_path); |
|||
}); |
|||
} |
|||
|
|||
/** |
|||
* Deletes all the files in the given directory. |
|||
* |
|||
* @param string $src_dir The directory from which to delete the files. |
|||
* @param iterable $src_file_names The names of the files in $src_dir to be deleted. The file names must NOT begin |
|||
* with a path separator. |
|||
* @return int Returns the number of deleted files. |
|||
*/ |
|||
public function multi_delete(array $config, string $dir, iterable $file_names): int { |
|||
$dir = self::ensure_end_path_separator($dir); |
|||
$deleted = 0; |
|||
|
|||
foreach ($file_names as $file_name) { |
|||
$target_path = $dir . $file_name; |
|||
|
|||
$success = \unlink($target_path); |
|||
|
|||
if ($success) { |
|||
$deleted++; |
|||
} elseif ($config['syslog']) { |
|||
_syslog(LOG_ERR, "Failed to delete $target_path"); |
|||
} |
|||
} |
|||
|
|||
return $deleted; |
|||
} |
|||
|
|||
// Static class. |
|||
private function __construct() {} |
|||
} |
Loading…
Reference in new issue