From fe1bddeecf30b74cdb620feee7ab5bea1442c32a Mon Sep 17 00:00:00 2001 From: Git'Fellow <12234510+solracsf@users.noreply.github.com> Date: Thu, 10 Sep 2026 12:13:08 +0200 Subject: [PATCH] fix(files_external): clean up filecache when deleting an external storage StoragesService::removeStorage() dispatched StorageDeletedEvent before Storage::cleanByMountId(). MountCacheService handles that event by removing the storage's oc_mounts rows, and cleanByMountId() looks the storages up through those rows, so it found nothing and left the oc_filecache and oc_storages rows behind. occ files:cleanup cannot recover them because the oc_storages row still exists. Dispatch the event after the cleanup, which restores the order used before the event was introduced. Signed-off-by: Git'Fellow <12234510+solracsf@users.noreply.github.com> --- .../lib/Service/StoragesService.php | 4 ++- .../tests/Service/StoragesServiceTestCase.php | 33 +++++++++++++++++++ 2 files changed, 36 insertions(+), 1 deletion(-) diff --git a/apps/files_external/lib/Service/StoragesService.php b/apps/files_external/lib/Service/StoragesService.php index a0088f4950346..0397e0d80907c 100644 --- a/apps/files_external/lib/Service/StoragesService.php +++ b/apps/files_external/lib/Service/StoragesService.php @@ -424,12 +424,14 @@ public function removeStorage(int $id): void { $this->dbConfig->removeMount($id); $deletedStorage = $this->getStorageConfigFromDBMount($existingMount); - $this->eventDispatcher->dispatchTyped(new StorageDeletedEvent($deletedStorage)); $this->triggerHooks($deletedStorage, Filesystem::signal_delete_mount); // delete oc_storages entries and oc_filecache Storage::cleanByMountId($id); + // listeners remove the oc_mounts rows that cleanByMountId() uses to find the storages + $this->eventDispatcher->dispatchTyped(new StorageDeletedEvent($deletedStorage)); + $this->updateOverwriteHomeFolders(); } diff --git a/apps/files_external/tests/Service/StoragesServiceTestCase.php b/apps/files_external/tests/Service/StoragesServiceTestCase.php index 6ce7c7438eef9..29be458d83e3d 100644 --- a/apps/files_external/tests/Service/StoragesServiceTestCase.php +++ b/apps/files_external/tests/Service/StoragesServiceTestCase.php @@ -11,6 +11,7 @@ use OC\Files\Cache\Storage; use OC\Files\Filesystem; +use OCA\Files_External\Event\StorageDeletedEvent; use OCA\Files_External\Lib\Auth\AuthMechanism; use OCA\Files_External\Lib\Auth\InvalidAuth; use OCA\Files_External\Lib\Auth\NullMechanism; @@ -22,6 +23,8 @@ use OCA\Files_External\Service\BackendService; use OCA\Files_External\Service\DBConfigService; use OCA\Files_External\Service\StoragesService; +use OCP\DB\QueryBuilder\IQueryBuilder; +use OCP\EventDispatcher\Event; use OCP\EventDispatcher\IEventDispatcher; use OCP\Files\Cache\ICache; use OCP\Files\Config\IUserMountCache; @@ -284,6 +287,26 @@ public function testDeleteStorage(array $backendOptions, string $rustyStorageId) // get numeric id for later check $numericId = $storageCache->getNumericId(); + $path = $this->getUniqueID('file'); + $qb = Server::get(IDBConnection::class)->getQueryBuilder(); + $qb->insert('filecache') + ->values([ + 'storage' => $qb->createNamedParameter($numericId, IQueryBuilder::PARAM_INT), + 'path' => $qb->createNamedParameter($path), + 'path_hash' => $qb->createNamedParameter(md5($path)), + 'parent' => $qb->createNamedParameter(-1, IQueryBuilder::PARAM_INT), + 'name' => $qb->createNamedParameter($path), + ]) + ->executeStatement(); + + // listeners of StorageDeletedEvent clear the mount cache, like MountCacheService does + $this->eventDispatcher->method('dispatchTyped') + ->willReturnCallback(function (Event $event) use ($mountCache, $user): void { + if ($event instanceof StorageDeletedEvent) { + $mountCache->removeMount('dummy', $user); + } + }); + $this->service->removeStorage($id); $caught = false; @@ -305,6 +328,16 @@ public function testDeleteStorage(array $backendOptions, string $rustyStorageId) $storages = $result->fetchAll(); $result->closeCursor(); $this->assertCount(0, $storages, 'expected 0 storages, got ' . json_encode($storages)); + + // filecache entries of the storage were removed + $qb = Server::get(IDBConnection::class)->getQueryBuilder(); + $result = $qb->select('fileid') + ->from('filecache') + ->where($qb->expr()->eq('storage', $qb->createNamedParameter($numericId, IQueryBuilder::PARAM_INT))) + ->executeQuery(); + $fileIds = $result->fetchFirstColumn(); + $result->closeCursor(); + $this->assertCount(0, $fileIds, 'expected 0 filecache entries, got ' . json_encode($fileIds)); } protected function actualDeletedUnexistingStorageTest() {