* This file is licensed under the Affero General Public License version 3 or * later. * See the COPYING-README file. */ namespace Test\Files\Config; use OC\DB\QueryBuilder\Literal; use OC\Files\Mount\MountPoint; use OC\Files\Storage\Storage; use OC\User\Manager; use OCP\Cache\CappedMemoryCache; use OCP\Diagnostics\IEventLogger; use OCP\EventDispatcher\IEventDispatcher; use OCP\Files\Config\ICachedMountInfo; use OCP\ICacheFactory; use OCP\IConfig; use OCP\IDBConnection; use OCP\IUserManager; use Psr\Log\LoggerInterface; use Test\TestCase; use Test\Util\User\Dummy; /** * @group DB */ class UserMountCacheTest extends TestCase { /** * @var IDBConnection */ private $connection; /** * @var IUserManager */ private $userManager; /** * @var \OC\Files\Config\UserMountCache */ private $cache; private $fileIds = []; protected function setUp(): void { parent::setUp(); $this->fileIds = []; $this->connection = \OC::$server->getDatabaseConnection(); $config = $this->getMockBuilder(IConfig::class) ->disableOriginalConstructor() ->getMock(); $config ->expects($this->any()) ->method('getUserValue') ->willReturnArgument(3); $config ->expects($this->any()) ->method('getAppValue') ->willReturnArgument(2); $this->userManager = new Manager($config, $this->createMock(ICacheFactory::class), $this->createMock(IEventDispatcher::class), $this->createMock(LoggerInterface::class)); $userBackend = new Dummy(); $userBackend->createUser('u1', ''); $userBackend->createUser('u2', ''); $userBackend->createUser('u3', ''); $this->userManager->registerBackend($userBackend); $this->cache = new \OC\Files\Config\UserMountCache($this->connection, $this->userManager, $this->createMock(LoggerInterface::class), $this->createMock(IEventLogger::class)); } protected function tearDown(): void { $builder = $this->connection->getQueryBuilder(); $builder->delete('mounts')->execute(); $builder = $this->connection->getQueryBuilder(); foreach ($this->fileIds as $fileId) { $builder->delete('filecache') ->where($builder->expr()->eq('fileid', new Literal($fileId))) ->execute(); } } private function getStorage($storageId) { $rootId = $this->createCacheEntry('', $storageId); $storageCache = $this->getMockBuilder('\OC\Files\Cache\Storage') ->disableOriginalConstructor() ->getMock(); $storageCache->expects($this->any()) ->method('getNumericId') ->willReturn($storageId); $cache = $this->getMockBuilder('\OC\Files\Cache\Cache') ->disableOriginalConstructor() ->getMock(); $cache->expects($this->any()) ->method('getId') ->willReturn($rootId); $storage = $this->getMockBuilder('\OC\Files\Storage\Storage') ->disableOriginalConstructor() ->getMock(); $storage->expects($this->any()) ->method('getStorageCache') ->willReturn($storageCache); $storage->expects($this->any()) ->method('getCache') ->willReturn($cache); return [$storage, $rootId]; } private function clearCache() { $this->invokePrivate($this->cache, 'mountsForUsers', [new CappedMemoryCache()]); } private function keyForMount(MountPoint $mount): string { return $mount->getStorageRootId().'::'.$mount->getMountPoint(); } public function testNewMounts() { $user = $this->userManager->get('u1'); [$storage] = $this->getStorage(10); $mount = new MountPoint($storage, '/asd/'); $this->cache->registerMounts($user, [$mount]); $this->clearCache(); $cachedMounts = $this->cache->getMountsForUser($user); $this->assertCount(1, $cachedMounts); $cachedMount = $cachedMounts[$this->keyForMount($mount)]; $this->assertEquals('/asd/', $cachedMount->getMountPoint()); $this->assertEquals($user, $cachedMount->getUser()); $this->assertEquals($storage->getCache()->getId(''), $cachedMount->getRootId()); $this->assertEquals($storage->getStorageCache()->getNumericId(), $cachedMount->getStorageId()); } public function testSameMounts() { $user = $this->userManager->get('u1'); [$storage] = $this->getStorage(10); $mount = new MountPoint($storage, '/asd/'); $this->cache->registerMounts($user, [$mount]); $this->clearCache(); $this->cache->registerMounts($user, [$mount]); $this->clearCache(); $cachedMounts = $this->cache->getMountsForUser($user); $this->assertCount(1, $cachedMounts); $cachedMount = $cachedMounts[$this->keyForMount($mount)]; $this->assertEquals('/asd/', $cachedMount->getMountPoint()); $this->assertEquals($user, $cachedMount->getUser()); $this->assertEquals($storage->getCache()->getId(''), $cachedMount->getRootId()); $this->assertEquals($storage->getStorageCache()->getNumericId(), $cachedMount->getStorageId()); } public function testRemoveMounts() { $user = $this->userManager->get('u1'); [$storage] = $this->getStorage(10); $mount = new MountPoint($storage, '/asd/'); $this->cache->registerMounts($user, [$mount]); $this->clearCache(); $this->cache->registerMounts($user, []); $this->clearCache(); $cachedMounts = $this->cache->getMountsForUser($user); $this->assertCount(0, $cachedMounts); } public function testChangeMounts() { $user = $this->userManager->get('u1'); [$storage] = $this->getStorage(10); $mount = new MountPoint($storage, '/bar/'); $this->cache->registerMounts($user, [$mount]); $this->clearCache(); $mount = new MountPoint($storage, '/foo/'); $this->cache->registerMounts($user, [$mount]); $this->clearCache(); $cachedMounts = $this->cache->getMountsForUser($user); $this->assertCount(1, $cachedMounts); $cachedMount = $cachedMounts[$this->keyForMount($mount)]; $this->assertEquals('/foo/', $cachedMount->getMountPoint()); } public function testChangeMountId() { $user = $this->userManager->get('u1'); [$storage] = $this->getStorage(10); $mount = new MountPoint($storage, '/foo/', null, null, null, null); $this->cache->registerMounts($user, [$mount]); $this->clearCache(); $mount = new MountPoint($storage, '/foo/', null, null, null, 1); $this->cache->registerMounts($user, [$mount]); $this->clearCache(); $cachedMounts = $this->cache->getMountsForUser($user); $this->assertCount(1, $cachedMounts); $cachedMount = $cachedMounts[$this->keyForMount($mount)]; $this->assertEquals(1, $cachedMount->getMountId()); } public function testGetMountsForUser() { $user1 = $this->userManager->get('u1'); $user2 = $this->userManager->get('u2'); $user3 = $this->userManager->get('u3'); [$storage1, $id1] = $this->getStorage(1); [$storage2, $id2] = $this->getStorage(2); $mount1 = new MountPoint($storage1, '/foo/'); $mount2 = new MountPoint($storage2, '/bar/'); $this->cache->registerMounts($user1, [$mount1, $mount2]); $this->cache->registerMounts($user2, [$mount2]); $this->cache->registerMounts($user3, [$mount2]); $this->clearCache(); $user3->delete(); $cachedMounts = $this->cache->getMountsForUser($user1); $this->assertCount(2, $cachedMounts); $this->assertEquals('/foo/', $cachedMounts[$this->keyForMount($mount1)]->getMountPoint()); $this->assertEquals($user1, $cachedMounts[$this->keyForMount($mount1)]->getUser()); $this->assertEquals($id1, $cachedMounts[$this->keyForMount($mount1)]->getRootId()); $this->assertEquals(1, $cachedMounts[$this->keyForMount($mount1)]->getStorageId()); $this->assertEquals('/bar/', $cachedMounts[$this->keyForMount($mount2)]->getMountPoint()); $this->assertEquals($user1, $cachedMounts[$this->keyForMount($mount2)]->getUser()); $this->assertEquals($id2, $cachedMounts[$this->keyForMount($mount2)]->getRootId()); $this->assertEquals(2, $cachedMounts[$this->keyForMount($mount2)]->getStorageId()); $cachedMounts = $this->cache->getMountsForUser($user3); $this->assertEmpty($cachedMounts); } public function testGetMountsByStorageId() { $user1 = $this->userManager->get('u1'); $user2 = $this->userManager->get('u2'); [$storage1, $id1] = $this->getStorage(1); [$storage2, $id2] = $this->getStorage(2); $mount1 = new MountPoint($storage1, '/foo/'); $mount2 = new MountPoint($storage2, '/bar/'); $this->cache->registerMounts($user1, [$mount1, $mount2]); $this->cache->registerMounts($user2, [$mount2]); $this->clearCache(); $cachedMounts = $this->cache->getMountsForStorageId(2); $this->sortMounts($cachedMounts); $this->assertCount(2, $cachedMounts); $this->assertEquals('/bar/', $cachedMounts[0]->getMountPoint()); $this->assertEquals($user1, $cachedMounts[0]->getUser()); $this->assertEquals($id2, $cachedMounts[0]->getRootId()); $this->assertEquals(2, $cachedMounts[0]->getStorageId()); $this->assertEquals('/bar/', $cachedMounts[1]->getMountPoint()); $this->assertEquals($user2, $cachedMounts[1]->getUser()); $this->assertEquals($id2, $cachedMounts[1]->getRootId()); $this->assertEquals(2, $cachedMounts[1]->getStorageId()); } public function testGetMountsByRootId() { $user1 = $this->userManager->get('u1'); $user2 = $this->userManager->get('u2'); [$storage1, $id1] = $this->getStorage(1); [$storage2, $id2] = $this->getStorage(2); $mount1 = new MountPoint($storage1, '/foo/'); $mount2 = new MountPoint($storage2, '/bar/'); $this->cache->registerMounts($user1, [$mount1, $mount2]); $this->cache->registerMounts($user2, [$mount2]); $this->clearCache(); $cachedMounts = $this->cache->getMountsForRootId($id2); $this->sortMounts($cachedMounts); $this->assertCount(2, $cachedMounts); $this->assertEquals('/bar/', $cachedMounts[0]->getMountPoint()); $this->assertEquals($user1, $cachedMounts[0]->getUser()); $this->assertEquals($id2, $cachedMounts[0]->getRootId()); $this->assertEquals(2, $cachedMounts[0]->getStorageId()); $this->assertEquals('/bar/', $cachedMounts[1]->getMountPoint()); $this->assertEquals($user2, $cachedMounts[1]->getUser()); $this->assertEquals($id2, $cachedMounts[1]->getRootId()); $this->assertEquals(2, $cachedMounts[1]->getStorageId()); } private function sortMounts(&$mounts) { usort($mounts, function (ICachedMountInfo $a, ICachedMountInfo $b) { return strcmp($a->getUser()->getUID(), $b->getUser()->getUID()); }); } private function createCacheEntry($internalPath, $storageId, $size = 0) { $internalPath = trim($internalPath, '/'); $inserted = $this->connection->insertIfNotExist('*PREFIX*filecache', [ 'storage' => $storageId, 'path' => $internalPath, 'path_hash' => md5($internalPath), 'parent' => -1, 'name' => basename($internalPath), 'mimetype' => 0, 'mimepart' => 0, 'size' => $size, 'storage_mtime' => 0, 'encrypted' => 0, 'unencrypted_size' => 0, 'etag' => '', 'permissions' => 31 ], ['storage', 'path_hash']); if ($inserted) { $id = (int)$this->connection->lastInsertId('*PREFIX*filecache'); $this->fileIds[] = $id; } else { $sql = 'SELECT `fileid` FROM `*PREFIX*filecache` WHERE `storage` = ? AND `path_hash` =?'; $query = $this->connection->prepare($sql); $query->execute([$storageId, md5($internalPath)]); return (int)$query->fetchOne(); } return $id; } public function testGetMountsForFileIdRootId() { $user1 = $this->userManager->get('u1'); [$storage1, $rootId] = $this->getStorage(2); $mount1 = new MountPoint($storage1, '/foo/'); $this->cache->registerMounts($user1, [$mount1]); $this->clearCache(); $cachedMounts = $this->cache->getMountsForFileId($rootId); $this->assertCount(1, $cachedMounts); $this->assertEquals('/foo/', $cachedMounts[0]->getMountPoint()); $this->assertEquals($user1, $cachedMounts[0]->getUser()); $this->assertEquals($rootId, $cachedMounts[0]->getRootId()); $this->assertEquals(2, $cachedMounts[0]->getStorageId()); } public function testGetMountsForFileIdSubFolder() { $user1 = $this->userManager->get('u1'); $fileId = $this->createCacheEntry('/foo/bar', 2); [$storage1, $rootId] = $this->getStorage(2); $mount1 = new MountPoint($storage1, '/foo/'); $this->cache->registerMounts($user1, [$mount1]); $this->clearCache(); $cachedMounts = $this->cache->getMountsForFileId($fileId); $this->assertCount(1, $cachedMounts); $this->assertEquals('/foo/', $cachedMounts[0]->getMountPoint()); $this->assertEquals($user1, $cachedMounts[0]->getUser()); $this->assertEquals($rootId, $cachedMounts[0]->getRootId()); $this->assertEquals(2, $cachedMounts[0]->getStorageId()); $this->assertEquals('foo/bar', $cachedMounts[0]->getInternalPath()); $this->assertEquals('/foo/foo/bar', $cachedMounts[0]->getPath()); } public function testGetMountsForFileIdSubFolderMount() { $user1 = $this->userManager->get('u1'); [$storage1, $rootId] = $this->getStorage(2); $folderId = $this->createCacheEntry('/foo', 2); $fileId = $this->createCacheEntry('/foo/bar', 2); $mount1 = $this->getMockBuilder('\OC\Files\Mount\MountPoint') ->setConstructorArgs([$storage1, '/']) ->setMethods(['getStorageRootId']) ->getMock(); $mount1->expects($this->any()) ->method('getStorageRootId') ->willReturn($folderId); $this->cache->registerMounts($user1, [$mount1]); $this->clearCache(); $cachedMounts = $this->cache->getMountsForFileId($fileId); $this->assertCount(1, $cachedMounts); $this->assertEquals('/', $cachedMounts[0]->getMountPoint()); $this->assertEquals($user1, $cachedMounts[0]->getUser()); $this->assertEquals($folderId, $cachedMounts[0]->getRootId()); $this->assertEquals(2, $cachedMounts[0]->getStorageId()); $this->assertEquals('foo', $cachedMounts[0]->getRootInternalPath()); $this->assertEquals('bar', $cachedMounts[0]->getInternalPath()); $this->assertEquals('/bar', $cachedMounts[0]->getPath()); } public function testGetMountsForFileIdSubFolderMountOutside() { $user1 = $this->userManager->get('u1'); [$storage1, $rootId] = $this->getStorage(2); $folderId = $this->createCacheEntry('/foo', 2); $fileId = $this->createCacheEntry('/bar/asd', 2); $mount1 = $this->getMockBuilder('\OC\Files\Mount\MountPoint') ->setConstructorArgs([$storage1, '/foo/']) ->setMethods(['getStorageRootId']) ->getMock(); $mount1->expects($this->any()) ->method('getStorageRootId') ->willReturn($folderId); $this->cache->registerMounts($user1, [$mount1]); $this->cache->registerMounts($user1, [$mount1]); $this->clearCache(); $cachedMounts = $this->cache->getMountsForFileId($fileId); $this->assertCount(0, $cachedMounts); } public function testGetMountsForFileIdDeletedUser() { $user1 = $this->userManager->get('u1'); [$storage1, $rootId] = $this->getStorage(2); $rootId = $this->createCacheEntry('', 2); $mount1 = new MountPoint($storage1, '/foo/'); $this->cache->registerMounts($user1, [$mount1]); $user1->delete(); $this->clearCache(); $cachedMounts = $this->cache->getMountsForFileId($rootId); $this->assertEmpty($cachedMounts); } public function testGetUsedSpaceForUsers() { $user1 = $this->userManager->get('u1'); $user2 = $this->userManager->get('u2'); /** @var Storage $storage1 */ [$storage1, $rootId] = $this->getStorage(2); $folderId = $this->createCacheEntry('files', 2, 100); $fileId = $this->createCacheEntry('files/foo', 2, 7); $storage1->getCache()->put($folderId, ['size' => 100]); $storage1->getCache()->update($fileId, ['size' => 70]); $mount1 = $this->getMockBuilder(MountPoint::class) ->setConstructorArgs([$storage1, '/u1/']) ->setMethods(['getStorageRootId', 'getNumericStorageId']) ->getMock(); $mount1->expects($this->any()) ->method('getStorageRootId') ->willReturn($rootId); $mount1->expects($this->any()) ->method('getNumericStorageId') ->willReturn(2); $this->cache->registerMounts($user1, [$mount1]); $result = $this->cache->getUsedSpaceForUsers([$user1, $user2]); $this->assertEquals(['u1' => 100], $result); } public function testMigrateMountProvider() { $user1 = $this->userManager->get('u1'); [$storage1, $rootId] = $this->getStorage(2); $rootId = $this->createCacheEntry('', 2); $mount1 = new MountPoint($storage1, '/foo/'); $this->cache->registerMounts($user1, [$mount1]); $this->clearCache(); $cachedMounts = $this->cache->getMountsForUser($user1); $this->assertCount(1, $cachedMounts); $this->assertEquals('', $cachedMounts[$this->keyForMount($mount1)]->getMountProvider()); $mount1 = new MountPoint($storage1, '/foo/', null, null, null, null, 'dummy'); $this->cache->registerMounts($user1, [$mount1], ['dummy']); $this->clearCache(); $cachedMounts = $this->cache->getMountsForUser($user1); $this->assertCount(1, $cachedMounts); $this->assertEquals('dummy', $cachedMounts[$this->keyForMount($mount1)]->getMountProvider()); } }