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