migrate therinaldos.com data
Build & Deploy to DigitalOcean Space / build (push) Failing after 2m38s

This commit is contained in:
2024-05-05 15:50:45 -04:00
commit ef1ff240d4
23182 changed files with 3801898 additions and 0 deletions
@@ -0,0 +1,57 @@
<?php
/**
* @copyright Copyright (c) 2017 Robin Appelman <robin@icewind.nl>
*
* @author Robin Appelman <robin@icewind.nl>
* @author Roeland Jago Douma <roeland@famdouma.nl>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* 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
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OC\Files\Config;
use OCP\Files\Config\ICachedMountFileInfo;
use OCP\IUser;
class CachedMountFileInfo extends CachedMountInfo implements ICachedMountFileInfo {
private string $internalPath;
public function __construct(
IUser $user,
int $storageId,
int $rootId,
string $mountPoint,
?int $mountId,
string $mountProvider,
string $rootInternalPath,
string $internalPath
) {
parent::__construct($user, $storageId, $rootId, $mountPoint, $mountProvider, $mountId, $rootInternalPath);
$this->internalPath = $internalPath;
}
public function getInternalPath(): string {
if ($this->getRootInternalPath()) {
return substr($this->internalPath, strlen($this->getRootInternalPath()) + 1);
} else {
return $this->internalPath;
}
}
public function getPath(): string {
return $this->getMountPoint() . $this->getInternalPath();
}
}
@@ -0,0 +1,141 @@
<?php
/**
* @copyright Copyright (c) 2016, ownCloud, Inc.
*
* @author Robin Appelman <robin@icewind.nl>
* @author Semih Serhat Karakaya <karakayasemi@itu.edu.tr>
*
* @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 {
protected IUser $user;
protected int $storageId;
protected int $rootId;
protected string $mountPoint;
protected ?int $mountId;
protected string $rootInternalPath;
protected string $mountProvider;
protected string $key;
/**
* CachedMountInfo constructor.
*
* @param IUser $user
* @param int $storageId
* @param int $rootId
* @param string $mountPoint
* @param int|null $mountId
* @param string $rootInternalPath
*/
public function __construct(
IUser $user,
int $storageId,
int $rootId,
string $mountPoint,
string $mountProvider,
int $mountId = null,
string $rootInternalPath = ''
) {
$this->user = $user;
$this->storageId = $storageId;
$this->rootId = $rootId;
$this->mountPoint = $mountPoint;
$this->mountId = $mountId;
$this->rootInternalPath = $rootInternalPath;
if (strlen($mountProvider) > 128) {
throw new \Exception("Mount provider $mountProvider name exceeds the limit of 128 characters");
}
$this->mountProvider = $mountProvider;
$this->key = $rootId . '::' . $mountPoint;
}
/**
* @return IUser
*/
public function getUser(): IUser {
return $this->user;
}
/**
* @return int the numeric storage id of the mount
*/
public function getStorageId(): int {
return $this->storageId;
}
/**
* @return int the fileid of the root of the mount
*/
public function getRootId(): int {
return $this->rootId;
}
/**
* @return Node|null the root node of the mount
*/
public function getMountPointNode(): ?Node {
// TODO injection etc
Filesystem::initMountPoints($this->getUser()->getUID());
$userNode = \OC::$server->getUserFolder($this->getUser()->getUID());
$nodes = $userNode->getParent()->getById($this->getRootId());
if (count($nodes) > 0) {
return $nodes[0];
} else {
return null;
}
}
/**
* @return string the mount point of the mount for the user
*/
public function getMountPoint(): string {
return $this->mountPoint;
}
/**
* Get the id of the configured mount
*
* @return int|null mount id or null if not applicable
* @since 9.1.0
*/
public function getMountId(): ?int {
return $this->mountId;
}
/**
* Get the internal path (within the storage) of the root of the mount
*
* @return string
*/
public function getRootInternalPath(): string {
return $this->rootInternalPath;
}
public function getMountProvider(): string {
return $this->mountProvider;
}
public function getKey(): string {
return $this->key;
}
}
@@ -0,0 +1,98 @@
<?php
/**
* @copyright Copyright (c) 2016, ownCloud, Inc.
*
* @author Robin Appelman <robin@icewind.nl>
*
* @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 OCP\Files\Mount\IMountPoint;
use OCP\IUser;
class LazyStorageMountInfo extends CachedMountInfo {
private IMountPoint $mount;
/**
* CachedMountInfo constructor.
*
* @param IUser $user
* @param IMountPoint $mount
*/
public function __construct(IUser $user, IMountPoint $mount) {
$this->user = $user;
$this->mount = $mount;
$this->rootId = 0;
$this->storageId = 0;
$this->mountPoint = '';
$this->key = '';
}
/**
* @return int the numeric storage id of the mount
*/
public function getStorageId(): int {
if (!$this->storageId) {
$this->storageId = $this->mount->getNumericStorageId();
}
return parent::getStorageId();
}
/**
* @return int the fileid of the root of the mount
*/
public function getRootId(): int {
if (!$this->rootId) {
$this->rootId = $this->mount->getStorageRootId();
}
return parent::getRootId();
}
/**
* @return string the mount point of the mount for the user
*/
public function getMountPoint(): string {
if (!$this->mountPoint) {
$this->mountPoint = $this->mount->getMountPoint();
}
return parent::getMountPoint();
}
public function getMountId(): ?int {
return $this->mount->getMountId();
}
/**
* Get the internal path (within the storage) of the root of the mount
*
* @return string
*/
public function getRootInternalPath(): string {
return $this->mount->getInternalPath($this->mount->getMountPoint());
}
public function getMountProvider(): string {
return $this->mount->getMountProvider();
}
public function getKey(): string {
if (!$this->key) {
$this->key = $this->getRootId() . '::' . $this->getMountPoint();
}
return $this->key;
}
}
@@ -0,0 +1,258 @@
<?php
/**
* @copyright Copyright (c) 2016, ownCloud, Inc.
*
* @author Christoph Wurst <christoph@winzerhof-wurst.at>
* @author Morris Jobke <hey@morrisjobke.de>
* @author Robin Appelman <robin@icewind.nl>
* @author Roeland Jago Douma <roeland@famdouma.nl>
*
* @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\Hooks\Emitter;
use OC\Hooks\EmitterTrait;
use OCP\Diagnostics\IEventLogger;
use OCP\Files\Config\IHomeMountProvider;
use OCP\Files\Config\IMountProvider;
use OCP\Files\Config\IMountProviderCollection;
use OCP\Files\Config\IRootMountProvider;
use OCP\Files\Config\IUserMountCache;
use OCP\Files\Mount\IMountManager;
use OCP\Files\Mount\IMountPoint;
use OCP\Files\Storage\IStorageFactory;
use OCP\IUser;
class MountProviderCollection implements IMountProviderCollection, Emitter {
use EmitterTrait;
/**
* @var \OCP\Files\Config\IHomeMountProvider[]
*/
private $homeProviders = [];
/**
* @var \OCP\Files\Config\IMountProvider[]
*/
private $providers = [];
/** @var \OCP\Files\Config\IRootMountProvider[] */
private $rootProviders = [];
/**
* @var \OCP\Files\Storage\IStorageFactory
*/
private $loader;
/**
* @var \OCP\Files\Config\IUserMountCache
*/
private $mountCache;
/** @var callable[] */
private $mountFilters = [];
private IEventLogger $eventLogger;
/**
* @param \OCP\Files\Storage\IStorageFactory $loader
* @param IUserMountCache $mountCache
*/
public function __construct(
IStorageFactory $loader,
IUserMountCache $mountCache,
IEventLogger $eventLogger
) {
$this->loader = $loader;
$this->mountCache = $mountCache;
$this->eventLogger = $eventLogger;
}
private function getMountsFromProvider(IMountProvider $provider, IUser $user, IStorageFactory $loader): array {
$class = str_replace('\\', '_', get_class($provider));
$uid = $user->getUID();
$this->eventLogger->start('fs:setup:provider:' . $class, "Getting mounts from $class for $uid");
$mounts = $provider->getMountsForUser($user, $loader) ?? [];
$this->eventLogger->end('fs:setup:provider:' . $class);
return $mounts;
}
/**
* @param IUser $user
* @param IMountProvider[] $providers
* @return IMountPoint[]
*/
private function getUserMountsForProviders(IUser $user, array $providers): array {
$loader = $this->loader;
$mounts = array_map(function (IMountProvider $provider) use ($user, $loader) {
return $this->getMountsFromProvider($provider, $user, $loader);
}, $providers);
$mounts = array_reduce($mounts, function (array $mounts, array $providerMounts) {
return array_merge($mounts, $providerMounts);
}, []);
return $this->filterMounts($user, $mounts);
}
public function getMountsForUser(IUser $user): array {
return $this->getUserMountsForProviders($user, $this->providers);
}
public function getUserMountsForProviderClasses(IUser $user, array $mountProviderClasses): array {
$providers = array_filter(
$this->providers,
fn (IMountProvider $mountProvider) => (in_array(get_class($mountProvider), $mountProviderClasses))
);
return $this->getUserMountsForProviders($user, $providers);
}
public function addMountForUser(IUser $user, IMountManager $mountManager, callable $providerFilter = null) {
// shared mount provider gets to go last since it needs to know existing files
// to check for name collisions
$firstMounts = [];
if ($providerFilter) {
$providers = array_filter($this->providers, $providerFilter);
} else {
$providers = $this->providers;
}
$firstProviders = array_filter($providers, function (IMountProvider $provider) {
return (get_class($provider) !== 'OCA\Files_Sharing\MountProvider');
});
$lastProviders = array_filter($providers, function (IMountProvider $provider) {
return (get_class($provider) === 'OCA\Files_Sharing\MountProvider');
});
foreach ($firstProviders as $provider) {
$mounts = $this->getMountsFromProvider($provider, $user, $this->loader);
$firstMounts = array_merge($firstMounts, $mounts);
}
$firstMounts = $this->filterMounts($user, $firstMounts);
array_walk($firstMounts, [$mountManager, 'addMount']);
$lateMounts = [];
foreach ($lastProviders as $provider) {
$mounts = $this->getMountsFromProvider($provider, $user, $this->loader);
$lateMounts = array_merge($lateMounts, $mounts);
}
$lateMounts = $this->filterMounts($user, $lateMounts);
$this->eventLogger->start("fs:setup:add-mounts", "Add mounts to the filesystem");
array_walk($lateMounts, [$mountManager, 'addMount']);
$this->eventLogger->end("fs:setup:add-mounts");
return array_merge($lateMounts, $firstMounts);
}
/**
* Get the configured home mount for this user
*
* @param \OCP\IUser $user
* @return \OCP\Files\Mount\IMountPoint
* @since 9.1.0
*/
public function getHomeMountForUser(IUser $user) {
/** @var \OCP\Files\Config\IHomeMountProvider[] $providers */
$providers = array_reverse($this->homeProviders); // call the latest registered provider first to give apps an opportunity to overwrite builtin
foreach ($providers as $homeProvider) {
if ($mount = $homeProvider->getHomeMountForUser($user, $this->loader)) {
$mount->setMountPoint('/' . $user->getUID()); //make sure the mountpoint is what we expect
return $mount;
}
}
throw new \Exception('No home storage configured for user ' . $user);
}
/**
* Add a provider for mount points
*
* @param \OCP\Files\Config\IMountProvider $provider
*/
public function registerProvider(IMountProvider $provider) {
$this->providers[] = $provider;
$this->emit('\OC\Files\Config', 'registerMountProvider', [$provider]);
}
public function registerMountFilter(callable $filter) {
$this->mountFilters[] = $filter;
}
private function filterMounts(IUser $user, array $mountPoints) {
return array_filter($mountPoints, function (IMountPoint $mountPoint) use ($user) {
foreach ($this->mountFilters as $filter) {
if ($filter($mountPoint, $user) === false) {
return false;
}
}
return true;
});
}
/**
* Add a provider for home mount points
*
* @param \OCP\Files\Config\IHomeMountProvider $provider
* @since 9.1.0
*/
public function registerHomeProvider(IHomeMountProvider $provider) {
$this->homeProviders[] = $provider;
$this->emit('\OC\Files\Config', 'registerHomeMountProvider', [$provider]);
}
/**
* 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;
}
public function registerRootProvider(IRootMountProvider $provider) {
$this->rootProviders[] = $provider;
}
/**
* Get all root mountpoints
*
* @return \OCP\Files\Mount\IMountPoint[]
* @since 20.0.0
*/
public function getRootMounts(): array {
$loader = $this->loader;
$mounts = array_map(function (IRootMountProvider $provider) use ($loader) {
return $provider->getRootMounts($loader);
}, $this->rootProviders);
$mounts = array_reduce($mounts, function (array $mounts, array $providerMounts) {
return array_merge($mounts, $providerMounts);
}, []);
if (count($mounts) === 0) {
throw new \Exception("No root mounts provided by any provider");
}
return $mounts;
}
public function clearProviders() {
$this->providers = [];
$this->homeProviders = [];
$this->rootProviders = [];
}
public function getProviders(): array {
return $this->providers;
}
}
@@ -0,0 +1,483 @@
<?php
/**
* @copyright Copyright (c) 2016, ownCloud, Inc.
*
* @author Dariusz Olszewski <starypatyk@users.noreply.github.com>
* @author Joas Schilling <coding@schilljs.com>
* @author Julius Härtl <jus@bitgrid.net>
* @author Lukas Reschke <lukas@statuscode.ch>
* @author Morris Jobke <hey@morrisjobke.de>
* @author Robin Appelman <robin@icewind.nl>
* @author Roeland Jago Douma <roeland@famdouma.nl>
* @author Vincent Petry <vincent@nextcloud.com>
*
* @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 OCP\Cache\CappedMemoryCache;
use OCP\DB\QueryBuilder\IQueryBuilder;
use OCP\Diagnostics\IEventLogger;
use OCP\Files\Config\ICachedMountFileInfo;
use OCP\Files\Config\ICachedMountInfo;
use OCP\Files\Config\IUserMountCache;
use OCP\Files\NotFoundException;
use OCP\IDBConnection;
use OCP\IUser;
use OCP\IUserManager;
use Psr\Log\LoggerInterface;
/**
* Cache mounts points per user in the cache so we can easily look them up
*/
class UserMountCache implements IUserMountCache {
private IDBConnection $connection;
private IUserManager $userManager;
/**
* Cached mount info.
* @var CappedMemoryCache<ICachedMountInfo[]>
**/
private CappedMemoryCache $mountsForUsers;
private LoggerInterface $logger;
/** @var CappedMemoryCache<array> */
private CappedMemoryCache $cacheInfoCache;
private IEventLogger $eventLogger;
/**
* UserMountCache constructor.
*/
public function __construct(
IDBConnection $connection,
IUserManager $userManager,
LoggerInterface $logger,
IEventLogger $eventLogger
) {
$this->connection = $connection;
$this->userManager = $userManager;
$this->logger = $logger;
$this->eventLogger = $eventLogger;
$this->cacheInfoCache = new CappedMemoryCache();
$this->mountsForUsers = new CappedMemoryCache();
}
public function registerMounts(IUser $user, array $mounts, array $mountProviderClasses = null) {
$this->eventLogger->start('fs:setup:user:register', 'Registering mounts for user');
/** @var array<string, ICachedMountInfo> $newMounts */
$newMounts = [];
foreach ($mounts as $mount) {
// filter out any storages which aren't scanned yet since we aren't interested in files from those storages (yet)
if ($mount->getStorageRootId() !== -1) {
$mountInfo = new LazyStorageMountInfo($user, $mount);
$newMounts[$mountInfo->getKey()] = $mountInfo;
}
}
$cachedMounts = $this->getMountsForUser($user);
if (is_array($mountProviderClasses)) {
$cachedMounts = array_filter($cachedMounts, function (ICachedMountInfo $mountInfo) use ($mountProviderClasses, $newMounts) {
// for existing mounts that didn't have a mount provider set
// we still want the ones that map to new mounts
if ($mountInfo->getMountProvider() === '' && isset($newMounts[$mountInfo->getKey()])) {
return true;
}
return in_array($mountInfo->getMountProvider(), $mountProviderClasses);
});
}
$addedMounts = [];
$removedMounts = [];
foreach ($newMounts as $mountKey => $newMount) {
if (!isset($cachedMounts[$mountKey])) {
$addedMounts[] = $newMount;
}
}
foreach ($cachedMounts as $mountKey => $cachedMount) {
if (!isset($newMounts[$mountKey])) {
$removedMounts[] = $cachedMount;
}
}
$changedMounts = $this->findChangedMounts($newMounts, $cachedMounts);
if ($addedMounts || $removedMounts || $changedMounts) {
$this->connection->beginTransaction();
$userUID = $user->getUID();
try {
foreach ($addedMounts as $mount) {
$this->addToCache($mount);
/** @psalm-suppress InvalidArgument */
$this->mountsForUsers[$userUID][$mount->getKey()] = $mount;
}
foreach ($removedMounts as $mount) {
$this->removeFromCache($mount);
unset($this->mountsForUsers[$userUID][$mount->getKey()]);
}
foreach ($changedMounts as $mount) {
$this->updateCachedMount($mount);
/** @psalm-suppress InvalidArgument */
$this->mountsForUsers[$userUID][$mount->getKey()] = $mount;
}
$this->connection->commit();
} catch (\Throwable $e) {
$this->connection->rollBack();
throw $e;
}
}
$this->eventLogger->end('fs:setup:user:register');
}
/**
* @param array<string, ICachedMountInfo> $newMounts
* @param array<string, ICachedMountInfo> $cachedMounts
* @return ICachedMountInfo[]
*/
private function findChangedMounts(array $newMounts, array $cachedMounts) {
$changed = [];
foreach ($cachedMounts as $key => $cachedMount) {
if (isset($newMounts[$key])) {
$newMount = $newMounts[$key];
if (
$newMount->getStorageId() !== $cachedMount->getStorageId() ||
$newMount->getMountId() !== $cachedMount->getMountId() ||
$newMount->getMountProvider() !== $cachedMount->getMountProvider()
) {
$changed[] = $newMount;
}
}
}
return $changed;
}
private function addToCache(ICachedMountInfo $mount) {
if ($mount->getStorageId() !== -1) {
$this->connection->insertIfNotExist('*PREFIX*mounts', [
'storage_id' => $mount->getStorageId(),
'root_id' => $mount->getRootId(),
'user_id' => $mount->getUser()->getUID(),
'mount_point' => $mount->getMountPoint(),
'mount_id' => $mount->getMountId(),
'mount_provider_class' => $mount->getMountProvider(),
], ['root_id', 'user_id', 'mount_point']);
} else {
// in some cases this is legitimate, like orphaned shares
$this->logger->debug('Could not get storage info for mount at ' . $mount->getMountPoint());
}
}
private function updateCachedMount(ICachedMountInfo $mount) {
$builder = $this->connection->getQueryBuilder();
$query = $builder->update('mounts')
->set('storage_id', $builder->createNamedParameter($mount->getStorageId()))
->set('mount_point', $builder->createNamedParameter($mount->getMountPoint()))
->set('mount_id', $builder->createNamedParameter($mount->getMountId(), IQueryBuilder::PARAM_INT))
->set('mount_provider_class', $builder->createNamedParameter($mount->getMountProvider()))
->where($builder->expr()->eq('user_id', $builder->createNamedParameter($mount->getUser()->getUID())))
->andWhere($builder->expr()->eq('root_id', $builder->createNamedParameter($mount->getRootId(), IQueryBuilder::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(), IQueryBuilder::PARAM_INT)))
->andWhere($builder->expr()->eq('mount_point', $builder->createNamedParameter($mount->getMountPoint())));
$query->execute();
}
private function dbRowToMountInfo(array $row) {
$user = $this->userManager->get($row['user_id']);
if (is_null($user)) {
return null;
}
$mount_id = $row['mount_id'];
if (!is_null($mount_id)) {
$mount_id = (int)$mount_id;
}
return new CachedMountInfo(
$user,
(int)$row['storage_id'],
(int)$row['root_id'],
$row['mount_point'],
$row['mount_provider_class'] ?? '',
$mount_id,
$row['path'] ?? '',
);
}
/**
* @param IUser $user
* @return ICachedMountInfo[]
*/
public function getMountsForUser(IUser $user) {
$userUID = $user->getUID();
if (!isset($this->mountsForUsers[$userUID])) {
$builder = $this->connection->getQueryBuilder();
$query = $builder->select('storage_id', 'root_id', 'user_id', 'mount_point', 'mount_id', 'f.path', 'mount_provider_class')
->from('mounts', 'm')
->innerJoin('m', 'filecache', 'f', $builder->expr()->eq('m.root_id', 'f.fileid'))
->where($builder->expr()->eq('user_id', $builder->createPositionalParameter($userUID)));
$result = $query->execute();
$rows = $result->fetchAll();
$result->closeCursor();
$this->mountsForUsers[$userUID] = [];
/** @var array<string, ICachedMountInfo> $mounts */
foreach ($rows as $row) {
$mount = $this->dbRowToMountInfo($row);
if ($mount !== null) {
$this->mountsForUsers[$userUID][$mount->getKey()] = $mount;
}
}
}
return $this->mountsForUsers[$userUID];
}
/**
* @param int $numericStorageId
* @param string|null $user limit the results to a single user
* @return CachedMountInfo[]
*/
public function getMountsForStorageId($numericStorageId, $user = null) {
$builder = $this->connection->getQueryBuilder();
$query = $builder->select('storage_id', 'root_id', 'user_id', 'mount_point', 'mount_id', 'f.path', 'mount_provider_class')
->from('mounts', 'm')
->innerJoin('m', 'filecache', 'f', $builder->expr()->eq('m.root_id', 'f.fileid'))
->where($builder->expr()->eq('storage_id', $builder->createPositionalParameter($numericStorageId, IQueryBuilder::PARAM_INT)));
if ($user) {
$query->andWhere($builder->expr()->eq('user_id', $builder->createPositionalParameter($user)));
}
$result = $query->execute();
$rows = $result->fetchAll();
$result->closeCursor();
return array_filter(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', 'mount_id', 'f.path', 'mount_provider_class')
->from('mounts', 'm')
->innerJoin('m', 'filecache', 'f', $builder->expr()->eq('m.root_id', 'f.fileid'))
->where($builder->expr()->eq('root_id', $builder->createPositionalParameter($rootFileId, IQueryBuilder::PARAM_INT)));
$result = $query->execute();
$rows = $result->fetchAll();
$result->closeCursor();
return array_filter(array_map([$this, 'dbRowToMountInfo'], $rows));
}
/**
* @param $fileId
* @return array{int, string, int}
* @throws \OCP\Files\NotFoundException
*/
private function getCacheInfoFromFileId($fileId): array {
if (!isset($this->cacheInfoCache[$fileId])) {
$builder = $this->connection->getQueryBuilder();
$query = $builder->select('storage', 'path', 'mimetype')
->from('filecache')
->where($builder->expr()->eq('fileid', $builder->createNamedParameter($fileId, IQueryBuilder::PARAM_INT)));
$result = $query->execute();
$row = $result->fetch();
$result->closeCursor();
if (is_array($row)) {
$this->cacheInfoCache[$fileId] = [
(int)$row['storage'],
(string)$row['path'],
(int)$row['mimetype']
];
} else {
throw new NotFoundException('File with id "' . $fileId . '" not found');
}
}
return $this->cacheInfoCache[$fileId];
}
/**
* @param int $fileId
* @param string|null $user optionally restrict the results to a single user
* @return ICachedMountFileInfo[]
* @since 9.0.0
*/
public function getMountsForFileId($fileId, $user = null) {
try {
[$storageId, $internalPath] = $this->getCacheInfoFromFileId($fileId);
} catch (NotFoundException $e) {
return [];
}
$builder = $this->connection->getQueryBuilder();
$query = $builder->select('storage_id', 'root_id', 'user_id', 'mount_point', 'mount_id', 'f.path', 'mount_provider_class')
->from('mounts', 'm')
->innerJoin('m', 'filecache', 'f', $builder->expr()->eq('m.root_id', 'f.fileid'))
->where($builder->expr()->eq('storage_id', $builder->createPositionalParameter($storageId, IQueryBuilder::PARAM_INT)));
if ($user) {
$query->andWhere($builder->expr()->eq('user_id', $builder->createPositionalParameter($user)));
}
$result = $query->execute();
$rows = $result->fetchAll();
$result->closeCursor();
// filter mounts that are from the same storage but a different directory
$filteredMounts = array_filter($rows, function (array $row) use ($internalPath, $fileId) {
if ($fileId === (int)$row['root_id']) {
return true;
}
$internalMountPath = $row['path'] ?? '';
return $internalMountPath === '' || substr($internalPath, 0, strlen($internalMountPath) + 1) === $internalMountPath . '/';
});
$filteredMounts = array_filter(array_map([$this, 'dbRowToMountInfo'], $filteredMounts));
return array_map(function (ICachedMountInfo $mount) use ($internalPath) {
return new CachedMountFileInfo(
$mount->getUser(),
$mount->getStorageId(),
$mount->getRootId(),
$mount->getMountPoint(),
$mount->getMountId(),
$mount->getMountProvider(),
$mount->getRootInternalPath(),
$internalPath
);
}, $filteredMounts);
}
/**
* 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, IQueryBuilder::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, IQueryBuilder::PARAM_INT)));
$query->execute();
}
/**
* @param array $users
* @return array
*/
public function getUsedSpaceForUsers(array $users) {
$builder = $this->connection->getQueryBuilder();
$slash = $builder->createNamedParameter('/');
$mountPoint = $builder->func()->concat(
$builder->func()->concat($slash, 'user_id'),
$slash
);
$userIds = array_map(function (IUser $user) {
return $user->getUID();
}, $users);
$query = $builder->select('m.user_id', 'f.size')
->from('mounts', 'm')
->innerJoin('m', 'filecache', 'f',
$builder->expr()->andX(
$builder->expr()->eq('m.storage_id', 'f.storage'),
$builder->expr()->eq('f.path_hash', $builder->createNamedParameter(md5('files')))
))
->where($builder->expr()->eq('m.mount_point', $mountPoint))
->andWhere($builder->expr()->in('m.user_id', $builder->createNamedParameter($userIds, IQueryBuilder::PARAM_STR_ARRAY)));
$result = $query->execute();
$results = [];
while ($row = $result->fetch()) {
$results[$row['user_id']] = $row['size'];
}
$result->closeCursor();
return $results;
}
public function clear(): void {
$this->cacheInfoCache = new CappedMemoryCache();
$this->mountsForUsers = new CappedMemoryCache();
}
public function getMountForPath(IUser $user, string $path): ICachedMountInfo {
$mounts = $this->getMountsForUser($user);
$mountPoints = array_map(function (ICachedMountInfo $mount) {
return $mount->getMountPoint();
}, $mounts);
$mounts = array_combine($mountPoints, $mounts);
$current = rtrim($path, '/');
// walk up the directory tree until we find a path that has a mountpoint set
// the loop will return if a mountpoint is found or break if none are found
while (true) {
$mountPoint = $current . '/';
if (isset($mounts[$mountPoint])) {
return $mounts[$mountPoint];
} elseif ($current === '') {
break;
}
$current = dirname($current);
if ($current === '.' || $current === '/') {
$current = '';
}
}
throw new NotFoundException("No cached mount for path " . $path);
}
public function getMountsInPath(IUser $user, string $path): array {
$path = rtrim($path, '/') . '/';
$mounts = $this->getMountsForUser($user);
return array_filter($mounts, function (ICachedMountInfo $mount) use ($path) {
return $mount->getMountPoint() !== $path && str_starts_with($mount->getMountPoint(), $path);
});
}
}
@@ -0,0 +1,48 @@
<?php
/**
* @copyright Copyright (c) 2016, ownCloud, Inc.
*
* @author Robin Appelman <robin@icewind.nl>
*
* @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']);
}
}