From 467487ecd8d558d8b340fe06a8a4ed8d4fbaa860 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Tue, 6 Jan 2026 14:26:57 +0100 Subject: [PATCH 1/3] feat: dont reload authoritative mount providers when doing by-path setup Signed-off-by: Robin Appelman --- lib/private/Files/SetupManager.php | 61 ++++++++++++++++++++---------- 1 file changed, 41 insertions(+), 20 deletions(-) diff --git a/lib/private/Files/SetupManager.php b/lib/private/Files/SetupManager.php index 7f8bc2ed5485b..5bcc8f7942184 100644 --- a/lib/private/Files/SetupManager.php +++ b/lib/private/Files/SetupManager.php @@ -31,6 +31,7 @@ use OCP\Constants; use OCP\Diagnostics\IEventLogger; use OCP\EventDispatcher\IEventDispatcher; +use OCP\Files\Config\IAuthoritativeMountProvider; use OCP\Files\Config\ICachedMountInfo; use OCP\Files\Config\IHomeMountProvider; use OCP\Files\Config\IMountProvider; @@ -226,6 +227,24 @@ function ($mountPoint, IStorage $storage, IMountPoint $mount) use ($reSharingEna Filesystem::logWarningWhenAddingStorageWrapper($prevLogging); } + /** + * Update the cached mounts for all non-authoritative mount providers for a user. + */ + private function updateNonAuthoritativeProviders(IUser $user): void { + $providers = $this->mountProviderCollection->getProviders(); + $nonAuthoritativeProviders = array_filter( + $providers, + fn (IMountProvider $provider) => !( + $provider instanceof IAuthoritativeMountProvider + || $provider instanceof IRootMountProvider + || $provider instanceof IHomeMountProvider + ) + ); + $providerNames = array_map(fn (IMountProvider $provider) => get_class($provider), $nonAuthoritativeProviders); + $mount = $this->mountProviderCollection->getUserMountsForProviderClasses($user, $providerNames); + $this->userMountCache->registerMounts($user, $mount, $providerNames); + } + /** * Setup the full filesystem for the specified user */ @@ -335,12 +354,16 @@ private function afterUserFullySetup(IUser $user, array $previouslySetupProvider }); $this->registerMounts($user, $mounts, $newProviders); + $this->markUserMountsCached($user); + $this->eventLogger->end('fs:setup:user:full:post'); + } + + private function markUserMountsCached(IUser $user): void { $cacheDuration = $this->config->getSystemValueInt('fs_mount_cache_duration', 5 * 60); if ($cacheDuration > 0) { $this->cache->set($user->getUID(), true, $cacheDuration); $this->fullSetupRequired[$user->getUID()] = false; } - $this->eventLogger->end('fs:setup:user:full:post'); } /** @@ -434,8 +457,8 @@ public function setupForPath(string $path, bool $includeChildren = false): void } if ($this->fullSetupRequired($user)) { - $this->setupForUser($user); - return; + $this->updateNonAuthoritativeProviders($user); + $this->markUserMountsCached($user); } // for the user's home folder, and includes children we need everything always @@ -505,11 +528,10 @@ public function setupForPath(string $path, bool $includeChildren = false): void $subCachedMounts = $this->userMountCache->getMountsInPath($user, $path); $this->eventLogger->end('fs:setup:user:path:find'); - $needsFullSetup - = array_any( - $subCachedMounts, - fn (ICachedMountInfo $info) => $info->getMountProvider() === '' - ); + $needsFullSetup = array_any( + $subCachedMounts, + fn (ICachedMountInfo $info) => $info->getMountProvider() === '' + ); if ($needsFullSetup) { $this->logger->debug('mount has no provider set, performing full setup'); @@ -542,11 +564,10 @@ public function setupForPath(string $path, bool $includeChildren = false): void $currentProviders[] = $mountProvider; $setupProviders[] = $mountProvider; - $fullProviderMounts[] - = $this->mountProviderCollection->getUserMountsForProviderClasses( - $user, - [$mountProvider] - ); + $fullProviderMounts[] = $this->mountProviderCollection->getUserMountsForProviderClasses( + $user, + [$mountProvider] + ); } if (!empty($authoritativeCachedMounts)) { @@ -573,13 +594,12 @@ static function (ICachedMountInfo $info) use ($rootsMetadata) { }, $cachedMounts )); - $authoritativeMounts[] - = $this->mountProviderCollection->getUserMountsFromProviderByPath( - $providerClass, - $path, - true, - $providerArgs, - ); + $authoritativeMounts[] = $this->mountProviderCollection->getUserMountsFromProviderByPath( + $providerClass, + $path, + true, + $providerArgs, + ); } } } else { @@ -758,6 +778,7 @@ private function registerMounts(IUser $user, array $mounts, ?array $mountProvide /** * Drops partially set-up mounts for the given user + * * @param class-string[] $providers */ public function dropPartialMountsForUser(IUser $user, array $providers = []): void { From e5497c773167975a4404e47790ce6426f88df947 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Wed, 14 Jan 2026 11:21:27 +0100 Subject: [PATCH 2/3] fix: protect against infinite loops in setup Signed-off-by: Robin Appelman --- lib/private/Files/SetupManager.php | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/lib/private/Files/SetupManager.php b/lib/private/Files/SetupManager.php index 5bcc8f7942184..90a8d468a0212 100644 --- a/lib/private/Files/SetupManager.php +++ b/lib/private/Files/SetupManager.php @@ -231,6 +231,13 @@ function ($mountPoint, IStorage $storage, IMountPoint $mount) use ($reSharingEna * Update the cached mounts for all non-authoritative mount providers for a user. */ private function updateNonAuthoritativeProviders(IUser $user): void { + // prevent recursion loop from when getting mounts from providers ends up setting up the filesystem + static $updatingProviders = false; + if ($updatingProviders) { + return; + } + $updatingProviders = true; + $providers = $this->mountProviderCollection->getProviders(); $nonAuthoritativeProviders = array_filter( $providers, @@ -243,6 +250,8 @@ private function updateNonAuthoritativeProviders(IUser $user): void { $providerNames = array_map(fn (IMountProvider $provider) => get_class($provider), $nonAuthoritativeProviders); $mount = $this->mountProviderCollection->getUserMountsForProviderClasses($user, $providerNames); $this->userMountCache->registerMounts($user, $mount, $providerNames); + + $updatingProviders = false; } /** From 47530ef95ff79c39a7bf8f5fe781c667745157ef Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Wed, 14 Jan 2026 13:03:01 +0100 Subject: [PATCH 3/3] feat: add a debug config option to disable authoritative mount optimizations Signed-off-by: Robin Appelman --- lib/private/Files/SetupManager.php | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/lib/private/Files/SetupManager.php b/lib/private/Files/SetupManager.php index 90a8d468a0212..c29b8199fef82 100644 --- a/lib/private/Files/SetupManager.php +++ b/lib/private/Files/SetupManager.php @@ -85,7 +85,8 @@ class SetupManager { private bool $listeningForProviders; private array $fullSetupRequired = []; private bool $setupBuiltinWrappersDone = false; - private bool $forceFullSetup = false; + private bool $forceFullSetup; + private bool $optimizeAuthoritativeProviders; private const SETUP_WITH_CHILDREN = 1; private const SETUP_WITHOUT_CHILDREN = 0; @@ -108,6 +109,7 @@ public function __construct( $this->cache = $cacheFactory->createDistributed('setupmanager::'); $this->listeningForProviders = false; $this->forceFullSetup = $this->config->getSystemValueBool('debug.force-full-fs-setup'); + $this->optimizeAuthoritativeProviders = $this->config->getSystemValueBool('debug.optimize-authoritative-providers', true); $this->setupListeners(); } @@ -466,8 +468,13 @@ public function setupForPath(string $path, bool $includeChildren = false): void } if ($this->fullSetupRequired($user)) { - $this->updateNonAuthoritativeProviders($user); - $this->markUserMountsCached($user); + if ($this->optimizeAuthoritativeProviders) { + $this->updateNonAuthoritativeProviders($user); + $this->markUserMountsCached($user); + } else { + $this->setupForUser($user); + return; + } } // for the user's home folder, and includes children we need everything always