d70a869070
CI / unit (8.2) (push) Has been cancelled
CI / unit (8.3) (push) Has been cancelled
CI / unit (8.4) (push) Has been cancelled
CI / psalm (push) Has been cancelled
CI / integration (mariadb, 32) (push) Has been cancelled
CI / integration (mariadb, 33) (push) Has been cancelled
CI / integration (mariadb, 34) (push) Has been cancelled
CI / integration (mariadb, 35) (push) Has been cancelled
CI / integration (pgsql, 32) (push) Has been cancelled
CI / integration (pgsql, 33) (push) Has been cancelled
CI / integration (pgsql, 34) (push) Has been cancelled
CI / integration (pgsql, 35) (push) Has been cancelled
CI / integration (sqlite, 32) (push) Has been cancelled
CI / integration (sqlite, 33) (push) Has been cancelled
CI / integration (sqlite, 34) (push) Has been cancelled
CI / integration (sqlite, 35) (push) Has been cancelled
CI / integration-targets (push) Has been cancelled
CI / integration-user (32) (push) Has been cancelled
CI / integration-user (33) (push) Has been cancelled
CI / integration-user (34) (push) Has been cancelled
CI / integration-user (35) (push) Has been cancelled
CI / integration-full-restore (push) Has been cancelled
backup:verify accepts a user export path (users/<uid>/<export>): every blob its manifest references is checked the same way as for a snapshot, shallow or --deep. The SMB backend is refused at backup:target:add when the native smbclient PECL extension is not loaded, instead of registering and then failing the first backup with "Seek failed": with only the smbclient binary files_external falls back to a shell stream that cannot seek. The per-user integration job now runs on Nextcloud 32-35, not just 35: it calls user_migration's internal UserMigrationService, whose API may change between versions.
49 lines
1.8 KiB
PHP
49 lines
1.8 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
// SPDX-FileCopyrightText: 2026 Dillard Blom
|
|
// SPDX-License-Identifier: AGPL-3.0-or-later
|
|
|
|
namespace OCA\NgBackup\Service;
|
|
|
|
use OCA\NgBackup\Db\Target;
|
|
use OCP\Lock\LockedException;
|
|
|
|
/** Checksum audit of a snapshot (occ backup:verify), see Repository::verify(). */
|
|
final class VerifyService {
|
|
public function __construct(
|
|
private TargetService $targets,
|
|
private LeaseService $leases,
|
|
) {
|
|
}
|
|
|
|
/** @return array{filesChecked:int, blobsChecked:int, bytesChecked:int, missing:list<string>, failed:list<string>} */
|
|
public function verify(Target $target, string $snapshotId, bool $deep = false): array {
|
|
return $this->run($target, $deep, fn (\OCA\NgBackup\Repository\Repository $repo, callable $heartbeat): array => $repo->verify($snapshotId, $deep, $heartbeat));
|
|
}
|
|
|
|
/** @return array{filesChecked:int, blobsChecked:int, bytesChecked:int, missing:list<string>, failed:list<string>} */
|
|
public function verifyUserExport(Target $target, string $manifestPath, bool $deep = false): array {
|
|
return $this->run($target, $deep, fn (\OCA\NgBackup\Repository\Repository $repo, callable $heartbeat): array => $repo->verifyUserExport($manifestPath, $deep, $heartbeat));
|
|
}
|
|
|
|
private function run(Target $target, bool $deep, callable $fn): array {
|
|
$ttl = $deep ? 600 : 300;
|
|
try {
|
|
return $this->leases->with(PruneService::lockKey($target), LeaseService::SHARED, $ttl, function (callable $refresh) use ($target, $deep, $ttl, $fn): array {
|
|
$last = time();
|
|
$heartbeat = function () use ($refresh, $ttl, &$last): void {
|
|
if (time() - $last >= intdiv($ttl, 2)) {
|
|
$refresh();
|
|
$last = time();
|
|
}
|
|
};
|
|
return $fn($this->targets->repository($target), $heartbeat);
|
|
});
|
|
} catch (LockedException) {
|
|
throw new \RuntimeException('A cleanup of ' . $target->getName() . ' is in progress; try again later');
|
|
}
|
|
}
|
|
}
|