From b79af98a31f27ffdb3f2469b32bff11c8c3380b2 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Tue, 3 Feb 2026 16:16:22 +0100 Subject: [PATCH 1/3] fix: fix partial external storage provider not finding root mount Signed-off-by: Robin Appelman --- apps/files_external/lib/Service/DBConfigService.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/apps/files_external/lib/Service/DBConfigService.php b/apps/files_external/lib/Service/DBConfigService.php index fd482ecbef7e5..f20d22254f625 100644 --- a/apps/files_external/lib/Service/DBConfigService.php +++ b/apps/files_external/lib/Service/DBConfigService.php @@ -121,6 +121,9 @@ private function getSelectQueryBuilder(): IQueryBuilder { public function getMountsForUserAndPath(string $userId, array $groupIds, string $path, bool $forChildren): array { $path = str_replace('/' . $userId . '/files', '', $path); $path = rtrim($path, '/'); + if ($path === '') { + $path = '/'; + } $builder = $this->getSelectQueryBuilder(); $builder->where($builder->expr()->orX( $builder->expr()->andX( // global mounts From 522663b9f0833ee031f6ab52efea0cf444b6d288 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Tue, 3 Feb 2026 16:20:41 +0100 Subject: [PATCH 2/3] fix: partial external storage config matching non-child mounts Signed-off-by: Robin Appelman --- .../lib/Service/DBConfigService.php | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/apps/files_external/lib/Service/DBConfigService.php b/apps/files_external/lib/Service/DBConfigService.php index f20d22254f625..d6135ff88cccb 100644 --- a/apps/files_external/lib/Service/DBConfigService.php +++ b/apps/files_external/lib/Service/DBConfigService.php @@ -122,27 +122,30 @@ public function getMountsForUserAndPath(string $userId, array $groupIds, string $path = str_replace('/' . $userId . '/files', '', $path); $path = rtrim($path, '/'); if ($path === '') { - $path = '/'; + $nonChildPath = '/'; + } else { + $nonChildPath = $path; } + $builder = $this->getSelectQueryBuilder(); + $pathFilter = $forChildren + ? $builder->expr()->like('m.mount_point', $builder->createNamedParameter($this->connection->escapeLikeParameter($path) . '/_%', IQueryBuilder::PARAM_STR)) + : $builder->expr()->eq('m.mount_point', $builder->createNamedParameter($nonChildPath, IQueryBuilder::PARAM_STR)); $builder->where($builder->expr()->orX( $builder->expr()->andX( // global mounts $builder->expr()->eq('a.type', $builder->createNamedParameter(self::APPLICABLE_TYPE_GLOBAL, IQueryBuilder::PARAM_INT)), $builder->expr()->isNull('a.value'), - $forChildren ? $builder->expr()->like('m.mount_point', $builder->createNamedParameter($this->connection->escapeLikeParameter($path) . '_%', IQueryBuilder::PARAM_STR)) - : $builder->expr()->eq('m.mount_point', $builder->createNamedParameter($path, IQueryBuilder::PARAM_STR)), + $pathFilter, ), $builder->expr()->andX( // mounts for user $builder->expr()->eq('a.type', $builder->createNamedParameter(self::APPLICABLE_TYPE_USER, IQueryBuilder::PARAM_INT)), $builder->expr()->eq('a.value', $builder->createNamedParameter($userId)), - $forChildren ? $builder->expr()->like('m.mount_point', $builder->createNamedParameter($this->connection->escapeLikeParameter($path) . '_%', IQueryBuilder::PARAM_STR)) - : $builder->expr()->eq('m.mount_point', $builder->createNamedParameter($path, IQueryBuilder::PARAM_STR)), + $pathFilter, ), $builder->expr()->andX( // mounts for group $builder->expr()->eq('a.type', $builder->createNamedParameter(self::APPLICABLE_TYPE_GROUP, IQueryBuilder::PARAM_INT)), $builder->expr()->in('a.value', $builder->createNamedParameter($groupIds, IQueryBuilder::PARAM_STR_ARRAY)), - $forChildren ? $builder->expr()->like('m.mount_point', $builder->createNamedParameter($this->connection->escapeLikeParameter($path) . '_%', IQueryBuilder::PARAM_STR)) - : $builder->expr()->eq('m.mount_point', $builder->createNamedParameter($path, IQueryBuilder::PARAM_STR)), + $pathFilter, ), )); @@ -160,7 +163,7 @@ public function getGlobalMounts(): array { ->where($builder->expr()->andX( // global mounts $builder->expr()->eq('a.type', $builder->createNamedParameter(self::APPLICABLE_TYPE_GLOBAL, IQueryBuilder::PARAM_INT)), $builder->expr()->isNull('a.value'), - ), ); + )); return $this->getMountsFromQuery($query); } From 720e58872703bf216b93c80f850a79c3f0b8fcc9 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Tue, 3 Feb 2026 16:34:08 +0100 Subject: [PATCH 3/3] test: add tests for getMountsForUserAndPath Signed-off-by: Robin Appelman --- .../tests/Service/DBConfigServiceTest.php | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/apps/files_external/tests/Service/DBConfigServiceTest.php b/apps/files_external/tests/Service/DBConfigServiceTest.php index b1730eb10fef3..d41c2ee31813c 100644 --- a/apps/files_external/tests/Service/DBConfigServiceTest.php +++ b/apps/files_external/tests/Service/DBConfigServiceTest.php @@ -12,6 +12,7 @@ use OCP\IDBConnection; use OCP\Security\ICrypto; use OCP\Server; +use PHPUnit\Framework\Attributes\DataProvider; use Test\TestCase; #[\PHPUnit\Framework\Attributes\Group(name: 'DB')] @@ -271,4 +272,32 @@ public function testGetAllMounts(): void { $this->assertEquals($id1, $mounts[0]['mount_id']); $this->assertEquals($id2, $mounts[1]['mount_id']); } + + public static function mountsForPathProvider(): array { + return [ + ['/test/files/test/', false, ['/test']], + ['/test/files/test/', true, ['/test/more']], + ['/test/files/', false, ['/']], + ['/test/files/', true, ['/test', '/test/more', '/test2']], + ]; + } + + #[DataProvider('mountsForPathProvider')] + public function testGetMountsForUserAndPath(string $path, bool $forChildren, array $expectedMountPoints): void { + sort($expectedMountPoints); + $id1 = $this->addMount('/test', 'foo', 'bar', 100, DBConfigService::MOUNT_TYPE_ADMIN); + $this->dbConfig->addApplicable($id1, DBConfigService::APPLICABLE_TYPE_GLOBAL, null); + $id2 = $this->addMount('/test2', 'foo2', 'bar2', 100, DBConfigService::MOUNT_TYPE_PERSONAL); + $this->dbConfig->addApplicable($id2, DBConfigService::APPLICABLE_TYPE_GLOBAL, null); + $id3 = $this->addMount('/test/more', 'foo', 'bar', 100, DBConfigService::MOUNT_TYPE_ADMIN); + $this->dbConfig->addApplicable($id3, DBConfigService::APPLICABLE_TYPE_GLOBAL, null); + $id4 = $this->addMount('/', 'foo', 'bar', 100, DBConfigService::MOUNT_TYPE_ADMIN); + $this->dbConfig->addApplicable($id4, DBConfigService::APPLICABLE_TYPE_GLOBAL, null); + + $mounts = $this->dbConfig->getMountsForUserAndPath('test', [], $path, $forChildren); + $mountPoints = array_map(fn (array $mountInfo) => $mountInfo['mount_point'], $mounts); + sort($mountPoints); + $this->assertEquals($expectedMountPoints, $mountPoints); + + } }