fix: don't scan the same storage multiple times

Signed-off-by: Robin Appelman <robin@icewind.nl>
pull/53675/head
Robin Appelman 2025-06-23 16:25:05 +07:00 committed by backportbot[bot]
parent e9ce122b85
commit 881abecd34
1 changed files with 44 additions and 4 deletions

@ -11,6 +11,7 @@ use OC\Core\Command\Base;
use OC\Core\Command\InterruptedException;
use OC\DB\Connection;
use OC\DB\ConnectionAdapter;
use OC\Files\Storage\Wrapper\Jail;
use OC\Files\Utils\Scanner;
use OC\FilesMetadata\FilesMetadataManager;
use OC\ForbiddenException;
@ -98,7 +99,15 @@ class Scan extends Base {
);
}
protected function scanFiles(string $user, string $path, ?string $scanMetadata, OutputInterface $output, bool $backgroundScan = false, bool $recursive = true, bool $homeOnly = false): void {
protected function scanFiles(
string $user,
string $path,
?string $scanMetadata,
OutputInterface $output,
callable $mountFilter,
bool $backgroundScan = false,
bool $recursive = true,
): void {
$connection = $this->reconnectToDatabase($output);
$scanner = new Scanner(
$user,
@ -152,7 +161,7 @@ class Scan extends Base {
if ($backgroundScan) {
$scanner->backgroundScan($path);
} else {
$scanner->scan($path, $recursive, $homeOnly ? [$this, 'filterHomeMount'] : null);
$scanner->scan($path, $recursive, $mountFilter);
}
} catch (ForbiddenException $e) {
$output->writeln("<error>Home storage for user $user not writable or 'files' subdirectory missing</error>");
@ -178,7 +187,7 @@ class Scan extends Base {
}
}
public function filterHomeMount(IMountPoint $mountPoint): bool {
public function isHomeMount(IMountPoint $mountPoint): bool {
// any mountpoint inside '/$user/files/'
return substr_count($mountPoint->getMountPoint(), '/') <= 3;
}
@ -210,6 +219,29 @@ class Scan extends Base {
$metadata = $input->getOption('generate-metadata') ?? '';
}
$homeOnly = $input->getOption('home-only');
$scannedStorages = [];
$mountFilter = function (IMountPoint $mount) use ($homeOnly, &$scannedStorages) {
if ($homeOnly && !$this->isHomeMount($mount)) {
return false;
}
// when scanning multiple users, the scanner might encounter the same storage multiple times (e.g. external storages, or group folders)
// we can filter out any storage we've already scanned to avoid double work
$storage = $mount->getStorage();
$storageKey = $storage->getId();
while ($storage->instanceOfStorage(Jail::class)) {
$storageKey .= '/' . $storage->getUnjailedPath('');
$storage = $storage->getUnjailedStorage();
}
if (array_key_exists($storageKey, $scannedStorages)) {
return false;
}
$scannedStorages[$storageKey] = true;
return true;
};
$user_count = 0;
foreach ($users as $user) {
if (is_object($user)) {
@ -219,7 +251,15 @@ class Scan extends Base {
++$user_count;
if ($this->userManager->userExists($user)) {
$output->writeln("Starting scan for user $user_count out of $users_total ($user)");
$this->scanFiles($user, $path, $metadata, $output, $input->getOption('unscanned'), !$input->getOption('shallow'), $input->getOption('home-only'));
$this->scanFiles(
$user,
$path,
$metadata,
$output,
$mountFilter,
$input->getOption('unscanned'),
!$input->getOption('shallow'),
);
$output->writeln('', OutputInterface::VERBOSITY_VERBOSE);
} else {
$output->writeln("<error>Unknown user $user_count $user</error>");