Merge pull request #20768 from owncloud/mount-cache

cache mountpoints in the db
This commit is contained in:
Thomas Müller 2016-01-22 11:01:54 +01:00
commit 1410120758
20 changed files with 1038 additions and 34 deletions

View File

@ -26,6 +26,7 @@
namespace OCA\Files_External\AppInfo;
use \OCP\AppFramework\App;
use OCP\AppFramework\IAppContainer;
use \OCP\IContainer;
use \OCA\Files_External\Service\BackendService;
@ -33,9 +34,13 @@ use \OCA\Files_External\Service\BackendService;
* @package OCA\Files_External\Appinfo
*/
class Application extends App {
public function __construct(array $urlParams=array()) {
public function __construct(array $urlParams = array()) {
parent::__construct('files_external', $urlParams);
$this->getContainer()->registerService('OCP\Files\Config\IUserMountCache', function (IAppContainer $c) {
return $c->getServer()->query('UserMountCache');
});
$this->loadBackends();
$this->loadAuthMechanisms();

View File

@ -29,6 +29,7 @@ use OCA\Files_external\Service\LegacyStoragesService;
use OCA\Files_external\Service\StoragesService;
use OCA\Files_external\Service\UserLegacyStoragesService;
use OCA\Files_external\Service\UserStoragesService;
use OCP\Files\Config\IUserMountCache;
use OCP\IConfig;
use OCP\IDBConnection;
use OCP\ILogger;
@ -64,6 +65,9 @@ class StorageMigrator {
*/
private $logger;
/** @var IUserMountCache */
private $userMountCache;
/**
* StorageMigrator constructor.
*
@ -72,19 +76,22 @@ class StorageMigrator {
* @param IConfig $config
* @param IDBConnection $connection
* @param ILogger $logger
* @param IUserMountCache $userMountCache
*/
public function __construct(
BackendService $backendService,
DBConfigService $dbConfig,
IConfig $config,
IDBConnection $connection,
ILogger $logger
ILogger $logger,
IUserMountCache $userMountCache
) {
$this->backendService = $backendService;
$this->dbConfig = $dbConfig;
$this->config = $config;
$this->connection = $connection;
$this->logger = $logger;
$this->userMountCache = $userMountCache;
}
private function migrate(LegacyStoragesService $legacyService, StoragesService $storageService) {
@ -107,7 +114,7 @@ class StorageMigrator {
*/
public function migrateGlobal() {
$legacyService = new GlobalLegacyStoragesService($this->backendService);
$storageService = new GlobalStoragesService($this->backendService, $this->dbConfig);
$storageService = new GlobalStoragesService($this->backendService, $this->dbConfig, $this->userMountCache);
$this->migrate($legacyService, $storageService);
}
@ -125,7 +132,7 @@ class StorageMigrator {
if (version_compare($userVersion, '0.5.0', '<')) {
$this->config->setUserValue($userId, 'files_external', 'config_version', '0.5.0');
$legacyService = new UserLegacyStoragesService($this->backendService, $dummySession);
$storageService = new UserStoragesService($this->backendService, $this->dbConfig, $dummySession);
$storageService = new UserStoragesService($this->backendService, $this->dbConfig, $dummySession, $this->userMountCache);
$this->migrate($legacyService, $storageService);
}

View File

@ -31,6 +31,7 @@ use \OCA\Files_external\Lib\StorageConfig;
use \OCA\Files_external\NotFoundException;
use \OCA\Files_External\Lib\Backend\Backend;
use \OCA\Files_External\Lib\Auth\AuthMechanism;
use OCP\Files\Config\IUserMountCache;
use \OCP\Files\StorageNotAvailableException;
/**
@ -46,13 +47,20 @@ abstract class StoragesService {
*/
protected $dbConfig;
/**
* @var IUserMountCache
*/
protected $userMountCache;
/**
* @param BackendService $backendService
* @param DBConfigService $dbConfigService
* @param IUserMountCache $userMountCache
*/
public function __construct(BackendService $backendService, DBConfigService $dbConfigService) {
public function __construct(BackendService $backendService, DBConfigService $dbConfigService, IUserMountCache $userMountCache) {
$this->backendService = $backendService;
$this->dbConfig = $dbConfigService;
$this->userMountCache = $userMountCache;
}
protected function readDBConfig() {
@ -416,6 +424,15 @@ abstract class StoragesService {
$this->triggerChangeHooks($oldStorage, $updatedStorage);
if (($wasGlobal && !$isGlobal) || count($removedGroups) > 0) { // to expensive to properly handle these on the fly
$this->userMountCache->remoteStorageMounts($this->getStorageId($updatedStorage));
} else {
$storageId = $this->getStorageId($updatedStorage);
foreach ($removedUsers as $userId) {
$this->userMountCache->removeUserStorageMount($storageId, $userId);
}
}
return $this->getStorage($id);
}
@ -480,4 +497,25 @@ abstract class StoragesService {
return $storageImpl->getId();
}
/**
* Construct the storage implementation
*
* @param StorageConfig $storageConfig
* @return int
*/
private function getStorageId(StorageConfig $storageConfig) {
try {
$class = $storageConfig->getBackend()->getStorageClass();
/** @var \OC\Files\Storage\Storage $storage */
$storage = new $class($storageConfig->getBackendOptions());
// auth mechanism should fire first
$storage = $storageConfig->getBackend()->wrapStorage($storage);
$storage = $storageConfig->getAuthMechanism()->wrapStorage($storage);
return $storage->getStorageCache()->getNumericId();
} catch (\Exception $e) {
return -1;
}
}
}

View File

@ -25,6 +25,7 @@ namespace OCA\Files_External\Service;
use \OCA\Files_external\Service\GlobalStoragesService;
use \OCA\Files_External\Service\BackendService;
use OCP\Files\Config\IUserMountCache;
use \OCP\IUserSession;
use \OCP\IGroupManager;
use \OCA\Files_External\Service\UserTrait;
@ -46,14 +47,16 @@ class UserGlobalStoragesService extends GlobalStoragesService {
* @param DBConfigService $dbConfig
* @param IUserSession $userSession
* @param IGroupManager $groupManager
* @param IUserMountCache $userMountCache
*/
public function __construct(
BackendService $backendService,
DBConfigService $dbConfig,
IUserSession $userSession,
IGroupManager $groupManager
IGroupManager $groupManager,
IUserMountCache $userMountCache
) {
parent::__construct($backendService, $dbConfig);
parent::__construct($backendService, $dbConfig, $userMountCache);
$this->userSession = $userSession;
$this->groupManager = $groupManager;
}

View File

@ -23,6 +23,7 @@
namespace OCA\Files_external\Service;
use OCP\Files\Config\IUserMountCache;
use \OCP\IUserSession;
use \OC\Files\Filesystem;
@ -44,14 +45,16 @@ class UserStoragesService extends StoragesService {
* @param BackendService $backendService
* @param DBConfigService $dbConfig
* @param IUserSession $userSession user session
* @param IUserMountCache $userMountCache
*/
public function __construct(
BackendService $backendService,
DBConfigService $dbConfig,
IUserSession $userSession
IUserSession $userSession,
IUserMountCache $userMountCache
) {
$this->userSession = $userSession;
parent::__construct($backendService, $dbConfig);
parent::__construct($backendService, $dbConfig, $userMountCache);
}
protected function readDBConfig() {

View File

@ -34,7 +34,7 @@ use \OCA\Files_external\Lib\StorageConfig;
class GlobalStoragesServiceTest extends StoragesServiceTest {
public function setUp() {
parent::setUp();
$this->service = new GlobalStoragesService($this->backendService, $this->dbConfig);
$this->service = new GlobalStoragesService($this->backendService, $this->dbConfig, $this->mountCache);
}
public function tearDown() {

View File

@ -76,6 +76,11 @@ abstract class StoragesServiceTest extends \Test\TestCase {
*/
protected static $hookCalls;
/**
* @var \PHPUnit_Framework_MockObject_MockObject|\OCP\Files\Config\IUserMountCache
*/
protected $mountCache;
public function setUp() {
parent::setUp();
$this->dbConfig = new CleaningDBConfig(\OC::$server->getDatabaseConnection());
@ -87,6 +92,8 @@ abstract class StoragesServiceTest extends \Test\TestCase {
);
\OC_Mount_Config::$skipTest = true;
$this->mountCache = $this->getMock('OCP\Files\Config\IUserMountCache');
// prepare BackendService mock
$this->backendService =
$this->getMockBuilder('\OCA\Files_External\Service\BackendService')

View File

@ -94,7 +94,8 @@ class UserGlobalStoragesServiceTest extends GlobalStoragesServiceTest {
$this->backendService,
$this->dbConfig,
$userSession,
$this->groupManager
$this->groupManager,
$this->mountCache
);
}

View File

@ -49,7 +49,7 @@ class UserStoragesServiceTest extends StoragesServiceTest {
public function setUp() {
parent::setUp();
$this->globalStoragesService = new GlobalStoragesService($this->backendService, $this->dbConfig);
$this->globalStoragesService = new GlobalStoragesService($this->backendService, $this->dbConfig, $this->mountCache);
$this->userId = $this->getUniqueID('user_');
$this->createUser($this->userId, $this->userId);
@ -62,7 +62,7 @@ class UserStoragesServiceTest extends StoragesServiceTest {
->method('getUser')
->will($this->returnValue($this->user));
$this->service = new UserStoragesService($this->backendService, $this->dbConfig, $userSession);
$this->service = new UserStoragesService($this->backendService, $this->dbConfig, $userSession, $this->mountCache);
}
private function makeTestStorageData() {

View File

@ -127,6 +127,93 @@
</table>
<!-- a list of all mounted storage per user, populated on filesystem setup -->
<table>
<name>*dbprefix*mounts</name>
<declaration>
<field>
<name>id</name>
<type>integer</type>
<default>0</default>
<notnull>true</notnull>
<autoincrement>1</autoincrement>
<length>4</length>
</field>
<field>
<name>storage_id</name>
<type>integer</type>
<notnull>true</notnull>
</field>
<!-- fileid of the root of the mount, foreign key: oc_filecache.fileid -->
<field>
<name>root_id</name>
<type>integer</type>
<notnull>true</notnull>
</field>
<field>
<name>user_id</name>
<type>text</type>
<notnull>true</notnull>
<length>64</length>
</field>
<field>
<name>mount_point</name>
<type>text</type>
<notnull>true</notnull>
<length>4000</length>
</field>
<index>
<name>mounts_user_index</name>
<unique>false</unique>
<field>
<name>user_id</name>
<sorting>ascending</sorting>
</field>
</index>
<index>
<name>mounts_storage_index</name>
<unique>false</unique>
<field>
<name>storage_id</name>
<sorting>ascending</sorting>
</field>
</index>
<index>
<name>mounts_root_index</name>
<unique>false</unique>
<field>
<name>root_id</name>
<sorting>ascending</sorting>
</field>
</index>
<index>
<name>mounts_user_root_index</name>
<unique>true</unique>
<field>
<name>user_id</name>
<sorting>ascending</sorting>
</field>
<field>
<name>root_id</name>
<sorting>ascending</sorting>
</field>
</index>
</declaration>
</table>
<table>
<!--

View File

@ -0,0 +1,107 @@
<?php
/**
* @author Robin Appelman <icewind@owncloud.com>
*
* @copyright Copyright (c) 2015, ownCloud, Inc.
* @license AGPL-3.0
*
* This code is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License, version 3,
* along with this program. If not, see <http://www.gnu.org/licenses/>
*
*/
namespace OC\Files\Config;
use OC\Files\Filesystem;
use OCP\Files\Config\ICachedMountInfo;
use OCP\Files\Node;
use OCP\IUser;
class CachedMountInfo implements ICachedMountInfo {
/**
* @var IUser
*/
private $user;
/**
* @var int
*/
private $storageId;
/**
* @var int
*/
private $rootId;
/**
* @var string
*/
private $mountPoint;
/**
* CachedMountInfo constructor.
*
* @param IUser $user
* @param int $storageId
* @param int $rootId
* @param string $mountPoint
*/
public function __construct(IUser $user, $storageId, $rootId, $mountPoint) {
$this->user = $user;
$this->storageId = $storageId;
$this->rootId = $rootId;
$this->mountPoint = $mountPoint;
}
/**
* @return IUser
*/
public function getUser() {
return $this->user;
}
/**
* @return int the numeric storage id of the mount
*/
public function getStorageId() {
return $this->storageId;
}
/**
* @return int the fileid of the root of the mount
*/
public function getRootId() {
return $this->rootId;
}
/**
* @return Node the root node of the mount
*/
public function getMountPointNode() {
// TODO injection etc
Filesystem::initMountPoints($this->user->getUID());
$userNode = \OC::$server->getUserFolder($this->user->getUID());
$nodes = $userNode->getById($this->rootId);
if (count($nodes) > 0) {
return $nodes[0];
} else {
return null;
}
}
/**
* @return string the mount point of the mount for the user
*/
public function getMountPoint() {
return $this->mountPoint;
}
}

View File

@ -26,6 +26,8 @@ use OC\Hooks\Emitter;
use OC\Hooks\EmitterTrait;
use OCP\Files\Config\IMountProviderCollection;
use OCP\Files\Config\IMountProvider;
use OCP\Files\Config\IUserMountCache;
use OCP\Files\Mount\IMountPoint;
use OCP\Files\Storage\IStorageFactory;
use OCP\IUser;
@ -43,10 +45,17 @@ class MountProviderCollection implements IMountProviderCollection, Emitter {
private $loader;
/**
* @param \OCP\Files\Storage\IStorageFactory $loader
* @var \OCP\Files\Config\IUserMountCache
*/
public function __construct(IStorageFactory $loader) {
private $mountCache;
/**
* @param \OCP\Files\Storage\IStorageFactory $loader
* @param IUserMountCache $mountCache
*/
public function __construct(IStorageFactory $loader, IUserMountCache $mountCache) {
$this->loader = $loader;
$this->mountCache = $mountCache;
}
/**
@ -77,4 +86,23 @@ class MountProviderCollection implements IMountProviderCollection, Emitter {
$this->providers[] = $provider;
$this->emit('\OC\Files\Config', 'registerMountProvider', [$provider]);
}
/**
* Cache mounts for user
*
* @param IUser $user
* @param IMountPoint[] $mountPoints
*/
public function registerMounts(IUser $user, array $mountPoints) {
$this->mountCache->registerMounts($user, $mountPoints);
}
/**
* Get the mount cache which can be used to search for mounts without setting up the filesystem
*
* @return IUserMountCache
*/
public function getMountCache() {
return $this->mountCache;
}
}

View File

@ -0,0 +1,232 @@
<?php
/**
* @author Robin Appelman <icewind@owncloud.com>
*
* @copyright Copyright (c) 2015, ownCloud, Inc.
* @license AGPL-3.0
*
* This code is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License, version 3,
* along with this program. If not, see <http://www.gnu.org/licenses/>
*
*/
namespace OC\Files\Config;
use Doctrine\DBAL\Exception\UniqueConstraintViolationException;
use OCP\Files\Config\ICachedMountInfo;
use OCP\Files\Config\IUserMountCache;
use OCP\Files\Mount\IMountPoint;
use OCP\ICache;
use OCP\IDBConnection;
use OCP\ILogger;
use OCP\IUser;
use OCP\IUserManager;
/**
* Cache mounts points per user in the cache so we can easilly look them up
*/
class UserMountCache implements IUserMountCache {
/**
* @var IDBConnection
*/
private $connection;
/**
* @var IUserManager
*/
private $userManager;
/** @var ICachedMountInfo[][] [$userId => [$cachedMountInfo, ....], ...] */
private $mountsForUsers = [];
/**
* @var ILogger
*/
private $logger;
/**
* UserMountCache constructor.
*
* @param IDBConnection $connection
* @param IUserManager $userManager
* @param ILogger $logger
*/
public function __construct(IDBConnection $connection, IUserManager $userManager, ILogger $logger) {
$this->connection = $connection;
$this->userManager = $userManager;
$this->logger = $logger;
}
public function registerMounts(IUser $user, array $mounts) {
// filter out non-proper storages coming from unit tests
$mounts = array_filter($mounts, function (IMountPoint $mount) {
return $mount->getStorage()->getCache();
});
/** @var ICachedMountInfo[] $newMounts */
$newMounts = array_map(function (IMountPoint $mount) use ($user) {
$storage = $mount->getStorage();
$rootId = (int)$storage->getCache()->getId('');
$storageId = (int)$storage->getStorageCache()->getNumericId();
// filter out any storages which aren't scanned yet since we aren't interested in files from those storages (yet)
if ($rootId === -1) {
return null;
} else {
return new CachedMountInfo($user, $storageId, $rootId, $mount->getMountPoint());
}
}, $mounts);
$newMounts = array_values(array_filter($newMounts));
$cachedMounts = $this->getMountsForUser($user);
$mountDiff = function (ICachedMountInfo $mount1, ICachedMountInfo $mount2) {
// since we are only looking for mounts for a specific user comparing on root id is enough
return $mount1->getRootId() - $mount2->getRootId();
};
/** @var ICachedMountInfo[] $addedMounts */
$addedMounts = array_udiff($newMounts, $cachedMounts, $mountDiff);
/** @var ICachedMountInfo[] $removedMounts */
$removedMounts = array_udiff($cachedMounts, $newMounts, $mountDiff);
$changedMounts = array_uintersect($newMounts, $cachedMounts, function (ICachedMountInfo $mount1, ICachedMountInfo $mount2) {
// filter mounts with the same root id and different mountpoints
if ($mount1->getRootId() !== $mount2->getRootId()) {
return -1;
}
return ($mount1->getMountPoint() !== $mount2->getMountPoint()) ? 0 : 1;
});
foreach ($addedMounts as $mount) {
$this->addToCache($mount);
$this->mountsForUsers[$user->getUID()][] = $mount;
}
foreach ($removedMounts as $mount) {
$this->removeFromCache($mount);
$index = array_search($mount, $this->mountsForUsers[$user->getUID()]);
unset($this->mountsForUsers[$user->getUID()][$index]);
}
foreach ($changedMounts as $mount) {
$this->setMountPoint($mount);
}
}
private function addToCache(ICachedMountInfo $mount) {
$this->connection->insertIfNotExist('*PREFIX*mounts', [
'storage_id' => $mount->getStorageId(),
'root_id' => $mount->getRootId(),
'user_id' => $mount->getUser()->getUID(),
'mount_point' => $mount->getMountPoint()
]);
}
private function setMountPoint(ICachedMountInfo $mount) {
$builder = $this->connection->getQueryBuilder();
$query = $builder->update('mounts')
->set('mount_point', $builder->createNamedParameter($mount->getMountPoint()))
->where($builder->expr()->eq('user_id', $builder->createNamedParameter($mount->getUser()->getUID())))
->andWhere($builder->expr()->eq('root_id', $builder->createNamedParameter($mount->getRootId(), \PDO::PARAM_INT)));
$query->execute();
}
private function removeFromCache(ICachedMountInfo $mount) {
$builder = $this->connection->getQueryBuilder();
$query = $builder->delete('mounts')
->where($builder->expr()->eq('user_id', $builder->createNamedParameter($mount->getUser()->getUID())))
->andWhere($builder->expr()->eq('root_id', $builder->createNamedParameter($mount->getRootId(), \PDO::PARAM_INT)));
$query->execute();
}
private function dbRowToMountInfo(array $row) {
$user = $this->userManager->get($row['user_id']);
return new CachedMountInfo($user, (int)$row['storage_id'], (int)$row['root_id'], $row['mount_point']);
}
/**
* @param IUser $user
* @return ICachedMountInfo[]
*/
public function getMountsForUser(IUser $user) {
if (!isset($this->mountsForUsers[$user->getUID()])) {
$builder = $this->connection->getQueryBuilder();
$query = $builder->select('storage_id', 'root_id', 'user_id', 'mount_point')
->from('mounts')
->where($builder->expr()->eq('user_id', $builder->createPositionalParameter($user->getUID())));
$rows = $query->execute()->fetchAll();
$this->mountsForUsers[$user->getUID()] = array_map([$this, 'dbRowToMountInfo'], $rows);
}
return $this->mountsForUsers[$user->getUID()];
}
/**
* @param int $numericStorageId
* @return CachedMountInfo[]
*/
public function getMountsForStorageId($numericStorageId) {
$builder = $this->connection->getQueryBuilder();
$query = $builder->select('storage_id', 'root_id', 'user_id', 'mount_point')
->from('mounts')
->where($builder->expr()->eq('storage_id', $builder->createPositionalParameter($numericStorageId, \PDO::PARAM_INT)));
$rows = $query->execute()->fetchAll();
return array_map([$this, 'dbRowToMountInfo'], $rows);
}
/**
* @param int $rootFileId
* @return CachedMountInfo[]
*/
public function getMountsForRootId($rootFileId) {
$builder = $this->connection->getQueryBuilder();
$query = $builder->select('storage_id', 'root_id', 'user_id', 'mount_point')
->from('mounts')
->where($builder->expr()->eq('root_id', $builder->createPositionalParameter($rootFileId, \PDO::PARAM_INT)));
$rows = $query->execute()->fetchAll();
return array_map([$this, 'dbRowToMountInfo'], $rows);
}
/**
* Remove all cached mounts for a user
*
* @param IUser $user
*/
public function removeUserMounts(IUser $user) {
$builder = $this->connection->getQueryBuilder();
$query = $builder->delete('mounts')
->where($builder->expr()->eq('user_id', $builder->createNamedParameter($user->getUID())));
$query->execute();
}
public function removeUserStorageMount($storageId, $userId) {
$builder = $this->connection->getQueryBuilder();
$query = $builder->delete('mounts')
->where($builder->expr()->eq('user_id', $builder->createNamedParameter($userId)))
->andWhere($builder->expr()->eq('storage_id', $builder->createNamedParameter($storageId, \PDO::PARAM_INT)));
$query->execute();
}
public function remoteStorageMounts($storageId) {
$builder = $this->connection->getQueryBuilder();
$query = $builder->delete('mounts')
->where($builder->expr()->eq('storage_id', $builder->createNamedParameter($storageId, \PDO::PARAM_INT)));
$query->execute();
}
}

View File

@ -0,0 +1,48 @@
<?php
/**
* @author Robin Appelman <icewind@owncloud.com>
*
* @copyright Copyright (c) 2015, ownCloud, Inc.
* @license AGPL-3.0
*
* This code is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License, version 3,
* along with this program. If not, see <http://www.gnu.org/licenses/>
*
*/
namespace OC\Files\Config;
use OC\User\Manager;
use OCP\Files\Config\IUserMountCache;
/**
* Listen to hooks and update the mount cache as needed
*/
class UserMountCacheListener {
/**
* @var IUserMountCache
*/
private $userMountCache;
/**
* UserMountCacheListener constructor.
*
* @param IUserMountCache $userMountCache
*/
public function __construct(IUserMountCache $userMountCache) {
$this->userMountCache = $userMountCache;
}
public function listen(Manager $manager) {
$manager->listen('\OC\User', 'postDelete', [$this->userMountCache, 'removeUserMounts']);
}
}

View File

@ -59,8 +59,10 @@
namespace OC\Files;
use OC\Files\Config\MountProviderCollection;
use OC\Files\Mount\MountPoint;
use OC\Files\Storage\StorageFactory;
use OCP\Files\Config\IMountProvider;
use OCP\Files\Mount\IMountPoint;
use OCP\Files\NotFoundException;
use OCP\IUserManager;
@ -412,7 +414,8 @@ class Filesystem {
$homeStorage['arguments']['legacy'] = true;
}
self::mount($homeStorage['class'], $homeStorage['arguments'], $user);
$mount = new MountPoint($homeStorage['class'], '/' . $user, $homeStorage['arguments'], self::getLoader());
self::getMountManager()->addMount($mount);
$home = \OC\Files\Filesystem::getStorage($user);
@ -424,6 +427,8 @@ class Filesystem {
if ($userObject) {
$mounts = $mountConfigManager->getMountsForUser($userObject);
array_walk($mounts, array(self::$mounts, 'addMount'));
$mounts[] = $mount;
$mountConfigManager->registerMounts($userObject, $mounts);
}
self::listenForNewMountProviders($mountConfigManager, $userManager);

View File

@ -47,6 +47,8 @@ use OC\Diagnostics\EventLogger;
use OC\Diagnostics\NullEventLogger;
use OC\Diagnostics\NullQueryLogger;
use OC\Diagnostics\QueryLogger;
use OC\Files\Config\UserMountCache;
use OC\Files\Config\UserMountCacheListener;
use OC\Files\Node\HookConnector;
use OC\Files\Node\Root;
use OC\Files\View;
@ -136,7 +138,7 @@ class Server extends ServerContainer implements IServerContainer {
return new Encryption\Keys\Storage($view, $util);
});
$this->registerService('TagMapper', function(Server $c) {
$this->registerService('TagMapper', function (Server $c) {
return new TagMapper($c->getDatabaseConnection());
});
$this->registerService('TagManager', function (Server $c) {
@ -276,13 +278,13 @@ class Server extends ServerContainer implements IServerContainer {
$this->registerService('MemCacheFactory', function (Server $c) {
$config = $c->getConfig();
if($config->getSystemValue('installed', false) && !(defined('PHPUNIT_RUN') && PHPUNIT_RUN)) {
if ($config->getSystemValue('installed', false) && !(defined('PHPUNIT_RUN') && PHPUNIT_RUN)) {
$v = \OC_App::getAppVersions();
$v['core'] = md5(file_get_contents(\OC::$SERVERROOT . '/version.php'));
$version = implode(',', $v);
$instanceId = \OC_Util::getInstanceId();
$path = \OC::$SERVERROOT;
$prefix = md5($instanceId.'-'.$version.'-'.$path);
$prefix = md5($instanceId . '-' . $version . '-' . $path);
return new \OC\Memcache\Factory($prefix, $c->getLogger(),
$config->getSystemValue('memcache.local', null),
$config->getSystemValue('memcache.distributed', null),
@ -393,7 +395,7 @@ class Server extends ServerContainer implements IServerContainer {
$c->getConfig()
);
});
$this->registerService('AppManager', function(Server $c) {
$this->registerService('AppManager', function (Server $c) {
return new \OC\App\AppManager(
$c->getUserSession(),
$c->getAppConfig(),
@ -401,13 +403,13 @@ class Server extends ServerContainer implements IServerContainer {
$c->getMemCacheFactory()
);
});
$this->registerService('DateTimeZone', function(Server $c) {
$this->registerService('DateTimeZone', function (Server $c) {
return new DateTimeZone(
$c->getConfig(),
$c->getSession()
);
});
$this->registerService('DateTimeFormatter', function(Server $c) {
$this->registerService('DateTimeFormatter', function (Server $c) {
$language = $c->getConfig()->getUserValue($c->getSession()->get('user_id'), 'core', 'lang', null);
return new DateTimeFormatter(
@ -415,9 +417,16 @@ class Server extends ServerContainer implements IServerContainer {
$c->getL10N('lib', $language)
);
});
$this->registerService('MountConfigManager', function () {
$this->registerService('UserMountCache', function (Server $c) {
$mountCache = new UserMountCache($c->getDatabaseConnection(), $c->getUserManager(), $c->getLogger());
$listener = new UserMountCacheListener($mountCache);
$listener->listen($c->getUserManager());
return $mountCache;
});
$this->registerService('MountConfigManager', function (Server $c) {
$loader = \OC\Files\Filesystem::getLoader();
return new \OC\Files\Config\MountProviderCollection($loader);
$mountCache = $c->query('UserMountCache');
return new \OC\Files\Config\MountProviderCollection($loader, $mountCache);
});
$this->registerService('IniWrapper', function ($c) {
return new IniGetWrapper();
@ -489,14 +498,14 @@ class Server extends ServerContainer implements IServerContainer {
$stream
);
});
$this->registerService('Mailer', function(Server $c) {
$this->registerService('Mailer', function (Server $c) {
return new Mailer(
$c->getConfig(),
$c->getLogger(),
new \OC_Defaults()
);
});
$this->registerService('OcsClient', function(Server $c) {
$this->registerService('OcsClient', function (Server $c) {
return new OCSClient(
$this->getHTTPClientService(),
$this->getConfig(),
@ -518,24 +527,24 @@ class Server extends ServerContainer implements IServerContainer {
$this->registerService('MountManager', function () {
return new \OC\Files\Mount\Manager();
});
$this->registerService('MimeTypeDetector', function(Server $c) {
$this->registerService('MimeTypeDetector', function (Server $c) {
return new \OC\Files\Type\Detection(
$c->getURLGenerator(),
\OC::$SERVERROOT . '/config/',
\OC::$SERVERROOT . '/resources/config/'
);
);
});
$this->registerService('MimeTypeLoader', function(Server $c) {
$this->registerService('MimeTypeLoader', function (Server $c) {
return new \OC\Files\Type\Loader(
$c->getDatabaseConnection()
);
});
$this->registerService('NotificationManager', function() {
$this->registerService('NotificationManager', function () {
return new Manager();
});
$this->registerService('CapabilitiesManager', function (Server $c) {
$manager = new \OC\CapabilitiesManager();
$manager->registerCapability(function() use ($c) {
$manager->registerCapability(function () use ($c) {
return new \OC\OCS\CoreCapabilities($c->getConfig());
});
return $manager;
@ -547,7 +556,7 @@ class Server extends ServerContainer implements IServerContainer {
$factory = new $factoryClass($this);
return $factory->getManager();
});
$this->registerService('EventDispatcher', function() {
$this->registerService('EventDispatcher', function () {
return new EventDispatcher();
});
$this->registerService('CryptoWrapper', function (Server $c) {
@ -932,6 +941,7 @@ class Server extends ServerContainer implements IServerContainer {
/**
* Returns an instance of the db facade
*
* @deprecated use getDatabaseConnection, will be removed in ownCloud 10
* @return \OCP\IDb
*/
@ -941,6 +951,7 @@ class Server extends ServerContainer implements IServerContainer {
/**
* Returns an instance of the HTTP helper class
*
* @deprecated Use getHTTPClientService()
* @return \OC\HTTPHelper
*/
@ -1066,7 +1077,7 @@ class Server extends ServerContainer implements IServerContainer {
/**
* @return \OCP\Files\Config\IMountProviderCollection
*/
public function getMountProviderCollection(){
public function getMountProviderCollection() {
return $this->query('MountConfigManager');
}
@ -1082,7 +1093,7 @@ class Server extends ServerContainer implements IServerContainer {
/**
* @return \OCP\Command\IBus
*/
public function getCommandBus(){
public function getCommandBus() {
return $this->query('AsyncCommandBus');
}
@ -1182,6 +1193,7 @@ class Server extends ServerContainer implements IServerContainer {
/**
* Not a public API as of 8.2, wait for 9.0
*
* @return \OCA\Files_External\Service\BackendService
*/
public function getStoragesBackendService() {
@ -1190,6 +1202,7 @@ class Server extends ServerContainer implements IServerContainer {
/**
* Not a public API as of 8.2, wait for 9.0
*
* @return \OCA\Files_External\Service\GlobalStoragesService
*/
public function getGlobalStoragesService() {
@ -1198,6 +1211,7 @@ class Server extends ServerContainer implements IServerContainer {
/**
* Not a public API as of 8.2, wait for 9.0
*
* @return \OCA\Files_External\Service\UserGlobalStoragesService
*/
public function getUserGlobalStoragesService() {
@ -1206,6 +1220,7 @@ class Server extends ServerContainer implements IServerContainer {
/**
* Not a public API as of 8.2, wait for 9.0
*
* @return \OCA\Files_External\Service\UserStoragesService
*/
public function getUserStoragesService() {
@ -1219,4 +1234,5 @@ class Server extends ServerContainer implements IServerContainer {
public function getShareManager() {
return $this->query('ShareManager');
}
}

View File

@ -0,0 +1,62 @@
<?php
/**
* @author Robin Appelman <icewind@owncloud.com>
*
* @copyright Copyright (c) 2015, ownCloud, Inc.
* @license AGPL-3.0
*
* This code is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License, version 3,
* along with this program. If not, see <http://www.gnu.org/licenses/>
*
*/
namespace OCP\Files\Config;
use OCP\Files\Node;
use OCP\IUser;
/**
* Holds information about a mount for a user
*
* @since 9.0.0
*/
interface ICachedMountInfo {
/**
* @return IUser
* @since 9.0.0
*/
public function getUser();
/**
* @return int the numeric storage id of the mount
* @since 9.0.0
*/
public function getStorageId();
/**
* @return int the fileid of the root of the mount
* @since 9.0.0
*/
public function getRootId();
/**
* @return Node the root node of the mount
* @since 9.0.0
*/
public function getMountPointNode();
/**
* @return string the mount point of the mount for the user
* @since 9.0.0
*/
public function getMountPoint();
}

View File

@ -22,6 +22,7 @@
namespace OCP\Files\Config;
use OCP\Files\Mount\IMountPoint;
use OCP\IUser;
/**
@ -45,4 +46,12 @@ interface IMountProviderCollection {
* @since 8.0.0
*/
public function registerProvider(IMountProvider $provider);
/**
* Get the mount cache which can be used to search for mounts without setting up the filesystem
*
* @return IUserMountCache
* @since 9.0.0
*/
public function getMountCache();
}

View File

@ -0,0 +1,89 @@
<?php
/**
* @author Robin Appelman <icewind@owncloud.com>
*
* @copyright Copyright (c) 2015, ownCloud, Inc.
* @license AGPL-3.0
*
* This code is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License, version 3,
* along with this program. If not, see <http://www.gnu.org/licenses/>
*
*/
namespace OCP\Files\Config;
use OCP\Files\Mount\IMountPoint;
use OCP\IUser;
/**
* Cache mounts points per user in the cache so we can easily look them up
*
* @since 9.0.0
*/
interface IUserMountCache {
/**
* Register mounts for a user to the cache
*
* @param IUser $user
* @param IMountPoint[] $mounts
* @since 9.0.0
*/
public function registerMounts(IUser $user, array $mounts);
/**
* @param IUser $user
* @return ICachedMountInfo[]
* @since 9.0.0
*/
public function getMountsForUser(IUser $user);
/**
* @param int $numericStorageId
* @return ICachedMountInfo[]
* @since 9.0.0
*/
public function getMountsForStorageId($numericStorageId);
/**
* @param int $rootFileId
* @return ICachedMountInfo[]
* @since 9.0.0
*/
public function getMountsForRootId($rootFileId);
/**
* Remove all cached mounts for a user
*
* @param IUser $user
* @since 9.0.0
*/
public function removeUserMounts(IUser $user);
/**
* Remove all mounts for a user and storage
*
* @param $storageId
* @param string $userId
* @return mixed
* @since 9.0.0
*/
public function removeUserStorageMount($storageId, $userId);
/**
* Remove all cached mounts for a storage
*
* @param $storageId
* @return mixed
* @since 9.0.0
*/
public function remoteStorageMounts($storageId);
}

View File

@ -0,0 +1,257 @@
<?php
/**
* Copyright (c) 2015 Robin Appelman <icewind@owncloud.com>
* 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\Files\Mount\MountPoint;
use OC\Files\Storage\Temporary;
use OC\Log;
use OC\User\Manager;
use OCP\Files\Config\ICachedMountInfo;
use OCP\IDBConnection;
use OCP\IUserManager;
use Test\TestCase;
use Test\Util\User\Dummy;
/**
* @group DB
*/
class UserMountCache extends TestCase {
/**
* @var IDBConnection
*/
private $connection;
/**
* @var IUserManager
*/
private $userManager;
/**
* @var \OC\Files\Config\UserMountCache
*/
private $cache;
public function setUp() {
$this->connection = \OC::$server->getDatabaseConnection();
$this->userManager = new Manager(null);
$userBackend = new Dummy();
$userBackend->createUser('u1', '');
$userBackend->createUser('u2', '');
$this->userManager->registerBackend($userBackend);
$this->cache = new \OC\Files\Config\UserMountCache($this->connection, $this->userManager, $this->getMock('\OC\Log'));
}
public function tearDown() {
$builder = $this->connection->getQueryBuilder();
$builder->delete('mounts')->execute();
}
private function getStorage($storageId, $rootId) {
$storageCache = $this->getMockBuilder('\OC\Files\Cache\Storage')
->disableOriginalConstructor()
->getMock();
$storageCache->expects($this->any())
->method('getNumericId')
->will($this->returnValue($storageId));
$cache = $this->getMockBuilder('\OC\Files\Cache\Cache')
->disableOriginalConstructor()
->getMock();
$cache->expects($this->any())
->method('getId')
->will($this->returnValue($rootId));
$storage = $this->getMockBuilder('\OC\Files\Storage\Storage')
->disableOriginalConstructor()
->getMock();
$storage->expects($this->any())
->method('getStorageCache')
->will($this->returnValue($storageCache));
$storage->expects($this->any())
->method('getCache')
->will($this->returnValue($cache));
return $storage;
}
private function clearCache() {
$this->invokePrivate($this->cache, 'mountsForUsers', [[]]);
}
public function testNewMounts() {
$user = $this->userManager->get('u1');
$storage = $this->getStorage(10, 20);
$mount = new MountPoint($storage, '/asd/');
$this->cache->registerMounts($user, [$mount]);
$this->clearCache();
$cachedMounts = $this->cache->getMountsForUser($user);
$this->assertCount(1, $cachedMounts);
$cachedMount = $cachedMounts[0];
$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, 20);
$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[0];
$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, 20);
$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, 20);
$mount = new MountPoint($storage, '/foo/');
$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[0];
$this->assertEquals('/foo/', $cachedMount->getMountPoint());
}
public function testGetMountsForUser() {
$user1 = $this->userManager->get('u1');
$user2 = $this->userManager->get('u2');
$mount1 = new MountPoint($this->getStorage(1, 2), '/foo/');
$mount2 = new MountPoint($this->getStorage(3, 4), '/bar/');
$this->cache->registerMounts($user1, [$mount1, $mount2]);
$this->cache->registerMounts($user2, [$mount2]);
$this->clearCache();
$cachedMounts = $this->cache->getMountsForUser($user1);
$this->assertCount(2, $cachedMounts);
$this->assertEquals('/foo/', $cachedMounts[0]->getMountPoint());
$this->assertEquals($user1, $cachedMounts[0]->getUser());
$this->assertEquals(2, $cachedMounts[0]->getRootId());
$this->assertEquals(1, $cachedMounts[0]->getStorageId());
$this->assertEquals('/bar/', $cachedMounts[1]->getMountPoint());
$this->assertEquals($user1, $cachedMounts[1]->getUser());
$this->assertEquals(4, $cachedMounts[1]->getRootId());
$this->assertEquals(3, $cachedMounts[1]->getStorageId());
}
public function testGetMountsByStorageId() {
$user1 = $this->userManager->get('u1');
$user2 = $this->userManager->get('u2');
$mount1 = new MountPoint($this->getStorage(1, 2), '/foo/');
$mount2 = new MountPoint($this->getStorage(3, 4), '/bar/');
$this->cache->registerMounts($user1, [$mount1, $mount2]);
$this->cache->registerMounts($user2, [$mount2]);
$this->clearCache();
$cachedMounts = $this->cache->getMountsForStorageId(3);
usort($cachedMounts, function (ICachedMountInfo $a, ICachedMountInfo $b) {
return strcmp($a->getUser()->getUID(), $b->getUser()->getUID());
});
$this->assertCount(2, $cachedMounts);
$this->assertEquals('/bar/', $cachedMounts[0]->getMountPoint());
$this->assertEquals($user1, $cachedMounts[0]->getUser());
$this->assertEquals(4, $cachedMounts[0]->getRootId());
$this->assertEquals(3, $cachedMounts[0]->getStorageId());
$this->assertEquals('/bar/', $cachedMounts[1]->getMountPoint());
$this->assertEquals($user2, $cachedMounts[1]->getUser());
$this->assertEquals(4, $cachedMounts[1]->getRootId());
$this->assertEquals(3, $cachedMounts[1]->getStorageId());
}
public function testGetMountsByRootId() {
$user1 = $this->userManager->get('u1');
$user2 = $this->userManager->get('u2');
$mount1 = new MountPoint($this->getStorage(1, 2), '/foo/');
$mount2 = new MountPoint($this->getStorage(3, 4), '/bar/');
$this->cache->registerMounts($user1, [$mount1, $mount2]);
$this->cache->registerMounts($user2, [$mount2]);
$this->clearCache();
$cachedMounts = $this->cache->getMountsForRootId(4);
usort($cachedMounts, function (ICachedMountInfo $a, ICachedMountInfo $b) {
return strcmp($a->getUser()->getUID(), $b->getUser()->getUID());
});
$this->assertCount(2, $cachedMounts);
$this->assertEquals('/bar/', $cachedMounts[0]->getMountPoint());
$this->assertEquals($user1, $cachedMounts[0]->getUser());
$this->assertEquals(4, $cachedMounts[0]->getRootId());
$this->assertEquals(3, $cachedMounts[0]->getStorageId());
$this->assertEquals('/bar/', $cachedMounts[1]->getMountPoint());
$this->assertEquals($user2, $cachedMounts[1]->getUser());
$this->assertEquals(4, $cachedMounts[1]->getRootId());
$this->assertEquals(3, $cachedMounts[1]->getStorageId());
}
}