Skip to content

Commit dfc46c3

Browse files
committed
fix: drop set up partial mounts on setupForUser
Signed-off-by: Salvatore Martire <[email protected]>
1 parent b33fdaf commit dfc46c3

File tree

2 files changed

+175
-0
lines changed

2 files changed

+175
-0
lines changed

lib/private/Files/SetupManager.php

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -236,6 +236,8 @@ public function setupForUser(IUser $user): void {
236236

237237
$this->eventLogger->start('fs:setup:user:full', 'Setup full filesystem for user');
238238

239+
$this->dropPartialMountsForUser($user);
240+
239241
$this->setupUserMountProviders[$user->getUID()] ??= [];
240242
$previouslySetupProviders = $this->setupUserMountProviders[$user->getUID()];
241243

@@ -658,6 +660,7 @@ public function setupForProvider(string $path, array $providers): void {
658660
$this->eventLogger->end('fs:setup:user:providers');
659661
return;
660662
} else {
663+
$this->dropPartialMountsForUser($user, $providers);
661664
$this->setupUserMountProviders[$user->getUID()] = array_merge($setupProviders, $providers);
662665
$mounts = $this->mountProviderCollection->getUserMountsForProviderClasses($user, $providers);
663666
}
@@ -741,4 +744,39 @@ private function registerMounts(IUser $user, array $mounts, ?array $mountProvide
741744
$this->userMountCache->registerMounts($user, $mounts, $mountProviderClasses);
742745
}
743746
}
747+
748+
/**
749+
* Drops partially set-up mounts for the given user
750+
* @param class-string<IMountProvider>[] $providers
751+
*/
752+
public function dropPartialMountsForUser(IUser $user, array $providers = []): void {
753+
// mounts are cached by mount-point
754+
$mounts = $this->mountManager->getAll();
755+
$partialMounts = array_filter($this->setupMountProviderPaths,
756+
static function (string $mountPoint) use (
757+
$providers,
758+
$user,
759+
$mounts
760+
) {
761+
$userMount = str_starts_with($mountPoint, '/' . $user->getUID() . '/files');
762+
763+
if (!$userMount) {
764+
return false;
765+
}
766+
767+
$mountProvider = ($mounts[$mountPoint] ?? null)?->getMountProvider();
768+
769+
return empty($providers)
770+
|| \in_array($mountProvider, $providers, true);
771+
},
772+
ARRAY_FILTER_USE_KEY);
773+
774+
if (!empty($partialMounts)) {
775+
// remove partially set up mounts
776+
foreach ($partialMounts as $mountPoint => $_mount) {
777+
$this->mountManager->removeMount($mountPoint);
778+
unset($this->setupMountProviderPaths[$mountPoint]);
779+
}
780+
}
781+
}
744782
}

tests/lib/Files/SetupManagerTest.php

Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -495,6 +495,143 @@ public function testSetupForPathHandlesPartialAndFullProvidersWithChildren(): vo
495495
$this->setupManager->setupForPath($this->path, true);
496496
}
497497

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+
498635
private function getAddMountCheckCallback(InvokedCount $invokedCount, $expectations): \Closure {
499636
return function (IMountPoint $actualMount) use ($invokedCount, $expectations) {
500637
$expectedMount = $expectations[$invokedCount->numberOfInvocations()] ?? null;

0 commit comments

Comments
 (0)