Browse Source

Refactor FsUtils

safer-thread-move
Zankaria 2 months ago
parent
commit
1b74ab7d49
  1. 92
      inc/functions/fs.php

92
inc/functions/fs.php

@ -4,65 +4,71 @@ namespace Vichan\Functions;
class FsUtils { class FsUtils {
public const ATOMIC_MULTI_ERR_NONE = 0; public const MULTI_ERR_NONE = 0;
public const ATOMIC_MULTI_ERR_SRC_BAD = 1; public const MULTI_ERR_SRC_BAD = 1;
public const ATOMIC_MULTI_ERR_SRC_NO_PERM = 2; public const MULTI_ERR_SRC_NO_PERM = 2;
public const ATOMIC_MULTI_ERR_TARGET_BAD = 3; public const MULTI_ERR_TARGET_BAD = 3;
public const ATOMIC_MULTI_ERR_TARGET_NO_PERM = 4; public const MULTI_ERR_TARGET_NO_PERM = 4;
public const ATOMIC_MULTI_ERR_ACTION_ERROR = 5; public const MULTI_ERR_ACTION_FAILED = 5;
private static function atomic_multi(array $config, string $src_dir, string $target_dir, iterable $src_file_names, callable $action, callable $action_rollback): int { private static function multiAction(array $config, string $src_dir, string $target_dir, iterable $src_file_names, callable $action, callable $action_rollback): int {
if (!is_dir($src_dir)) { if (!is_dir($src_dir)) {
return self::ATOMIC_MULTI_ERR_SRC_BAD; return self::MULTI_ERR_SRC_BAD;
} }
if (!is_readable($src_dir)) { if (!is_readable($src_dir)) {
return self::ATOMIC_MULTI_ERR_SRC_NO_PERM; return self::MULTI_ERR_SRC_NO_PERM;
} }
if (!is_dir($target_dir)) { if (!is_dir($target_dir)) {
return self::ATOMIC_MULTI_ERR_TARGET_BAD; return self::MULTI_ERR_TARGET_BAD;
} }
if (!is_writable($target_dir)) { if (!is_writable($target_dir)) {
return self::ATOMIC_MULTI_ERR_TARGET_NO_PERM; return self::MULTI_ERR_TARGET_NO_PERM;
} }
$src_dir = self::ensure_end_path_separator($src_dir); $src_dir = self::ensureEndPathSeparator($src_dir);
$target_dir = self::ensure_end_path_separator($target_dir); $target_dir = self::ensureEndPathSeparator($target_dir);
$linked = []; $linked = [];
foreach ($src_file_names as $file_name) { foreach ($src_file_names as $file_name) {
$src_path = $src_dir . $file_name; $src_path = $src_dir . $file_name;
$target_path = $target_dir . $file_name; $target_path = $target_dir . $file_name;
$success = $action($src_path, $target_path); if (!file_exists($src_path)) {
if ($success) {
$linked[] = $file_name;
}
// Rollback.
else {
if ($config['syslog']) { if ($config['syslog']) {
$l_count = count($linked); _syslog(LOG_WARNING, "Attempting to operate on non-exiting file $src_path");
_syslog(LOG_ERR, "Failed to link $src_path in $target_dir, rolling back $l_count linked files."); }
} else {
$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) { foreach ($linked as $file_name) {
$src_path = $src_dir . $file_name; $src_path = $src_dir . $file_name;
$target_path = $target_dir . $file_name; $target_path = $target_dir . $file_name;
// Already falling back, ignore eventual errors and just log them. // Already falling back, ignore eventual errors and just log them.
$success = $action_rollback($src_path, $target_path); $success = $action_rollback($src_path, $target_path);
if (!$success && $config['syslog']) { if (!$success && $config['syslog']) {
_syslog(LOG_ERR, "Failed to rollback $target_path in $src_dir."); _syslog(LOG_ERR, "Failed to rollback $target_path in $src_dir.");
}
} }
}
return self::ATOMIC_MULTI_ERR_ACTION_ERROR; return self::MULTI_ERR_ACTION_FAILED;
}
} }
} }
return self::ATOMIC_MULTI_ERR_NONE; return self::MULTI_ERR_NONE;
} }
@ -70,7 +76,7 @@ class FsUtils {
* Appends a '/' to the given path if not already present. * Appends a '/' to the given path if not already present.
* @return string Returns the directory path terminating with a path separator. * @return string Returns the directory path terminating with a path separator.
*/ */
public static function ensure_end_path_separator(string $dir): string { public static function ensureEndPathSeparator(string $dir): string {
if ($dir[strlen($dir) - 1] !== '/') { if ($dir[strlen($dir) - 1] !== '/') {
return "$dir/"; return "$dir/";
} }
@ -84,11 +90,11 @@ class FsUtils {
* @param string $src_dir The directory from which to fetch the files. * @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 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 * @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. * may be path fragments, but must NOT begin with a path separator.
* @return int Returns one of the class's ATOMIC_MULTI_ERR_* constants. * @return int Returns one of the class's MULTI_ERR_* constants.
*/ */
public function atomic_multi_copy(array $config, string $src_dir, string $target_dir, iterable $src_file_names) { public static function multiCopy(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) { self::multiAction($config, $src_dir, $target_dir, $src_file_names, 'link', function($src_path, $target_path) {
// Delete the hard links. // Delete the hard links.
\unlink($target_path); \unlink($target_path);
}); });
@ -101,11 +107,11 @@ class FsUtils {
* @param string $src_dir The directory from which to fetch the files. * @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 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 * @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. * may be path fragments, but must NOT begin with a path separator.
* @return int Returns one of the class's ATOMIC_MULTI_ERR_* constants. * @return int Returns one of the class's MULTI_ERR_* constants.
*/ */
public function atomic_multi_move(array $config, string $src_dir, string $target_dir, iterable $src_file_names) { public static function multiMove(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) { self::multiAction($config, $src_dir, $target_dir, $src_file_names, 'rename', function ($src_path, $target_path) {
// Move the files back into the source directory. // Move the files back into the source directory.
\rename($target_path, $src_path); \rename($target_path, $src_path);
}); });
@ -119,8 +125,8 @@ class FsUtils {
* with a path separator. * with a path separator.
* @return int Returns the number of deleted files. * @return int Returns the number of deleted files.
*/ */
public function multi_delete(array $config, string $dir, iterable $file_names): int { public function multiDelete(array $config, string $dir, iterable $file_names): int {
$dir = self::ensure_end_path_separator($dir); $dir = self::ensureEndPathSeparator($dir);
$deleted = 0; $deleted = 0;
foreach ($file_names as $file_name) { foreach ($file_names as $file_name) {

Loading…
Cancel
Save