Completed
Push — master ( ac4e82...aaf07a )
by
unknown
24:46
created
lib/public/Files/Config/IPartialMountProvider.php 1 patch
Indentation   +21 added lines, -21 removed lines patch added patch discarded remove patch
@@ -19,25 +19,25 @@
 block discarded – undo
19 19
  */
20 20
 interface IPartialMountProvider extends IMountProvider {
21 21
 
22
-	/**
23
-	 * Called during the Filesystem setup of a specific path.
24
-	 *
25
-	 * The provided arguments give information about the path being set up,
26
-	 * as well as information about mount points known to be provided by the
27
-	 * mount provider and contained in the path or in its sub-paths.
28
-	 *
29
-	 * Implementations should verify the IMountProviderArgs and return the
30
-	 * corresponding IMountPoint instances.
31
-	 *
32
-	 * @param string $path path for which the mounts are set up
33
-	 * @param IMountProviderArgs[] $mountProviderArgs
34
-	 * @param IStorageFactory $loader
35
-	 * @return array<string, IMountPoint> IMountPoint instances, indexed by
36
-	 *                                    mount-point
37
-	 */
38
-	public function getMountsForPath(
39
-		string $path,
40
-		array $mountProviderArgs,
41
-		IStorageFactory $loader,
42
-	): array;
22
+    /**
23
+     * Called during the Filesystem setup of a specific path.
24
+     *
25
+     * The provided arguments give information about the path being set up,
26
+     * as well as information about mount points known to be provided by the
27
+     * mount provider and contained in the path or in its sub-paths.
28
+     *
29
+     * Implementations should verify the IMountProviderArgs and return the
30
+     * corresponding IMountPoint instances.
31
+     *
32
+     * @param string $path path for which the mounts are set up
33
+     * @param IMountProviderArgs[] $mountProviderArgs
34
+     * @param IStorageFactory $loader
35
+     * @return array<string, IMountPoint> IMountPoint instances, indexed by
36
+     *                                    mount-point
37
+     */
38
+    public function getMountsForPath(
39
+        string $path,
40
+        array $mountProviderArgs,
41
+        IStorageFactory $loader,
42
+    ): array;
43 43
 }
Please login to merge, or discard this patch.
lib/public/Files/Config/IMountProviderArgs.php 1 patch
Indentation   +5 added lines, -5 removed lines patch added patch discarded remove patch
@@ -16,9 +16,9 @@
 block discarded – undo
16 16
  * @since 33.0.0
17 17
  */
18 18
 class IMountProviderArgs {
19
-	public function __construct(
20
-		public ICachedMountInfo $mountInfo,
21
-		public ICacheEntry $cacheEntry,
22
-	) {
23
-	}
19
+    public function __construct(
20
+        public ICachedMountInfo $mountInfo,
21
+        public ICacheEntry $cacheEntry,
22
+    ) {
23
+    }
24 24
 }
Please login to merge, or discard this patch.
lib/private/Files/SetupManager.php 1 patch
Indentation   +677 added lines, -677 removed lines patch added patch discarded remove patch
@@ -62,683 +62,683 @@
 block discarded – undo
62 62
 use function in_array;
63 63
 
