From 93b9e43fa3bbe087d5a01e178af0e4e7f945070e Mon Sep 17 00:00:00 2001 From: Cristian Scheid Date: Fri, 27 Feb 2026 08:08:36 -0300 Subject: [PATCH 1/2] feat(mount-provider): implement IPartialMountProvider Signed-off-by: Cristian Scheid --- lib/Db/CoreQueryBuilder.php | 20 ++++++++++ lib/Db/MountRequest.php | 7 +++- lib/MountManager/CircleMountProvider.php | 49 +++++++++++++++++++++++- 3 files changed, 74 insertions(+), 2 deletions(-) diff --git a/lib/Db/CoreQueryBuilder.php b/lib/Db/CoreQueryBuilder.php index 22be02462..d7d839441 100644 --- a/lib/Db/CoreQueryBuilder.php +++ b/lib/Db/CoreQueryBuilder.php @@ -1574,6 +1574,26 @@ public function leftJoinMountpoint(string $aliasMount, IFederatedUser $federated } + /** + * @param string $aliasMount + * @param string $path + * @param bool $forChildren + * + * @throws RequestBuilderException + */ + public function limitToMountpoint(string $aliasMount, string $path, bool $forChildren = false): void { + if ($forChildren) { + $this->andWhere( + $this->expr()->like($aliasMount . '.mountpoint', $this->createNamedParameter($path . '/%')) + ); + } else { + $this->andWhere( + $this->expr()->eq($aliasMount . '.mountpoint', $this->createNamedParameter($path)) + ); + } + } + + /** * @param string $alias * @param array $default diff --git a/lib/Db/MountRequest.php b/lib/Db/MountRequest.php index ec3765ab9..6e378be4c 100644 --- a/lib/Db/MountRequest.php +++ b/lib/Db/MountRequest.php @@ -58,16 +58,21 @@ public function delete(string $token): void { /** * @param IFederatedUser $federatedUser + * @param string|null $path + * @param bool $forChildren * * @return Mount[] * @throws RequestBuilderException */ - public function getForUser(IFederatedUser $federatedUser): array { + public function getForUser(IFederatedUser $federatedUser, ?string $path = null, bool $forChildren = false): array { $qb = $this->getMountSelectSql(); $qb->setOptions([CoreQueryBuilder::MOUNT], ['getData' => true]); $qb->leftJoinMember(CoreQueryBuilder::MOUNT); $qb->leftJoinMountpoint(CoreQueryBuilder::MOUNT, $federatedUser); $qb->limitToInitiator(CoreQueryBuilder::MOUNT, $federatedUser, 'circle_id'); + if ($path !== null) { + $qb->limitToMountpoint(CoreQueryBuilder::MOUNT, $path, $forChildren); + } return $this->getItemsFromRequest($qb); diff --git a/lib/MountManager/CircleMountProvider.php b/lib/MountManager/CircleMountProvider.php index 58c9ab27e..9d21c536f 100644 --- a/lib/MountManager/CircleMountProvider.php +++ b/lib/MountManager/CircleMountProvider.php @@ -29,6 +29,8 @@ use OCP\DB\Exception; use OCP\Federation\ICloudIdManager; use OCP\Files\Config\IMountProvider; +use OCP\Files\Config\IPartialMountProvider; +use OCP\Files\Config\MountProviderArgs; use OCP\Files\Folder; use OCP\Files\IRootFolder; use OCP\Files\Mount\IMountPoint; @@ -36,9 +38,10 @@ use OCP\Files\Storage\IStorageFactory; use OCP\Http\Client\IClientService; use OCP\IUser; +use Override; use Psr\Log\LoggerInterface; -class CircleMountProvider implements IMountProvider { +class CircleMountProvider implements IMountProvider, IPartialMountProvider { use TArrayTools; public const EXTERNAL_STORAGE = ExternalStorage::class; @@ -247,4 +250,48 @@ private function generateIncrementedMountpoint(Folder $fs, Mount $mount, IFedera $n++; } } + + #[Override] + public function getMountsForPath(string $setupPathHint, bool $forChildren, array $mountProviderArgs, IStorageFactory $loader): array { + /** @var array $userItems */ + $userItems = []; + /** @var array $mounts */ + $mounts = []; + + /** @var MountProviderArgs $mountProviderArg */ + foreach ($mountProviderArgs as $mountProviderArg) { + $user = $mountProviderArg->mountInfo->getUser(); + + $parts = explode('/', $mountProviderArg->mountInfo->getMountPoint()); + if ($parts[1] !== $user->getUID() || $parts[2] !== 'files') { + continue; + } + + if (!isset($userItems[$user->getUID()])) { + $federatedUser = $this->federatedUserService->getLocalFederatedUser($user->getUID()); + $path = '/' . implode('/', array_slice($parts, 3)); + $userItems[$user->getUID()] = $this->mountRequest->getForUser( + $federatedUser, + $path, + $forChildren + ); + } + + foreach ($userItems[$user->getUID()] as $item) { + $mountPoint = '/' . $user->getUID() . '/files' . $item->getMountPoint(); + if (isset($mounts[$mountPoint])) { + continue; + } + + try { + $this->fixDuplicateFile($user->getUID(), $item); + $mounts[$mountPoint] = $this->generateCircleMount($item, $loader); + } catch (\Exception $e) { + $this->logger->warning('issue with teams\' partial mounts', ['exception' => $e]); + } + } + } + + return $mounts; + } } From a1632ed23f3aeb51eb58fd9ef0c4a9813bd13501 Mon Sep 17 00:00:00 2001 From: Cristian Scheid Date: Mon, 2 Mar 2026 19:13:40 -0300 Subject: [PATCH 2/2] perf(mount-provider): allow filtering mounts for multiple paths in a single query Signed-off-by: Cristian Scheid --- lib/Db/CoreQueryBuilder.php | 25 +++++++++++------- lib/Db/MountRequest.php | 8 +++--- lib/MountManager/CircleMountProvider.php | 33 ++++++++++++++---------- 3 files changed, 39 insertions(+), 27 deletions(-) diff --git a/lib/Db/CoreQueryBuilder.php b/lib/Db/CoreQueryBuilder.php index d7d839441..ca19c57e5 100644 --- a/lib/Db/CoreQueryBuilder.php +++ b/lib/Db/CoreQueryBuilder.php @@ -1576,21 +1576,26 @@ public function leftJoinMountpoint(string $aliasMount, IFederatedUser $federated /** * @param string $aliasMount - * @param string $path + * @param string[] $paths * @param bool $forChildren - * - * @throws RequestBuilderException */ - public function limitToMountpoint(string $aliasMount, string $path, bool $forChildren = false): void { + public function limitToMountpoints(string $aliasMount, array $paths, bool $forChildren = false): void { + if (count($paths) === 0) { + return; + } + + $expr = $this->expr(); + $orX = $expr->orX(); + if ($forChildren) { - $this->andWhere( - $this->expr()->like($aliasMount . '.mountpoint', $this->createNamedParameter($path . '/%')) - ); + foreach ($paths as $path) { + $orX->add($expr->like($aliasMount . '.mountpoint', $this->createNamedParameter($path . '/%'))); + } } else { - $this->andWhere( - $this->expr()->eq($aliasMount . '.mountpoint', $this->createNamedParameter($path)) - ); + $orX->add($expr->in($aliasMount . '.mountpoint', $this->createNamedParameter($paths, IQueryBuilder::PARAM_STR_ARRAY))); } + + $this->andWhere($orX); } diff --git a/lib/Db/MountRequest.php b/lib/Db/MountRequest.php index 6e378be4c..b55b95412 100644 --- a/lib/Db/MountRequest.php +++ b/lib/Db/MountRequest.php @@ -58,20 +58,20 @@ public function delete(string $token): void { /** * @param IFederatedUser $federatedUser - * @param string|null $path + * @param string[] $paths * @param bool $forChildren * * @return Mount[] * @throws RequestBuilderException */ - public function getForUser(IFederatedUser $federatedUser, ?string $path = null, bool $forChildren = false): array { + public function getForUser(IFederatedUser $federatedUser, array $paths = [], bool $forChildren = false): array { $qb = $this->getMountSelectSql(); $qb->setOptions([CoreQueryBuilder::MOUNT], ['getData' => true]); $qb->leftJoinMember(CoreQueryBuilder::MOUNT); $qb->leftJoinMountpoint(CoreQueryBuilder::MOUNT, $federatedUser); $qb->limitToInitiator(CoreQueryBuilder::MOUNT, $federatedUser, 'circle_id'); - if ($path !== null) { - $qb->limitToMountpoint(CoreQueryBuilder::MOUNT, $path, $forChildren); + if (count($paths) !== 0) { + $qb->limitToMountpoints(CoreQueryBuilder::MOUNT, $paths, $forChildren); } return $this->getItemsFromRequest($qb); diff --git a/lib/MountManager/CircleMountProvider.php b/lib/MountManager/CircleMountProvider.php index 9d21c536f..0edd9b0b6 100644 --- a/lib/MountManager/CircleMountProvider.php +++ b/lib/MountManager/CircleMountProvider.php @@ -253,8 +253,8 @@ private function generateIncrementedMountpoint(Folder $fs, Mount $mount, IFedera #[Override] public function getMountsForPath(string $setupPathHint, bool $forChildren, array $mountProviderArgs, IStorageFactory $loader): array { - /** @var array $userItems */ - $userItems = []; + /** @var array $userMountRequests */ + $userMountRequests = []; /** @var array $mounts */ $mounts = []; @@ -267,24 +267,31 @@ public function getMountsForPath(string $setupPathHint, bool $forChildren, array continue; } - if (!isset($userItems[$user->getUID()])) { + if (!isset($userMountRequests[$user->getUID()])) { $federatedUser = $this->federatedUserService->getLocalFederatedUser($user->getUID()); - $path = '/' . implode('/', array_slice($parts, 3)); - $userItems[$user->getUID()] = $this->mountRequest->getForUser( - $federatedUser, - $path, - $forChildren - ); + $userMountRequests[$user->getUID()] = [ + 'federatedUser' => $federatedUser, + 'paths' => [], + ]; } - foreach ($userItems[$user->getUID()] as $item) { - $mountPoint = '/' . $user->getUID() . '/files' . $item->getMountPoint(); + $userMountRequests[$user->getUID()]['paths'][] = '/' . implode('/', array_slice($parts, 3)); + } + + foreach ($userMountRequests as $uid => $userMountRequest) { + $userItems = $this->mountRequest->getForUser( + $userMountRequest['federatedUser'], + $userMountRequest['paths'], + $forChildren + ); + + foreach ($userItems as $item) { + $mountPoint = '/' . $uid . '/files' . $item->getMountPoint(); if (isset($mounts[$mountPoint])) { continue; } - try { - $this->fixDuplicateFile($user->getUID(), $item); + $this->fixDuplicateFile($uid, $item); $mounts[$mountPoint] = $this->generateCircleMount($item, $loader); } catch (\Exception $e) { $this->logger->warning('issue with teams\' partial mounts', ['exception' => $e]);