aboutsummaryrefslogtreecommitdiffstats
path: root/apps/files_sharing/lib/SharedStorage.php
diff options
context:
space:
mode:
Diffstat (limited to 'apps/files_sharing/lib/SharedStorage.php')
-rw-r--r--apps/files_sharing/lib/SharedStorage.php237
1 files changed, 106 insertions, 131 deletions
diff --git a/apps/files_sharing/lib/SharedStorage.php b/apps/files_sharing/lib/SharedStorage.php
index a67b79542e1..e310c5f3138 100644
--- a/apps/files_sharing/lib/SharedStorage.php
+++ b/apps/files_sharing/lib/SharedStorage.php
@@ -1,4 +1,5 @@
<?php
+
/**
* SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
* SPDX-FileCopyrightText: 2016 ownCloud, Inc.
@@ -7,43 +8,52 @@
namespace OCA\Files_Sharing;
use OC\Files\Cache\CacheDependencies;
+use OC\Files\Cache\CacheEntry;
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\FailedStorage;
use OC\Files\Storage\Home;
+use OC\Files\Storage\Storage;
+use OC\Files\Storage\Wrapper\Jail;
use OC\Files\Storage\Wrapper\PermissionsMask;
use OC\Files\Storage\Wrapper\Wrapper;
+use OC\Files\View;
+use OC\Share\Share;
use OC\User\NoUserException;
-use OCA\Files_External\Config\ConfigAdapter;
+use OCA\Files_Sharing\ISharedStorage as LegacyISharedStorage;
use OCP\Constants;
+use OCP\Files\Cache\ICache;
use OCP\Files\Cache\ICacheEntry;
-use OCP\Files\Config\IUserMountCache;
+use OCP\Files\Cache\IScanner;
+use OCP\Files\Cache\IWatcher;
use OCP\Files\Folder;
use OCP\Files\IHomeStorage;
use OCP\Files\IRootFolder;
-use OCP\Files\Node;
use OCP\Files\NotFoundException;
use OCP\Files\Storage\IDisableEncryptionStorage;
+use OCP\Files\Storage\ILockingStorage;
+use OCP\Files\Storage\ISharedStorage;
use OCP\Files\Storage\IStorage;
use OCP\Lock\ILockingProvider;
+use OCP\Server;
use OCP\Share\IShare;
+use OCP\Util;
use Psr\Log\LoggerInterface;
/**
* Convert target path to source path and pass the function call to the correct storage provider
*/
-class SharedStorage extends \OC\Files\Storage\Wrapper\Jail implements ISharedStorage, IDisableEncryptionStorage {
- /** @var \OCP\Share\IShare */
+class SharedStorage extends Jail implements LegacyISharedStorage, ISharedStorage, IDisableEncryptionStorage {
+ /** @var IShare */
private $superShare;
- /** @var \OCP\Share\IShare[] */
+ /** @var IShare[] */
private $groupedShares;
/**
- * @var \OC\Files\View
+ * @var View
*/
private $ownerView;
@@ -59,7 +69,7 @@ class SharedStorage extends \OC\Files\Storage\Wrapper\Jail implements ISharedSto
private LoggerInterface $logger;
- /** @var IStorage */
+ /** @var IStorage */
private $nonMaskedStorage;
private array $mountOptions = [];
@@ -76,20 +86,20 @@ class SharedStorage extends \OC\Files\Storage\Wrapper\Jail implements ISharedSto
/**
* @psalm-suppress NonInvariantDocblockPropertyType
- * @var ?\OC\Files\Storage\Storage $storage
+ * @var ?Storage $storage
*/
protected $storage;
- public function __construct($arguments) {
- $this->ownerView = $arguments['ownerView'];
- $this->logger = \OC::$server->get(LoggerInterface::class);
+ public function __construct(array $parameters) {
+ $this->ownerView = $parameters['ownerView'];
+ $this->logger = Server::get(LoggerInterface::class);
- $this->superShare = $arguments['superShare'];
- $this->groupedShares = $arguments['groupedShares'];
+ $this->superShare = $parameters['superShare'];
+ $this->groupedShares = $parameters['groupedShares'];
- $this->user = $arguments['user'];
- if (isset($arguments['sharingDisabledForUser'])) {
- $this->sharingDisabledForUser = $arguments['sharingDisabledForUser'];
+ $this->user = $parameters['user'];
+ if (isset($parameters['sharingDisabledForUser'])) {
+ $this->sharingDisabledForUser = $parameters['sharingDisabledForUser'];
} else {
$this->sharingDisabledForUser = false;
}
@@ -116,7 +126,7 @@ class SharedStorage extends \OC\Files\Storage\Wrapper\Jail implements ISharedSto
}
/**
- * @psalm-assert \OC\Files\Storage\Storage $this->storage
+ * @psalm-assert Storage $this->storage
*/
private function init() {
if ($this->initialized) {
@@ -138,25 +148,37 @@ class SharedStorage extends \OC\Files\Storage\Wrapper\Jail implements ISharedSto
try {
if (self::$initDepth > 10) {
- throw new \Exception("Maximum share depth reached");
+ throw new \Exception('Maximum share depth reached');
}
/** @var IRootFolder $rootFolder */
- $rootFolder = \OC::$server->get(IRootFolder::class);
+ $rootFolder = Server::get(IRootFolder::class);
$this->ownerUserFolder = $rootFolder->getUserFolder($this->superShare->getShareOwner());
$sourceId = $this->superShare->getNodeId();
- $ownerNode = $this->ownerUserFolder->getFirstNodeById($sourceId);
- if (!$ownerNode) {
+ $ownerNodes = $this->ownerUserFolder->getById($sourceId);
+
+ if (count($ownerNodes) === 0) {
$this->storage = new FailedStorage(['exception' => new NotFoundException("File by id $sourceId not found")]);
$this->cache = new FailedCache();
$this->rootPath = '';
} else {
- if ($this->nonMaskedStorage instanceof Wrapper && $this->nonMaskedStorage->isWrapperOf($this)) {
+ foreach ($ownerNodes as $ownerNode) {
+ $nonMaskedStorage = $ownerNode->getStorage();
+
+ // check if potential source node would lead to a recursive share setup
+ if ($nonMaskedStorage instanceof Wrapper && $nonMaskedStorage->isWrapperOf($this)) {
+ continue;
+ }
+ $this->nonMaskedStorage = $nonMaskedStorage;
+ $this->sourcePath = $ownerNode->getPath();
+ $this->rootPath = $ownerNode->getInternalPath();
+ $this->cache = null;
+ break;
+ }
+ if (!$this->nonMaskedStorage) {
+ // all potential source nodes would have been recursive
throw new \Exception('recursive share detected');
}
- $this->nonMaskedStorage = $ownerNode->getStorage();
- $this->sourcePath = $ownerNode->getPath();
- $this->rootPath = $ownerNode->getInternalPath();
$this->storage = new PermissionsMask([
'storage' => $this->nonMaskedStorage,
'mask' => $this->superShare->getPermissions(),
@@ -185,10 +207,7 @@ class SharedStorage extends \OC\Files\Storage\Wrapper\Jail implements ISharedSto
self::$initDepth--;
}
- /**
- * @inheritdoc
- */
- public function instanceOfStorage($class): bool {
+ public function instanceOfStorage(string $class): bool {
if ($class === '\OC\Files\Storage\Common' || $class == Common::class) {
return true;
}
@@ -216,22 +235,11 @@ class SharedStorage extends \OC\Files\Storage\Wrapper\Jail implements ISharedSto
return $this->getSourceRootInfo() && ($this->getSourceRootInfo()->getPermissions() & Constants::PERMISSION_SHARE) === Constants::PERMISSION_SHARE;
}
- /**
- * get id of the mount point
- *
- * @return string
- */
public function getId(): string {
return 'shared::' . $this->getMountPoint();
}
- /**
- * Get the permissions granted for a shared file
- *
- * @param string $path Shared target file path
- * @return int CRUDS permissions granted
- */
- public function getPermissions($path = ''): int {
+ public function getPermissions(string $path = ''): int {
if (!$this->isValid()) {
return 0;
}
@@ -239,21 +247,21 @@ class SharedStorage extends \OC\Files\Storage\Wrapper\Jail implements ISharedSto
// part files and the mount point always have delete permissions
if ($path === '' || pathinfo($path, PATHINFO_EXTENSION) === 'part') {
- $permissions |= \OCP\Constants::PERMISSION_DELETE;
+ $permissions |= Constants::PERMISSION_DELETE;
}
if ($this->sharingDisabledForUser) {
- $permissions &= ~\OCP\Constants::PERMISSION_SHARE;
+ $permissions &= ~Constants::PERMISSION_SHARE;
}
return $permissions;
}
- public function isCreatable($path): bool {
- return (bool)($this->getPermissions($path) & \OCP\Constants::PERMISSION_CREATE);
+ public function isCreatable(string $path): bool {
+ return (bool)($this->getPermissions($path) & Constants::PERMISSION_CREATE);
}
- public function isReadable($path): bool {
+ public function isReadable(string $path): bool {
if (!$this->isValid()) {
return false;
}
@@ -266,22 +274,22 @@ class SharedStorage extends \OC\Files\Storage\Wrapper\Jail implements ISharedSto
return $storage->isReadable($internalPath);
}
- public function isUpdatable($path): bool {
- return (bool)($this->getPermissions($path) & \OCP\Constants::PERMISSION_UPDATE);
+ public function isUpdatable(string $path): bool {
+ return (bool)($this->getPermissions($path) & Constants::PERMISSION_UPDATE);
}
- public function isDeletable($path): bool {
- return (bool)($this->getPermissions($path) & \OCP\Constants::PERMISSION_DELETE);
+ public function isDeletable(string $path): bool {
+ return (bool)($this->getPermissions($path) & Constants::PERMISSION_DELETE);
}
- public function isSharable($path): bool {
- if (\OCP\Util::isSharingDisabledForUser() || !\OC\Share\Share::isResharingAllowed()) {
+ public function isSharable(string $path): bool {
+ if (Util::isSharingDisabledForUser() || !Share::isResharingAllowed()) {
return false;
}
- return (bool)($this->getPermissions($path) & \OCP\Constants::PERMISSION_SHARE);
+ return (bool)($this->getPermissions($path) & Constants::PERMISSION_SHARE);
}
- public function fopen($path, $mode) {
+ public function fopen(string $path, string $mode) {
$source = $this->getUnjailedPath($path);
switch ($mode) {
case 'r+':
@@ -329,18 +337,11 @@ class SharedStorage extends \OC\Files\Storage\Wrapper\Jail implements ISharedSto
'source' => $source,
'mode' => $mode,
];
- \OCP\Util::emitHook('\OC\Files\Storage\Shared', 'fopen', $info);
+ Util::emitHook('\OC\Files\Storage\Shared', 'fopen', $info);
return $this->nonMaskedStorage->fopen($this->getUnjailedPath($path), $mode);
}
- /**
- * see https://www.php.net/manual/en/function.rename.php
- *
- * @param string $source
- * @param string $target
- * @return bool
- */
- public function rename($source, $target): bool {
+ public function rename(string $source, string $target): bool {
$this->init();
$isPartFile = pathinfo($source, PATHINFO_EXTENSION) === 'part';
$targetExists = $this->file_exists($target);
@@ -368,10 +369,7 @@ class SharedStorage extends \OC\Files\Storage\Wrapper\Jail implements ISharedSto
return $this->superShare->getTarget();
}
- /**
- * @param string $path
- */
- public function setMountPoint($path): void {
+ public function setMountPoint(string $path): void {
$this->superShare->setTarget($path);
foreach ($this->groupedShares as $share) {
@@ -388,9 +386,6 @@ class SharedStorage extends \OC\Files\Storage\Wrapper\Jail implements ISharedSto
return $this->superShare->getShareOwner();
}
- /**
- * @return \OCP\Share\IShare
- */
public function getShare(): IShare {
return $this->superShare;
}
@@ -404,7 +399,7 @@ class SharedStorage extends \OC\Files\Storage\Wrapper\Jail implements ISharedSto
return $this->superShare->getNodeType();
}
- public function getCache($path = '', $storage = null) {
+ public function getCache(string $path = '', ?IStorage $storage = null): ICache {
if ($this->cache) {
return $this->cache;
}
@@ -416,44 +411,44 @@ class SharedStorage extends \OC\Files\Storage\Wrapper\Jail implements ISharedSto
return new FailedCache();
}
- $this->cache = new \OCA\Files_Sharing\Cache(
+ $this->cache = new Cache(
$storage,
$sourceRoot,
- \OC::$server->get(CacheDependencies::class),
+ Server::get(CacheDependencies::class),
$this->getShare()
);
return $this->cache;
}
- public function getScanner($path = '', $storage = null) {
+ public function getScanner(string $path = '', ?IStorage $storage = null): IScanner {
if (!$storage) {
$storage = $this;
}
- return new \OCA\Files_Sharing\Scanner($storage);
+ return new Scanner($storage);
}
- public function getOwner($path): string {
+ public function getOwner(string $path): string|false {
return $this->superShare->getShareOwner();
}
- public function getWatcher($path = '', $storage = null): Watcher {
+ public function getWatcher(string $path = '', ?IStorage $storage = null): IWatcher {
if ($this->watcher) {
return $this->watcher;
}
// Get node information
$node = $this->getShare()->getNodeCacheEntry();
- if ($node) {
- /** @var IUserMountCache $userMountCache */
- $userMountCache = \OC::$server->get(IUserMountCache::class);
- $mounts = $userMountCache->getMountsForStorageId($node->getStorageId());
- foreach ($mounts as $mount) {
- // If the share is originating from an external storage
- if ($mount->getMountProvider() === ConfigAdapter::class) {
- // Propagate original storage scan
- $this->watcher = parent::getWatcher($path, $storage);
- return $this->watcher;
+ if ($node instanceof CacheEntry) {
+ $storageId = $node->getData()['storage_string_id'] ?? null;
+ // for shares from the home storage we can rely on the home storage to keep itself up to date
+ // for other storages we need use the proper watcher
+ if ($storageId !== null && !(str_starts_with($storageId, 'home::') || str_starts_with($storageId, 'object::user'))) {
+ $cache = $this->getCache();
+ $this->watcher = parent::getWatcher($path, $storage);
+ if ($cache instanceof Cache) {
+ $this->watcher->onUpdate($cache->markRootChanged(...));
}
+ return $this->watcher;
}
}
@@ -469,19 +464,13 @@ class SharedStorage extends \OC\Files\Storage\Wrapper\Jail implements ISharedSto
*/
public function unshareStorage(): bool {
foreach ($this->groupedShares as $share) {
- \OC::$server->getShareManager()->deleteFromSelf($share, $this->user);
+ Server::get(\OCP\Share\IManager::class)->deleteFromSelf($share, $this->user);
}
return true;
}
- /**
- * @param string $path
- * @param int $type \OCP\Lock\ILockingProvider::LOCK_SHARED or \OCP\Lock\ILockingProvider::LOCK_EXCLUSIVE
- * @param \OCP\Lock\ILockingProvider $provider
- * @throws \OCP\Lock\LockedException
- */
- public function acquireLock($path, $type, ILockingProvider $provider) {
- /** @var \OCP\Files\Storage $targetStorage */
+ public function acquireLock(string $path, int $type, ILockingProvider $provider): void {
+ /** @var ILockingStorage $targetStorage */
[$targetStorage, $targetInternalPath] = $this->resolvePath($path);
$targetStorage->acquireLock($targetInternalPath, $type, $provider);
// lock the parent folders of the owner when locking the share as recipient
@@ -491,13 +480,8 @@ class SharedStorage extends \OC\Files\Storage\Wrapper\Jail implements ISharedSto
}
}
- /**
- * @param string $path
- * @param int $type \OCP\Lock\ILockingProvider::LOCK_SHARED or \OCP\Lock\ILockingProvider::LOCK_EXCLUSIVE
- * @param \OCP\Lock\ILockingProvider $provider
- */
- public function releaseLock($path, $type, ILockingProvider $provider) {
- /** @var \OCP\Files\Storage $targetStorage */
+ public function releaseLock(string $path, int $type, ILockingProvider $provider): void {
+ /** @var ILockingStorage $targetStorage */
[$targetStorage, $targetInternalPath] = $this->resolvePath($path);
$targetStorage->releaseLock($targetInternalPath, $type, $provider);
// unlock the parent folders of the owner when unlocking the share as recipient
@@ -507,21 +491,13 @@ class SharedStorage extends \OC\Files\Storage\Wrapper\Jail implements ISharedSto
}
}
- /**
- * @param string $path
- * @param int $type \OCP\Lock\ILockingProvider::LOCK_SHARED or \OCP\Lock\ILockingProvider::LOCK_EXCLUSIVE
- * @param \OCP\Lock\ILockingProvider $provider
- */
- public function changeLock($path, $type, ILockingProvider $provider) {
- /** @var \OCP\Files\Storage $targetStorage */
+ public function changeLock(string $path, int $type, ILockingProvider $provider): void {
+ /** @var ILockingStorage $targetStorage */
[$targetStorage, $targetInternalPath] = $this->resolvePath($path);
$targetStorage->changeLock($targetInternalPath, $type, $provider);
}
- /**
- * @return array [ available, last_checked ]
- */
- public function getAvailability() {
+ public function getAvailability(): array {
// shares do not participate in availability logic
return [
'available' => true,
@@ -529,10 +505,7 @@ class SharedStorage extends \OC\Files\Storage\Wrapper\Jail implements ISharedSto
];
}
- /**
- * @param bool $isAvailable
- */
- public function setAvailability($isAvailable) {
+ public function setAvailability(bool $isAvailable): void {
// shares do not participate in availability logic
}
@@ -541,14 +514,14 @@ class SharedStorage extends \OC\Files\Storage\Wrapper\Jail implements ISharedSto
return $this->nonMaskedStorage;
}
- public function getWrapperStorage() {
+ public function getWrapperStorage(): Storage {
$this->init();
/**
* @psalm-suppress DocblockTypeContradiction
*/
if (!$this->storage) {
- $message = "no storage set after init for share " . $this->getShareId();
+ $message = 'no storage set after init for share ' . $this->getShareId();
$this->logger->error($message);
$this->storage = new FailedStorage(['exception' => new \Exception($message)]);
}
@@ -556,34 +529,36 @@ class SharedStorage extends \OC\Files\Storage\Wrapper\Jail implements ISharedSto
return $this->storage;
}
- public function file_get_contents($path) {
+ public function file_get_contents(string $path): string|false {
$info = [
'target' => $this->getMountPoint() . '/' . $path,
'source' => $this->getUnjailedPath($path),
];
- \OCP\Util::emitHook('\OC\Files\Storage\Shared', 'file_get_contents', $info);
+ Util::emitHook('\OC\Files\Storage\Shared', 'file_get_contents', $info);
return parent::file_get_contents($path);
}
- public function file_put_contents($path, $data) {
+ public function file_put_contents(string $path, mixed $data): int|float|false {
$info = [
'target' => $this->getMountPoint() . '/' . $path,
'source' => $this->getUnjailedPath($path),
];
- \OCP\Util::emitHook('\OC\Files\Storage\Shared', 'file_put_contents', $info);
+ Util::emitHook('\OC\Files\Storage\Shared', 'file_put_contents', $info);
return parent::file_put_contents($path, $data);
}
- /**
- * @return void
- */
- public function setMountOptions(array $options) {
+ public function setMountOptions(array $options): void {
/* Note: This value is never read */
$this->mountOptions = $options;
}
- public function getUnjailedPath($path) {
+ public function getUnjailedPath(string $path): string {
$this->init();
return parent::getUnjailedPath($path);
}
+
+ public function getDirectDownload(string $path): array|false {
+ // disable direct download for shares
+ return [];
+ }
}