64 64
 class SetupManager {
65
-	private bool $rootSetup = false;
66
-	// List of users for which at least one mount is setup
67
-	private array $setupUsers = [];
68
-	// List of users for which all mounts are setup
69
-	private array $setupUsersComplete = [];
70
-	/**
71
-	 * An array of provider classes that have been set up, indexed by UserUID.
72
-	 *
73
-	 * @var array<string, class-string<IMountProvider>[]>
74
-	 */
75
-	private array $setupUserMountProviders = [];
76
-	/**
77
-	 * An array of paths that have already been set up
78
-	 *
79
-	 * @var array<string, int>
80
-	 */
81
-	private array $setupMountProviderPaths = [];
82
-	private ICache $cache;
83
-	private bool $listeningForProviders;
84
-	private array $fullSetupRequired = [];
85
-	private bool $setupBuiltinWrappersDone = false;
86
-	private bool $forceFullSetup = false;
87
-	private const SETUP_WITH_CHILDREN = 1;
88
-	private const SETUP_WITHOUT_CHILDREN = 0;
89
-
90
-	public function __construct(
91
-		private IEventLogger $eventLogger,
92
-		private MountProviderCollection $mountProviderCollection,
93
-		private IMountManager $mountManager,
94
-		private IUserManager $userManager,
95
-		private IEventDispatcher $eventDispatcher,
96
-		private IUserMountCache $userMountCache,
97
-		private ILockdownManager $lockdownManager,
98
-		private IUserSession $userSession,
99
-		ICacheFactory $cacheFactory,
100
-		private LoggerInterface $logger,
101
-		private IConfig $config,
102
-		private ShareDisableChecker $shareDisableChecker,
103
-		private IAppManager $appManager,
104
-		private FileAccess $fileAccess,
105
-	) {
106
-		$this->cache = $cacheFactory->createDistributed('setupmanager::');
107
-		$this->listeningForProviders = false;
108
-		$this->forceFullSetup = $this->config->getSystemValueBool('debug.force-full-fs-setup');
109
-
110
-		$this->setupListeners();
111
-	}
112
-
113
-	private function isSetupStarted(IUser $user): bool {
114
-		return in_array($user->getUID(), $this->setupUsers, true);
115
-	}
116
-
117
-	public function isSetupComplete(IUser $user): bool {
118
-		return in_array($user->getUID(), $this->setupUsersComplete, true);
119
-	}
120
-
121
-	/**
122
-	 * Checks if a path has been cached either directly or through a full setup
123
-	 * of one of its parents.
124
-	 */
125
-	private function isPathSetup(string $path): bool {
126
-		// if the exact path was already setup with or without children
127
-		if (array_key_exists($path, $this->setupMountProviderPaths)) {
128
-			return true;
129
-		}
130
-
131
-		// or if any of the ancestors was fully setup
132
-		while (($path = dirname($path)) !== '/') {
133
-			$setupPath = $this->setupMountProviderPaths[$path] ?? null;
134
-			if ($setupPath === self::SETUP_WITH_CHILDREN) {
135
-				return true;
136
-			}
137
-		}
138
-
139
-		return false;
140
-	}
141
-
142
-	private function setupBuiltinWrappers() {
143
-		if ($this->setupBuiltinWrappersDone) {
144
-			return;
145
-		}
146
-		$this->setupBuiltinWrappersDone = true;
147
-
148
-		// load all filesystem apps before, so no setup-hook gets lost
149
-		$this->appManager->loadApps(['filesystem']);
150
-		$prevLogging = Filesystem::logWarningWhenAddingStorageWrapper(false);
151
-
152
-		Filesystem::addStorageWrapper('mount_options', function ($mountPoint, IStorage $storage, IMountPoint $mount) {
153
-			if ($storage->instanceOfStorage(Common::class)) {
154
-				$options = array_merge($mount->getOptions(), ['mount_point' => $mountPoint]);
155
-				$storage->setMountOptions($options);
156
-			}
157
-			return $storage;
158
-		});
159
-
160
-		$reSharingEnabled = Share::isResharingAllowed();
161
-		$user = $this->userSession->getUser();
162
-		$sharingEnabledForUser = $user ? !$this->shareDisableChecker->sharingDisabledForUser($user->getUID()) : true;
163
-		Filesystem::addStorageWrapper(
164
-			'sharing_mask',
165
-			function ($mountPoint, IStorage $storage, IMountPoint $mount) use ($reSharingEnabled, $sharingEnabledForUser) {
166
-				$sharingEnabledForMount = $mount->getOption('enable_sharing', true);
167
-				$isShared = $mount instanceof ISharedMountPoint;
168
-				if (!$sharingEnabledForMount || !$sharingEnabledForUser || (!$reSharingEnabled && $isShared)) {
169
-					return new PermissionsMask([
170
-						'storage' => $storage,
171
-						'mask' => Constants::PERMISSION_ALL - Constants::PERMISSION_SHARE,
172
-					]);
173
-				}
174
-				return $storage;
175
-			}
176
-		);
177
-
178
-		// install storage availability wrapper, before most other wrappers
179
-		Filesystem::addStorageWrapper('oc_availability', function ($mountPoint, IStorage $storage, IMountPoint $mount) {
180
-			$externalMount = $mount instanceof ExternalMountPoint || $mount instanceof Mount;
181
-			if ($externalMount && !$storage->isLocal()) {
182
-				return new Availability(['storage' => $storage]);
183
-			}
184
-			return $storage;
185
-		});
186
-
187
-		Filesystem::addStorageWrapper('oc_encoding', function ($mountPoint, IStorage $storage, IMountPoint $mount) {
188
-			if ($mount->getOption('encoding_compatibility', false) && !$mount instanceof SharedMount) {
189
-				return new Encoding(['storage' => $storage]);
190
-			}
191
-			return $storage;
192
-		});
193
-
194
-		$quotaIncludeExternal = $this->config->getSystemValue('quota_include_external_storage', false);
195
-		Filesystem::addStorageWrapper('oc_quota', function ($mountPoint, $storage, IMountPoint $mount) use ($quotaIncludeExternal) {
196
-			// set up quota for home storages, even for other users
197
-			// which can happen when using sharing
198
-			if ($mount instanceof HomeMountPoint) {
199
-				$user = $mount->getUser();
200
-				return new Quota(['storage' => $storage, 'quotaCallback' => function () use ($user) {
201
-					return $user->getQuotaBytes();
202
-				}, 'root' => 'files', 'include_external_storage' => $quotaIncludeExternal]);
203
-			}
204
-
205
-			return $storage;
206
-		});
207
-
208
-		Filesystem::addStorageWrapper('readonly', function ($mountPoint, IStorage $storage, IMountPoint $mount) {
209
-			/*
65
+    private bool $rootSetup = false;
66
+    // List of users for which at least one mount is setup
67
+    private array $setupUsers = [];
68
+    // List of users for which all mounts are setup
69
+    private array $setupUsersComplete = [];
70
+    /**
71
+     * An array of provider classes that have been set up, indexed by UserUID.
72
+     *
73
+     * @var array<string, class-string<IMountProvider>[]>
74
+     */
75
+    private array $setupUserMountProviders = [];
76
+    /**
77
+     * An array of paths that have already been set up
78
+     *
79
+     * @var array<string, int>
80
+     */
81
+    private array $setupMountProviderPaths = [];
82
+    private ICache $cache;
83
+    private bool $listeningForProviders;
84
+    private array $fullSetupRequired = [];
85
+    private bool $setupBuiltinWrappersDone = false;
86
+    private bool $forceFullSetup = false;
87
+    private const SETUP_WITH_CHILDREN = 1;
88
+    private const SETUP_WITHOUT_CHILDREN = 0;
89
+
90
+    public function __construct(
91
+        private IEventLogger $eventLogger,
92
+        private MountProviderCollection $mountProviderCollection,
93
+        private IMountManager $mountManager,
94
+        private IUserManager $userManager,
95
+        private IEventDispatcher $eventDispatcher,
96
+        private IUserMountCache $userMountCache,
97
+        private ILockdownManager $lockdownManager,
98
+        private IUserSession $userSession,
99
+        ICacheFactory $cacheFactory,
100
+        private LoggerInterface $logger,
101
+        private IConfig $config,
102
+        private ShareDisableChecker $shareDisableChecker,
103
+        private IAppManager $appManager,
104
+        private FileAccess $fileAccess,
105
+    ) {
106
+        $this->cache = $cacheFactory->createDistributed('setupmanager::');
107
+        $this->listeningForProviders = false;
108
+        $this->forceFullSetup = $this->config->getSystemValueBool('debug.force-full-fs-setup');
109
+
110
+        $this->setupListeners();
111
+    }
112
+
113
+    private function isSetupStarted(IUser $user): bool {
114
+        return in_array($user->getUID(), $this->setupUsers, true);
115
+    }
116
+
117
+    public function isSetupComplete(IUser $user): bool {
118
+        return in_array($user->getUID(), $this->setupUsersComplete, true);
119
+    }
120
+
121
+    /**
122
+     * Checks if a path has been cached either directly or through a full setup
123
+     * of one of its parents.
124
+     */
125
+    private function isPathSetup(string $path): bool {
126
+        // if the exact path was already setup with or without children
127
+        if (array_key_exists($path, $this->setupMountProviderPaths)) {
128
+            return true;
129
+        }
130
+
131
+        // or if any of the ancestors was fully setup
132
+        while (($path = dirname($path)) !== '/') {
133
+            $setupPath = $this->setupMountProviderPaths[$path] ?? null;
134
+            if ($setupPath === self::SETUP_WITH_CHILDREN) {
135
+                return true;
136
+            }
137
+        }
138
+
139
+        return false;
140
+    }
141
+
142
+    private function setupBuiltinWrappers() {
143
+        if ($this->setupBuiltinWrappersDone) {
144
+            return;
145
+        }
146
+        $this->setupBuiltinWrappersDone = true;
147
+
148
+        // load all filesystem apps before, so no setup-hook gets lost
149
+        $this->appManager->loadApps(['filesystem']);
150
+        $prevLogging = Filesystem::logWarningWhenAddingStorageWrapper(false);
151
+
152
+        Filesystem::addStorageWrapper('mount_options', function ($mountPoint, IStorage $storage, IMountPoint $mount) {
153
+            if ($storage->instanceOfStorage(Common::class)) {
154
+                $options = array_merge($mount->getOptions(), ['mount_point' => $mountPoint]);
155
+                $storage->setMountOptions($options);
156
+            }
157
+            return $storage;
158
+        });
159
+
160
+        $reSharingEnabled = Share::isResharingAllowed();
161
+        $user = $this->userSession->getUser();
162
+        $sharingEnabledForUser = $user ? !$this->shareDisableChecker->sharingDisabledForUser($user->getUID()) : true;
163
+        Filesystem::addStorageWrapper(
164
+            'sharing_mask',
165
+            function ($mountPoint, IStorage $storage, IMountPoint $mount) use ($reSharingEnabled, $sharingEnabledForUser) {
166
+                $sharingEnabledForMount = $mount->getOption('enable_sharing', true);
167
+                $isShared = $mount instanceof ISharedMountPoint;
168
+                if (!$sharingEnabledForMount || !$sharingEnabledForUser || (!$reSharingEnabled && $isShared)) {
169
+                    return new PermissionsMask([
170
+                        'storage' => $storage,
171
+                        'mask' => Constants::PERMISSION_ALL - Constants::PERMISSION_SHARE,
172
+                    ]);
173
+                }
174
+                return $storage;
175
+            }
176
+        );
177
+
178
+        // install storage availability wrapper, before most other wrappers
179
+        Filesystem::addStorageWrapper('oc_availability', function ($mountPoint, IStorage $storage, IMountPoint $mount) {
180
+            $externalMount = $mount instanceof ExternalMountPoint || $mount instanceof Mount;
181
+            if ($externalMount && !$storage->isLocal()) {
182
+                return new Availability(['storage' => $storage]);
183
+            }
184
+            return $storage;
185
+        });
186
+
187
+        Filesystem::addStorageWrapper('oc_encoding', function ($mountPoint, IStorage $storage, IMountPoint $mount) {
188
+            if ($mount->getOption('encoding_compatibility', false) && !$mount instanceof SharedMount) {
189
+                return new Encoding(['storage' => $storage]);
190
+            }
191
+            return $storage;
192
+        });
193
+
194
+        $quotaIncludeExternal = $this->config->getSystemValue('quota_include_external_storage', false);
195
+        Filesystem::addStorageWrapper('oc_quota', function ($mountPoint, $storage, IMountPoint $mount) use ($quotaIncludeExternal) {
196
+            // set up quota for home storages, even for other users
197
+            // which can happen when using sharing
198
+            if ($mount instanceof HomeMountPoint) {
199
+                $user = $mount->getUser();
200
+                return new Quota(['storage' => $storage, 'quotaCallback' => function () use ($user) {
201
+                    return $user->getQuotaBytes();
202
+                }, 'root' => 'files', 'include_external_storage' => $quotaIncludeExternal]);
203
+            }
204
+
205
+            return $storage;
206
+        });
207
+
208
+        Filesystem::addStorageWrapper('readonly', function ($mountPoint, IStorage $storage, IMountPoint $mount) {
209
+            /*
210 210
 			 * Do not allow any operations that modify the storage
211 211
 			 */
212
-			if ($mount->getOption('readonly', false)) {
213
-				return new PermissionsMask([
214
-					'storage' => $storage,
215
-					'mask' => Constants::PERMISSION_ALL & ~(
216
-						Constants::PERMISSION_UPDATE
217
-						| Constants::PERMISSION_CREATE
218
-						| Constants::PERMISSION_DELETE
219
-					),
220
-				]);
221
-			}
222
-			return $storage;
223
-		});
224
-
225
-		Filesystem::logWarningWhenAddingStorageWrapper($prevLogging);
226
-	}
227
-
228
-	/**
229
-	 * Setup the full filesystem for the specified user
230
-	 */
231
-	public function setupForUser(IUser $user): void {
232
-		if ($this->isSetupComplete($user)) {
233
-			return;
234
-		}
235
-		$this->setupUsersComplete[] = $user->getUID();
236
-
237
-		$this->eventLogger->start('fs:setup:user:full', 'Setup full filesystem for user');
238
-
239
-		$this->setupUserMountProviders[$user->getUID()] ??= [];
240
-		$previouslySetupProviders = $this->setupUserMountProviders[$user->getUID()];
241
-
242
-		$this->setupForUserWith($user, function () use ($user) {
243
-			$this->mountProviderCollection->addMountForUser($user, $this->mountManager, function (
244
-				string $providerClass,
245
-			) use ($user) {
246
-				return !in_array($providerClass, $this->setupUserMountProviders[$user->getUID()]);
247
-			});
248
-		});
249
-		$this->afterUserFullySetup($user, $previouslySetupProviders);
250
-		$this->eventLogger->end('fs:setup:user:full');
251
-	}
252
-
253
-	/**
254
-	 * Part of the user setup that is run only once per user.
255
-	 */
256
-	private function oneTimeUserSetup(IUser $user) {
257
-		if ($this->isSetupStarted($user)) {
258
-			return;
259
-		}
260
-		$this->setupUsers[] = $user->getUID();
261
-
262
-		$this->setupRoot();
263
-
264
-		$this->eventLogger->start('fs:setup:user:onetime', 'Onetime filesystem for user');
265
-
266
-		$this->setupBuiltinWrappers();
267
-
268
-		$prevLogging = Filesystem::logWarningWhenAddingStorageWrapper(false);
269
-
270
-		// TODO remove hook
271
-		OC_Hook::emit('OC_Filesystem', 'preSetup', ['user' => $user->getUID()]);
272
-
273
-		$event = new BeforeFileSystemSetupEvent($user);
274
-		$this->eventDispatcher->dispatchTyped($event);
275
-
276
-		Filesystem::logWarningWhenAddingStorageWrapper($prevLogging);
277
-
278
-		$userDir = '/' . $user->getUID() . '/files';
279
-
280
-		Filesystem::initInternal($userDir);
281
-
282
-		if ($this->lockdownManager->canAccessFilesystem()) {
283
-			$this->eventLogger->start('fs:setup:user:home', 'Setup home filesystem for user');
284
-			// home mounts are handled separate since we need to ensure this is mounted before we call the other mount providers
285
-			$homeMount = $this->mountProviderCollection->getHomeMountForUser($user);
286
-			$this->mountManager->addMount($homeMount);
287
-
288
-			if ($homeMount->getStorageRootId() === -1) {
289
-				$this->eventLogger->start('fs:setup:user:home:scan', 'Scan home filesystem for user');
290
-				$homeMount->getStorage()->mkdir('');
291
-				$homeMount->getStorage()->getScanner()->scan('');
292
-				$this->eventLogger->end('fs:setup:user:home:scan');
293
-			}
294
-			$this->eventLogger->end('fs:setup:user:home');
295
-		} else {
296
-			$this->mountManager->addMount(new MountPoint(
297
-				new NullStorage([]),
298
-				'/' . $user->getUID()
299
-			));
300
-			$this->mountManager->addMount(new MountPoint(
301
-				new NullStorage([]),
302
-				'/' . $user->getUID() . '/files'
303
-			));
304
-			$this->setupUsersComplete[] = $user->getUID();
305
-		}
306
-
307
-		$this->listenForNewMountProviders();
308
-
309
-		$this->eventLogger->end('fs:setup:user:onetime');
310
-	}
311
-
312
-	/**
313
-	 * Final housekeeping after a user has been fully setup
314
-	 */
315
-	private function afterUserFullySetup(IUser $user, array $previouslySetupProviders): void {
316
-		$this->eventLogger->start('fs:setup:user:full:post', 'Housekeeping after user is setup');
317
-		$userRoot = '/' . $user->getUID() . '/';
318
-		$mounts = $this->mountManager->getAll();
319
-		$mounts = array_filter($mounts, function (IMountPoint $mount) use ($userRoot) {
320
-			return str_starts_with($mount->getMountPoint(), $userRoot);
321
-		});
322
-		$allProviders = array_map(function (IMountProvider|IHomeMountProvider|IRootMountProvider $provider) {
323
-			return get_class($provider);
324
-		}, array_merge(
325
-			$this->mountProviderCollection->getProviders(),
326
-			$this->mountProviderCollection->getHomeProviders(),
327
-			$this->mountProviderCollection->getRootProviders(),
328
-		));
329
-		$newProviders = array_diff($allProviders, $previouslySetupProviders);
330
-		$mounts = array_filter($mounts, function (IMountPoint $mount) use ($previouslySetupProviders) {
331
-			return !in_array($mount->getMountProvider(), $previouslySetupProviders);
332
-		});
333
-		$this->registerMounts($user, $mounts, $newProviders);
334
-
335
-		$cacheDuration = $this->config->getSystemValueInt('fs_mount_cache_duration', 5 * 60);
336
-		if ($cacheDuration > 0) {
337
-			$this->cache->set($user->getUID(), true, $cacheDuration);
338
-			$this->fullSetupRequired[$user->getUID()] = false;
339
-		}
340
-		$this->eventLogger->end('fs:setup:user:full:post');
341
-	}
342
-
343
-	/**
344
-	 * Executes the one-time user setup and, if the user can access the
345
-	 * filesystem, executes $mountCallback.
346
-	 *
347
-	 * @param IUser $user
348
-	 * @param IMountPoint $mounts
349
-	 * @return void
350
-	 * @throws \OCP\HintException
351
-	 * @throws \OC\ServerNotAvailableException
352
-	 * @see self::oneTimeUserSetup()
353
-	 *
354
-	 */
355
-	private function setupForUserWith(IUser $user, callable $mountCallback): void {
356
-		$this->oneTimeUserSetup($user);
357
-
358
-		if ($this->lockdownManager->canAccessFilesystem()) {
359
-			$mountCallback();
360
-		}
361
-		$this->eventLogger->start('fs:setup:user:post-init-mountpoint', 'post_initMountPoints legacy hook');
362
-		\OC_Hook::emit('OC_Filesystem', 'post_initMountPoints', ['user' => $user->getUID()]);
363
-		$this->eventLogger->end('fs:setup:user:post-init-mountpoint');
364
-
365
-		$userDir = '/' . $user->getUID() . '/files';
366
-		$this->eventLogger->start('fs:setup:user:setup-hook', 'setup legacy hook');
367
-		OC_Hook::emit('OC_Filesystem', 'setup', ['user' => $user->getUID(), 'user_dir' => $userDir]);
368
-		$this->eventLogger->end('fs:setup:user:setup-hook');
369
-	}
370
-
371
-	/**
372
-	 * Set up the root filesystem
373
-	 */
374
-	public function setupRoot(): void {
375
-		//setting up the filesystem twice can only lead to trouble
376
-		if ($this->rootSetup) {
377
-			return;
378
-		}
379
-
380
-		$this->setupBuiltinWrappers();
381
-
382
-		$this->rootSetup = true;
383
-
384
-		$this->eventLogger->start('fs:setup:root', 'Setup root filesystem');
385
-
386
-		$rootMounts = $this->mountProviderCollection->getRootMounts();
387
-		foreach ($rootMounts as $rootMountProvider) {
388
-			$this->mountManager->addMount($rootMountProvider);
389
-		}
390
-
391
-		$this->eventLogger->end('fs:setup:root');
392
-	}
393
-
394
-	/**
395
-	 * Get the user to setup for a path or `null` if the root needs to be setup
396
-	 *
397
-	 * @param string $path
398
-	 * @return IUser|null
399
-	 */
400
-	private function getUserForPath(string $path) {
401
-		if (str_starts_with($path, '/__groupfolders')) {
402
-			return null;
403
-		} elseif (substr_count($path, '/') < 2) {
404
-			if ($user = $this->userSession->getUser()) {
405
-				return $user;
406
-			} else {
407
-				return null;
408
-			}
409
-		} elseif (str_starts_with($path, '/appdata_' . \OC_Util::getInstanceId()) || str_starts_with($path, '/files_external/')) {
410
-			return null;
411
-		} else {
412
-			[, $userId] = explode('/', $path);
413
-		}
414
-
415
-		return $this->userManager->get($userId);
416
-	}
417
-
418
-	/**
419
-	 * Set up the filesystem for the specified path, optionally including all
420
-	 * children mounts.
421
-	 */
422
-	public function setupForPath(string $path, bool $includeChildren = false): void {
423
-		$user = $this->getUserForPath($path);
424
-		if (!$user) {
425
-			$this->setupRoot();
426
-			return;
427
-		}
428
-
429
-		if ($this->isSetupComplete($user)) {
430
-			return;
431
-		}
432
-
433
-		if ($this->fullSetupRequired($user)) {
434
-			$this->setupForUser($user);
435
-			return;
436
-		}
437
-
438
-		// for the user's home folder, and includes children we need everything always
439
-		if (rtrim($path) === '/' . $user->getUID() . '/files' && $includeChildren) {
440
-			$this->setupForUser($user);
441
-			return;
442
-		}
443
-
444
-		if (!isset($this->setupUserMountProviders[$user->getUID()])) {
445
-			$this->setupUserMountProviders[$user->getUID()] = [];
446
-		}
447
-		$setupProviders = &$this->setupUserMountProviders[$user->getUID()];
448
-		$currentProviders = [];
449
-
450
-		try {
451
-			$cachedMount = $this->userMountCache->getMountForPath($user, $path);
452
-		} catch (NotFoundException $e) {
453
-			$this->setupForUser($user);
454
-			return;
455
-		}
456
-
457
-		$this->oneTimeUserSetup($user);
458
-
459
-		$this->eventLogger->start('fs:setup:user:path', "Setup $path filesystem for user");
460
-		$this->eventLogger->start('fs:setup:user:path:find', "Find mountpoint for $path");
461
-
462
-		$fullProviderMounts = [];
463
-		$authoritativeMounts = [];
464
-
465
-		$mountProvider = $cachedMount->getMountProvider();
466
-		$mountPoint = $cachedMount->getMountPoint();
467
-		$isMountProviderSetup = in_array($mountProvider, $setupProviders);
468
-		$isPathSetupAsAuthoritative
469
-			= $this->isPathSetup($mountPoint);
470
-		if (!$isMountProviderSetup && !$isPathSetupAsAuthoritative) {
471
-			if ($mountProvider === '') {
472
-				$this->logger->debug('mount at ' . $cachedMount->getMountPoint() . ' has no provider set, performing full setup');
473
-				$this->eventLogger->end('fs:setup:user:path:find');
474
-				$this->setupForUser($user);
475
-				$this->eventLogger->end('fs:setup:user:path');
476
-				return;
477
-			}
478
-
479
-			if (is_a($mountProvider, IPartialMountProvider::class, true)) {
480
-				$rootId = $cachedMount->getRootId();
481
-				$rootMetadata = $this->fileAccess->getByFileId($rootId);
482
-				$providerArgs = new IMountProviderArgs($cachedMount, $rootMetadata);
483
-				// mark the path as cached (without children for now...)
484
-				$cacheKey = rtrim($mountPoint, '/');
485
-				$this->setupMountProviderPaths[$cacheKey] = self::SETUP_WITHOUT_CHILDREN;
486
-				$authoritativeMounts[] = array_values(
487
-					$this->mountProviderCollection->getUserMountsFromProviderByPath(
488
-						$mountProvider,
489
-						$path,
490
-						[$providerArgs]
491
-					)
492
-				);
493
-			} else {
494
-				$currentProviders[] = $mountProvider;
495
-				$setupProviders[] = $mountProvider;
496
-				$fullProviderMounts[]
497
-					= $this->mountProviderCollection->getUserMountsForProviderClasses($user, [$mountProvider]);
498
-			}
499
-		}
500
-
501
-		if ($includeChildren) {
502
-			$subCachedMounts = $this->userMountCache->getMountsInPath($user, $path);
503
-			$this->eventLogger->end('fs:setup:user:path:find');
504
-
505
-			$needsFullSetup
506
-				= array_any(
507
-					$subCachedMounts,
508
-					fn (ICachedMountInfo $info) => $info->getMountProvider() === ''
509
-				);
510
-
511
-			if ($needsFullSetup) {
512
-				$this->logger->debug('mount has no provider set, performing full setup');
513
-				$this->setupForUser($user);
514
-				$this->eventLogger->end('fs:setup:user:path');
515
-				return;
516
-			}
517
-
518
-			/** @var array<class-string<IMountProvider>, ICachedMountInfo[]> $authoritativeCachedMounts */
519
-			$authoritativeCachedMounts = [];
520
-			foreach ($subCachedMounts as $cachedMount) {
521
-				/** @var class-string<IMountProvider> $mountProvider */
522
-				$mountProvider = $cachedMount->getMountProvider();
523
-
524
-				// skip setup for already set up providers
525
-				if (in_array($mountProvider, $setupProviders)) {
526
-					continue;
527
-				}
528
-
529
-				if (is_a($mountProvider, IPartialMountProvider::class, true)) {
530
-					// skip setup if path was set up as authoritative before
531
-					if ($this->isPathSetup($cachedMount->getMountPoint())) {
532
-						continue;
533
-					}
534
-					// collect cached mount points for authoritative providers
535
-					$authoritativeCachedMounts[$mountProvider] ??= [];
536
-					$authoritativeCachedMounts[$mountProvider][] = $cachedMount;
537
-					continue;
538
-				}
539
-
540
-				$currentProviders[] = $mountProvider;
541
-				$setupProviders[] = $mountProvider;
542
-				$fullProviderMounts[]
543
-					= $this->mountProviderCollection->getUserMountsForProviderClasses(
544
-						$user,
545
-						[$mountProvider]
546
-					);
547
-			}
548
-
549
-			if (!empty($authoritativeCachedMounts)) {
550
-				$rootIds = array_map(
551
-					fn (ICachedMountInfo $mount) => $mount->getRootId(),
552
-					array_merge(...array_values($authoritativeCachedMounts)),
553
-				);
554
-
555
-				$rootsMetadata = [];
556
-				foreach (array_chunk($rootIds, 1000) as $chunk) {
557
-					foreach ($this->fileAccess->getByFileIds($chunk) as $id => $fileMetadata) {
558
-						$rootsMetadata[$id] = $fileMetadata;
559
-					}
560
-				}
561
-				$cacheKey = rtrim($mountPoint, '/');
562
-				$this->setupMountProviderPaths[$cacheKey] = self::SETUP_WITH_CHILDREN;
563
-				foreach ($authoritativeCachedMounts as $providerClass => $cachedMounts) {
564
-					$providerArgs = array_filter(array_map(
565
-						static function (ICachedMountInfo $info) use ($rootsMetadata) {
566
-							$rootMetadata = $rootsMetadata[$info->getRootId()] ?? null;
567
-
568
-							return $rootMetadata
569
-								? new IMountProviderArgs($info, $rootMetadata)
570
-								: null;
571
-						},
572
-						$cachedMounts
573
-					));
574
-					$authoritativeMounts[]
575
-						= $this->mountProviderCollection->getUserMountsFromProviderByPath(
576
-							$providerClass,
577
-							$path,
578
-							$providerArgs,
579
-						);
580
-				}
581
-			}
582
-		} else {
583
-			$this->eventLogger->end('fs:setup:user:path:find');
584
-		}
585
-
586
-		$fullProviderMounts = array_merge(...$fullProviderMounts);
587
-		$authoritativeMounts = array_merge(...$authoritativeMounts);
588
-
589
-		if (count($fullProviderMounts) || count($authoritativeMounts)) {
590
-			if (count($fullProviderMounts)) {
591
-				$this->registerMounts($user, $fullProviderMounts, $currentProviders);
592
-			}
593
-
594
-			$this->setupForUserWith($user, function () use ($fullProviderMounts, $authoritativeMounts) {
595
-				$allMounts = [...$fullProviderMounts, ...$authoritativeMounts];
596
-				array_walk($allMounts, $this->mountManager->addMount(...));
597
-			});
598
-		} elseif (!$this->isSetupStarted($user)) {
599
-			$this->oneTimeUserSetup($user);
600
-		}
601
-		$this->eventLogger->end('fs:setup:user:path');
602
-	}
603
-
604
-	private function fullSetupRequired(IUser $user): bool {
605
-		if ($this->forceFullSetup) {
606
-			return true;
607
-		}
608
-
609
-		// we perform a "cached" setup only after having done the full setup recently
610
-		// this is also used to trigger a full setup after handling events that are likely
611
-		// to change the available mounts
612
-		if (!isset($this->fullSetupRequired[$user->getUID()])) {
613
-			$this->fullSetupRequired[$user->getUID()] = !$this->cache->get($user->getUID());
614
-		}
615
-		return $this->fullSetupRequired[$user->getUID()];
616
-	}
617
-
618
-	/**
619
-	 * @param string $path
620
-	 * @param string[] $providers
621
-	 */
622
-	public function setupForProvider(string $path, array $providers): void {
623
-		$user = $this->getUserForPath($path);
624
-		if (!$user) {
625
-			$this->setupRoot();
626
-			return;
627
-		}
628
-
629
-		if ($this->isSetupComplete($user)) {
630
-			return;
631
-		}
632
-
633
-		if ($this->fullSetupRequired($user)) {
634
-			$this->setupForUser($user);
635
-			return;
636
-		}
637
-
638
-		$this->eventLogger->start('fs:setup:user:providers', 'Setup filesystem for ' . implode(', ', $providers));
639
-
640
-		$this->oneTimeUserSetup($user);
641
-
642
-		// home providers are always used
643
-		$providers = array_filter($providers, function (string $provider) {
644
-			return !is_subclass_of($provider, IHomeMountProvider::class);
645
-		});
646
-
647
-		if (in_array('', $providers)) {
648
-			$this->setupForUser($user);
649
-			return;
650
-		}
651
-		$setupProviders = $this->setupUserMountProviders[$user->getUID()] ?? [];
652
-
653
-		$providers = array_diff($providers, $setupProviders);
654
-		if (count($providers) === 0) {
655
-			if (!$this->isSetupStarted($user)) {
656
-				$this->oneTimeUserSetup($user);
657
-			}
658
-			$this->eventLogger->end('fs:setup:user:providers');
659
-			return;
660
-		} else {
661
-			$this->setupUserMountProviders[$user->getUID()] = array_merge($setupProviders, $providers);
662
-			$mounts = $this->mountProviderCollection->getUserMountsForProviderClasses($user, $providers);
663
-		}
664
-
665
-		$this->registerMounts($user, $mounts, $providers);
666
-		$this->setupForUserWith($user, function () use ($mounts) {
667
-			array_walk($mounts, [$this->mountManager, 'addMount']);
668
-		});
669
-		$this->eventLogger->end('fs:setup:user:providers');
670
-	}
671
-
672
-	public function tearDown() {
673
-		$this->setupUsers = [];
674
-		$this->setupUsersComplete = [];
675
-		$this->setupUserMountProviders = [];
676
-		$this->setupMountProviderPaths = [];
677
-		$this->fullSetupRequired = [];
678
-		$this->rootSetup = false;
679
-		$this->mountManager->clear();
680
-		$this->eventDispatcher->dispatchTyped(new FilesystemTornDownEvent());
681
-	}
682
-
683
-	/**
684
-	 * Get mounts from mount providers that are registered after setup
685
-	 */
686
-	private function listenForNewMountProviders() {
687
-		if (!$this->listeningForProviders) {
688
-			$this->listeningForProviders = true;
689
-			$this->mountProviderCollection->listen('\OC\Files\Config', 'registerMountProvider', function (
690
-				IMountProvider $provider,
691
-			) {
692
-				foreach ($this->setupUsers as $userId) {
693
-					$user = $this->userManager->get($userId);
694
-					if ($user) {
695
-						$mounts = $provider->getMountsForUser($user, Filesystem::getLoader());
696
-						array_walk($mounts, [$this->mountManager, 'addMount']);
697
-					}
698
-				}
699
-			});
700
-		}
701
-	}
702
-
703
-	private function setupListeners() {
704
-		// note that this event handling is intentionally pessimistic
705
-		// clearing the cache to often is better than not enough
706
-
707
-		$this->eventDispatcher->addListener(UserAddedEvent::class, function (UserAddedEvent $event) {
708
-			$this->cache->remove($event->getUser()->getUID());
709
-		});
710
-		$this->eventDispatcher->addListener(UserRemovedEvent::class, function (UserRemovedEvent $event) {
711
-			$this->cache->remove($event->getUser()->getUID());
712
-		});
713
-		$this->eventDispatcher->addListener(ShareCreatedEvent::class, function (ShareCreatedEvent $event) {
714
-			$this->cache->remove($event->getShare()->getSharedWith());
715
-		});
716
-		$this->eventDispatcher->addListener(InvalidateMountCacheEvent::class, function (InvalidateMountCacheEvent $event,
717
-		) {
718
-			if ($user = $event->getUser()) {
719
-				$this->cache->remove($user->getUID());
720
-			} else {
721
-				$this->cache->clear();
722
-			}
723
-		});
724
-
725
-		$genericEvents = [
726
-			'OCA\Circles\Events\CreatingCircleEvent',
727
-			'OCA\Circles\Events\DestroyingCircleEvent',
728
-			'OCA\Circles\Events\AddingCircleMemberEvent',
729
-			'OCA\Circles\Events\RemovingCircleMemberEvent',
730
-		];
731
-
732
-		foreach ($genericEvents as $genericEvent) {
733
-			$this->eventDispatcher->addListener($genericEvent, function ($event) {
734
-				$this->cache->clear();
735
-			});
736
-		}
737
-	}
738
-
739
-	private function registerMounts(IUser $user, array $mounts, ?array $mountProviderClasses = null): void {
740
-		if ($this->lockdownManager->canAccessFilesystem()) {
741
-			$this->userMountCache->registerMounts($user, $mounts, $mountProviderClasses);
742
-		}
743
-	}
212
+            if ($mount->getOption('readonly', false)) {
213
+                return new PermissionsMask([
214
+                    'storage' => $storage,
215
+                    'mask' => Constants::PERMISSION_ALL & ~(
216
+                        Constants::PERMISSION_UPDATE
217
+                        | Constants::PERMISSION_CREATE
218
+                        | Constants::PERMISSION_DELETE
219
+                    ),
220
+                ]);
221
+            }
222
+            return $storage;
223
+        });
224
+
225
+        Filesystem::logWarningWhenAddingStorageWrapper($prevLogging);
226
+    }
227
+
228
+    /**
229
+     * Setup the full filesystem for the specified user
230
+     */
231
+    public function setupForUser(IUser $user): void {
232
+        if ($this->isSetupComplete($user)) {
233
+            return;
234
+        }
235
+        $this->setupUsersComplete[] = $user->getUID();
236
+
237
+        $this->eventLogger->start('fs:setup:user:full', 'Setup full filesystem for user');
238
+
239
+        $this->setupUserMountProviders[$user->getUID()] ??= [];
240
+        $previouslySetupProviders = $this->setupUserMountProviders[$user->getUID()];
241
+
242
+        $this->setupForUserWith($user, function () use ($user) {
243
+            $this->mountProviderCollection->addMountForUser($user, $this->mountManager, function (
244
+                string $providerClass,
245
+            ) use ($user) {
246
+                return !in_array($providerClass, $this->setupUserMountProviders[$user->getUID()]);
247
+            });
248
+        });
249
+        $this->afterUserFullySetup($user, $previouslySetupProviders);
250
+        $this->eventLogger->end('fs:setup:user:full');
251
+    }
252
+
253
+    /**
254
+     * Part of the user setup that is run only once per user.
255
+     */
256
+    private function oneTimeUserSetup(IUser $user) {
257
+        if ($this->isSetupStarted($user)) {
258
+            return;
259
+        }
260
+        $this->setupUsers[] = $user->getUID();
261
+
262
+        $this->setupRoot();
263
+
264
+        $this->eventLogger->start('fs:setup:user:onetime', 'Onetime filesystem for user');
265
+
266
+        $this->setupBuiltinWrappers();
267
+
268
+        $prevLogging = Filesystem::logWarningWhenAddingStorageWrapper(false);
269
+
270
+        // TODO remove hook
271
+        OC_Hook::emit('OC_Filesystem', 'preSetup', ['user' => $user->getUID()]);
272
+
273
+        $event = new BeforeFileSystemSetupEvent($user);
274
+        $this->eventDispatcher->dispatchTyped($event);
275
+
276
+        Filesystem::logWarningWhenAddingStorageWrapper($prevLogging);
277
+
278
+        $userDir = '/' . $user->getUID() . '/files';
279
+
280
+        Filesystem::initInternal($userDir);
281
+
282
+        if ($this->lockdownManager->canAccessFilesystem()) {
283
+            $this->eventLogger->start('fs:setup:user:home', 'Setup home filesystem for user');
284
+            // home mounts are handled separate since we need to ensure this is mounted before we call the other mount providers
285
+            $homeMount = $this->mountProviderCollection->getHomeMountForUser($user);
286
+            $this->mountManager->addMount($homeMount);
287
+
288
+            if ($homeMount->getStorageRootId() === -1) {
289
+                $this->eventLogger->start('fs:setup:user:home:scan', 'Scan home filesystem for user');
290
+                $homeMount->getStorage()->mkdir('');
291
+                $homeMount->getStorage()->getScanner()->scan('');
292
+                $this->eventLogger->end('fs:setup:user:home:scan');
293
+            }
294
+            $this->eventLogger->end('fs:setup:user:home');
295
+        } else {
296
+            $this->mountManager->addMount(new MountPoint(
297
+                new NullStorage([]),
298
+                '/' . $user->getUID()
299
+            ));
300
+            $this->mountManager->addMount(new MountPoint(
301
+                new NullStorage([]),
302
+                '/' . $user->getUID() . '/files'
303
+            ));
304
+            $this->setupUsersComplete[] = $user->getUID();
305
+        }
306
+
307
+        $this->listenForNewMountProviders();
308
+
309
+        $this->eventLogger->end('fs:setup:user:onetime');
310
+    }
311
+
312
+    /**
313
+     * Final housekeeping after a user has been fully setup
314
+     */
315
+    private function afterUserFullySetup(IUser $user, array $previouslySetupProviders): void {
316
+        $this->eventLogger->start('fs:setup:user:full:post', 'Housekeeping after user is setup');
317
+        $userRoot = '/' . $user->getUID() . '/';
318
+        $mounts = $this->mountManager->getAll();
319
+        $mounts = array_filter($mounts, function (IMountPoint $mount) use ($userRoot) {
320
+            return str_starts_with($mount->getMountPoint(), $userRoot);
321
+        });
322
+        $allProviders = array_map(function (IMountProvider|IHomeMountProvider|IRootMountProvider $provider) {
323
+            return get_class($provider);
324
+        }, array_merge(
325
+            $this->mountProviderCollection->getProviders(),
326
+            $this->mountProviderCollection->getHomeProviders(),
327
+            $this->mountProviderCollection->getRootProviders(),
328
+        ));
329
+        $newProviders = array_diff($allProviders, $previouslySetupProviders);
330
+        $mounts = array_filter($mounts, function (IMountPoint $mount) use ($previouslySetupProviders) {
331
+            return !in_array($mount->getMountProvider(), $previouslySetupProviders);
332
+        });
333
+        $this->registerMounts($user, $mounts, $newProviders);
334
+
335
+        $cacheDuration = $this->config->getSystemValueInt('fs_mount_cache_duration', 5 * 60);
336
+        if ($cacheDuration > 0) {
337
+            $this->cache->set($user->getUID(), true, $cacheDuration);
338
+            $this->fullSetupRequired[$user->getUID()] = false;
339
+        }
340
+        $this->eventLogger->end('fs:setup:user:full:post');
341
+    }
342
+
343
+    /**
344
+     * Executes the one-time user setup and, if the user can access the
345
+     * filesystem, executes $mountCallback.
346
+     *
347
+     * @param IUser $user
348
+     * @param IMountPoint $mounts
349
+     * @return void
350
+     * @throws \OCP\HintException
351
+     * @throws \OC\ServerNotAvailableException
352
+     * @see self::oneTimeUserSetup()
353
+     *
354
+     */
355
+    private function setupForUserWith(IUser $user, callable $mountCallback): void {
356
+        $this->oneTimeUserSetup($user);
357
+
358
+        if ($this->lockdownManager->canAccessFilesystem()) {
359
+            $mountCallback();
360
+        }
361
+        $this->eventLogger->start('fs:setup:user:post-init-mountpoint', 'post_initMountPoints legacy hook');
362
+        \OC_Hook::emit('OC_Filesystem', 'post_initMountPoints', ['user' => $user->getUID()]);
363
+        $this->eventLogger->end('fs:setup:user:post-init-mountpoint');
364
+
365
+        $userDir = '/' . $user->getUID() . '/files';
366
+        $this->eventLogger->start('fs:setup:user:setup-hook', 'setup legacy hook');
367
+        OC_Hook::emit('OC_Filesystem', 'setup', ['user' => $user->getUID(), 'user_dir' => $userDir]);
368
+        $this->eventLogger->end('fs:setup:user:setup-hook');
369
+    }
370
+
371
+    /**
372
+     * Set up the root filesystem
373
+     */
374
+    public function setupRoot(): void {
375
+        //setting up the filesystem twice can only lead to trouble
376
+        if ($this->rootSetup) {
377
+            return;
378
+        }
379
+
380
+        $this->setupBuiltinWrappers();
381
+
382
+        $this->rootSetup = true;
383
+
384
+        $this->eventLogger->start('fs:setup:root', 'Setup root filesystem');
385
+
386
+        $rootMounts = $this->mountProviderCollection->getRootMounts();
387
+        foreach ($rootMounts as $rootMountProvider) {
388
+            $this->mountManager->addMount($rootMountProvider);
389
+        }
390
+
391
+        $this->eventLogger->end('fs:setup:root');
392
+    }
393
+
394
+    /**
395
+     * Get the user to setup for a path or `null` if the root needs to be setup
396
+     *
397
+     * @param string $path
398
+     * @return IUser|null
399
+     */
400
+    private function getUserForPath(string $path) {
401
+        if (str_starts_with($path, '/__groupfolders')) {
402
+            return null;
403
+        } elseif (substr_count($path, '/') < 2) {
404
+            if ($user = $this->userSession->getUser()) {
405
+                return $user;
406
+            } else {
407
+                return null;
408
+            }
409
+        } elseif (str_starts_with($path, '/appdata_' . \OC_Util::getInstanceId()) || str_starts_with($path, '/files_external/')) {
410
+            return null;
411
+        } else {
412
+            [, $userId] = explode('/', $path);
413
+        }
414
+
415
+        return $this->userManager->get($userId);
416
+    }
417
+
418
+    /**
419
+     * Set up the filesystem for the specified path, optionally including all
420
+     * children mounts.
421
+     */
422
+    public function setupForPath(string $path, bool $includeChildren = false): void {
423
+        $user = $this->getUserForPath($path);
424
+        if (!$user) {
425
+            $this->setupRoot();
426
+            return;
427
+        }
428
+
429
+        if ($this->isSetupComplete($user)) {
430
+            return;
431
+        }
432
+
433
+        if ($this->fullSetupRequired($user)) {
434
+            $this->setupForUser($user);
435
+            return;
436
+        }
437
+
438
+        // for the user's home folder, and includes children we need everything always
439
+        if (rtrim($path) === '/' . $user->getUID() . '/files' && $includeChildren) {
440
+            $this->setupForUser($user);
441
+            return;
442
+        }
443
+
444
+        if (!isset($this->setupUserMountProviders[$user->getUID()])) {
445
+            $this->setupUserMountProviders[$user->getUID()] = [];
446
+        }
447
+        $setupProviders = &$this->setupUserMountProviders[$user->getUID()];
448
+        $currentProviders = [];
449
+
450
+        try {
451
+            $cachedMount = $this->userMountCache->getMountForPath($user, $path);
452
+        } catch (NotFoundException $e) {
453
+            $this->setupForUser($user);
454
+            return;
455
+        }
456
+
457
+        $this->oneTimeUserSetup($user);
458
+
459
+        $this->eventLogger->start('fs:setup:user:path', "Setup $path filesystem for user");
460
+        $this->eventLogger->start('fs:setup:user:path:find', "Find mountpoint for $path");
461
+
462
+        $fullProviderMounts = [];
463
+        $authoritativeMounts = [];
464
+
465
+        $mountProvider = $cachedMount->getMountProvider();
466
+        $mountPoint = $cachedMount->getMountPoint();
467
+        $isMountProviderSetup = in_array($mountProvider, $setupProviders);
468
+        $isPathSetupAsAuthoritative
469
+            = $this->isPathSetup($mountPoint);
470
+        if (!$isMountProviderSetup && !$isPathSetupAsAuthoritative) {
471
+            if ($mountProvider === '') {
472
+                $this->logger->debug('mount at ' . $cachedMount->getMountPoint() . ' has no provider set, performing full setup');
473
+                $this->eventLogger->end('fs:setup:user:path:find');
474
+                $this->setupForUser($user);
475
+                $this->eventLogger->end('fs:setup:user:path');
476
+                return;
477
+            }
478
+
479
+            if (is_a($mountProvider, IPartialMountProvider::class, true)) {
480
+                $rootId = $cachedMount->getRootId();
481
+                $rootMetadata = $this->fileAccess->getByFileId($rootId);
482
+                $providerArgs = new IMountProviderArgs($cachedMount, $rootMetadata);
483
+                // mark the path as cached (without children for now...)
484
+                $cacheKey = rtrim($mountPoint, '/');
485
+                $this->setupMountProviderPaths[$cacheKey] = self::SETUP_WITHOUT_CHILDREN;
486
+                $authoritativeMounts[] = array_values(
487
+                    $this->mountProviderCollection->getUserMountsFromProviderByPath(
488
+                        $mountProvider,
489
+                        $path,
490
+                        [$providerArgs]
491
+                    )
492
+                );
493
+            } else {
494
+                $currentProviders[] = $mountProvider;
495
+                $setupProviders[] = $mountProvider;
496
+                $fullProviderMounts[]
497
+                    = $this->mountProviderCollection->getUserMountsForProviderClasses($user, [$mountProvider]);
498
+            }
499
+        }
500
+
501
+        if ($includeChildren) {
502
+            $subCachedMounts = $this->userMountCache->getMountsInPath($user, $path);
503
+            $this->eventLogger->end('fs:setup:user:path:find');
504
+
505
+            $needsFullSetup
506
+                = array_any(
507
+                    $subCachedMounts,
508
+                    fn (ICachedMountInfo $info) => $info->getMountProvider() === ''
509
+                );
510
+
511
+            if ($needsFullSetup) {
512
+                $this->logger->debug('mount has no provider set, performing full setup');
513
+                $this->setupForUser($user);
514
+                $this->eventLogger->end('fs:setup:user:path');
515
+                return;
516
+            }
517
+
518
+            /** @var array<class-string<IMountProvider>, ICachedMountInfo[]> $authoritativeCachedMounts */
519
+            $authoritativeCachedMounts = [];
520
+            foreach ($subCachedMounts as $cachedMount) {
521
+                /** @var class-string<IMountProvider> $mountProvider */
522
+                $mountProvider = $cachedMount->getMountProvider();
523
+
524
+                // skip setup for already set up providers
525
+                if (in_array($mountProvider, $setupProviders)) {
526
+                    continue;
527
+                }
528
+
529
+                if (is_a($mountProvider, IPartialMountProvider::class, true)) {
530
+                    // skip setup if path was set up as authoritative before
531
+                    if ($this->isPathSetup($cachedMount->getMountPoint())) {
532
+                        continue;
533
+                    }
534
+                    // collect cached mount points for authoritative providers
535
+                    $authoritativeCachedMounts[$mountProvider] ??= [];
536
+                    $authoritativeCachedMounts[$mountProvider][] = $cachedMount;
537
+                    continue;
538
+                }
539
+
540
+                $currentProviders[] = $mountProvider;
541
+                $setupProviders[] = $mountProvider;
542
+                $fullProviderMounts[]
543
+                    = $this->mountProviderCollection->getUserMountsForProviderClasses(
544
+                        $user,
545
+                        [$mountProvider]
546
+                    );
547
+            }
548
+
549
+            if (!empty($authoritativeCachedMounts)) {
550
+                $rootIds = array_map(
551
+                    fn (ICachedMountInfo $mount) => $mount->getRootId(),
552
+                    array_merge(...array_values($authoritativeCachedMounts)),
553
+                );
554
+
555
+                $rootsMetadata = [];
556
+                foreach (array_chunk($rootIds, 1000) as $chunk) {
557
+                    foreach ($this->fileAccess->getByFileIds($chunk) as $id => $fileMetadata) {
558
+                        $rootsMetadata[$id] = $fileMetadata;
559
+                    }
560
+                }
561
+                $cacheKey = rtrim($mountPoint, '/');
562
+                $this->setupMountProviderPaths[$cacheKey] = self::SETUP_WITH_CHILDREN;
563
+                foreach ($authoritativeCachedMounts as $providerClass => $cachedMounts) {
564
+                    $providerArgs = array_filter(array_map(
565
+                        static function (ICachedMountInfo $info) use ($rootsMetadata) {
566
+                            $rootMetadata = $rootsMetadata[$info->getRootId()] ?? null;
567
+
568
+                            return $rootMetadata
569
+                                ? new IMountProviderArgs($info, $rootMetadata)
570
+                                : null;
571
+                        },
572
+                        $cachedMounts
573
+                    ));
574
+                    $authoritativeMounts[]
575
+                        = $this->mountProviderCollection->getUserMountsFromProviderByPath(
576
+                            $providerClass,
577
+                            $path,
578
+                            $providerArgs,
579
+                        );
580
+                }
581
+            }
582
+        } else {
583
+            $this->eventLogger->end('fs:setup:user:path:find');
584
+        }
585
+
586
+        $fullProviderMounts = array_merge(...$fullProviderMounts);
587
+        $authoritativeMounts = array_merge(...$authoritativeMounts);
588
+
589
+        if (count($fullProviderMounts) || count($authoritativeMounts)) {
590
+            if (count($fullProviderMounts)) {
591
+                $this->registerMounts($user, $fullProviderMounts, $currentProviders);
592
+            }
593
+
594
+            $this->setupForUserWith($user, function () use ($fullProviderMounts, $authoritativeMounts) {
595
+                $allMounts = [...$fullProviderMounts, ...$authoritativeMounts];
596
+                array_walk($allMounts, $this->mountManager->addMount(...));
597
+            });
598
+        } elseif (!$this->isSetupStarted($user)) {
599
+            $this->oneTimeUserSetup($user);
600
+        }
601
+        $this->eventLogger->end('fs:setup:user:path');
602
+    }
603
+
604
+    private function fullSetupRequired(IUser $user): bool {
605
+        if ($this->forceFullSetup) {
606
+            return true;
607
+        }
608
+
609
+        // we perform a "cached" setup only after having done the full setup recently
610
+        // this is also used to trigger a full setup after handling events that are likely
611
+        // to change the available mounts
612
+        if (!isset($this->fullSetupRequired[$user->getUID()])) {
613
+            $this->fullSetupRequired[$user->getUID()] = !$this->cache->get($user->getUID());
614
+        }
615
+        return $this->fullSetupRequired[$user->getUID()];
616
+    }
617
+
618
+    /**
619
+     * @param string $path
620
+     * @param string[] $providers
621
+     */
622
+    public function setupForProvider(string $path, array $providers): void {
623
+        $user = $this->getUserForPath($path);
624
+        if (!$user) {
625
+            $this->setupRoot();
626
+            return;
627
+        }
628
+
629
+        if ($this->isSetupComplete($user)) {
630
+            return;
631
+        }
632
+
633
+        if ($this->fullSetupRequired($user)) {
634
+            $this->setupForUser($user);
635
+            return;
636
+        }
637
+
638
+        $this->eventLogger->start('fs:setup:user:providers', 'Setup filesystem for ' . implode(', ', $providers));
639
+
640
+        $this->oneTimeUserSetup($user);
641
+
642
+        // home providers are always used
643
+        $providers = array_filter($providers, function (string $provider) {
644
+            return !is_subclass_of($provider, IHomeMountProvider::class);
645
+        });
646
+
647
+        if (in_array('', $providers)) {
648
+            $this->setupForUser($user);
649
+            return;
650
+        }
651
+        $setupProviders = $this->setupUserMountProviders[$user->getUID()] ?? [];
652
+
653
+        $providers = array_diff($providers, $setupProviders);
654
+        if (count($providers) === 0) {
655
+            if (!$this->isSetupStarted($user)) {
656
+                $this->oneTimeUserSetup($user);
657
+            }
658
+            $this->eventLogger->end('fs:setup:user:providers');
659
+            return;
660
+        } else {
661
+            $this->setupUserMountProviders[$user->getUID()] = array_merge($setupProviders, $providers);
662
+            $mounts = $this->mountProviderCollection->getUserMountsForProviderClasses($user, $providers);
663
+        }
664
+
665
+        $this->registerMounts($user, $mounts, $providers);
666
+        $this->setupForUserWith($user, function () use ($mounts) {
667
+            array_walk($mounts, [$this->mountManager, 'addMount']);
668
+        });
669
+        $this->eventLogger->end('fs:setup:user:providers');
670
+    }
671
+
672
+    public function tearDown() {
673
+        $this->setupUsers = [];
674
+        $this->setupUsersComplete = [];
675
+        $this->setupUserMountProviders = [];
676
+        $this->setupMountProviderPaths = [];
677
+        $this->fullSetupRequired = [];
678
+        $this->rootSetup = false;
679
+        $this->mountManager->clear();
680
+        $this->eventDispatcher->dispatchTyped(new FilesystemTornDownEvent());
681
+    }
682
+
683
+    /**
684
+     * Get mounts from mount providers that are registered after setup
685
+     */
686
+    private function listenForNewMountProviders() {
687
+        if (!$this->listeningForProviders) {
688
+            $this->listeningForProviders = true;
689
+            $this->mountProviderCollection->listen('\OC\Files\Config', 'registerMountProvider', function (
690
+                IMountProvider $provider,
691
+            ) {
692
+                foreach ($this->setupUsers as $userId) {
693
+                    $user = $this->userManager->get($userId);
694
+                    if ($user) {
695
+                        $mounts = $provider->getMountsForUser($user, Filesystem::getLoader());
696
+                        array_walk($mounts, [$this->mountManager, 'addMount']);
697
+                    }
698
+                }
699
+            });
700
+        }
701
+    }
702
+
703
+    private function setupListeners() {
704
+        // note that this event handling is intentionally pessimistic
705
+        // clearing the cache to often is better than not enough
706
+
707
+        $this->eventDispatcher->addListener(UserAddedEvent::class, function (UserAddedEvent $event) {
708
+            $this->cache->remove($event->getUser()->getUID());
709
+        });
710
+        $this->eventDispatcher->addListener(UserRemovedEvent::class, function (UserRemovedEvent $event) {
711
+            $this->cache->remove($event->getUser()->getUID());
712
+        });
713
+        $this->eventDispatcher->addListener(ShareCreatedEvent::class, function (ShareCreatedEvent $event) {
714
+            $this->cache->remove($event->getShare()->getSharedWith());
715
+        });
716
+        $this->eventDispatcher->addListener(InvalidateMountCacheEvent::class, function (InvalidateMountCacheEvent $event,
717
+        ) {
718
+            if ($user = $event->getUser()) {
719
+                $this->cache->remove($user->getUID());
720
+            } else {
721
+                $this->cache->clear();
722
+            }
723
+        });
724
+
725
+        $genericEvents = [
726
+            'OCA\Circles\Events\CreatingCircleEvent',
727
+            'OCA\Circles\Events\DestroyingCircleEvent',
728
+            'OCA\Circles\Events\AddingCircleMemberEvent',
729
+            'OCA\Circles\Events\RemovingCircleMemberEvent',
730
+        ];
731
+
732
+        foreach ($genericEvents as $genericEvent) {
733
+            $this->eventDispatcher->addListener($genericEvent, function ($event) {
734
+                $this->cache->clear();
735
+            });
736
+        }
737
+    }
738
+
739
+    private function registerMounts(IUser $user, array $mounts, ?array $mountProviderClasses = null): void {
740
+        if ($this->lockdownManager->canAccessFilesystem()) {
741
+            $this->userMountCache->registerMounts($user, $mounts, $mountProviderClasses);
742
+        }
743
+    }
744 744
 }
