2013-04-25 18:00:18 -04:00
|
|
|
<?php
|
2024-05-23 03:26:56 -04:00
|
|
|
|
2013-04-25 18:00:18 -04:00
|
|
|
/**
|
2024-05-23 03:26:56 -04:00
|
|
|
* SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
|
|
|
|
|
* SPDX-FileCopyrightText: 2016 ownCloud, Inc.
|
|
|
|
|
* SPDX-License-Identifier: AGPL-3.0-only
|
2013-04-25 18:00:18 -04:00
|
|
|
*/
|
|
|
|
|
namespace OC\Files\Cache;
|
|
|
|
|
|
2024-02-26 12:01:59 -05:00
|
|
|
use Doctrine\DBAL\Exception\UniqueConstraintViolationException;
|
2021-05-10 11:34:10 -04:00
|
|
|
use OCP\DB\QueryBuilder\IQueryBuilder;
|
2019-01-22 11:08:32 -05:00
|
|
|
use OCP\Files\Storage\IStorage;
|
2023-08-15 12:41:53 -04:00
|
|
|
use OCP\IDBConnection;
|
2021-03-25 11:14:20 -04:00
|
|
|
use Psr\Log\LoggerInterface;
|
2019-01-22 11:08:32 -05:00
|
|
|
|
2013-04-25 18:00:18 -04:00
|
|
|
/**
|
2015-05-05 10:06:28 -04:00
|
|
|
* Handle the mapping between the string and numeric storage ids
|
2013-04-25 18:00:18 -04:00
|
|
|
*
|
2015-05-05 10:06:28 -04:00
|
|
|
* Each storage has 2 different ids
|
|
|
|
|
* a string id which is generated by the storage backend and reflects the configuration of the storage (e.g. 'smb://user@host/share')
|
|
|
|
|
* and a numeric storage id which is referenced in the file cache
|
|
|
|
|
*
|
2022-07-27 08:51:42 -04:00
|
|
|
* A mapping between the two storage ids is stored in the database and accessible through this class
|
2013-04-25 18:00:18 -04:00
|
|
|
*
|
|
|
|
|
* @package OC\Files\Cache
|
|
|
|
|
*/
|
|
|
|
|
class Storage {
|
2016-09-02 09:41:59 -04:00
|
|
|
/** @var StorageGlobal|null */
|
|
|
|
|
private static $globalCache = null;
|
2013-04-25 18:00:18 -04:00
|
|
|
private $storageId;
|
|
|
|
|
private $numericId;
|
|
|
|
|
|
2016-09-02 09:41:59 -04:00
|
|
|
/**
|
|
|
|
|
* @return StorageGlobal
|
|
|
|
|
*/
|
|
|
|
|
public static function getGlobalCache() {
|
|
|
|
|
if (is_null(self::$globalCache)) {
|
|
|
|
|
self::$globalCache = new StorageGlobal(\OC::$server->getDatabaseConnection());
|
|
|
|
|
}
|
|
|
|
|
return self::$globalCache;
|
|
|
|
|
}
|
|
|
|
|
|
2013-04-25 18:00:18 -04:00
|
|
|
/**
|
|
|
|
|
* @param \OC\Files\Storage\Storage|string $storage
|
2015-01-23 16:53:21 -05:00
|
|
|
* @param bool $isAvailable
|
2015-03-11 04:33:50 -04:00
|
|
|
* @throws \RuntimeException
|
2013-04-25 18:00:18 -04:00
|
|
|
*/
|
2023-08-15 12:41:53 -04:00
|
|
|
public function __construct($storage, $isAvailable, IDBConnection $connection) {
|
2019-01-22 11:08:32 -05:00
|
|
|
if ($storage instanceof IStorage) {
|
2013-04-25 18:00:18 -04:00
|
|
|
$this->storageId = $storage->getId();
|
|
|
|
|
} else {
|
|
|
|
|
$this->storageId = $storage;
|
|
|
|
|
}
|
2014-03-25 07:52:32 -04:00
|
|
|
$this->storageId = self::adjustStorageId($this->storageId);
|
2013-04-25 18:00:18 -04:00
|
|
|
|
2015-01-23 16:53:21 -05:00
|
|
|
if ($row = self::getStorageById($this->storageId)) {
|
2016-08-22 07:35:51 -04:00
|
|
|
$this->numericId = (int)$row['numeric_id'];
|
2013-04-25 18:00:18 -04:00
|
|
|
} else {
|
2015-09-10 17:01:02 -04:00
|
|
|
$available = $isAvailable ? 1 : 0;
|
2024-02-26 12:01:59 -05:00
|
|
|
$query = $connection->getQueryBuilder();
|
|
|
|
|
$query->insert('storages')
|
|
|
|
|
->values([
|
|
|
|
|
'id' => $query->createNamedParameter($this->storageId),
|
|
|
|
|
'available' => $query->createNamedParameter($available, IQueryBuilder::PARAM_INT),
|
|
|
|
|
]);
|
|
|
|
|
try {
|
|
|
|
|
$query->executeStatement();
|
|
|
|
|
$this->numericId = $query->getLastInsertId();
|
|
|
|
|
} catch (UniqueConstraintViolationException $e) {
|
2015-01-23 16:53:21 -05:00
|
|
|
if ($row = self::getStorageById($this->storageId)) {
|
2016-08-22 07:35:51 -04:00
|
|
|
$this->numericId = (int)$row['numeric_id'];
|
2015-03-09 14:07:22 -04:00
|
|
|
} else {
|
2019-09-24 07:55:37 -04:00
|
|
|
throw new \RuntimeException('Storage could neither be inserted nor be selected from the database: ' . $this->storageId);
|
2015-03-09 14:02:34 -04:00
|
|
|
}
|
|
|
|
|
}
|
2013-04-25 18:00:18 -04:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-01-23 16:53:21 -05:00
|
|
|
/**
|
|
|
|
|
* @param string $storageId
|
2016-09-02 09:41:59 -04:00
|
|
|
* @return array
|
2015-01-23 16:53:21 -05:00
|
|
|
*/
|
|
|
|
|
public static function getStorageById($storageId) {
|
2016-09-02 09:41:59 -04:00
|
|
|
return self::getGlobalCache()->getStorageInfo($storageId);
|
2015-01-23 16:53:21 -05:00
|
|
|
}
|
|
|
|
|
|
2014-03-25 07:52:32 -04:00
|
|
|
/**
|
|
|
|
|
* Adjusts the storage id to use md5 if too long
|
|
|
|
|
* @param string $storageId storage id
|
2015-05-05 10:06:28 -04:00
|
|
|
* @return string unchanged $storageId if its length is less than 64 characters,
|
2024-08-23 09:10:27 -04:00
|
|
|
* else returns the md5 of $storageId
|
2014-03-25 07:52:32 -04:00
|
|
|
*/
|
|
|
|
|
public static function adjustStorageId($storageId) {
|
|
|
|
|
if (strlen($storageId) > 64) {
|
|
|
|
|
return md5($storageId);
|
|
|
|
|
}
|
|
|
|
|
return $storageId;
|
|
|
|
|
}
|
|
|
|
|
|
2014-06-06 05:39:35 -04:00
|
|
|
/**
|
2015-05-05 10:06:28 -04:00
|
|
|
* Get the numeric id for the storage
|
|
|
|
|
*
|
|
|
|
|
* @return int
|
2014-06-06 05:39:35 -04:00
|
|
|
*/
|
2013-04-25 18:00:18 -04:00
|
|
|
public function getNumericId() {
|
|
|
|
|
return $this->numericId;
|
|
|
|
|
}
|
|
|
|
|
|
2014-02-06 10:30:58 -05:00
|
|
|
/**
|
2015-05-05 10:06:28 -04:00
|
|
|
* Get the string id for the storage
|
|
|
|
|
*
|
|
|
|
|
* @param int $numericId
|
|
|
|
|
* @return string|null either the storage id string or null if the numeric id is not known
|
2014-02-06 10:30:58 -05:00
|
|
|
*/
|
2022-03-31 08:10:20 -04:00
|
|
|
public static function getStorageId(int $numericId): ?string {
|
|
|
|
|
$storage = self::getGlobalCache()->getStorageInfoByNumericId($numericId);
|
|
|
|
|
return $storage['id'] ?? null;
|
2013-04-25 18:00:18 -04:00
|
|
|
}
|
2013-10-28 19:26:35 -04:00
|
|
|
|
2014-02-06 10:30:58 -05:00
|
|
|
/**
|
2015-05-05 10:06:28 -04:00
|
|
|
* Get the numeric of the storage with the provided string id
|
|
|
|
|
*
|
|
|
|
|
* @param $storageId
|
2022-07-27 08:51:42 -04:00
|
|
|
* @return int|null either the numeric storage id or null if the storage id is not known
|
2014-02-06 10:30:58 -05:00
|
|
|
*/
|
2014-06-06 05:39:35 -04:00
|
|
|
public static function getNumericStorageId($storageId) {
|
2014-03-25 07:52:32 -04:00
|
|
|
$storageId = self::adjustStorageId($storageId);
|
2014-06-06 05:39:35 -04:00
|
|
|
|
2015-01-23 16:53:21 -05:00
|
|
|
if ($row = self::getStorageById($storageId)) {
|
2016-08-22 07:35:51 -04:00
|
|
|
return (int)$row['numeric_id'];
|
2013-10-28 19:26:35 -04:00
|
|
|
} else {
|
2014-06-06 05:39:35 -04:00
|
|
|
return null;
|
2013-10-28 19:26:35 -04:00
|
|
|
}
|
|
|
|
|
}
|
2014-01-21 17:58:48 -05:00
|
|
|
|
2015-01-23 16:53:21 -05:00
|
|
|
/**
|
2022-02-25 09:25:13 -05:00
|
|
|
* @return array [ available, last_checked ]
|
2015-01-23 16:53:21 -05:00
|
|
|
*/
|
|
|
|
|
public function getAvailability() {
|
|
|
|
|
if ($row = self::getStorageById($this->storageId)) {
|
|
|
|
|
return [
|
2018-01-26 17:46:40 -05:00
|
|
|
'available' => (int)$row['available'] === 1,
|
2015-01-23 16:53:21 -05:00
|
|
|
'last_checked' => $row['last_checked']
|
|
|
|
|
];
|
|
|
|
|
} else {
|
2022-02-25 09:25:13 -05:00
|
|
|
return [
|
|
|
|
|
'available' => true,
|
|
|
|
|
'last_checked' => time(),
|
|
|
|
|
];
|
2015-01-23 16:53:21 -05:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @param bool $isAvailable
|
2019-08-22 06:19:51 -04:00
|
|
|
* @param int $delay amount of seconds to delay reconsidering that storage further
|
2015-01-23 16:53:21 -05:00
|
|
|
*/
|
2019-08-22 06:19:51 -04:00
|
|
|
public function setAvailability($isAvailable, int $delay = 0) {
|
2015-09-10 17:01:02 -04:00
|
|
|
$available = $isAvailable ? 1 : 0;
|
2021-03-25 11:14:20 -04:00
|
|
|
if (!$isAvailable) {
|
|
|
|
|
\OC::$server->get(LoggerInterface::class)->info('Storage with ' . $this->storageId . ' marked as unavailable', ['app' => 'lib']);
|
|
|
|
|
}
|
2020-11-06 06:06:24 -05:00
|
|
|
|
|
|
|
|
$query = \OC::$server->getDatabaseConnection()->getQueryBuilder();
|
|
|
|
|
$query->update('storages')
|
|
|
|
|
->set('available', $query->createNamedParameter($available))
|
|
|
|
|
->set('last_checked', $query->createNamedParameter(time() + $delay))
|
|
|
|
|
->where($query->expr()->eq('id', $query->createNamedParameter($this->storageId)));
|
|
|
|
|
$query->execute();
|
2015-01-23 16:53:21 -05:00
|
|
|
}
|
|
|
|
|
|
2014-06-06 05:39:35 -04:00
|
|
|
/**
|
2015-05-05 10:06:28 -04:00
|
|
|
* Check if a string storage id is known
|
|
|
|
|
*
|
2014-06-06 05:39:35 -04:00
|
|
|
* @param string $storageId
|
|
|
|
|
* @return bool
|
|
|
|
|
*/
|
|
|
|
|
public static function exists($storageId) {
|
|
|
|
|
return !is_null(self::getNumericStorageId($storageId));
|
|
|
|
|
}
|
|
|
|
|
|
2014-01-21 17:58:48 -05:00
|
|
|
/**
|
|
|
|
|
* remove the entry for the storage
|
|
|
|
|
*
|
|
|
|
|
* @param string $storageId
|
|
|
|
|
*/
|
|
|
|
|
public static function remove($storageId) {
|
2014-03-25 07:52:32 -04:00
|
|
|
$storageId = self::adjustStorageId($storageId);
|
2014-10-13 09:52:48 -04:00
|
|
|
$numericId = self::getNumericStorageId($storageId);
|
2020-11-06 06:06:24 -05:00
|
|
|
|
|
|
|
|
$query = \OC::$server->getDatabaseConnection()->getQueryBuilder();
|
|
|
|
|
$query->delete('storages')
|
|
|
|
|
->where($query->expr()->eq('id', $query->createNamedParameter($storageId)));
|
|
|
|
|
$query->execute();
|
2014-02-03 10:36:21 -05:00
|
|
|
|
2014-06-06 05:39:35 -04:00
|
|
|
if (!is_null($numericId)) {
|
2020-11-06 06:06:24 -05:00
|
|
|
$query = \OC::$server->getDatabaseConnection()->getQueryBuilder();
|
|
|
|
|
$query->delete('filecache')
|
|
|
|
|
->where($query->expr()->eq('storage', $query->createNamedParameter($numericId)));
|
|
|
|
|
$query->execute();
|
2014-06-06 05:39:35 -04:00
|
|
|
}
|
2014-01-21 17:58:48 -05:00
|
|
|
}
|
2021-05-10 11:34:10 -04:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* remove the entry for the storage by the mount id
|
|
|
|
|
*
|
|
|
|
|
* @param int $mountId
|
|
|
|
|
*/
|
|
|
|
|
public static function cleanByMountId(int $mountId) {
|
|
|
|
|
$db = \OC::$server->getDatabaseConnection();
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
$db->beginTransaction();
|
|
|
|
|
|
|
|
|
|
$query = $db->getQueryBuilder();
|
|
|
|
|
$query->select('storage_id')
|
|
|
|
|
->from('mounts')
|
|
|
|
|
->where($query->expr()->eq('mount_id', $query->createNamedParameter($mountId, IQueryBuilder::PARAM_INT)));
|
|
|
|
|
$storageIds = $query->executeQuery()->fetchAll(\PDO::FETCH_COLUMN);
|
2022-04-04 01:06:55 -04:00
|
|
|
$storageIds = array_unique($storageIds);
|
2021-05-10 11:34:10 -04:00
|
|
|
|
|
|
|
|
$query = $db->getQueryBuilder();
|
|
|
|
|
$query->delete('filecache')
|
|
|
|
|
->where($query->expr()->in('storage', $query->createNamedParameter($storageIds, IQueryBuilder::PARAM_INT_ARRAY)));
|
|
|
|
|
$query->executeStatement();
|
|
|
|
|
|
|
|
|
|
$query = $db->getQueryBuilder();
|
|
|
|
|
$query->delete('storages')
|
2022-11-29 12:24:56 -05:00
|
|
|
->where($query->expr()->in('numeric_id', $query->createNamedParameter($storageIds, IQueryBuilder::PARAM_INT_ARRAY)));
|
2021-05-10 11:34:10 -04:00
|
|
|
$query->executeStatement();
|
|
|
|
|
|
|
|
|
|
$query = $db->getQueryBuilder();
|
|
|
|
|
$query->delete('mounts')
|
|
|
|
|
->where($query->expr()->eq('mount_id', $query->createNamedParameter($mountId, IQueryBuilder::PARAM_INT)));
|
|
|
|
|
$query->executeStatement();
|
|
|
|
|
|
|
|
|
|
$db->commit();
|
|
|
|
|
} catch (\Exception $e) {
|
|
|
|
|
$db->rollBack();
|
|
|
|
|
throw $e;
|
|
|
|
|
}
|
|
|
|
|
}
|
2013-04-25 18:00:18 -04:00
|
|
|
}
|