diff options
31 files changed, 518 insertions, 92 deletions
diff --git a/.drone.yml b/.drone.yml index 79cd295210e..e3ee9ca6ce9 100644 --- a/.drone.yml +++ b/.drone.yml @@ -1214,7 +1214,7 @@ steps: commands: # JavaScript files are not used in integration tests so it is not needed to # build them. - - git clone --depth 1 https://github.com/nextcloud/spreed apps/spreed + - git clone --depth 1 --branch stable24 https://github.com/nextcloud/spreed apps/spreed - name: integration-sharing-v1-video-verification image: ghcr.io/nextcloud/continuous-integration-integration-php7.4:latest commands: diff --git a/apps/dav/lib/Connector/Sabre/Directory.php b/apps/dav/lib/Connector/Sabre/Directory.php index 9e0b89596cd..ed98b5050f8 100644 --- a/apps/dav/lib/Connector/Sabre/Directory.php +++ b/apps/dav/lib/Connector/Sabre/Directory.php @@ -47,6 +47,7 @@ use OCP\Files\NotPermittedException; use OCP\Files\StorageNotAvailableException; use OCP\Lock\ILockingProvider; use OCP\Lock\LockedException; +use Psr\Log\LoggerInterface; use Sabre\DAV\Exception\BadRequest; use Sabre\DAV\Exception\Locked; use Sabre\DAV\Exception\NotFound; @@ -331,6 +332,8 @@ class Directory extends \OCA\DAV\Connector\Sabre\Node implements \Sabre\DAV\ICol * @return array */ public function getQuotaInfo() { + /** @var LoggerInterface $logger */ + $logger = \OC::$server->get(LoggerInterface::class); if ($this->quotaInfo) { return $this->quotaInfo; } @@ -347,10 +350,13 @@ class Directory extends \OCA\DAV\Connector\Sabre\Node implements \Sabre\DAV\ICol ]; return $this->quotaInfo; } catch (\OCP\Files\NotFoundException $e) { + $logger->warning("error while getting quota into", ['exception' => $e]); return [0, 0]; } catch (\OCP\Files\StorageNotAvailableException $e) { + $logger->warning("error while getting quota into", ['exception' => $e]); return [0, 0]; } catch (NotPermittedException $e) { + $logger->warning("error while getting quota into", ['exception' => $e]); return [0, 0]; } } diff --git a/apps/dav/lib/DAV/CustomPropertiesBackend.php b/apps/dav/lib/DAV/CustomPropertiesBackend.php index 5f512995ce8..acee65cd00d 100644 --- a/apps/dav/lib/DAV/CustomPropertiesBackend.php +++ b/apps/dav/lib/DAV/CustomPropertiesBackend.php @@ -54,6 +54,28 @@ class CustomPropertiesBackend implements BackendInterface { '{http://owncloud.org/ns}dDC', '{http://owncloud.org/ns}size', '{http://nextcloud.org/ns}is-encrypted', + + // Currently, returning null from any propfind handler would still trigger the backend, + // so we add all known Nextcloud custom properties in here to avoid that + + // text app + '{http://nextcloud.org/ns}rich-workspace', + '{http://nextcloud.org/ns}rich-workspace-file', + // groupfolders + '{http://nextcloud.org/ns}acl-enabled', + '{http://nextcloud.org/ns}acl-can-manage', + '{http://nextcloud.org/ns}acl-list', + '{http://nextcloud.org/ns}inherited-acl-list', + '{http://nextcloud.org/ns}group-folder-id', + // files_lock + '{http://nextcloud.org/ns}lock', + '{http://nextcloud.org/ns}lock-owner-type', + '{http://nextcloud.org/ns}lock-owner', + '{http://nextcloud.org/ns}lock-owner-displayname', + '{http://nextcloud.org/ns}lock-owner-editor', + '{http://nextcloud.org/ns}lock-time', + '{http://nextcloud.org/ns}lock-timeout', + '{http://nextcloud.org/ns}lock-token', ]; /** diff --git a/apps/dav/lib/UserMigration/CalendarMigrator.php b/apps/dav/lib/UserMigration/CalendarMigrator.php index d94e3ec109e..015ce6faa86 100644 --- a/apps/dav/lib/UserMigration/CalendarMigrator.php +++ b/apps/dav/lib/UserMigration/CalendarMigrator.php @@ -108,14 +108,7 @@ class CalendarMigrator implements IMigrator { */ private function getCalendarExportData(IUser $user, ICalendar $calendar, OutputInterface $output): array { $userId = $user->getUID(); - $calendarId = $calendar->getKey(); - $calendarInfo = $this->calDavBackend->getCalendarById($calendarId); - - if (empty($calendarInfo)) { - throw new CalendarMigratorException("Invalid info for calendar ID $calendarId"); - } - - $uri = $calendarInfo['uri']; + $uri = $calendar->getUri(); $path = CalDAVPlugin::CALENDAR_ROOT . "/$userId/$uri"; /** @@ -227,12 +220,12 @@ class CalendarMigrator implements IMigrator { try { /** - * @var string $name - * @var VCalendar $vCalendar - */ + * @var string $name + * @var VCalendar $vCalendar + */ foreach ($calendarExports as ['name' => $name, 'vCalendar' => $vCalendar]) { - // Set filename to sanitized calendar name appended with the date - $filename = preg_replace('/[^a-zA-Z0-9-_ ]/um', '', $name) . '_' . date('Y-m-d') . CalendarMigrator::FILENAME_EXT; + // Set filename to sanitized calendar name + $filename = preg_replace('/[^a-z0-9-_]/iu', '', $name) . CalendarMigrator::FILENAME_EXT; $exportPath = CalendarMigrator::EXPORT_ROOT . $filename; $exportDestination->addFileContents($exportPath, $vCalendar->serialize()); @@ -445,11 +438,11 @@ class CalendarMigrator implements IMigrator { throw new CalendarMigratorException("Invalid calendar data contained in \"$importPath\""); } - $splitFilename = explode('_', $filename, 2); + $splitFilename = explode('.', $filename, 2); if (count($splitFilename) !== 2) { - throw new CalendarMigratorException("Invalid filename \"$filename\", expected filename of the format \"<calendar_name>_YYYY-MM-DD" . CalendarMigrator::FILENAME_EXT . '"'); + throw new CalendarMigratorException("Invalid filename \"$filename\", expected filename of the format \"<calendar_name>" . CalendarMigrator::FILENAME_EXT . '"'); } - [$initialCalendarUri, $suffix] = $splitFilename; + [$initialCalendarUri, $ext] = $splitFilename; try { $this->importCalendar( diff --git a/apps/dav/lib/UserMigration/ContactsMigrator.php b/apps/dav/lib/UserMigration/ContactsMigrator.php index 065ef05ceea..aed41e5c82f 100644 --- a/apps/dav/lib/UserMigration/ContactsMigrator.php +++ b/apps/dav/lib/UserMigration/ContactsMigrator.php @@ -168,7 +168,7 @@ class ContactsMigrator implements IMigrator { } $existingAddressBookUris = array_map( - fn (array $addressBookInfo) => $addressBookInfo['uri'], + fn (array $addressBookInfo): string => $addressBookInfo['uri'], $this->cardDavBackend->getAddressBooksForUser($principalUri), ); @@ -207,14 +207,14 @@ class ContactsMigrator implements IMigrator { try { /** - * @var string $name - * @var string $displayName - * @var ?string $description - * @var VCard[] $vCards - */ + * @var string $name + * @var string $displayName + * @var ?string $description + * @var VCard[] $vCards + */ foreach ($addressBookExports as ['name' => $name, 'displayName' => $displayName, 'description' => $description, 'vCards' => $vCards]) { - // Set filename to sanitized address book name appended with the date - $basename = preg_replace('/[^a-zA-Z0-9-_ ]/um', '', $name) . '_' . date('Y-m-d'); + // Set filename to sanitized address book name + $basename = preg_replace('/[^a-z0-9-_]/iu', '', $name); $exportPath = ContactsMigrator::PATH_ROOT . $basename . '.' . ContactsMigrator::FILENAME_EXT; $metadataExportPath = ContactsMigrator::PATH_ROOT . $basename . '.' . ContactsMigrator::METADATA_EXT; @@ -340,11 +340,11 @@ class ContactsMigrator implements IMigrator { $vCards[] = $vCard; } - $splitFilename = explode('_', $addressBookFilename, 2); + $splitFilename = explode('.', $addressBookFilename, 2); if (count($splitFilename) !== 2) { - throw new ContactsMigratorException("Invalid filename \"$addressBookFilename\", expected filename of the format \"<address_book_name>_YYYY-MM-DD." . ContactsMigrator::FILENAME_EXT . '"'); + throw new ContactsMigratorException("Invalid filename \"$addressBookFilename\", expected filename of the format \"<address_book_name>." . ContactsMigrator::FILENAME_EXT . '"'); } - [$initialAddressBookUri, $suffix] = $splitFilename; + [$initialAddressBookUri, $ext] = $splitFilename; /** @var array{displayName: string, description?: string} $metadata */ $metadata = json_decode($importSource->getFileContents($metadataImportPath), true, 512, JSON_THROW_ON_ERROR); diff --git a/apps/files_sharing/lib/AppInfo/Application.php b/apps/files_sharing/lib/AppInfo/Application.php index befd1532d02..2539247b561 100644 --- a/apps/files_sharing/lib/AppInfo/Application.php +++ b/apps/files_sharing/lib/AppInfo/Application.php @@ -30,6 +30,7 @@ namespace OCA\Files_Sharing\AppInfo; use OC\Share\Share; +use OC\User\DisplayNameCache; use OCA\Files_Sharing\Capabilities; use OCA\Files_Sharing\Event\BeforeTemplateRenderedEvent; use OCA\Files_Sharing\External\Manager; @@ -65,6 +66,7 @@ use OCP\IUserSession; use OCP\L10N\IFactory; use OCP\Share\Events\ShareCreatedEvent; use OCP\Share\IManager; +use OCP\User\Events\UserChangedEvent; use OCP\Util; use Psr\Container\ContainerInterface; use Symfony\Component\EventDispatcher\EventDispatcherInterface; @@ -98,6 +100,7 @@ class Application extends App implements IBootstrap { $context->registerCapability(Capabilities::class); $context->registerNotifierService(Notifier::class); + $context->registerEventListener(UserChangedEvent::class, DisplayNameCache::class); } public function boot(IBootContext $context): void { diff --git a/apps/files_sharing/lib/Cache.php b/apps/files_sharing/lib/Cache.php index 8729426221b..d245727b138 100644 --- a/apps/files_sharing/lib/Cache.php +++ b/apps/files_sharing/lib/Cache.php @@ -33,6 +33,7 @@ use OC\Files\Cache\Wrapper\CacheJail; use OC\Files\Search\SearchBinaryOperator; use OC\Files\Search\SearchComparison; use OC\Files\Storage\Wrapper\Jail; +use OC\User\DisplayNameCache; use OCP\Files\Cache\ICacheEntry; use OCP\Files\Search\ISearchBinaryOperator; use OCP\Files\Search\ISearchComparison; @@ -46,27 +47,22 @@ use OCP\IUserManager; * don't use this class directly if you need to get metadata, use \OC\Files\Filesystem::getFileInfo instead */ class Cache extends CacheJail { - /** @var \OCA\Files_Sharing\SharedStorage */ + /** @var SharedStorage */ private $storage; - /** @var ICacheEntry */ - private $sourceRootInfo; - /** @var IUserManager */ - private $userManager; - - private $rootUnchanged = true; - - private $ownerDisplayName; - + private ICacheEntry $sourceRootInfo; + private bool $rootUnchanged = true; + private ?string $ownerDisplayName = null; private $numericId; + private DisplayNameCache $displayNameCache; /** - * @param \OCA\Files_Sharing\SharedStorage $storage + * @param SharedStorage $storage */ - public function __construct($storage, ICacheEntry $sourceRootInfo, IUserManager $userManager) { + public function __construct($storage, ICacheEntry $sourceRootInfo, DisplayNameCache $displayNameCache) { $this->storage = $storage; $this->sourceRootInfo = $sourceRootInfo; - $this->userManager = $userManager; $this->numericId = $sourceRootInfo->getStorageId(); + $this->displayNameCache = $displayNameCache; parent::__construct( null, @@ -173,12 +169,7 @@ class Cache extends CacheJail { private function getOwnerDisplayName() { if (!$this->ownerDisplayName) { $uid = $this->storage->getOwner(''); - $user = $this->userManager->get($uid); - if ($user) { - $this->ownerDisplayName = $user->getDisplayName(); - } else { - $this->ownerDisplayName = $uid; - } + $this->ownerDisplayName = $this->displayNameCache->getDisplayName($uid); } return $this->ownerDisplayName; } diff --git a/apps/files_sharing/lib/MountProvider.php b/apps/files_sharing/lib/MountProvider.php index 27edf5074e1..bfb40387622 100644 --- a/apps/files_sharing/lib/MountProvider.php +++ b/apps/files_sharing/lib/MountProvider.php @@ -34,6 +34,7 @@ use OCA\Files_Sharing\Event\ShareMountedEvent; use OCP\EventDispatcher\IEventDispatcher; use OCP\Files\Config\IMountProvider; use OCP\Files\Storage\IStorageFactory; +use OCP\ICacheFactory; use OCP\IConfig; use OCP\ILogger; use OCP\IUser; @@ -59,6 +60,9 @@ class MountProvider implements IMountProvider { /** @var IEventDispatcher */ protected $eventDispatcher; + /** @var ICacheFactory */ + protected $cacheFactory; + /** * @param \OCP\IConfig $config * @param IManager $shareManager @@ -68,12 +72,14 @@ class MountProvider implements IMountProvider { IConfig $config, IManager $shareManager, ILogger $logger, - IEventDispatcher $eventDispatcher + IEventDispatcher $eventDispatcher, + ICacheFactory $cacheFactory ) { $this->config = $config; $this->shareManager = $shareManager; $this->logger = $logger; $this->eventDispatcher = $eventDispatcher; + $this->cacheFactory = $cacheFactory; } @@ -136,7 +142,8 @@ class MountProvider implements IMountProvider { $view, $foldersExistCache, $this->eventDispatcher, - $user + $user, + $this->cacheFactory->createLocal('share-valid-mountpoint') ); $event = new ShareMountedEvent($mount); diff --git a/apps/files_sharing/lib/SharedMount.php b/apps/files_sharing/lib/SharedMount.php index 60361e25fd0..398da5eaf23 100644 --- a/apps/files_sharing/lib/SharedMount.php +++ b/apps/files_sharing/lib/SharedMount.php @@ -26,6 +26,7 @@ * along with this program. If not, see <http://www.gnu.org/licenses/> * */ + namespace OCA\Files_Sharing; use OC\Cache\CappedMemoryCache; @@ -36,6 +37,7 @@ use OC\Files\View; use OCP\EventDispatcher\IEventDispatcher; use OCP\Files\Events\InvalidateMountCacheEvent; use OCP\Files\Storage\IStorageFactory; +use OCP\ICache; use OCP\IUser; use OCP\Share\Events\VerifyMountPointEvent; @@ -63,6 +65,8 @@ class SharedMount extends MountPoint implements MoveableMount { private IEventDispatcher $eventDispatcher; + private ICache $cache; + public function __construct( $storage, array $mountpoints, @@ -71,11 +75,13 @@ class SharedMount extends MountPoint implements MoveableMount { View $recipientView, CappedMemoryCache $folderExistCache, IEventDispatcher $eventDispatcher, - IUser $user + IUser $user, + ICache $cache ) { $this->user = $user; $this->recipientView = $recipientView; $this->eventDispatcher = $eventDispatcher; + $this->cache = $cache; $this->superShare = $arguments['superShare']; $this->groupedShares = $arguments['groupedShares']; @@ -92,7 +98,17 @@ class SharedMount extends MountPoint implements MoveableMount { * @param SharedMount[] $mountpoints * @return string */ - private function verifyMountPoint(\OCP\Share\IShare $share, array $mountpoints, CappedMemoryCache $folderExistCache) { + private function verifyMountPoint( + \OCP\Share\IShare $share, + array $mountpoints, + CappedMemoryCache $folderExistCache + ) { + $cacheKey = $this->user->getUID() . '/' . $share->getTarget(); + $cached = $this->cache->get($cacheKey); + if ($cached !== null) { + return $cached; + } + $mountPoint = basename($share->getTarget()); $parent = dirname($share->getTarget()); @@ -120,6 +136,8 @@ class SharedMount extends MountPoint implements MoveableMount { $this->updateFileTarget($newMountPoint, $share); } + $this->cache->set($cacheKey, $newMountPoint, 60 * 60); + return $newMountPoint; } diff --git a/apps/files_sharing/lib/SharedStorage.php b/apps/files_sharing/lib/SharedStorage.php index 6a342f0bdbf..37f1c12a2e4 100644 --- a/apps/files_sharing/lib/SharedStorage.php +++ b/apps/files_sharing/lib/SharedStorage.php @@ -35,7 +35,12 @@ namespace OCA\Files_Sharing; use OC\Files\Cache\FailedCache; use OC\Files\Cache\NullWatcher; use OC\Files\Cache\Watcher; +use OC\Files\ObjectStore\HomeObjectStoreStorage; +use OC\Files\Storage\Common; +use OC\Files\Storage\Home; +use OC\User\DisplayNameCache; use OCP\Files\Folder; +use OCP\Files\IHomeStorage; use OCP\Files\Node; use OC\Files\Storage\FailedStorage; use OC\Files\Storage\Wrapper\PermissionsMask; @@ -47,7 +52,6 @@ use OCP\Files\IRootFolder; use OCP\Files\NotFoundException; use OCP\Files\Storage\IDisableEncryptionStorage; use OCP\Files\Storage\IStorage; -use OCP\IUserManager; use OCP\Lock\ILockingProvider; use OCP\Share\IShare; @@ -182,10 +186,17 @@ class SharedStorage extends \OC\Files\Storage\Wrapper\Jail implements ISharedSto * @inheritdoc */ public function instanceOfStorage($class): bool { - if ($class === '\OC\Files\Storage\Common') { + if ($class === '\OC\Files\Storage\Common' || $class == Common::class) { return true; } - if (in_array($class, ['\OC\Files\Storage\Home', '\OC\Files\ObjectStore\HomeObjectStoreStorage', '\OCP\Files\IHomeStorage'])) { + if (in_array($class, [ + '\OC\Files\Storage\Home', + '\OC\Files\ObjectStore\HomeObjectStoreStorage', + '\OCP\Files\IHomeStorage', + Home::class, + HomeObjectStoreStorage::class, + IHomeStorage::class + ])) { return false; } return parent::instanceOfStorage($class); @@ -405,7 +416,7 @@ class SharedStorage extends \OC\Files\Storage\Wrapper\Jail implements ISharedSto $this->cache = new \OCA\Files_Sharing\Cache( $storage, $sourceRoot, - \OC::$server->get(IUserManager::class) + \OC::$server->get(DisplayNameCache::class) ); return $this->cache; } diff --git a/apps/files_sharing/tests/MountProviderTest.php b/apps/files_sharing/tests/MountProviderTest.php index 756c6f95d42..00ae847eaac 100644 --- a/apps/files_sharing/tests/MountProviderTest.php +++ b/apps/files_sharing/tests/MountProviderTest.php @@ -29,10 +29,12 @@ */ namespace OCA\Files_Sharing\Tests; +use OC\Memcache\NullCache; use OCA\Files_Sharing\MountProvider; use OCP\EventDispatcher\IEventDispatcher; use OCP\Files\IRootFolder; use OCP\Files\Storage\IStorageFactory; +use OCP\ICacheFactory; use OCP\IConfig; use OCP\ILogger; use OCP\IUser; @@ -72,8 +74,11 @@ class MountProviderTest extends \Test\TestCase { $this->shareManager = $this->getMockBuilder(IManager::class)->getMock(); $this->logger = $this->getMockBuilder(ILogger::class)->getMock(); $eventDispatcher = $this->createMock(IEventDispatcher::class); + $cacheFactory = $this->createMock(ICacheFactory::class); + $cacheFactory->method('createLocal') + ->willReturn(new NullCache()); - $this->provider = new MountProvider($this->config, $this->shareManager, $this->logger, $eventDispatcher); + $this->provider = new MountProvider($this->config, $this->shareManager, $this->logger, $eventDispatcher, $cacheFactory); } private function makeMockShare($id, $nodeId, $owner = 'user2', $target = null, $permissions = 31) { diff --git a/apps/files_sharing/tests/TestCase.php b/apps/files_sharing/tests/TestCase.php index bb1e3125ab2..234ea2c69c8 100644 --- a/apps/files_sharing/tests/TestCase.php +++ b/apps/files_sharing/tests/TestCase.php @@ -39,6 +39,7 @@ use OCA\Files_Sharing\MountProvider; use OCP\Files\Config\IMountProviderCollection; use OCP\Share\IShare; use Test\Traits\MountProviderTrait; +use OC\User\DisplayNameCache; /** * Class TestCase @@ -116,6 +117,7 @@ abstract class TestCase extends \Test\TestCase { protected function setUp(): void { parent::setUp(); + \OC::$server->get(DisplayNameCache::class)->clear(); //login as user1 self::loginHelper(self::TEST_FILES_SHARING_API_USER1); diff --git a/core/Migrations/Version240000Date20220202150027.php b/core/Migrations/Version24000Date20220202150027.php index b13afa67d1e..76a3ae8c73c 100644 --- a/core/Migrations/Version240000Date20220202150027.php +++ b/core/Migrations/Version24000Date20220202150027.php @@ -13,7 +13,7 @@ use OCP\Migration\SimpleMigrationStep; /** * Auto-generated migration step: Please modify to your needs! */ -class Version240000Date20220202150027 extends SimpleMigrationStep { +class Version24000Date20220202150027 extends SimpleMigrationStep { /** * @param IOutput $output * @param Closure $schemaClosure The `\Closure` returns a `ISchemaWrapper` diff --git a/core/Migrations/Version240000Date20220404230027.php b/core/Migrations/Version24000Date20220404230027.php index f45f8d5b500..a650fcb7021 100644 --- a/core/Migrations/Version240000Date20220404230027.php +++ b/core/Migrations/Version24000Date20220404230027.php @@ -30,9 +30,9 @@ use OCP\Migration\SimpleMigrationStep; /** * Add oc_file_metadata table - * @see OC\Metadata\FileMetadata + * @see \OC\Metadata\FileMetadata */ -class Version240000Date20220404230027 extends SimpleMigrationStep { +class Version24000Date20220404230027 extends SimpleMigrationStep { /** * @param IOutput $output * @param Closure $schemaClosure The `\Closure` returns a `ISchemaWrapper` diff --git a/core/Migrations/Version24000Date20220425072957.php b/core/Migrations/Version24000Date20220425072957.php new file mode 100644 index 00000000000..87d62873c6b --- /dev/null +++ b/core/Migrations/Version24000Date20220425072957.php @@ -0,0 +1,53 @@ +<?php + +declare(strict_types=1); + +/** + * @copyright Copyright (c) 2022 Joas Schilling <coding@schilljs.com> + * + * @author Joas Schilling <coding@schilljs.com> + * + * @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\Core\Migrations; + +use Closure; +use OCP\DB\ISchemaWrapper; +use OCP\Migration\IOutput; +use OCP\Migration\SimpleMigrationStep; + +class Version24000Date20220425072957 extends SimpleMigrationStep { + /** + * @param IOutput $output + * @param Closure $schemaClosure The `\Closure` returns a `ISchemaWrapper` + * @param array $options + * @return null|ISchemaWrapper + */ + public function changeSchema(IOutput $output, Closure $schemaClosure, array $options): ?ISchemaWrapper { + /** @var ISchemaWrapper $schema */ + $schema = $schemaClosure(); + + $table = $schema->getTable('mounts'); + if (!$table->hasIndex('mount_user_storage')) { + $table->addIndex(['storage_id', 'user_id'], 'mount_user_storage'); + return $schema; + } + + return null; + } +} @@ -147,6 +147,7 @@ try { break; } + $logger->debug('CLI cron call has selected job with ID ' . strval($job->getId()), ['app' => 'cron']); $job->execute($jobList, $logger); // clean up after unclean jobs \OC_Util::tearDownFS(); @@ -169,6 +170,7 @@ try { $jobList = \OC::$server->getJobList(); $job = $jobList->getNext(); if ($job != null) { + $logger->debug('WebCron call has selected job with ID ' . strval($job->getId()), ['app' => 'cron']); $job->execute($jobList, $logger); $jobList->setLastJob($job); } diff --git a/lib/composer/composer/autoload_classmap.php b/lib/composer/composer/autoload_classmap.php index 685fae9bef1..2c962b0fc6d 100644 --- a/lib/composer/composer/autoload_classmap.php +++ b/lib/composer/composer/autoload_classmap.php @@ -1020,14 +1020,15 @@ return array( 'OC\\Core\\Migrations\\Version23000Date20210930122352' => $baseDir . '/core/Migrations/Version23000Date20210930122352.php', 'OC\\Core\\Migrations\\Version23000Date20211203110726' => $baseDir . '/core/Migrations/Version23000Date20211203110726.php', 'OC\\Core\\Migrations\\Version23000Date20211213203940' => $baseDir . '/core/Migrations/Version23000Date20211213203940.php', - 'OC\\Core\\Migrations\\Version240000Date20220202150027' => $baseDir . '/core/Migrations/Version240000Date20220202150027.php', - 'OC\\Core\\Migrations\\Version240000Date20220404230027' => $baseDir . '/core/Migrations/Version240000Date20220404230027.php', 'OC\\Core\\Migrations\\Version24000Date20211210141942' => $baseDir . '/core/Migrations/Version24000Date20211210141942.php', 'OC\\Core\\Migrations\\Version24000Date20211213081506' => $baseDir . '/core/Migrations/Version24000Date20211213081506.php', 'OC\\Core\\Migrations\\Version24000Date20211213081604' => $baseDir . '/core/Migrations/Version24000Date20211213081604.php', 'OC\\Core\\Migrations\\Version24000Date20211222112246' => $baseDir . '/core/Migrations/Version24000Date20211222112246.php', 'OC\\Core\\Migrations\\Version24000Date20211230140012' => $baseDir . '/core/Migrations/Version24000Date20211230140012.php', 'OC\\Core\\Migrations\\Version24000Date20220131153041' => $baseDir . '/core/Migrations/Version24000Date20220131153041.php', + 'OC\\Core\\Migrations\\Version24000Date20220202150027' => $baseDir . '/core/Migrations/Version24000Date20220202150027.php', + 'OC\\Core\\Migrations\\Version24000Date20220404230027' => $baseDir . '/core/Migrations/Version24000Date20220404230027.php', + 'OC\\Core\\Migrations\\Version24000Date20220425072957' => $baseDir . '/core/Migrations/Version24000Date20220425072957.php', 'OC\\Core\\Notification\\CoreNotifier' => $baseDir . '/core/Notification/CoreNotifier.php', 'OC\\Core\\Service\\LoginFlowV2Service' => $baseDir . '/core/Service/LoginFlowV2Service.php', 'OC\\DB\\Adapter' => $baseDir . '/lib/private/DB/Adapter.php', @@ -1540,6 +1541,8 @@ return array( 'OC\\UserStatus\\Manager' => $baseDir . '/lib/private/UserStatus/Manager.php', 'OC\\User\\Backend' => $baseDir . '/lib/private/User/Backend.php', 'OC\\User\\Database' => $baseDir . '/lib/private/User/Database.php', + 'OC\\User\\DisplayNameCache' => $baseDir . '/lib/private/User/DisplayNameCache.php', + 'OC\\User\\LazyUser' => $baseDir . '/lib/private/User/LazyUser.php', 'OC\\User\\LoginException' => $baseDir . '/lib/private/User/LoginException.php', 'OC\\User\\Manager' => $baseDir . '/lib/private/User/Manager.php', 'OC\\User\\NoUserException' => $baseDir . '/lib/private/User/NoUserException.php', diff --git a/lib/composer/composer/autoload_static.php b/lib/composer/composer/autoload_static.php index 7e778d73b83..da591ab0c0f 100644 --- a/lib/composer/composer/autoload_static.php +++ b/lib/composer/composer/autoload_static.php @@ -1049,14 +1049,15 @@ class ComposerStaticInit53792487c5a8370acc0b06b1a864ff4c 'OC\\Core\\Migrations\\Version23000Date20210930122352' => __DIR__ . '/../../..' . '/core/Migrations/Version23000Date20210930122352.php', 'OC\\Core\\Migrations\\Version23000Date20211203110726' => __DIR__ . '/../../..' . '/core/Migrations/Version23000Date20211203110726.php', 'OC\\Core\\Migrations\\Version23000Date20211213203940' => __DIR__ . '/../../..' . '/core/Migrations/Version23000Date20211213203940.php', - 'OC\\Core\\Migrations\\Version240000Date20220202150027' => __DIR__ . '/../../..' . '/core/Migrations/Version240000Date20220202150027.php', - 'OC\\Core\\Migrations\\Version240000Date20220404230027' => __DIR__ . '/../../..' . '/core/Migrations/Version240000Date20220404230027.php', 'OC\\Core\\Migrations\\Version24000Date20211210141942' => __DIR__ . '/../../..' . '/core/Migrations/Version24000Date20211210141942.php', 'OC\\Core\\Migrations\\Version24000Date20211213081506' => __DIR__ . '/../../..' . '/core/Migrations/Version24000Date20211213081506.php', 'OC\\Core\\Migrations\\Version24000Date20211213081604' => __DIR__ . '/../../..' . '/core/Migrations/Version24000Date20211213081604.php', 'OC\\Core\\Migrations\\Version24000Date20211222112246' => __DIR__ . '/../../..' . '/core/Migrations/Version24000Date20211222112246.php', 'OC\\Core\\Migrations\\Version24000Date20211230140012' => __DIR__ . '/../../..' . '/core/Migrations/Version24000Date20211230140012.php', 'OC\\Core\\Migrations\\Version24000Date20220131153041' => __DIR__ . '/../../..' . '/core/Migrations/Version24000Date20220131153041.php', + 'OC\\Core\\Migrations\\Version24000Date20220202150027' => __DIR__ . '/../../..' . '/core/Migrations/Version24000Date20220202150027.php', + 'OC\\Core\\Migrations\\Version24000Date20220404230027' => __DIR__ . '/../../..' . '/core/Migrations/Version24000Date20220404230027.php', + 'OC\\Core\\Migrations\\Version24000Date20220425072957' => __DIR__ . '/../../..' . '/core/Migrations/Version24000Date20220425072957.php', 'OC\\Core\\Notification\\CoreNotifier' => __DIR__ . '/../../..' . '/core/Notification/CoreNotifier.php', 'OC\\Core\\Service\\LoginFlowV2Service' => __DIR__ . '/../../..' . '/core/Service/LoginFlowV2Service.php', 'OC\\DB\\Adapter' => __DIR__ . '/../../..' . '/lib/private/DB/Adapter.php', @@ -1569,6 +1570,8 @@ class ComposerStaticInit53792487c5a8370acc0b06b1a864ff4c 'OC\\UserStatus\\Manager' => __DIR__ . '/../../..' . '/lib/private/UserStatus/Manager.php', 'OC\\User\\Backend' => __DIR__ . '/../../..' . '/lib/private/User/Backend.php', 'OC\\User\\Database' => __DIR__ . '/../../..' . '/lib/private/User/Database.php', + 'OC\\User\\DisplayNameCache' => __DIR__ . '/../../..' . '/lib/private/User/DisplayNameCache.php', + 'OC\\User\\LazyUser' => __DIR__ . '/../../..' . '/lib/private/User/LazyUser.php', 'OC\\User\\LoginException' => __DIR__ . '/../../..' . '/lib/private/User/LoginException.php', 'OC\\User\\Manager' => __DIR__ . '/../../..' . '/lib/private/User/Manager.php', 'OC\\User\\NoUserException' => __DIR__ . '/../../..' . '/lib/private/User/NoUserException.php', diff --git a/lib/private/BackgroundJob/JobList.php b/lib/private/BackgroundJob/JobList.php index 21af79c4686..fe65a1879bc 100644 --- a/lib/private/BackgroundJob/JobList.php +++ b/lib/private/BackgroundJob/JobList.php @@ -203,7 +203,7 @@ class JobList implements IJobList { * @param bool $onlyTimeSensitive * @return IJob|null */ - public function getNext(bool $onlyTimeSensitive = true): ?IJob { + public function getNext(bool $onlyTimeSensitive = false): ?IJob { $query = $this->connection->getQueryBuilder(); $query->select('*') ->from('jobs') diff --git a/lib/private/Files/Config/MountProviderCollection.php b/lib/private/Files/Config/MountProviderCollection.php index 334fce15d9e..0e08d9d0e83 100644 --- a/lib/private/Files/Config/MountProviderCollection.php +++ b/lib/private/Files/Config/MountProviderCollection.php @@ -234,4 +234,8 @@ class MountProviderCollection implements IMountProviderCollection, Emitter { $this->homeProviders = []; $this->rootProviders = []; } + + public function getProviders(): array { + return $this->providers; + } } diff --git a/lib/private/Files/Node/Root.php b/lib/private/Files/Node/Root.php index 8b599ddfd75..6dd65a4291d 100644 --- a/lib/private/Files/Node/Root.php +++ b/lib/private/Files/Node/Root.php @@ -365,6 +365,7 @@ class Root extends Folder implements IRootFolder { $userObject = $this->userManager->get($userId); if (is_null($userObject)) { + $e = new NoUserException('Backends provided no user object'); $this->logger->error( sprintf( 'Backends provided no user object for %s', @@ -372,9 +373,10 @@ class Root extends Folder implements IRootFolder { ), [ 'app' => 'files', + 'exception' => $e, ] ); - throw new NoUserException('Backends provided no user object'); + throw $e; } $userId = $userObject->getUID(); diff --git a/lib/private/Files/SetupManager.php b/lib/private/Files/SetupManager.php index e0575ea92a5..9fb7b030e1d 100644 --- a/lib/private/Files/SetupManager.php +++ b/lib/private/Files/SetupManager.php @@ -81,6 +81,7 @@ class SetupManager { private LoggerInterface $logger; private IConfig $config; private bool $listeningForProviders; + private array $fullSetupRequired = []; public function __construct( IEventLogger $eventLogger, @@ -202,6 +203,8 @@ class SetupManager { $this->setupUserMountProviders[$user->getUID()] = []; } + $previouslySetupProviders = $this->setupUserMountProviders[$user->getUID()]; + $this->setupForUserWith($user, function () use ($user) { $this->mountProviderCollection->addMountForUser($user, $this->mountManager, function ( IMountProvider $provider @@ -209,7 +212,7 @@ class SetupManager { return !in_array(get_class($provider), $this->setupUserMountProviders[$user->getUID()]); }); }); - $this->afterUserFullySetup($user); + $this->afterUserFullySetup($user, $previouslySetupProviders); } /** @@ -257,17 +260,25 @@ class SetupManager { /** * Final housekeeping after a user has been fully setup */ - private function afterUserFullySetup(IUser $user): void { + private function afterUserFullySetup(IUser $user, array $previouslySetupProviders): void { $userRoot = '/' . $user->getUID() . '/'; $mounts = $this->mountManager->getAll(); $mounts = array_filter($mounts, function (IMountPoint $mount) use ($userRoot) { return strpos($mount->getMountPoint(), $userRoot) === 0; }); - $this->userMountCache->registerMounts($user, $mounts); + $allProviders = array_map(function (IMountProvider $provider) { + return get_class($provider); + }, $this->mountProviderCollection->getProviders()); + $newProviders = array_diff($allProviders, $previouslySetupProviders); + $mounts = array_filter($mounts, function (IMountPoint $mount) use ($previouslySetupProviders) { + return !in_array($mount->getMountProvider(), $previouslySetupProviders); + }); + $this->userMountCache->registerMounts($user, $mounts, $newProviders); $cacheDuration = $this->config->getSystemValueInt('fs_mount_cache_duration', 5 * 60); if ($cacheDuration > 0) { $this->cache->set($user->getUID(), true, $cacheDuration); + $this->fullSetupRequired[$user->getUID()] = false; } } @@ -333,7 +344,9 @@ class SetupManager { * @return IUser|null */ private function getUserForPath(string $path) { - if (substr_count($path, '/') < 2) { + if (strpos($path, '/__groupfolders') === 0) { + return null; + } elseif (substr_count($path, '/') < 2) { if ($user = $this->userSession->getUser()) { return $user; } else { @@ -434,7 +447,10 @@ class SetupManager { // we perform a "cached" setup only after having done the full setup recently // this is also used to trigger a full setup after handling events that are likely // to change the available mounts - return !$this->cache->get($user->getUID()); + if (!isset($this->fullSetupRequired[$user->getUID()])) { + $this->fullSetupRequired[$user->getUID()] = !$this->cache->get($user->getUID()); + } + return $this->fullSetupRequired[$user->getUID()]; } /** @@ -489,6 +505,7 @@ class SetupManager { $this->setupUsers = []; $this->setupUsersComplete = []; $this->setupUserMountProviders = []; + $this->fullSetupRequired = []; $this->rootSetup = false; $this->mountManager->clear(); $this->eventDispatcher->dispatchTyped(new FilesystemTornDownEvent()); diff --git a/lib/private/Files/Storage/Local.php b/lib/private/Files/Storage/Local.php index 34ab9a5fc97..d637b3d194f 100644 --- a/lib/private/Files/Storage/Local.php +++ b/lib/private/Files/Storage/Local.php @@ -173,7 +173,11 @@ class Local extends \OC\Files\Storage\Common { * @inheritdoc */ public function getMetaData($path) { - $stat = $this->stat($path); + try { + $stat = $this->stat($path); + } catch (ForbiddenException $e) { + return null; + } if (!$stat) { return null; } diff --git a/lib/private/Files/View.php b/lib/private/Files/View.php index 30dc5518be8..c209c8594f7 100644 --- a/lib/private/Files/View.php +++ b/lib/private/Files/View.php @@ -49,6 +49,8 @@ namespace OC\Files; use Icewind\Streams\CallbackWrapper; use OC\Files\Mount\MoveableMount; use OC\Files\Storage\Storage; +use OC\User\DisplayNameCache; +use OC\User\LazyUser; use OC\User\User; use OCA\Files_Sharing\SharedMount; use OCP\Constants; @@ -102,6 +104,8 @@ class View { /** @var \OCP\ILogger */ private $logger; + private DisplayNameCache $displayNameCache; + /** * @param string $root * @throws \Exception If $root contains an invalid path @@ -118,6 +122,7 @@ class View { $this->lockingProvider = \OC::$server->getLockingProvider(); $this->lockingEnabled = !($this->lockingProvider instanceof \OC\Lock\NoopLockingProvider); $this->userManager = \OC::$server->getUserManager(); + $this->displayNameCache = \OC::$server->get(DisplayNameCache::class); $this->logger = \OC::$server->getLogger(); } @@ -1312,15 +1317,10 @@ class View { /** * @param string $ownerId - * @return \OC\User\User + * @return IUser */ - private function getUserObjectForOwner($ownerId) { - $owner = $this->userManager->get($ownerId); - if ($owner instanceof IUser) { - return $owner; - } else { - return new User($ownerId, null, \OC::$server->getEventDispatcher()); - } + private function getUserObjectForOwner(string $ownerId) { + return new LazyUser($ownerId, $this->displayNameCache, $this->userManager); } /** @@ -1517,10 +1517,8 @@ class View { if ($pos = strpos($relativePath, '/')) { //mountpoint inside subfolder add size to the correct folder $entryName = substr($relativePath, 0, $pos); - foreach ($files as &$entry) { - if ($entry->getName() === $entryName) { - $entry->addSubEntry($rootEntry, $mountPoint); - } + if (isset($files[$entryName])) { + $files[$entryName]->addSubEntry($rootEntry, $mountPoint); } } else { //mountpoint in this folder, add an entry for it $rootEntry['name'] = $relativePath; diff --git a/lib/private/Metadata/FileEventListener.php b/lib/private/Metadata/FileEventListener.php index fdec891c6e2..6d41ccdef30 100644 --- a/lib/private/Metadata/FileEventListener.php +++ b/lib/private/Metadata/FileEventListener.php @@ -31,12 +31,15 @@ use OCP\Files\File; use OCP\Files\Node; use OCP\Files\NotFoundException; use OCP\Files\FileInfo; +use Psr\Log\LoggerInterface; class FileEventListener implements IEventListener { private IMetadataManager $manager; + private LoggerInterface $logger; - public function __construct(IMetadataManager $manager) { + public function __construct(IMetadataManager $manager, LoggerInterface $logger) { $this->manager = $manager; + $this->logger = $logger; } private function shouldExtractMetadata(Node $node): bool { @@ -52,13 +55,31 @@ class FileEventListener implements IEventListener { } $path = $node->getPath(); + return $this->isCorrectPath($path); + } + + private function isCorrectPath(string $path): bool { // TODO make this more dynamic, we have the same issue in other places return !str_starts_with($path, 'appdata_') && !str_starts_with($path, 'files_versions/') && !str_starts_with($path, 'files_trashbin/'); } public function handle(Event $event): void { if ($event instanceof NodeRemovedFromCache) { + if (!$this->isCorrectPath($event->getPath())) { + // Don't listen to paths for which we don't extract metadata + return; + } $view = Filesystem::getView(); + if (!$view) { + // Should not happen since a scan in the user folder should setup + // the file system. + $e = new \Exception(); // don't trigger, just get backtrace + $this->logger->error('Detecting deletion of a file with possible metadata but file system setup is not setup', [ + 'exception' => $e, + 'app' => 'metadata' + ]); + return; + } $info = $view->getFileInfo($event->getPath()); if ($info && $info->getType() === FileInfo::TYPE_FILE) { $this->manager->clearMetadata($info->getId()); diff --git a/lib/private/Metadata/FileMetadata.php b/lib/private/Metadata/FileMetadata.php index c53f5d7f619..7d1db21cf39 100644 --- a/lib/private/Metadata/FileMetadata.php +++ b/lib/private/Metadata/FileMetadata.php @@ -30,7 +30,7 @@ use OCP\DB\Types; * @method void setGroupName(string $groupName) * @method string getMetadata() * @method void setMetadata(array $metadata) - * @see OC\Core\Migrations\Version240000Date20220404230027 + * @see \OC\Core\Migrations\Version240000Date20220404230027 */ class FileMetadata extends Entity { protected ?string $groupName = null; diff --git a/lib/private/User/DisplayNameCache.php b/lib/private/User/DisplayNameCache.php new file mode 100644 index 00000000000..22a79863e49 --- /dev/null +++ b/lib/private/User/DisplayNameCache.php @@ -0,0 +1,85 @@ +<?php + +declare(strict_types=1); + +/** + * @copyright Copyright 2022 Carl Schwan <carl@carlschwan.eu> + * @license AGPL-3.0-or-later + * + * 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\User; + +use OCP\EventDispatcher\Event; +use OCP\EventDispatcher\IEventListener; +use OCP\ICache; +use OCP\ICacheFactory; +use OCP\IUserManager; +use OCP\User\Events\UserChangedEvent; + +/** + * Class that cache the relation UserId -> Display name + * + * This saves fetching the user from a user backend and later on fetching + * their preferences. It's generally not an issue if this data is slightly + * outdated. + */ +class DisplayNameCache implements IEventListener { + private array $cache = []; + private ICache $memCache; + private IUserManager $userManager; + + public function __construct(ICacheFactory $cacheFactory, IUserManager $userManager) { + $this->memCache = $cacheFactory->createDistributed('displayNameMappingCache'); + $this->userManager = $userManager; + } + + public function getDisplayName(string $userId) { + if (isset($this->cache[$userId])) { + return $this->cache[$userId]; + } + $displayName = $this->memCache->get($userId); + if ($displayName) { + $this->cache[$userId] = $displayName; + return $displayName; + } + + $user = $this->userManager->get($userId); + if ($user) { + $displayName = $user->getDisplayName(); + } else { + $displayName = $userId; + } + $this->cache[$userId] = $displayName; + $this->memCache->set($userId, $displayName, 60 * 10); // 10 minutes + + return $displayName; + } + + public function clear(): void { + $this->cache = []; + $this->memCache->clear(); + } + + public function handle(Event $event): void { + if ($event instanceof UserChangedEvent && $event->getFeature() === 'displayName') { + $userId = $event->getUser()->getUID(); + $newDisplayName = $event->getValue(); + $this->cache[$userId] = $newDisplayName; + $this->memCache->set($userId, $newDisplayName, 60 * 10); // 10 minutes + } + } +} diff --git a/lib/private/User/LazyUser.php b/lib/private/User/LazyUser.php new file mode 100644 index 00000000000..8b98b112731 --- /dev/null +++ b/lib/private/User/LazyUser.php @@ -0,0 +1,149 @@ +<?php + +declare(strict_types=1); +/** + * @copyright Copyright (c) 2022 Robin Appelman <robin@icewind.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\User; + +use OCP\IUser; +use OCP\IUserManager; + +class LazyUser implements IUser { + private ?IUser $user = null; + private DisplayNameCache $displayNameCache; + private string $uid; + private IUserManager $userManager; + + public function __construct(string $uid, DisplayNameCache $displayNameCache, IUserManager $userManager) { + $this->displayNameCache = $displayNameCache; + $this->uid = $uid; + $this->userManager = $userManager; + } + + private function getUser(): IUser { + if ($this->user === null) { + $this->user = $this->userManager->get($this->uid); + } + /** @var IUser */ + $user = $this->user; + return $user; + } + + public function getUID() { + return $this->uid; + } + + public function getDisplayName() { + return $this->displayNameCache->getDisplayName($this->uid); + } + + public function setDisplayName($displayName) { + return $this->getUser()->setDisplayName($displayName); + } + + public function getLastLogin() { + return $this->getUser()->getLastLogin(); + } + + public function updateLastLoginTimestamp() { + return $this->getUser()->updateLastLoginTimestamp(); + } + + public function delete() { + return $this->getUser()->delete(); + } + + public function setPassword($password, $recoveryPassword = null) { + return $this->getUser()->setPassword($password, $recoveryPassword); + } + + public function getHome() { + return $this->getUser()->getHome(); + } + + public function getBackendClassName() { + return $this->getUser()->getBackendClassName(); + } + + public function getBackend() { + return $this->getUser()->getBackend(); + } + + public function canChangeAvatar() { + return $this->getUser()->canChangeAvatar(); + } + + public function canChangePassword() { + return $this->getUser()->canChangePassword(); + } + + public function canChangeDisplayName() { + return $this->getUser()->canChangeDisplayName(); + } + + public function isEnabled() { + return $this->getUser()->isEnabled(); + } + + public function setEnabled(bool $enabled = true) { + return $this->getUser()->setEnabled($enabled); + } + + public function getEMailAddress() { + return $this->getUser()->getEMailAddress(); + } + + public function getSystemEMailAddress(): ?string { + return $this->getUser()->getSystemEMailAddress(); + } + + public function getPrimaryEMailAddress(): ?string { + return $this->getUser()->getPrimaryEMailAddress(); + } + + public function getAvatarImage($size) { + return $this->getUser()->getAvatarImage($size); + } + + public function getCloudId() { + return $this->getUser()->getCloudId(); + } + + public function setEMailAddress($mailAddress) { + $this->getUser()->setEMailAddress($mailAddress); + } + + public function setSystemEMailAddress(string $mailAddress): void { + $this->getUser()->setSystemEMailAddress($mailAddress); + } + + public function setPrimaryEMailAddress(string $mailAddress): void { + $this->getUser()->setPrimaryEMailAddress($mailAddress); + } + + public function getQuota() { + return $this->getUser()->getQuota(); + } + + public function setQuota($quota) { + $this->getUser()->setQuota($quota); + } +} diff --git a/lib/private/legacy/OC_Helper.php b/lib/private/legacy/OC_Helper.php index 547ffef8607..0d1903007c2 100644 --- a/lib/private/legacy/OC_Helper.php +++ b/lib/private/legacy/OC_Helper.php @@ -44,8 +44,11 @@ * */ use bantu\IniGetWrapper\IniGetWrapper; +use OC\Files\Filesystem; use OCP\Files\Mount\IMountPoint; +use OCP\ICacheFactory; use OCP\IUser; +use Psr\Log\LoggerInterface; use Symfony\Component\Process\ExecutableFinder; /** @@ -486,9 +489,20 @@ class OC_Helper { * @throws \OCP\Files\NotFoundException */ public static function getStorageInfo($path, $rootInfo = null, $includeMountPoints = true) { + /** @var ICacheFactory $cacheFactory */ + $cacheFactory = \OC::$server->get(ICacheFactory::class); + $memcache = $cacheFactory->createLocal('storage_info'); + // return storage info without adding mount points $includeExtStorage = \OC::$server->getSystemConfig()->getValue('quota_include_external_storage', false); + $fullPath = Filesystem::getView()->getAbsolutePath($path); + $cacheKey = $fullPath. '::' . ($includeMountPoints ? 'include' : 'exclude'); + $cached = $memcache->get($cacheKey); + if ($cached) { + return $cached; + } + if (!$rootInfo) { $rootInfo = \OC\Files\Filesystem::getFileInfo($path, $includeExtStorage ? 'ext' : false); } @@ -505,7 +519,6 @@ class OC_Helper { $sourceStorage = $storage; if ($storage->instanceOfStorage('\OCA\Files_Sharing\SharedStorage')) { $includeExtStorage = false; - $sourceStorage = $storage->getSourceStorage(); $internalPath = $storage->getUnjailedPath($rootInfo->getInternalPath()); } else { $internalPath = $rootInfo->getInternalPath(); @@ -531,7 +544,19 @@ class OC_Helper { /** @var \OC\Files\Storage\Wrapper\Quota $storage */ $quota = $sourceStorage->getQuota(); } - $free = $sourceStorage->free_space($internalPath); + try { + $free = $sourceStorage->free_space($internalPath); + } catch (\Exception $e) { + if ($path === "") { + throw $e; + } + /** @var LoggerInterface $logger */ + $logger = \OC::$server->get(LoggerInterface::class); + $logger->warning("Error while getting quota info, using root quota", ['exception' => $e]); + $rootInfo = self::getStorageInfo(""); + $memcache->set($cacheKey, $rootInfo, 5 * 60); + return $rootInfo; + } if ($free >= 0) { $total = $free + $used; } else { @@ -559,7 +584,7 @@ class OC_Helper { [,,,$mountPoint] = explode('/', $mount->getMountPoint(), 4); } - return [ + $info = [ 'free' => $free, 'used' => $used, 'quota' => $quota, @@ -570,6 +595,10 @@ class OC_Helper { 'mountType' => $mount->getMountType(), 'mountPoint' => trim($mountPoint, '/'), ]; + + $memcache->set($cacheKey, $info, 5 * 60); + + return $info; } /** diff --git a/tests/lib/BackgroundJob/DummyJobList.php b/tests/lib/BackgroundJob/DummyJobList.php index ec06203a477..0751409f62c 100644 --- a/tests/lib/BackgroundJob/DummyJobList.php +++ b/tests/lib/BackgroundJob/DummyJobList.php @@ -78,7 +78,7 @@ class DummyJobList extends \OC\BackgroundJob\JobList { * @param bool $onlyTimeSensitive * @return IJob|null */ - public function getNext(bool $onlyTimeSensitive = true): ?IJob { + public function getNext(bool $onlyTimeSensitive = false): ?IJob { if (count($this->jobs) > 0) { if ($this->last < (count($this->jobs) - 1)) { $i = $this->last + 1; diff --git a/tests/lib/Files/Node/RootTest.php b/tests/lib/Files/Node/RootTest.php index fe151d70dc3..ee86eab5675 100644 --- a/tests/lib/Files/Node/RootTest.php +++ b/tests/lib/Files/Node/RootTest.php @@ -232,9 +232,7 @@ class RootTest extends \Test\TestCase { ->method('error') ->with( 'Backends provided no user object for NotExistingUser', - [ - 'app' => 'files', - ] + $this->anything() ); $root->getUserFolder('NotExistingUser'); |