Please login to merge, or discard this patch.
lib/private/Files/Config/MountProviderCollection.php 1 patch
Indentation   +261 added lines, -261 removed lines patch added patch discarded remove patch
@@ -26,265 +26,265 @@
 block discarded – undo
26 26
 use function in_array;
27 27
 
28 28
 class MountProviderCollection implements IMountProviderCollection, Emitter {
29
-	use EmitterTrait;
30
-
31
-	/**
32
-	 * @var list<IHomeMountProvider>
33
-	 */
34
-	private array $homeProviders = [];
35
-
36
-	/**
37
-	 * @var array<class-string<IMountProvider>, IMountProvider>
38
-	 */
39
-	private array $providers = [];
40
-
41
-	/** @var list<IRootMountProvider> */
42
-	private array $rootProviders = [];
43
-
44
-	/** @var list<callable> */
45
-	private array $mountFilters = [];
46
-
47
-	public function __construct(
48
-		private IStorageFactory $loader,
49
-		private IUserMountCache $mountCache,
50
-		private IEventLogger $eventLogger,
51
-	) {
52
-	}
53
-
54
-	/**
55
-	 * @return list<IMountPoint>
56
-	 */
57
-	private function getMountsFromProvider(IMountProvider $provider, IUser $user, IStorageFactory $loader): array {
58
-		$class = str_replace('\\', '_', get_class($provider));
59
-		$uid = $user->getUID();
60
-		$this->eventLogger->start('fs:setup:provider:' . $class, "Getting mounts from $class for $uid");
61
-		$mounts = $provider->getMountsForUser($user, $loader) ?? [];
62
-		$this->eventLogger->end('fs:setup:provider:' . $class);
63
-		return array_values($mounts);
64
-	}
65
-
66
-	/**
67
-	 * @param list<IMountProvider> $providers
68
-	 * @return list<IMountPoint>
69
-	 */
70
-	private function getUserMountsForProviders(IUser $user, array $providers): array {
71
-		$loader = $this->loader;
72
-		$mounts = array_map(function (IMountProvider $provider) use ($user, $loader) {
73
-			return $this->getMountsFromProvider($provider, $user, $loader);
74
-		}, $providers);
75
-		$mounts = array_merge(...$mounts);
76
-		return $this->filterMounts($user, $mounts);
77
-	}
78
-
79
-	/**
80
-	 * @return list<IMountPoint>
81
-	 */
82
-	public function getMountsForUser(IUser $user): array {
83
-		return $this->getUserMountsForProviders($user, array_values($this->providers));
84
-	}
85
-
86
-	/**
87
-	 * @param IMountProviderArgs[] $mountProviderArgs
88
-	 * @return array<string, IMountPoint> IMountPoint array indexed by mount
89
-	 *                                    point.
90
-	 */
91
-	public function getUserMountsFromProviderByPath(
92
-		string $providerClass,
93
-		string $path,
94
-		array $mountProviderArgs,
95
-	): array {
96
-		$provider = $this->providers[$providerClass] ?? null;
97
-		if ($provider === null) {
98
-			return [];
99
-		}
100
-
101
-		if (!is_a($providerClass, IPartialMountProvider::class, true)) {
102
-			throw new \LogicException(
103
-				'Mount provider does not support partial mounts'
104
-			);
105
-		}
106
-
107
-		/** @var IPartialMountProvider $provider */
108
-		return $provider->getMountsForPath(
109
-			$path,
110
-			$mountProviderArgs,
111
-			$this->loader,
112
-		);
113
-	}
114
-
115
-	/**
116
-	 * Returns the mounts for the user from the specified provider classes.
117
-	 * Providers not registered in the MountProviderCollection will be skipped.
118
-	 *
119
-	 * @inheritdoc
120
-	 *
121
-	 * @return list<IMountPoint>
122
-	 */
123
-	public function getUserMountsForProviderClasses(IUser $user, array $mountProviderClasses): array {
124
-		$providers = array_filter(
125
-			$this->providers,
126
-			fn (string $providerClass) => in_array($providerClass, $mountProviderClasses),
127
-			ARRAY_FILTER_USE_KEY
128
-		);
129
-		return $this->getUserMountsForProviders($user, array_values($providers));
130
-	}
131
-
132
-	/**
133
-	 * @return list<IMountPoint>
134
-	 */
135
-	public function addMountForUser(IUser $user, IMountManager $mountManager, ?callable $providerFilter = null): array {
136
-		// shared mount provider gets to go last since it needs to know existing files
137
-		// to check for name collisions
138
-		$firstMounts = [];
139
-		if ($providerFilter) {
140
-			$providers = array_filter($this->providers, $providerFilter, ARRAY_FILTER_USE_KEY);
141
-		} else {
142
-			$providers = $this->providers;
143
-		}
144
-		$firstProviders
145
-			= array_filter(
146
-				$providers,
147
-				fn (string $providerClass) => ($providerClass !== MountProvider::class),
148
-				ARRAY_FILTER_USE_KEY
149
-			);
150
-		$lastProviders = array_filter(
151
-			$providers,
152
-			fn (string $providerClass) => $providerClass === MountProvider::class,
153
-			ARRAY_FILTER_USE_KEY
154
-		);
155
-		foreach ($firstProviders as $provider) {
156
-			$mounts = $this->getMountsFromProvider($provider, $user, $this->loader);
157
-			$firstMounts = array_merge($firstMounts, $mounts);
158
-		}
159
-		$firstMounts = $this->filterMounts($user, $firstMounts);
160
-		array_walk($firstMounts, [$mountManager, 'addMount']);
161
-
162
-		$lateMounts = [];
163
-		foreach ($lastProviders as $provider) {
164
-			$mounts = $this->getMountsFromProvider($provider, $user, $this->loader);
165
-			$lateMounts = array_merge($lateMounts, $mounts);
166
-		}
167
-
168
-		$lateMounts = $this->filterMounts($user, $lateMounts);
169
-		$this->eventLogger->start('fs:setup:add-mounts', 'Add mounts to the filesystem');
170
-		array_walk($lateMounts, [$mountManager, 'addMount']);
171
-		$this->eventLogger->end('fs:setup:add-mounts');
172
-
173
-		return array_values(array_merge($lateMounts, $firstMounts));
174
-	}
175
-
176
-	/**
177
-	 * Get the configured home mount for this user
178
-	 *
179
-	 * @since 9.1.0
180
-	 */
181
-	public function getHomeMountForUser(IUser $user): IMountPoint {
182
-		$providers = array_reverse($this->homeProviders); // call the latest registered provider first to give apps an opportunity to overwrite builtin
183
-		foreach ($providers as $homeProvider) {
184
-			if ($mount = $homeProvider->getHomeMountForUser($user, $this->loader)) {
185
-				$mount->setMountPoint('/' . $user->getUID()); //make sure the mountpoint is what we expect
186
-				return $mount;
187
-			}
188
-		}
189
-		throw new \Exception('No home storage configured for user ' . $user);
190
-	}
191
-
192
-	/**
193
-	 * Add a provider for mount points
194
-	 */
195
-	public function registerProvider(IMountProvider $provider): void {
196
-		$this->providers[get_class($provider)] = $provider;
197
-
198
-		$this->emit('\OC\Files\Config', 'registerMountProvider', [$provider]);
199
-	}
200
-
201
-	public function registerMountFilter(callable $filter): void {
202
-		$this->mountFilters[] = $filter;
203
-	}
204
-
205
-	/**
206
-	 * @param list<IMountPoint> $mountPoints
207
-	 * @return list<IMountPoint>
208
-	 */
209
-	private function filterMounts(IUser $user, array $mountPoints): array {
210
-		return array_values(array_filter($mountPoints, function (IMountPoint $mountPoint) use ($user) {
211
-			foreach ($this->mountFilters as $filter) {
212
-				if ($filter($mountPoint, $user) === false) {
213
-					return false;
214
-				}
215
-			}
216
-			return true;
217
-		}));
218
-	}
219
-
220
-	/**
221
-	 * Add a provider for home mount points
222
-	 *
223
-	 * @param IHomeMountProvider $provider
224
-	 * @since 9.1.0
225
-	 */
226
-	public function registerHomeProvider(IHomeMountProvider $provider) {
227
-		$this->homeProviders[] = $provider;
228
-		$this->emit('\OC\Files\Config', 'registerHomeMountProvider', [$provider]);
229
-	}
230
-
231
-	/**
232
-	 * Get the mount cache which can be used to search for mounts without setting up the filesystem
233
-	 */
234
-	public function getMountCache(): IUserMountCache {
235
-		return $this->mountCache;
236
-	}
237
-
238
-	public function registerRootProvider(IRootMountProvider $provider): void {
239
-		$this->rootProviders[] = $provider;
240
-	}
241
-
242
-	/**
243
-	 * Get all root mountpoints
244
-	 *
245
-	 * @return list<IMountPoint>
246
-	 * @since 20.0.0
247
-	 */
248
-	public function getRootMounts(): array {
249
-		$loader = $this->loader;
250
-		$mounts = array_map(function (IRootMountProvider $provider) use ($loader) {
251
-			return $provider->getRootMounts($loader);
252
-		}, $this->rootProviders);
253
-		$mounts = array_reduce($mounts, function (array $mounts, array $providerMounts) {
254
-			return array_merge($mounts, $providerMounts);
255
-		}, []);
256
-
257
-		if (count($mounts) === 0) {
258
-			throw new \Exception('No root mounts provided by any provider');
259
-		}
260
-
261
-		return array_values($mounts);
262
-	}
263
-
264
-	public function clearProviders(): void {
265
-		$this->providers = [];
266
-		$this->homeProviders = [];
267
-		$this->rootProviders = [];
268
-	}
269
-
270
-	/**
271
-	 * @return list<IMountProvider>
272
-	 */
273
-	public function getProviders(): array {
274
-		return array_values($this->providers);
275
-	}
276
-
277
-	/**
278
-	 * @return list<IHomeMountProvider>
279
-	 */
280
-	public function getHomeProviders(): array {
281
-		return $this->homeProviders;
282
-	}
283
-
284
-	/**
285
-	 * @return list<IRootMountProvider>
286
-	 */
287
-	public function getRootProviders(): array {
288
-		return $this->rootProviders;
289
-	}
29
+    use EmitterTrait;
30
+
31
+    /**
32
+     * @var list<IHomeMountProvider>
33
+     */
34
+    private array $homeProviders = [];
35
+
36
+    /**
37
+     * @var array<class-string<IMountProvider>, IMountProvider>
38
+     */
39
+    private array $providers = [];
40
+
41
+    /** @var list<IRootMountProvider> */
42
+    private array $rootProviders = [];
43
+
44
+    /** @var list<callable> */
45
+    private array $mountFilters = [];
46
+
47
+    public function __construct(
48
+        private IStorageFactory $loader,
49
+        private IUserMountCache $mountCache,
50
+        private IEventLogger $eventLogger,
51
+    ) {
52
+    }
53
+
54
+    /**
55
+     * @return list<IMountPoint>
56
+     */
57
+    private function getMountsFromProvider(IMountProvider $provider, IUser $user, IStorageFactory $loader): array {
58
+        $class = str_replace('\\', '_', get_class($provider));
59
+        $uid = $user->getUID();
60
+        $this->eventLogger->start('fs:setup:provider:' . $class, "Getting mounts from $class for $uid");
61
+        $mounts = $provider->getMountsForUser($user, $loader) ?? [];
62
+        $this->eventLogger->end('fs:setup:provider:' . $class);
63
+        return array_values($mounts);
64
+    }
65
+
66
+    /**
67
+     * @param list<IMountProvider> $providers
68
+     * @return list<IMountPoint>
69
+     */
70
+    private function getUserMountsForProviders(IUser $user, array $providers): array {
71
+        $loader = $this->loader;
72
+        $mounts = array_map(function (IMountProvider $provider) use ($user, $loader) {
73
+            return $this->getMountsFromProvider($provider, $user, $loader);
74
+        }, $providers);
75
+        $mounts = array_merge(...$mounts);
76
+        return $this->filterMounts($user, $mounts);
77
+    }
78
+
79
+    /**
80
+     * @return list<IMountPoint>
81
+     */
82
+    public function getMountsForUser(IUser $user): array {
83
+        return $this->getUserMountsForProviders($user, array_values($this->providers));
84
+    }
85
+
86
+    /**
87
+     * @param IMountProviderArgs[] $mountProviderArgs
88
+     * @return array<string, IMountPoint> IMountPoint array indexed by mount
89
+     *                                    point.
90
+     */
91
+    public function getUserMountsFromProviderByPath(
92
+        string $providerClass,
93
+        string $path,
94
+        array $mountProviderArgs,
95
+    ): array {
96
+        $provider = $this->providers[$providerClass] ?? null;
97
+        if ($provider === null) {
98
+            return [];
99
+        }
100
+
101
+        if (!is_a($providerClass, IPartialMountProvider::class, true)) {
102
+            throw new \LogicException(
103
+                'Mount provider does not support partial mounts'
104
+            );
105
+        }
106
+
107
+        /** @var IPartialMountProvider $provider */
108
+        return $provider->getMountsForPath(
109
+            $path,
110
+            $mountProviderArgs,
111
+            $this->loader,
112
+        );
113
+    }
114
+
115
+    /**
116
+     * Returns the mounts for the user from the specified provider classes.
117
+     * Providers not registered in the MountProviderCollection will be skipped.
118
+     *
119
+     * @inheritdoc
120
+     *
121
+     * @return list<IMountPoint>
122
+     */
123
+    public function getUserMountsForProviderClasses(IUser $user, array $mountProviderClasses): array {
124
+        $providers = array_filter(
125
+            $this->providers,
126
+            fn (string $providerClass) => in_array($providerClass, $mountProviderClasses),
127
+            ARRAY_FILTER_USE_KEY
128
+        );
129
+        return $this->getUserMountsForProviders($user, array_values($providers));
130
+    }
131
+
132
+    /**
133
+     * @return list<IMountPoint>
134
+     */
135
+    public function addMountForUser(IUser $user, IMountManager $mountManager, ?callable $providerFilter = null): array {
136
+        // shared mount provider gets to go last since it needs to know existing files
137
+        // to check for name collisions
138
+        $firstMounts = [];
139
+        if ($providerFilter) {
140
+            $providers = array_filter($this->providers, $providerFilter, ARRAY_FILTER_USE_KEY);
141
+        } else {
142
+            $providers = $this->providers;
143
+        }
144
+        $firstProviders
145
+            = array_filter(
146
+                $providers,
147
+                fn (string $providerClass) => ($providerClass !== MountProvider::class),
148
+                ARRAY_FILTER_USE_KEY
149
+            );
150
+        $lastProviders = array_filter(
151
+            $providers,
152
+            fn (string $providerClass) => $providerClass === MountProvider::class,
153
+            ARRAY_FILTER_USE_KEY
154
+        );
155
+        foreach ($firstProviders as $provider) {
156
+            $mounts = $this->getMountsFromProvider($provider, $user, $this->loader);
157
+            $firstMounts = array_merge($firstMounts, $mounts);
158
+        }
159
+        $firstMounts = $this->filterMounts($user, $firstMounts);
160
+        array_walk($firstMounts, [$mountManager, 'addMount']);
161
+
162
+        $lateMounts = [];
163
+        foreach ($lastProviders as $provider) {
164
+            $mounts = $this->getMountsFromProvider($provider, $user, $this->loader);
165
+            $lateMounts = array_merge($lateMounts, $mounts);
166
+        }
167
+
168
+        $lateMounts = $this->filterMounts($user, $lateMounts);
169
+        $this->eventLogger->start('fs:setup:add-mounts', 'Add mounts to the filesystem');
170
+        array_walk($lateMounts, [$mountManager, 'addMount']);
171
+        $this->eventLogger->end('fs:setup:add-mounts');
172
+
173
+        return array_values(array_merge($lateMounts, $firstMounts));
174
+    }
175
+
176
+    /**
177
+     * Get the configured home mount for this user
178
+     *
179
+     * @since 9.1.0
180
+     */
181
+    public function getHomeMountForUser(IUser $user): IMountPoint {
182
+        $providers = array_reverse($this->homeProviders); // call the latest registered provider first to give apps an opportunity to overwrite builtin
183
+        foreach ($providers as $homeProvider) {
184
+            if ($mount = $homeProvider->getHomeMountForUser($user, $this->loader)) {
185
+                $mount->setMountPoint('/' . $user->getUID()); //make sure the mountpoint is what we expect
186
+                return $mount;
187
+            }
188
+        }
189
+        throw new \Exception('No home storage configured for user ' . $user);
190
+    }
191
+
192
+    /**
193
+     * Add a provider for mount points
194
+     */
195
+    public function registerProvider(IMountProvider $provider): void {
196
+        $this->providers[get_class($provider)] = $provider;
197
+
198
+        $this->emit('\OC\Files\Config', 'registerMountProvider', [$provider]);
199
+    }
200
+
201
+    public function registerMountFilter(callable $filter): void {
202
+        $this->mountFilters[] = $filter;
203
+    }
204
+
205
+    /**
206
+     * @param list<IMountPoint> $mountPoints
207
+     * @return list<IMountPoint>
208
+     */
209
+    private function filterMounts(IUser $user, array $mountPoints): array {
210
+        return array_values(array_filter($mountPoints, function (IMountPoint $mountPoint) use ($user) {
211
+            foreach ($this->mountFilters as $filter) {
212
+                if ($filter($mountPoint, $user) === false) {
213
+                    return false;
214
+                }
215
+            }
216
+            return true;
217
+        }));
218
+    }
219
+
220
+    /**
221
+     * Add a provider for home mount points
222
+     *
223
+     * @param IHomeMountProvider $provider
224
+     * @since 9.1.0
225
+     */
226
+    public function registerHomeProvider(IHomeMountProvider $provider) {
227
+        $this->homeProviders[] = $provider;
228
+        $this->emit('\OC\Files\Config', 'registerHomeMountProvider', [$provider]);
229
+    }
230
+
231
+    /**
232
+     * Get the mount cache which can be used to search for mounts without setting up the filesystem
233
+     */
234
+    public function getMountCache(): IUserMountCache {
235
+        return $this->mountCache;
236
+    }
237
+
238
+    public function registerRootProvider(IRootMountProvider $provider): void {
239
+        $this->rootProviders[] = $provider;
240
+    }
241
+
242
+    /**
243
+     * Get all root mountpoints
244
+     *
245
+     * @return list<IMountPoint>
246
+     * @since 20.0.0
247
+     */
248
+    public function getRootMounts(): array {
249
+        $loader = $this->loader;
250
+        $mounts = array_map(function (IRootMountProvider $provider) use ($loader) {
251
+            return $provider->getRootMounts($loader);
252
+        }, $this->rootProviders);
253
+        $mounts = array_reduce($mounts, function (array $mounts, array $providerMounts) {
254
+            return array_merge($mounts, $providerMounts);
255
+        }, []);
256
+
257
+        if (count($mounts) === 0) {
258
+            throw new \Exception('No root mounts provided by any provider');
259
+        }
260
+
261
+        return array_values($mounts);
262
+    }
263
+
264
+    public function clearProviders(): void {
265
+        $this->providers = [];
266
+        $this->homeProviders = [];
267
+        $this->rootProviders = [];
268
+    }
269
+
270
+    /**
271
+     * @return list<IMountProvider>
272
+     */
273
+    public function getProviders(): array {
274
+        return array_values($this->providers);
275
+    }
276
+
277
+    /**
278
+     * @return list<IHomeMountProvider>
279
+     */
280
+    public function getHomeProviders(): array {
281
+        return $this->homeProviders;
282
+    }
283
+
284
+    /**
285
+     * @return list<IRootMountProvider>
286
+     */
287
+    public function getRootProviders(): array {
288
+        return $this->rootProviders;
289
+    }
290 290
 }
