Skip to content

Commit 7c02e64

Browse files
DerDreschnerbackportbot[bot]
authored andcommitted
fix(files): Continue scanning after StorageInvalidException
When a federated share was already removed on the remote end, accessing it triggers a `StorageInvalidException` after the share and its mount are removed locally. When running `occ files:scan`, this exception isn't caught by the scanner and aborts the scan of all remaining mounts of that user, printing a stack trace instead. This can be pretty frustrating on larger instances with many federated shares. To avoid that, the `StorageInvalidException` is now caught and handled the same way as the `StorageNotAvailableException`: log it as an error, report it through the `StorageNotAvailable` hook and continue with the next mount. Assisted-by: ClaudeCode:claude-opus-5-5 Signed-off-by: David Dreschner <david.dreschner@nextcloud.com> [skip ci]
1 parent e1c1fb4 commit 7c02e64

5 files changed

Lines changed: 48 additions & 1 deletion

File tree

‎apps/files/lib/Command/Scan.php‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
use OCP\Files\IRootFolder;
2323
use OCP\Files\Mount\IMountPoint;
2424
use OCP\Files\NotFoundException;
25+
use OCP\Files\StorageInvalidException;
2526
use OCP\Files\StorageNotAvailableException;
2627
use OCP\FilesMetadata\IFilesMetadataManager;
2728
use OCP\IUserManager;

‎apps/files/lib/Command/ScanAppData.php‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
use OCP\Files\IRootFolder;
1919
use OCP\Files\Node;
2020
use OCP\Files\NotFoundException;
21+
use OCP\Files\StorageInvalidException;
2122
use OCP\Files\StorageNotAvailableException;
2223
use OCP\IConfig;
2324
use OCP\Server;

‎apps/files/tests/Command/ScanAppDataTest.php‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
use OCP\Files\IRootFolder;
2525
use OCP\Files\NotFoundException;
2626
use OCP\Files\Storage\IStorageFactory;
27+
use OCP\Files\StorageInvalidException;
2728
use OCP\IAppConfig;
2829
use OCP\IConfig;
2930
use OCP\IUser;

‎lib/private/Files/Utils/Scanner.php‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
use OCP\Files\Mount\IMountPoint;
2727
use OCP\Files\NotFoundException;
2828
use OCP\Files\Storage\IStorage;
29+
use OCP\Files\StorageInvalidException;
2930
use OCP\Files\StorageNotAvailableException;
3031
use OCP\IDBConnection;
3132
use OCP\Lock\ILockingProvider;
@@ -257,7 +258,7 @@ public function scan($dir = '', $recursive = \OC\Files\Cache\Scanner::SCAN_RECUR
257258
$cache->correctFolderSize($relativePath);
258259
}
259260
$propagator->commitBatch();
260-
} catch (StorageNotAvailableException $e) {
261+
} catch (StorageNotAvailableException|StorageInvalidException $e) {
261262
$this->logger->error('Storage ' . $storage->getId() . ' not available', ['exception' => $e]);
262263
$this->emit('\OC\Files\Utils\Scanner', 'StorageNotAvailable', [$e]);
263264
}

‎tests/lib/Files/Utils/ScannerTest.php‎

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616
use OCP\Files\Config\IMountProvider;
1717
use OCP\Files\Config\IMountProviderCollection;
1818
use OCP\Files\Storage\IStorageFactory;
19+
use OCP\Files\StorageInvalidException;
20+
use OCP\Files\StorageNotAvailableException;
1921
use OCP\IDBConnection;
2022
use OCP\IUser;
2123
use OCP\IUserManager;
@@ -223,4 +225,45 @@ public function testShallow(): void {
223225
$this->assertTrue($cache->inCache('folder/bar.txt'));
224226
$this->assertFalse($cache->inCache('folder/subfolder/foobar.txt'));
225227
}
228+
229+
public function testScanContinuesAfterInvalidStorage(): void {
230+
$invalidStorage = new class([]) extends Temporary {
231+
#[\Override]
232+
public function getMetaData(string $path): ?array {
233+
throw new StorageInvalidException('Remote share not found');
234+
}
235+
};
236+
$storage = new Temporary([]);
237+
$storage->file_put_contents('foo.txt', 'qwerty');
238+
239+
$logger = $this->createMock(LoggerInterface::class);
240+
$logger->expects($this->once())
241+
->method('error')
242+
->with(
243+
'Storage ' . $invalidStorage->getId() . ' not available',
244+
$this->callback(fn (array $context): bool => $context['exception'] instanceof StorageInvalidException),
245+
);
246+
247+
$scanner = new TestScanner(
248+
Server::get(IUserManager::class)->get(''),
249+
Server::get(IDBConnection::class),
250+
$this->createMock(IEventDispatcher::class),
251+
$logger,
252+
Server::get(SetupManager::class),
253+
);
254+
$scanner->addMount(new MountPoint($invalidStorage, '/invalid'));
255+
$scanner->addMount(new MountPoint($storage, '/valid'));
256+
257+
$emitted = [];
258+
$scanner->listen('\OC\Files\Utils\Scanner', 'StorageNotAvailable', function (StorageNotAvailableException|StorageInvalidException $e) use (&$emitted): void {
259+
$emitted[] = $e;
260+
});
261+
262+
$scanner->scan('');
263+
264+
$this->assertCount(1, $emitted);
265+
$this->assertInstanceOf(StorageInvalidException::class, $emitted[0]);
266+
$this->assertSame('Remote share not found', $emitted[0]->getMessage());
267+
$this->assertTrue($storage->getCache()->inCache('foo.txt'));
268+
}
226269
}

0 commit comments

Comments
 (0)