refactor(Files): Modernize Wrapper

Signed-off-by: provokateurin <kate@provokateurin.de>
This commit is contained in:
provokateurin 2026-02-12 16:14:59 +01:00
parent 672a29aa0c
commit 4eada2d804
No known key found for this signature in database
7 changed files with 71 additions and 55 deletions

View file

@ -85,12 +85,6 @@ class SharedStorage extends Jail implements LegacyISharedStorage, ISharedStorage
private static int $initDepth = 0;
/**
* @psalm-suppress NonInvariantDocblockPropertyType
* @var ?Storage $storage
*/
protected $storage;
public function __construct(array $parameters) {
$this->ownerView = $parameters['ownerView'];
$this->logger = Server::get(LoggerInterface::class);

View file

@ -18,6 +18,7 @@ return (require __DIR__ . '/rector-shared.php')
$nextcloudDir . '/lib/private/Settings/AuthorizedGroupMapper.php',
$nextcloudDir . '/apps/settings/lib/Service/AuthorizedGroupService.php',
$nextcloudDir . '/lib/private/Files/Storage/Storage.php',
$nextcloudDir . '/lib/private/Files/Storage/Wrapper/Wrapper.php',
])
->withPreparedSets(
deadCode: true,

View file

@ -11,6 +11,7 @@ use OC\Files\Cache\Wrapper\CacheJail;
use OC\Files\Cache\Wrapper\JailPropagator;
use OC\Files\Cache\Wrapper\JailWatcher;
use OC\Files\Filesystem;
use OC\Files\Storage\FailedStorage;
use OCP\Files\Cache\ICache;
use OCP\Files\Cache\IPropagator;
use OCP\Files\Cache\IWatcher;
@ -20,6 +21,7 @@ use OCP\Files\Storage\IWriteStreamStorage;
use OCP\IDBConnection;
use OCP\Lock\ILockingProvider;
use OCP\Server;
use Psr\Log\LoggerInterface;
/**
* Jail to a subdirectory of the wrapped storage
@ -51,6 +53,12 @@ class Jail extends Wrapper {
* This is separate from Wrapper::getWrapperStorage so we can get the jailed storage consistently even if the jail is inside another wrapper
*/
public function getUnjailedStorage(): IStorage {
if ($this->storage === null) {
$message = 'storage jail ' . get_class($this) . " doesn't have a wrapped storage set";
Server::get(LoggerInterface::class)->error($message);
$this->storage = new FailedStorage(['exception' => new \Exception($message)]);
}
return $this->storage;
}

View file

@ -1,5 +1,7 @@
<?php
declare(strict_types=1);
/**
* SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
* SPDX-FileCopyrightText: 2016 ownCloud, Inc.
@ -24,31 +26,32 @@ use Override;
use Psr\Log\LoggerInterface;
class Wrapper implements Storage, ILockingStorage, IWriteStreamStorage {
/**
* @var Storage $storage
*/
protected $storage;
protected ?Storage $storage = null;
public $cache;
public $scanner;
public $watcher;
public $propagator;
public $updater;
public ?ICache $cache = null;
public ?IScanner $scanner = null;
public ?IWatcher $watcher = null;
public ?IPropagator $propagator = null;
public ?IUpdater $updater = null;
/**
* @param array $parameters
* @param array{storage: Storage} $parameters
*/
public function __construct(array $parameters) {
$this->storage = $parameters['storage'];
}
public function getWrapperStorage(): Storage {
if (!$this->storage) {
$message = 'storage wrapper ' . get_class($this) . " doesn't have a wrapped storage set";
$logger = Server::get(LoggerInterface::class);
$logger->error($message);
if (!$this->storage instanceof Storage) {
$message = 'storage wrapper ' . static::class . " doesn't have a wrapped storage set";
Server::get(LoggerInterface::class)->error($message);
$this->storage = new FailedStorage(['exception' => new \Exception($message)]);
}
return $this->storage;
}
@ -169,16 +172,18 @@ class Wrapper implements Storage, ILockingStorage, IWriteStreamStorage {
}
public function getCache(string $path = '', ?IStorage $storage = null): ICache {
if (!$storage) {
if (!$storage instanceof IStorage) {
$storage = $this;
}
return $this->getWrapperStorage()->getCache($path, $storage);
}
public function getScanner(string $path = '', ?IStorage $storage = null): IScanner {
if (!$storage) {
if (!$storage instanceof IStorage) {
$storage = $this;
}
return $this->getWrapperStorage()->getScanner($path, $storage);
}
@ -187,23 +192,26 @@ class Wrapper implements Storage, ILockingStorage, IWriteStreamStorage {
}
public function getWatcher(string $path = '', ?IStorage $storage = null): IWatcher {
if (!$storage) {
if (!$storage instanceof IStorage) {
$storage = $this;
}
return $this->getWrapperStorage()->getWatcher($path, $storage);
}
public function getPropagator(?IStorage $storage = null): IPropagator {
if (!$storage) {
if (!$storage instanceof IStorage) {
$storage = $this;
}
return $this->getWrapperStorage()->getPropagator($storage);
}
public function getUpdater(?IStorage $storage = null): IUpdater {
if (!$storage) {
if (!$storage instanceof IStorage) {
$storage = $this;
}
return $this->getWrapperStorage()->getUpdater($storage);
}
@ -224,10 +232,6 @@ class Wrapper implements Storage, ILockingStorage, IWriteStreamStorage {
}
public function instanceOfStorage(string $class): bool {
if (ltrim($class, '\\') === 'OC\Files\Storage\Shared') {
// FIXME Temporary fix to keep existing checks working
$class = '\OCA\Files_Sharing\SharedStorage';
}
return is_a($this, $class) || $this->getWrapperStorage()->instanceOfStorage($class);
}
@ -242,11 +246,14 @@ class Wrapper implements Storage, ILockingStorage, IWriteStreamStorage {
if ($storage instanceof $class) {
break;
}
$storage = $storage->getWrapperStorage();
}
if (!($storage instanceof $class)) {
return null;
}
return $storage;
}
@ -261,6 +268,7 @@ class Wrapper implements Storage, ILockingStorage, IWriteStreamStorage {
#[Override]
public function getDirectDownload(string $path): array|false {
/** @psalm-suppress DeprecatedMethod */
return $this->getWrapperStorage()->getDirectDownload($path);
}
@ -302,20 +310,23 @@ class Wrapper implements Storage, ILockingStorage, IWriteStreamStorage {
}
public function acquireLock(string $path, int $type, ILockingProvider $provider): void {
if ($this->getWrapperStorage()->instanceOfStorage('\OCP\Files\Storage\ILockingStorage')) {
$this->getWrapperStorage()->acquireLock($path, $type, $provider);
$storage = $this->getWrapperStorage();
if ($storage->instanceOfStorage(ILockingStorage::class)) {
$storage->acquireLock($path, $type, $provider);
}
}
public function releaseLock(string $path, int $type, ILockingProvider $provider): void {
if ($this->getWrapperStorage()->instanceOfStorage('\OCP\Files\Storage\ILockingStorage')) {
$this->getWrapperStorage()->releaseLock($path, $type, $provider);
$storage = $this->getWrapperStorage();
if ($storage->instanceOfStorage(ILockingStorage::class)) {
$storage->releaseLock($path, $type, $provider);
}
}
public function changeLock(string $path, int $type, ILockingProvider $provider): void {
if ($this->getWrapperStorage()->instanceOfStorage('\OCP\Files\Storage\ILockingStorage')) {
$this->getWrapperStorage()->changeLock($path, $type, $provider);
$storage = $this->getWrapperStorage();
if ($storage->instanceOfStorage(ILockingStorage::class)) {
$storage->changeLock($path, $type, $provider);
}
}
@ -328,17 +339,21 @@ class Wrapper implements Storage, ILockingStorage, IWriteStreamStorage {
if ($storage->instanceOfStorage(IWriteStreamStorage::class)) {
/** @var IWriteStreamStorage $storage */
return $storage->writeStream($path, $stream, $size);
} else {
$target = $this->fopen($path, 'w');
$count = stream_copy_to_stream($stream, $target);
fclose($stream);
fclose($target);
if ($count === false) {
throw new GenericFileException('Failed to copy stream.');
}
return $count;
}
$target = $this->fopen($path, 'w');
if ($target === false) {
throw new GenericFileException('Failed to open ' . $path);
}
$count = stream_copy_to_stream($stream, $target);
fclose($stream);
fclose($target);
if ($count === false) {
throw new GenericFileException('Failed to copy stream.');
}
return $count;
}
public function getDirectoryContent(string $directory): \Traversable {
@ -350,9 +365,11 @@ class Wrapper implements Storage, ILockingStorage, IWriteStreamStorage {
if ($wrapped === $storage) {
return true;
}
if ($wrapped instanceof Wrapper) {
return $wrapped->isWrapperOf($storage);
}
return false;
}

View file

@ -24,6 +24,7 @@
<file name="lib/private/Settings/AuthorizedGroupMapper.php"/>
<file name="apps/settings/lib/Service/AuthorizedGroupService.php"/>
<file name="lib/private/Files/Storage/Storage.php"/>
<file name="lib/private/Files/Storage/Wrapper/Wrapper.php"/>
<ignoreFiles>
<directory name="apps/**/composer"/>
<directory name="apps/**/tests"/>

View file

@ -15,7 +15,6 @@ use OC\Memcache\ArrayCache;
use OCA\Files_Trashbin\Storage;
use OCP\Files\Mount\IMountPoint;
use OCP\Files\Storage\IDisableEncryptionStorage;
use OCP\Files\Storage\IStorage;
use Psr\Log\LoggerInterface;
use Test\TestCase;
@ -45,17 +44,13 @@ class EncryptionWrapperTest extends TestCase {
#[\PHPUnit\Framework\Attributes\DataProvider('provideWrapStorage')]
public function testWrapStorage($expectedWrapped, $wrappedStorages): void {
$storage = $this->getMockBuilder(IStorage::class)
$storage = $this->getMockBuilder(Storage::class)
->disableOriginalConstructor()
->getMock();
foreach ($wrappedStorages as $wrapper) {
$storage->expects($this->any())
->method('instanceOfStorage')
->willReturnMap([
[$wrapper, true],
]);
}
$storage->expects($this->any())
->method('instanceOfStorage')
->willReturnCallback(fn (string $storage): bool => in_array($storage, $wrappedStorages, true));
$mount = $this->getMockBuilder(IMountPoint::class)
->disableOriginalConstructor()

View file

@ -958,7 +958,7 @@ class EncryptionTest extends Storage {
$wrapper = $this->getMockBuilder(Encryption::class)
->setConstructorArgs(
[
['mountPoint' => '', 'mount' => $mount, 'storage' => ''],
['mountPoint' => '', 'mount' => $mount, 'storage' => null],
$encryptionManager,
$util,
$this->logger,