Please login to merge, or discard this patch.
lib/private/Files/SetupManagerFactory.php 1 patch
Indentation   +39 added lines, -39 removed lines patch added patch discarded remove patch
@@ -24,45 +24,45 @@
 block discarded – undo
24 24
 use Psr\Log\LoggerInterface;
25 25
 
26 26
 class SetupManagerFactory {
27
-	private ?SetupManager $setupManager;
27
+    private ?SetupManager $setupManager;
28 28
 
29
-	public function __construct(
30
-		private IEventLogger $eventLogger,
31
-		private IMountProviderCollection $mountProviderCollection,
32
-		private IUserManager $userManager,
33
-		private IEventDispatcher $eventDispatcher,
34
-		private IUserMountCache $userMountCache,
35
-		private ILockdownManager $lockdownManager,
36
-		private IUserSession $userSession,
37
-		private ICacheFactory $cacheFactory,
38
-		private LoggerInterface $logger,
39
-		private IConfig $config,
40
-		private ShareDisableChecker $shareDisableChecker,
41
-		private IAppManager $appManager,
42
-		private FileAccess $fileAccess,
43
-	) {
44
-		$this->setupManager = null;
45
-	}
29
+    public function __construct(
30
+        private IEventLogger $eventLogger,
31
+        private IMountProviderCollection $mountProviderCollection,
32
+        private IUserManager $userManager,
33
+        private IEventDispatcher $eventDispatcher,
34
+        private IUserMountCache $userMountCache,
35
+        private ILockdownManager $lockdownManager,
36
+        private IUserSession $userSession,
37
+        private ICacheFactory $cacheFactory,
38
+        private LoggerInterface $logger,
39
+        private IConfig $config,
40
+        private ShareDisableChecker $shareDisableChecker,
41
+        private IAppManager $appManager,
42
+        private FileAccess $fileAccess,
43
+    ) {
44
+        $this->setupManager = null;
45
+    }
46 46
 
47
-	public function create(IMountManager $mountManager): SetupManager {
48
-		if (!$this->setupManager) {
49
-			$this->setupManager = new SetupManager(
50
-				$this->eventLogger,
51
-				$this->mountProviderCollection,
52
-				$mountManager,
53
-				$this->userManager,
54
-				$this->eventDispatcher,
55
-				$this->userMountCache,
56
-				$this->lockdownManager,
57
-				$this->userSession,
58
-				$this->cacheFactory,
59
-				$this->logger,
60
-				$this->config,
61
-				$this->shareDisableChecker,
62
-				$this->appManager,
63
-				$this->fileAccess,
64
-			);
65
-		}
66
-		return $this->setupManager;
67
-	}
47
+    public function create(IMountManager $mountManager): SetupManager {
48
+        if (!$this->setupManager) {
49
+            $this->setupManager = new SetupManager(
50
+                $this->eventLogger,
51
+                $this->mountProviderCollection,
52
+                $mountManager,
53
+                $this->userManager,
54
+                $this->eventDispatcher,
55
+                $this->userMountCache,
56
+                $this->lockdownManager,
57
+                $this->userSession,
58
+                $this->cacheFactory,
59
+                $this->logger,
60
+                $this->config,
61
+                $this->shareDisableChecker,
62
+                $this->appManager,
63
+                $this->fileAccess,
64
+            );
65
+        }
66
+        return $this->setupManager;
67
+    }
68 68
 }
Please login to merge, or discard this patch.