diff options
22 files changed, 448 insertions, 214 deletions
diff --git a/apps/files_sharing/lib/Controller/ShareAPIController.php b/apps/files_sharing/lib/Controller/ShareAPIController.php index 35a01ccdd61..069cba42bb6 100644 --- a/apps/files_sharing/lib/Controller/ShareAPIController.php +++ b/apps/files_sharing/lib/Controller/ShareAPIController.php @@ -44,6 +44,7 @@ declare(strict_types=1); */ namespace OCA\Files_Sharing\Controller; +use OC\Files\FileInfo; use OCA\Files_Sharing\Exceptions\SharingRightsException; use OCA\Files_Sharing\External\Storage; use OCA\Files\Helper; @@ -469,12 +470,22 @@ class ShareAPIController extends OCSController { $userFolder = $this->rootFolder->getUserFolder($this->currentUser); try { - $path = $userFolder->get($path); + /** @var \OC\Files\Node\Node $node */ + $node = $userFolder->get($path); } catch (NotFoundException $e) { throw new OCSNotFoundException($this->l->t('Wrong path, file/folder does not exist')); } - $share->setNode($path); + // a user can have access to a file through different paths, with differing permissions + // combine all permissions to determine if the user can share this file + $nodes = $userFolder->getById($node->getId()); + foreach ($nodes as $nodeById) { + /** @var FileInfo $fileInfo */ + $fileInfo = $node->getFileInfo(); + $fileInfo['permissions'] |= $nodeById->getPermissions(); + } + + $share->setNode($node); try { $this->lock($share->getNode()); @@ -489,7 +500,7 @@ class ShareAPIController extends OCSController { // Shares always require read permissions $permissions |= Constants::PERMISSION_READ; - if ($path instanceof \OCP\Files\File) { + if ($node instanceof \OCP\Files\File) { // Single file shares should never have delete or create permissions $permissions &= ~Constants::PERMISSION_DELETE; $permissions &= ~Constants::PERMISSION_CREATE; @@ -500,8 +511,8 @@ class ShareAPIController extends OCSController { * We check the permissions via webdav. But the permissions of the mount point * do not equal the share permissions. Here we fix that for federated mounts. */ - if ($path->getStorage()->instanceOfStorage(Storage::class)) { - $permissions &= ~($permissions & ~$path->getPermissions()); + if ($node->getStorage()->instanceOfStorage(Storage::class)) { + $permissions &= ~($permissions & ~$node->getPermissions()); } if ($shareType === IShare::TYPE_USER) { @@ -537,7 +548,7 @@ class ShareAPIController extends OCSController { } // Public upload can only be set for folders - if ($path instanceof \OCP\Files\File) { + if ($node instanceof \OCP\Files\File) { throw new OCSNotFoundException($this->l->t('Public upload is only possible for publicly shared folders')); } @@ -573,7 +584,7 @@ class ShareAPIController extends OCSController { if ($sendPasswordByTalk === 'true') { if (!$this->appManager->isEnabledForUser('spreed')) { - throw new OCSForbiddenException($this->l->t('Sharing %s sending the password by Nextcloud Talk failed because Nextcloud Talk is not enabled', [$path->getPath()])); + throw new OCSForbiddenException($this->l->t('Sharing %s sending the password by Nextcloud Talk failed because Nextcloud Talk is not enabled', [$node->getPath()])); } $share->setSendPasswordByTalk(true); @@ -590,7 +601,7 @@ class ShareAPIController extends OCSController { } } elseif ($shareType === IShare::TYPE_REMOTE) { if (!$this->shareManager->outgoingServer2ServerSharesAllowed()) { - throw new OCSForbiddenException($this->l->t('Sharing %1$s failed because the back end does not allow shares from type %2$s', [$path->getPath(), $shareType])); + throw new OCSForbiddenException($this->l->t('Sharing %1$s failed because the back end does not allow shares from type %2$s', [$node->getPath(), $shareType])); } if ($shareWith === null) { @@ -609,7 +620,7 @@ class ShareAPIController extends OCSController { } } elseif ($shareType === IShare::TYPE_REMOTE_GROUP) { if (!$this->shareManager->outgoingServer2ServerGroupSharesAllowed()) { - throw new OCSForbiddenException($this->l->t('Sharing %1$s failed because the back end does not allow shares from type %2$s', [$path->getPath(), $shareType])); + throw new OCSForbiddenException($this->l->t('Sharing %1$s failed because the back end does not allow shares from type %2$s', [$node->getPath(), $shareType])); } if ($shareWith === null) { @@ -643,13 +654,13 @@ class ShareAPIController extends OCSController { try { $this->getRoomShareHelper()->createShare($share, $shareWith, $permissions, $expireDate); } catch (QueryException $e) { - throw new OCSForbiddenException($this->l->t('Sharing %s failed because the back end does not support room shares', [$path->getPath()])); + throw new OCSForbiddenException($this->l->t('Sharing %s failed because the back end does not support room shares', [$node->getPath()])); } } elseif ($shareType === IShare::TYPE_DECK) { try { $this->getDeckShareHelper()->createShare($share, $shareWith, $permissions, $expireDate); } catch (QueryException $e) { - throw new OCSForbiddenException($this->l->t('Sharing %s failed because the back end does not support room shares', [$path->getPath()])); + throw new OCSForbiddenException($this->l->t('Sharing %s failed because the back end does not support room shares', [$node->getPath()])); } } else { throw new OCSBadRequestException($this->l->t('Unknown share type')); diff --git a/apps/files_sharing/lib/SharedStorage.php b/apps/files_sharing/lib/SharedStorage.php index 3ded20eb495..6a342f0bdbf 100644 --- a/apps/files_sharing/lib/SharedStorage.php +++ b/apps/files_sharing/lib/SharedStorage.php @@ -35,13 +35,15 @@ namespace OCA\Files_Sharing; use OC\Files\Cache\FailedCache; use OC\Files\Cache\NullWatcher; use OC\Files\Cache\Watcher; -use OC\Files\Filesystem; +use OCP\Files\Folder; +use OCP\Files\Node; use OC\Files\Storage\FailedStorage; use OC\Files\Storage\Wrapper\PermissionsMask; use OC\User\NoUserException; use OCA\Files_External\Config\ExternalMountPoint; use OCP\Constants; use OCP\Files\Cache\ICacheEntry; +use OCP\Files\IRootFolder; use OCP\Files\NotFoundException; use OCP\Files\Storage\IDisableEncryptionStorage; use OCP\Files\Storage\IStorage; @@ -88,6 +90,11 @@ class SharedStorage extends \OC\Files\Storage\Wrapper\Jail implements ISharedSto /** @var boolean */ private $sharingDisabledForUser; + /** @var ?Folder $ownerUserFolder */ + private $ownerUserFolder = null; + + private string $sourcePath = ''; + public function __construct($arguments) { $this->ownerView = $arguments['ownerView']; $this->logger = \OC::$server->getLogger(); @@ -129,14 +136,26 @@ class SharedStorage extends \OC\Files\Storage\Wrapper\Jail implements ISharedSto } $this->initialized = true; try { - Filesystem::initMountPoints($this->superShare->getShareOwner()); - $storageId = $this->superShare->getNodeCacheEntry() ? $this->superShare->getNodeCacheEntry()->getStorageId() : null; - $sourcePath = $this->ownerView->getPath($this->superShare->getNodeId(), $storageId); - [$this->nonMaskedStorage, $this->rootPath] = $this->ownerView->resolvePath($sourcePath); - $this->storage = new PermissionsMask([ - 'storage' => $this->nonMaskedStorage, - 'mask' => $this->superShare->getPermissions(), - ]); + /** @var IRootFolder $rootFolder */ + $rootFolder = \OC::$server->get(IRootFolder::class); + $this->ownerUserFolder = $rootFolder->getUserFolder($this->superShare->getShareOwner()); + $sourceId = $this->superShare->getNodeId(); + $ownerNodes = $this->ownerUserFolder->getById($sourceId); + /** @var Node|false $ownerNode */ + $ownerNode = current($ownerNodes); + if (!$ownerNode) { + $this->storage = new FailedStorage(['exception' => new NotFoundException("File by id $sourceId not found")]); + $this->cache = new FailedCache(); + $this->rootPath = ''; + } else { + $this->nonMaskedStorage = $ownerNode->getStorage(); + $this->sourcePath = $ownerNode->getPath(); + $this->rootPath = $ownerNode->getInternalPath(); + $this->storage = new PermissionsMask([ + 'storage' => $this->nonMaskedStorage, + 'mask' => $this->superShare->getPermissions(), + ]); + } } catch (NotFoundException $e) { // original file not accessible or deleted, set FailedStorage $this->storage = new FailedStorage(['exception' => $e]); @@ -444,7 +463,7 @@ class SharedStorage extends \OC\Files\Storage\Wrapper\Jail implements ISharedSto $targetStorage->acquireLock($targetInternalPath, $type, $provider); // lock the parent folders of the owner when locking the share as recipient if ($path === '') { - $sourcePath = $this->ownerView->getPath($this->superShare->getNodeId()); + $sourcePath = $this->ownerUserFolder->getRelativePath($this->sourcePath); $this->ownerView->lockFile(dirname($sourcePath), ILockingProvider::LOCK_SHARED, true); } } @@ -460,7 +479,7 @@ class SharedStorage extends \OC\Files\Storage\Wrapper\Jail implements ISharedSto $targetStorage->releaseLock($targetInternalPath, $type, $provider); // unlock the parent folders of the owner when unlocking the share as recipient if ($path === '') { - $sourcePath = $this->ownerView->getPath($this->superShare->getNodeId()); + $sourcePath = $this->ownerUserFolder->getRelativePath($this->sourcePath); $this->ownerView->unlockFile(dirname($sourcePath), ILockingProvider::LOCK_SHARED, true); } } diff --git a/apps/files_sharing/tests/ApiTest.php b/apps/files_sharing/tests/ApiTest.php index f3a31578511..c7159e82163 100644 --- a/apps/files_sharing/tests/ApiTest.php +++ b/apps/files_sharing/tests/ApiTest.php @@ -383,6 +383,9 @@ class ApiTest extends TestCase { } public function testGetAllSharesWithMe() { + $this->loginAsUser(self::TEST_FILES_SHARING_API_USER2); + $this->logout(); + $node1 = $this->userFolder->get($this->filename); $share1 = $this->shareManager->newShare(); $share1->setNode($node1) diff --git a/apps/files_sharing/tests/Controller/ShareAPIControllerTest.php b/apps/files_sharing/tests/Controller/ShareAPIControllerTest.php index c7c2b6d8757..4b52f7a2a36 100644 --- a/apps/files_sharing/tests/Controller/ShareAPIControllerTest.php +++ b/apps/files_sharing/tests/Controller/ShareAPIControllerTest.php @@ -1619,6 +1619,8 @@ class ShareAPIControllerTest extends TestCase { ->method('get') ->with('valid-path') ->willReturn($path); + $userFolder->method('getById') + ->willReturn([]); $path->expects($this->once()) ->method('lock') @@ -1651,6 +1653,8 @@ class ShareAPIControllerTest extends TestCase { ->method('get') ->with('valid-path') ->willReturn($path); + $userFolder->method('getById') + ->willReturn([]); $path->expects($this->once()) ->method('lock') @@ -1683,6 +1687,8 @@ class ShareAPIControllerTest extends TestCase { ->method('get') ->with('valid-path') ->willReturn($path); + $userFolder->method('getById') + ->willReturn([]); $path->expects($this->once()) ->method('lock') ->with(\OCP\Lock\ILockingProvider::LOCK_SHARED); @@ -1733,6 +1739,8 @@ class ShareAPIControllerTest extends TestCase { ->method('get') ->with('valid-path') ->willReturn($path); + $userFolder->method('getById') + ->willReturn([]); $this->userManager->method('userExists')->with('validUser')->willReturn(true); @@ -1787,6 +1795,8 @@ class ShareAPIControllerTest extends TestCase { ->method('get') ->with('valid-path') ->willReturn($path); + $userFolder->method('getById') + ->willReturn([]); $path->expects($this->once()) ->method('lock') @@ -1844,6 +1854,8 @@ class ShareAPIControllerTest extends TestCase { ->method('get') ->with('valid-path') ->willReturn($path); + $userFolder->method('getById') + ->willReturn([]); $this->groupManager->method('groupExists')->with('validGroup')->willReturn(true); @@ -1896,6 +1908,8 @@ class ShareAPIControllerTest extends TestCase { ->method('get') ->with('valid-path') ->willReturn($path); + $userFolder->method('getById') + ->willReturn([]); $this->groupManager->method('groupExists')->with('validGroup')->willReturn(true); @@ -1926,6 +1940,8 @@ class ShareAPIControllerTest extends TestCase { $path->method('getStorage')->willReturn($storage); $this->rootFolder->method('getUserFolder')->with($this->currentUser)->willReturnSelf(); $this->rootFolder->method('get')->with('valid-path')->willReturn($path); + $this->rootFolder->method('getById') + ->willReturn([]); $this->shareManager->method('newShare')->willReturn(\OC::$server->getShareManager()->newShare()); @@ -1945,6 +1961,8 @@ class ShareAPIControllerTest extends TestCase { $path->method('getStorage')->willReturn($storage); $this->rootFolder->method('getUserFolder')->with($this->currentUser)->willReturnSelf(); $this->rootFolder->method('get')->with('valid-path')->willReturn($path); + $this->rootFolder->method('getById') + ->willReturn([]); $this->shareManager->method('newShare')->willReturn(\OC::$server->getShareManager()->newShare()); $this->shareManager->method('shareApiAllowLinks')->willReturn(true); @@ -1965,6 +1983,8 @@ class ShareAPIControllerTest extends TestCase { $path->method('getStorage')->willReturn($storage); $this->rootFolder->method('getUserFolder')->with($this->currentUser)->willReturnSelf(); $this->rootFolder->method('get')->with('valid-path')->willReturn($path); + $this->rootFolder->method('getById') + ->willReturn([]); $this->shareManager->method('newShare')->willReturn(\OC::$server->getShareManager()->newShare()); $this->shareManager->method('shareApiAllowLinks')->willReturn(true); @@ -1984,6 +2004,8 @@ class ShareAPIControllerTest extends TestCase { $path->method('getStorage')->willReturn($storage); $this->rootFolder->method('getUserFolder')->with($this->currentUser)->willReturnSelf(); $this->rootFolder->method('get')->with('valid-path')->willReturn($path); + $this->rootFolder->method('getById') + ->willReturn([]); $this->shareManager->method('newShare')->willReturn(\OC::$server->getShareManager()->newShare()); $this->shareManager->method('shareApiAllowLinks')->willReturn(true); @@ -2018,6 +2040,8 @@ class ShareAPIControllerTest extends TestCase { $path->method('getStorage')->willReturn($storage); $this->rootFolder->method('getUserFolder')->with($this->currentUser)->willReturnSelf(); $this->rootFolder->method('get')->with('valid-path')->willReturn($path); + $this->rootFolder->method('getById') + ->willReturn([]); $this->shareManager->method('newShare')->willReturn(\OC::$server->getShareManager()->newShare()); $this->shareManager->method('shareApiAllowLinks')->willReturn(true); @@ -2052,6 +2076,8 @@ class ShareAPIControllerTest extends TestCase { $path->method('getStorage')->willReturn($storage); $this->rootFolder->method('getUserFolder')->with($this->currentUser)->willReturnSelf(); $this->rootFolder->method('get')->with('valid-path')->willReturn($path); + $this->rootFolder->method('getById') + ->willReturn([]); $this->shareManager->method('newShare')->willReturn(\OC::$server->getShareManager()->newShare()); $this->shareManager->method('shareApiAllowLinks')->willReturn(true); @@ -2094,6 +2120,8 @@ class ShareAPIControllerTest extends TestCase { $path->method('getPath')->willReturn('valid-path'); $this->rootFolder->method('getUserFolder')->with($this->currentUser)->willReturnSelf(); $this->rootFolder->method('get')->with('valid-path')->willReturn($path); + $this->rootFolder->method('getById') + ->willReturn([]); $this->shareManager->method('newShare')->willReturn(\OC::$server->getShareManager()->newShare()); $this->shareManager->method('shareApiAllowLinks')->willReturn(true); @@ -2127,6 +2155,8 @@ class ShareAPIControllerTest extends TestCase { $path->method('getStorage')->willReturn($storage); $this->rootFolder->method('getUserFolder')->with($this->currentUser)->willReturnSelf(); $this->rootFolder->method('get')->with('valid-path')->willReturn($path); + $this->rootFolder->method('getById') + ->willReturn([]); $this->shareManager->method('newShare')->willReturn(\OC::$server->getShareManager()->newShare()); $this->shareManager->method('shareApiAllowLinks')->willReturn(true); @@ -2168,6 +2198,8 @@ class ShareAPIControllerTest extends TestCase { $path->method('getStorage')->willReturn($storage); $this->rootFolder->method('getUserFolder')->with($this->currentUser)->willReturnSelf(); $this->rootFolder->method('get')->with('valid-path')->willReturn($path); + $this->rootFolder->method('getById') + ->willReturn([]); $this->shareManager->method('newShare')->willReturn(\OC::$server->getShareManager()->newShare()); $this->shareManager->method('shareApiAllowLinks')->willReturn(true); @@ -2216,6 +2248,8 @@ class ShareAPIControllerTest extends TestCase { ->method('get') ->with('valid-path') ->willReturn($path); + $userFolder->method('getById') + ->willReturn([]); $this->userManager->method('userExists')->with('validUser')->willReturn(true); @@ -2286,6 +2320,8 @@ class ShareAPIControllerTest extends TestCase { ->method('get') ->with('valid-path') ->willReturn($path); + $userFolder->method('getById') + ->willReturn([]); $this->userManager->method('userExists')->with('validUser')->willReturn(true); @@ -2338,6 +2374,8 @@ class ShareAPIControllerTest extends TestCase { ->method('get') ->with('valid-path') ->willReturn($path); + $userFolder->method('getById') + ->willReturn([]); $path->expects($this->once()) ->method('lock') @@ -2421,6 +2459,8 @@ class ShareAPIControllerTest extends TestCase { ->method('get') ->with('valid-path') ->willReturn($path); + $userFolder->method('getById') + ->willReturn([]); $path->expects($this->once()) ->method('lock') @@ -2461,6 +2501,8 @@ class ShareAPIControllerTest extends TestCase { ->method('get') ->with('valid-path') ->willReturn($path); + $userFolder->method('getById') + ->willReturn([]); $path->expects($this->once()) ->method('lock') @@ -2541,6 +2583,8 @@ class ShareAPIControllerTest extends TestCase { ->method('get') ->with('valid-path') ->willReturn($path); + $userFolder->method('getById') + ->willReturn([]); $this->userManager->method('userExists')->with('validUser')->willReturn(true); diff --git a/apps/files_versions/lib/Storage.php b/apps/files_versions/lib/Storage.php index a6674ab244b..3f91e32ef95 100644 --- a/apps/files_versions/lib/Storage.php +++ b/apps/files_versions/lib/Storage.php @@ -543,9 +543,10 @@ class Storage { }); foreach ($versions as $version) { - \OC_Hook::emit('\OCP\Versions', 'preDelete', ['path' => $version->getInternalPath(), 'trigger' => self::DELETE_TRIGGER_RETENTION_CONSTRAINT]); + $internalPath = $version->getInternalPath(); + \OC_Hook::emit('\OCP\Versions', 'preDelete', ['path' => $internalPath, 'trigger' => self::DELETE_TRIGGER_RETENTION_CONSTRAINT]); $version->delete(); - \OC_Hook::emit('\OCP\Versions', 'delete', ['path' => $version->getInternalPath(), 'trigger' => self::DELETE_TRIGGER_RETENTION_CONSTRAINT]); + \OC_Hook::emit('\OCP\Versions', 'delete', ['path' => $internalPath, 'trigger' => self::DELETE_TRIGGER_RETENTION_CONSTRAINT]); } } diff --git a/lib/composer/composer/autoload_classmap.php b/lib/composer/composer/autoload_classmap.php index 2df13618053..c821786b74b 100644 --- a/lib/composer/composer/autoload_classmap.php +++ b/lib/composer/composer/autoload_classmap.php @@ -1209,6 +1209,7 @@ return array( 'OC\\Files\\Type\\Detection' => $baseDir . '/lib/private/Files/Type/Detection.php', 'OC\\Files\\Type\\Loader' => $baseDir . '/lib/private/Files/Type/Loader.php', 'OC\\Files\\Type\\TemplateManager' => $baseDir . '/lib/private/Files/Type/TemplateManager.php', + 'OC\\Files\\Utils\\PathHelper' => $baseDir . '/lib/private/Files/Utils/PathHelper.php', 'OC\\Files\\Utils\\Scanner' => $baseDir . '/lib/private/Files/Utils/Scanner.php', 'OC\\Files\\View' => $baseDir . '/lib/private/Files/View.php', 'OC\\ForbiddenException' => $baseDir . '/lib/private/ForbiddenException.php', diff --git a/lib/composer/composer/autoload_static.php b/lib/composer/composer/autoload_static.php index cd5d30b3574..1d414814fc4 100644 --- a/lib/composer/composer/autoload_static.php +++ b/lib/composer/composer/autoload_static.php @@ -1238,6 +1238,7 @@ class ComposerStaticInit53792487c5a8370acc0b06b1a864ff4c 'OC\\Files\\Type\\Detection' => __DIR__ . '/../../..' . '/lib/private/Files/Type/Detection.php', 'OC\\Files\\Type\\Loader' => __DIR__ . '/../../..' . '/lib/private/Files/Type/Loader.php', 'OC\\Files\\Type\\TemplateManager' => __DIR__ . '/../../..' . '/lib/private/Files/Type/TemplateManager.php', + 'OC\\Files\\Utils\\PathHelper' => __DIR__ . '/../../..' . '/lib/private/Files/Utils/PathHelper.php', 'OC\\Files\\Utils\\Scanner' => __DIR__ . '/../../..' . '/lib/private/Files/Utils/Scanner.php', 'OC\\Files\\View' => __DIR__ . '/../../..' . '/lib/private/Files/View.php', 'OC\\ForbiddenException' => __DIR__ . '/../../..' . '/lib/private/ForbiddenException.php', diff --git a/lib/private/Files/Config/MountProviderCollection.php b/lib/private/Files/Config/MountProviderCollection.php index 2b0acf7d69d..334fce15d9e 100644 --- a/lib/private/Files/Config/MountProviderCollection.php +++ b/lib/private/Files/Config/MountProviderCollection.php @@ -97,10 +97,10 @@ class MountProviderCollection implements IMountProviderCollection, Emitter { return $this->getUserMountsForProviders($user, $this->providers); } - public function getUserMountsForProviderClass(IUser $user, string $mountProviderClass): array { + public function getUserMountsForProviderClasses(IUser $user, array $mountProviderClasses): array { $providers = array_filter( $this->providers, - fn (IMountProvider $mountProvider) => (get_class($mountProvider) === $mountProviderClass) + fn (IMountProvider $mountProvider) => (in_array(get_class($mountProvider), $mountProviderClasses)) ); return $this->getUserMountsForProviders($user, $providers); } diff --git a/lib/private/Files/Mount/Manager.php b/lib/private/Files/Mount/Manager.php index ecd97760f17..69285018d17 100644 --- a/lib/private/Files/Mount/Manager.php +++ b/lib/private/Files/Mount/Manager.php @@ -158,7 +158,6 @@ class Manager implements IMountManager { * @return IMountPoint[] */ public function findByStorageId(string $id): array { - \OC_Util::setupFS(); if (\strlen($id) > 64) { $id = md5($id); } @@ -204,4 +203,22 @@ class Manager implements IMountManager { public function getSetupManager(): SetupManager { return $this->setupManager; } + + /** + * Return all mounts in a path from a specific mount provider + * + * @param string $path + * @param string[] $mountProviders + * @return MountPoint[] + */ + public function getMountsByMountProvider(string $path, array $mountProviders) { + $this->getSetupManager()->setupForProvider($path, $mountProviders); + if (in_array('', $mountProviders)) { + return $this->mounts; + } else { + return array_filter($this->mounts, function ($mount) use ($mountProviders) { + return in_array($mount->getMountProvider(), $mountProviders); + }); + } + } } diff --git a/lib/private/Files/Node/Folder.php b/lib/private/Files/Node/Folder.php index 400fd6bedcc..d058805b20e 100644 --- a/lib/private/Files/Node/Folder.php +++ b/lib/private/Files/Node/Folder.php @@ -36,6 +36,7 @@ use OC\Files\Cache\Wrapper\CacheJail; use OC\Files\Search\SearchComparison; use OC\Files\Search\SearchOrder; use OC\Files\Search\SearchQuery; +use OC\Files\Utils\PathHelper; use OCP\Files\Cache\ICacheEntry; use OCP\Files\FileInfo; use OCP\Files\Mount\IMountPoint; @@ -76,17 +77,7 @@ class Folder extends Node implements \OCP\Files\Folder { * @return string|null */ public function getRelativePath($path) { - if ($this->path === '' or $this->path === '/') { - return $this->normalizePath($path); - } - if ($path === $this->path) { - return '/'; - } elseif (strpos($path, $this->path . '/') !== 0) { - return null; - } else { - $path = substr($path, strlen($this->path)); - return $this->normalizePath($path); - } + return PathHelper::getRelativePath($this->getPath(), $path); } /** @@ -342,70 +333,7 @@ class Folder extends Node implements \OCP\Files\Folder { * @return \OC\Files\Node\Node[] */ public function getById($id) { - $mountCache = $this->root->getUserMountCache(); - if (strpos($this->getPath(), '/', 1) > 0) { - [, $user] = explode('/', $this->getPath()); - } else { - $user = null; - } - $mountsContainingFile = $mountCache->getMountsForFileId((int)$id, $user); - - // when a user has access trough the same storage trough multiple paths - // (such as an external storage that is both mounted for a user and shared to the user) - // the mount cache will only hold a single entry for the storage - // this can lead to issues as the different ways the user has access to a storage can have different permissions - // - // so instead of using the cached entries directly, we instead filter the current mounts by the rootid of the cache entry - - $mountRootIds = array_map(function ($mount) { - return $mount->getRootId(); - }, $mountsContainingFile); - $mountRootPaths = array_map(function ($mount) { - return $mount->getRootInternalPath(); - }, $mountsContainingFile); - $mountRoots = array_combine($mountRootIds, $mountRootPaths); - - $mounts = $this->root->getMountsIn($this->path); - $mounts[] = $this->root->getMount($this->path); - - $mountsContainingFile = array_filter($mounts, function ($mount) use ($mountRoots) { - return isset($mountRoots[$mount->getStorageRootId()]); - }); - - if (count($mountsContainingFile) === 0) { - if ($user === $this->getAppDataDirectoryName()) { - return $this->getByIdInRootMount((int)$id); - } - return []; - } - - $nodes = array_map(function (IMountPoint $mount) use ($id, $mountRoots) { - $rootInternalPath = $mountRoots[$mount->getStorageRootId()]; - $cacheEntry = $mount->getStorage()->getCache()->get((int)$id); - if (!$cacheEntry) { - return null; - } - - // cache jails will hide the "true" internal path - $internalPath = ltrim($rootInternalPath . '/' . $cacheEntry->getPath(), '/'); - $pathRelativeToMount = substr($internalPath, strlen($rootInternalPath)); - $pathRelativeToMount = ltrim($pathRelativeToMount, '/'); - $absolutePath = rtrim($mount->getMountPoint() . $pathRelativeToMount, '/'); - return $this->root->createNode($absolutePath, new \OC\Files\FileInfo( - $absolutePath, $mount->getStorage(), $cacheEntry->getPath(), $cacheEntry, $mount, - \OC::$server->getUserManager()->get($mount->getStorage()->getOwner($pathRelativeToMount)) - )); - }, $mountsContainingFile); - - $nodes = array_filter($nodes); - - $folders = array_filter($nodes, function (Node $node) { - return $this->getRelativePath($node->getPath()); - }); - usort($folders, function ($a, $b) { - return $b->getPath() <=> $a->getPath(); - }); - return $folders; + return $this->root->getByIdInPath((int)$id, $this->getPath()); } protected function getAppDataDirectoryName(): string { diff --git a/lib/private/Files/Node/LazyFolder.php b/lib/private/Files/Node/LazyFolder.php index 45451e5c53c..7d5038e85a2 100644 --- a/lib/private/Files/Node/LazyFolder.php +++ b/lib/private/Files/Node/LazyFolder.php @@ -25,6 +25,7 @@ declare(strict_types=1); */ namespace OC\Files\Node; +use OC\Files\Utils\PathHelper; use OCP\Constants; /** @@ -382,13 +383,6 @@ class LazyFolder implements \OCP\Files\Folder { /** * @inheritDoc */ - public function getRelativePath($path) { - return $this->__call(__FUNCTION__, func_get_args()); - } - - /** - * @inheritDoc - */ public function isSubNode($node) { return $this->__call(__FUNCTION__, func_get_args()); } @@ -518,4 +512,8 @@ class LazyFolder implements \OCP\Files\Folder { public function getUploadTime(): int { return $this->__call(__FUNCTION__, func_get_args()); } + + public function getRelativePath($path) { + return PathHelper::getRelativePath($this->getPath(), $path); + } } diff --git a/lib/private/Files/Node/LazyRoot.php b/lib/private/Files/Node/LazyRoot.php index c4d61f653e4..c01b9fdbb83 100644 --- a/lib/private/Files/Node/LazyRoot.php +++ b/lib/private/Files/Node/LazyRoot.php @@ -39,4 +39,8 @@ class LazyRoot extends LazyFolder implements IRootFolder { public function getUserFolder($userId) { return $this->__call(__FUNCTION__, func_get_args()); } + + public function getByIdInPath(int $id, string $path) { + return $this->__call(__FUNCTION__, func_get_args()); + } } diff --git a/lib/private/Files/Node/LazyUserFolder.php b/lib/private/Files/Node/LazyUserFolder.php index 4c9e89ce233..d91759117c1 100644 --- a/lib/private/Files/Node/LazyUserFolder.php +++ b/lib/private/Files/Node/LazyUserFolder.php @@ -29,28 +29,38 @@ use OCP\Files\NotFoundException; use OCP\IUser; class LazyUserFolder extends LazyFolder { - private IRootFolder $rootFolder; + private IRootFolder $root; private IUser $user; + private string $path; public function __construct(IRootFolder $rootFolder, IUser $user) { - $this->rootFolder = $rootFolder; + $this->root = $rootFolder; $this->user = $user; + $this->path = '/' . $user->getUID() . '/files'; parent::__construct(function () use ($user) { try { - return $this->rootFolder->get('/' . $user->getUID() . '/files'); + return $this->root->get('/' . $user->getUID() . '/files'); } catch (NotFoundException $e) { - if (!$this->rootFolder->nodeExists('/' . $user->getUID())) { - $this->rootFolder->newFolder('/' . $user->getUID()); + if (!$this->root->nodeExists('/' . $user->getUID())) { + $this->root->newFolder('/' . $user->getUID()); } - return $this->rootFolder->newFolder('/' . $user->getUID() . '/files'); + return $this->root->newFolder('/' . $user->getUID() . '/files'); } }, [ - 'path' => '/' . $user->getUID() . '/files', + 'path' => $this->path, 'permissions' => Constants::PERMISSION_ALL, ]); } public function get($path) { - return $this->rootFolder->get('/' . $this->user->getUID() . '/files/' . rtrim($path, '/')); + return $this->root->get('/' . $this->user->getUID() . '/files/' . ltrim($path, '/')); + } + + /** + * @param int $id + * @return \OC\Files\Node\Node[] + */ + public function getById($id) { + return $this->root->getByIdInPath((int)$id, $this->getPath()); } } diff --git a/lib/private/Files/Node/Node.php b/lib/private/Files/Node/Node.php index b939bfce945..c8975154059 100644 --- a/lib/private/Files/Node/Node.php +++ b/lib/private/Files/Node/Node.php @@ -31,6 +31,7 @@ namespace OC\Files\Node; use OC\Files\Filesystem; use OC\Files\Mount\MoveableMount; +use OC\Files\Utils\PathHelper; use OCP\Files\FileInfo; use OCP\Files\InvalidPathException; use OCP\Files\NotFoundException; @@ -153,12 +154,11 @@ class Node implements \OCP\Files\Node { } } - /** - * @return \OC\Files\Storage\Storage - * @throws \OCP\Files\NotFoundException - */ public function getStorage() { - [$storage,] = $this->view->resolvePath($this->path); + $storage = $this->getMountPoint()->getStorage(); + if (!$storage) { + throw new \Exception("No storage for node"); + } return $storage; } @@ -173,8 +173,7 @@ class Node implements \OCP\Files\Node { * @return string */ public function getInternalPath() { - [, $internalPath] = $this->view->resolvePath($this->path); - return $internalPath; + return $this->getFileInfo()->getInternalPath(); } /** @@ -298,23 +297,7 @@ class Node implements \OCP\Files\Node { * @return string */ protected function normalizePath($path) { - if ($path === '' or $path === '/') { - return '/'; - } - //no windows style slashes - $path = str_replace('\\', '/', $path); - //add leading slash - if ($path[0] !== '/') { - $path = '/' . $path; - } - //remove duplicate slashes - while (strpos($path, '//') !== false) { - $path = str_replace('//', '/', $path); - } - //remove trailing slash - $path = rtrim($path, '/'); - - return $path; + return PathHelper::normalizePath($path); } /** @@ -447,6 +430,15 @@ class Node implements \OCP\Files\Node { if (!$this->view->rename($this->path, $targetPath)) { throw new NotPermittedException('Could not move ' . $this->path . ' to ' . $targetPath); } + + $mountPoint = $this->getMountPoint(); + if ($mountPoint) { + // update the cached fileinfo with the new (internal) path + /** @var \OC\Files\FileInfo $oldFileInfo */ + $oldFileInfo = $this->getFileInfo(); + $this->fileInfo = new \OC\Files\FileInfo($targetPath, $oldFileInfo->getStorage(), $mountPoint->getInternalPath($targetPath), $oldFileInfo->getData(), $mountPoint, $oldFileInfo->getOwner()); + } + $targetNode = $this->root->get($targetPath); $this->sendHooks(['postRename'], [$this, $targetNode]); $this->sendHooks(['postWrite'], [$targetNode]); diff --git a/lib/private/Files/Node/Root.php b/lib/private/Files/Node/Root.php index a8b36ee7731..7592d4caf37 100644 --- a/lib/private/Files/Node/Root.php +++ b/lib/private/Files/Node/Root.php @@ -33,8 +33,10 @@ namespace OC\Files\Node; use OC\Cache\CappedMemoryCache; +use OC\Files\FileInfo; use OC\Files\Mount\Manager; use OC\Files\Mount\MountPoint; +use OC\Files\Utils\PathHelper; use OC\Files\View; use OC\Hooks\PublicEmitter; use OC\User\NoUserException; @@ -42,6 +44,7 @@ use OCP\EventDispatcher\IEventDispatcher; use OCP\Files\Config\IUserMountCache; use OCP\Files\Events\Node\FilesystemTornDownEvent; use OCP\Files\IRootFolder; +use OCP\Files\Mount\IMountPoint; use OCP\Files\NotFoundException; use OCP\Files\NotPermittedException; use OCP\IUser; @@ -214,7 +217,7 @@ class Root extends Folder implements IRootFolder { /** * @param string $targetPath - * @return \OC\Files\Node\Node + * @return Node * @throws \OCP\Files\NotPermittedException */ public function rename($targetPath) { @@ -227,7 +230,7 @@ class Root extends Folder implements IRootFolder { /** * @param string $targetPath - * @return \OC\Files\Node\Node + * @return Node * @throws \OCP\Files\NotPermittedException */ public function copy($targetPath) { @@ -402,4 +405,82 @@ class Root extends Folder implements IRootFolder { public function getUserMountCache() { return $this->userMountCache; } + + /** + * @param int $id + * @return Node[] + */ + public function getByIdInPath(int $id, string $path): array { + $mountCache = $this->getUserMountCache(); + if (strpos($path, '/', 1) > 0) { + [, $user] = explode('/', $path); + } else { + $user = null; + } + $mountsContainingFile = $mountCache->getMountsForFileId($id, $user); + + // when a user has access trough the same storage trough multiple paths + // (such as an external storage that is both mounted for a user and shared to the user) + // the mount cache will only hold a single entry for the storage + // this can lead to issues as the different ways the user has access to a storage can have different permissions + // + // so instead of using the cached entries directly, we instead filter the current mounts by the rootid of the cache entry + + $mountRootIds = array_map(function ($mount) { + return $mount->getRootId(); + }, $mountsContainingFile); + $mountRootPaths = array_map(function ($mount) { + return $mount->getRootInternalPath(); + }, $mountsContainingFile); + $mountProviders = array_unique(array_map(function ($mount) { + return $mount->getMountProvider(); + }, $mountsContainingFile)); + $mountRoots = array_combine($mountRootIds, $mountRootPaths); + + $mounts = $this->mountManager->getMountsByMountProvider($path, $mountProviders); + + $mountsContainingFile = array_filter($mounts, function ($mount) use ($mountRoots) { + return isset($mountRoots[$mount->getStorageRootId()]); + }); + + if (count($mountsContainingFile) === 0) { + if ($user === $this->getAppDataDirectoryName()) { + $folder = $this->get($path); + if ($folder instanceof Folder) { + return $folder->getByIdInRootMount($id); + } else { + throw new \Exception("getByIdInPath with non folder"); + } + } + return []; + } + + $nodes = array_map(function (IMountPoint $mount) use ($id, $mountRoots) { + $rootInternalPath = $mountRoots[$mount->getStorageRootId()]; + $cacheEntry = $mount->getStorage()->getCache()->get($id); + if (!$cacheEntry) { + return null; + } + + // cache jails will hide the "true" internal path + $internalPath = ltrim($rootInternalPath . '/' . $cacheEntry->getPath(), '/'); + $pathRelativeToMount = substr($internalPath, strlen($rootInternalPath)); + $pathRelativeToMount = ltrim($pathRelativeToMount, '/'); + $absolutePath = rtrim($mount->getMountPoint() . $pathRelativeToMount, '/'); + return $this->createNode($absolutePath, new FileInfo( + $absolutePath, $mount->getStorage(), $cacheEntry->getPath(), $cacheEntry, $mount, + \OC::$server->getUserManager()->get($mount->getStorage()->getOwner($pathRelativeToMount)) + )); + }, $mountsContainingFile); + + $nodes = array_filter($nodes); + + $folders = array_filter($nodes, function (Node $node) use ($path) { + return PathHelper::getRelativePath($path, $node->getPath()) !== null; + }); + usort($folders, function ($a, $b) { + return $b->getPath() <=> $a->getPath(); + }); + return $folders; + } } diff --git a/lib/private/Files/SetupManager.php b/lib/private/Files/SetupManager.php index da50983da32..ddb0bbceb81 100644 --- a/lib/private/Files/SetupManager.php +++ b/lib/private/Files/SetupManager.php @@ -40,6 +40,7 @@ use OC_Util; use OCP\Constants; use OCP\Diagnostics\IEventLogger; use OCP\EventDispatcher\IEventDispatcher; +use OCP\Files\Config\IHomeMountProvider; use OCP\Files\Config\IMountProvider; use OCP\Files\Config\IUserMountCache; use OCP\Files\Events\InvalidateMountCacheEvent; @@ -263,6 +264,11 @@ class SetupManager { return strpos($mount->getMountPoint(), $userRoot) === 0; }); $this->userMountCache->registerMounts($user, $mounts); + + $cacheDuration = $this->config->getSystemValueInt('fs_mount_cache_duration', 5 * 60); + if ($cacheDuration > 0) { + $this->cache->set($user->getUID(), true, $cacheDuration); + } } /** @@ -321,25 +327,32 @@ class SetupManager { } /** - * Set up the filesystem for the specified path + * Get the user to setup for a path or `null` if the root needs to be setup + * + * @param string $path + * @return IUser|null */ - public function setupForPath(string $path, bool $includeChildren = false): void { + private function getUserForPath(string $path) { if (substr_count($path, '/') < 2) { if ($user = $this->userSession->getUser()) { - $this->setupForUser($user); + return $user; } else { - $this->setupRoot(); + return null; } - return; } elseif (strpos($path, '/appdata_' . \OC_Util::getInstanceId()) === 0 || strpos($path, '/files_external/') === 0) { - $this->setupRoot(); - return; + return null; } else { [, $userId] = explode('/', $path); } - $user = $this->userManager->get($userId); + return $this->userManager->get($userId); + } + /** + * Set up the filesystem for the specified path + */ + public function setupForPath(string $path, bool $includeChildren = false): void { + $user = $this->getUserForPath($path); if (!$user) { $this->setupRoot(); return; @@ -349,17 +362,8 @@ class SetupManager { return; } - // 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 - $cachedSetup = $this->cache->get($user->getUID()); - if (!$cachedSetup) { + if ($this->fullSetupRequired($user)) { $this->setupForUser($user); - - $cacheDuration = $this->config->getSystemValueInt('fs_mount_cache_duration', 5 * 60); - if ($cacheDuration > 0) { - $this->cache->set($user->getUID(), true, $cacheDuration); - } return; } @@ -381,7 +385,7 @@ class SetupManager { $setupProviders[] = $cachedMount->getMountProvider(); $currentProviders[] = $cachedMount->getMountProvider(); if ($cachedMount->getMountProvider()) { - $mounts = $this->mountProviderCollection->getUserMountsForProviderClass($user, $cachedMount->getMountProvider()); + $mounts = $this->mountProviderCollection->getUserMountsForProviderClasses($user, [$cachedMount->getMountProvider()]); } else { $this->logger->debug("mount at " . $cachedMount->getMountPoint() . " has no provider set, performing full setup"); $this->setupForUser($user); @@ -396,7 +400,7 @@ class SetupManager { $setupProviders[] = $cachedMount->getMountProvider(); $currentProviders[] = $cachedMount->getMountProvider(); if ($cachedMount->getMountProvider()) { - $mounts = array_merge($mounts, $this->mountProviderCollection->getUserMountsForProviderClass($user, $cachedMount->getMountProvider())); + $mounts = array_merge($mounts, $this->mountProviderCollection->getUserMountsForProviderClasses($user, [$cachedMount->getMountProvider()])); } else { $this->logger->debug("mount at " . $cachedMount->getMountPoint() . " has no provider set, performing full setup"); $this->setupForUser($user); @@ -416,6 +420,60 @@ class SetupManager { } } + private function fullSetupRequired(IUser $user): bool { + // 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()); + } + + /** + * @param string $path + * @param string[] $providers + */ + public function setupForProvider(string $path, array $providers): void { + $user = $this->getUserForPath($path); + if (!$user) { + $this->setupRoot(); + return; + } + + if ($this->isSetupComplete($user)) { + return; + } + + if ($this->fullSetupRequired($user)) { + $this->setupForUser($user); + return; + } + + // home providers are always used + $providers = array_filter($providers, function (string $provider) { + return !is_subclass_of($provider, IHomeMountProvider::class); + }); + + if (in_array('', $providers)) { + $this->setupForUser($user); + } + $setupProviders = $this->setupUserMountProviders[$user->getUID()] ?? []; + + $providers = array_diff($providers, $setupProviders); + if (count($providers) === 0) { + if (!$this->isSetupStarted($user)) { + $this->oneTimeUserSetup($user); + } + return; + } else { + $this->setupUserMountProviders[$user->getUID()] = array_merge($setupProviders, $providers); + $mounts = $this->mountProviderCollection->getUserMountsForProviderClasses($user, $providers); + } + + $this->userMountCache->registerMounts($user, $mounts, $providers); + $this->setupForUserWith($user, function () use ($mounts) { + array_walk($mounts, [$this->mountManager, 'addMount']); + }); + } + public function tearDown() { $this->setupUsers = []; $this->setupUsersComplete = []; diff --git a/lib/private/Files/Utils/PathHelper.php b/lib/private/Files/Utils/PathHelper.php new file mode 100644 index 00000000000..07985e884ce --- /dev/null +++ b/lib/private/Files/Utils/PathHelper.php @@ -0,0 +1,71 @@ +<?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\Files\Utils; + +class PathHelper { + /** + * Make a path relative to a root path, or return null if the path is outside the root + * + * @param string $root + * @param string $path + * @return ?string + */ + public static function getRelativePath(string $root, string $path) { + if ($root === '' or $root === '/') { + return self::normalizePath($path); + } + if ($path === $root) { + return '/'; + } elseif (strpos($path, $root . '/') !== 0) { + return null; + } else { + $path = substr($path, strlen($root)); + return self::normalizePath($path); + } + } + + /** + * @param string $path + * @return string + */ + public static function normalizePath(string $path): string { + if ($path === '' or $path === '/') { + return '/'; + } + //no windows style slashes + $path = str_replace('\\', '/', $path); + //add leading slash + if ($path[0] !== '/') { + $path = '/' . $path; + } + //remove duplicate slashes + while (strpos($path, '//') !== false) { + $path = str_replace('//', '/', $path); + } + //remove trailing slash + $path = rtrim($path, '/'); + + return $path; + } +} diff --git a/lib/public/Files/Config/IMountProviderCollection.php b/lib/public/Files/Config/IMountProviderCollection.php index 5894d06a388..2d42246b863 100644 --- a/lib/public/Files/Config/IMountProviderCollection.php +++ b/lib/public/Files/Config/IMountProviderCollection.php @@ -42,11 +42,11 @@ interface IMountProviderCollection { * Get the configured mount points for the user from a specific mount provider * * @param \OCP\IUser $user - * @param class-string<IMountProvider> $mountProviderClass + * @param class-string<IMountProvider>[] $mountProviderClasses * @return \OCP\Files\Mount\IMountPoint[] * @since 24.0.0 */ - public function getUserMountsForProviderClass(IUser $user, string $mountProviderClass); + public function getUserMountsForProviderClasses(IUser $user, array $mountProviderClasses): array; /** * Get the configured home mount for this user diff --git a/lib/public/Files/IRootFolder.php b/lib/public/Files/IRootFolder.php index f89a0041146..7d007cb690c 100644 --- a/lib/public/Files/IRootFolder.php +++ b/lib/public/Files/IRootFolder.php @@ -38,11 +38,22 @@ interface IRootFolder extends Folder, Emitter { * Returns a view to user's files folder * * @param string $userId user ID - * @return \OCP\Files\Folder + * @return Folder * @throws NoUserException * @throws NotPermittedException * * @since 8.2.0 */ public function getUserFolder($userId); + + /** + * Get a file or folder by fileid, inside a parent path + * + * @param int $id + * @param string $path + * @return Node[] + * + * @since 24.0.0 + */ + public function getByIdInPath(int $id, string $path); } diff --git a/tests/lib/Files/Node/FileTest.php b/tests/lib/Files/Node/FileTest.php index c381fd9b2be..3305f9ac170 100644 --- a/tests/lib/Files/Node/FileTest.php +++ b/tests/lib/Files/Node/FileTest.php @@ -16,8 +16,12 @@ namespace Test\Files\Node; * @package Test\Files\Node */ class FileTest extends NodeTest { - protected function createTestNode($root, $view, $path) { - return new \OC\Files\Node\File($root, $view, $path); + protected function createTestNode($root, $view, $path, array $data = [], $internalPath = '', $storage = null) { + if ($data || $internalPath || $storage) { + return new \OC\Files\Node\File($root, $view, $path, $this->getFileInfo($data, $internalPath, $storage)); + } else { + return new \OC\Files\Node\File($root, $view, $path); + } } protected function getNodeClass() { diff --git a/tests/lib/Files/Node/FolderTest.php b/tests/lib/Files/Node/FolderTest.php index d604786905d..05f546874ef 100644 --- a/tests/lib/Files/Node/FolderTest.php +++ b/tests/lib/Files/Node/FolderTest.php @@ -38,8 +38,12 @@ use OCP\Files\Storage; * @package Test\Files\Node */ class FolderTest extends NodeTest { - protected function createTestNode($root, $view, $path) { - return new Folder($root, $view, $path); + protected function createTestNode($root, $view, $path, array $data = [], $internalPath = '', $storage = null) { + if ($data || $internalPath || $storage) { + return new Folder($root, $view, $path, $this->getFileInfo($data, $internalPath, $storage)); + } else { + return new Folder($root, $view, $path); + } } protected function getNodeClass() { @@ -512,9 +516,8 @@ class FolderTest extends NodeTest { ->with('/bar/foo') ->willReturn([]); - $root->method('getMount') - ->with('/bar/foo') - ->willReturn($mount); + $manager->method('getMountsByMountProvider') + ->willReturn([$mount]); $node = new Folder($root, $view, '/bar/foo'); $result = $node->getById(1); @@ -559,9 +562,8 @@ class FolderTest extends NodeTest { ->with(1) ->willReturn($fileInfo); - $root->method('getMount') - ->with('/bar') - ->willReturn($mount); + $manager->method('getMountsByMountProvider') + ->willReturn([$mount]); $node = new Folder($root, $view, '/bar'); $result = $node->getById(1); @@ -606,13 +608,8 @@ class FolderTest extends NodeTest { ->with(1) ->willReturn($fileInfo); - $root->method('getMountsIn') - ->with('/bar/foo') - ->willReturn([]); - - $root->method('getMount') - ->with('/bar/foo') - ->willReturn($mount); + $manager->method('getMountsByMountProvider') + ->willReturn([$mount]); $node = new Folder($root, $view, '/bar/foo'); $result = $node->getById(1); @@ -661,13 +658,8 @@ class FolderTest extends NodeTest { ->with(1) ->willReturn($fileInfo); - $root->method('getMountsIn') - ->with('/bar/foo') - ->willReturn([$mount2]); - - $root->method('getMount') - ->with('/bar/foo') - ->willReturn($mount1); + $manager->method('getMountsByMountProvider') + ->willReturn([$mount1, $mount2]); $node = new Folder($root, $view, '/bar/foo'); $result = $node->getById(1); diff --git a/tests/lib/Files/Node/NodeTest.php b/tests/lib/Files/Node/NodeTest.php index cc936baacb0..8c0d4cdb293 100644 --- a/tests/lib/Files/Node/NodeTest.php +++ b/tests/lib/Files/Node/NodeTest.php @@ -13,6 +13,7 @@ use OC\Files\Mount\Manager; use OC\Files\View; use OCP\EventDispatcher\IEventDispatcher; use OCP\Files\IRootFolder; +use OCP\Files\Mount\IMountPoint; use OCP\Files\Node; use OCP\Files\NotFoundException; use OCP\Files\Storage; @@ -70,7 +71,7 @@ abstract class NodeTest extends \Test\TestCase { * @param string $path * @return Node */ - abstract protected function createTestNode($root, $view, $path); + abstract protected function createTestNode($root, $view, $path, array $data = [], $internalPath = '', $storage = null); /** * @return string @@ -97,8 +98,11 @@ abstract class NodeTest extends \Test\TestCase { return $storage; } - protected function getFileInfo($data) { - return new FileInfo('', $this->getMockStorage(), '', $data, null); + protected function getFileInfo($data, $internalPath = '', $storage = null) { + $mount = $this->createMock(IMountPoint::class); + $mount->method('getStorage') + ->willReturn($storage); + return new FileInfo('', $this->getMockStorage(), $internalPath, $data, $mount); } public function testDelete() { @@ -165,18 +169,13 @@ abstract class NodeTest extends \Test\TestCase { $this->view->expects($this->any()) ->method('getFileInfo') ->with('/bar/foo') - ->willReturn($this->getFileInfo(['permissions' => \OCP\Constants::PERMISSION_ALL, 'fileid' => 1, 'mimetype' => 'text/plain'])); + ->willReturn($this->getFileInfo(['permissions' => \OCP\Constants::PERMISSION_ALL, 'fileid' => 1, 'mimetype' => 'text/plain'], 'foo')); $this->view->expects($this->once()) ->method($this->getViewDeleteMethod()) ->with('/bar/foo') ->willReturn(true); - $this->view->expects($this->any()) - ->method('resolvePath') - ->with('/bar/foo') - ->willReturn([null, 'foo']); - $node = $this->createTestNode($root, $this->view, '/bar/foo'); $node->delete(); $this->assertEquals(2, $hooksRun); @@ -318,13 +317,7 @@ abstract class NodeTest extends \Test\TestCase { ->disableOriginalConstructor() ->getMock(); - $this->view->expects($this->once()) - ->method('resolvePath') - ->with('/bar/foo') - ->willReturn([$storage, 'foo']); - - - $node = $this->createTestNode($this->root, $this->view, '/bar/foo'); + $node = $this->createTestNode($this->root, $this->view, '/bar/foo', [], 'foo', $storage); $this->assertEquals($storage, $node->getStorage()); } @@ -349,9 +342,9 @@ abstract class NodeTest extends \Test\TestCase { ->getMock(); $this->view->expects($this->once()) - ->method('resolvePath') + ->method('getFileInfo') ->with('/bar/foo') - ->willReturn([$storage, 'foo']); + ->willReturn($this->getFileInfo([], 'foo')); $node = $this->createTestNode($this->root, $this->view, '/bar/foo'); @@ -426,14 +419,9 @@ abstract class NodeTest extends \Test\TestCase { ->willReturn(true); $this->view->expects($this->any()) - ->method('resolvePath') - ->with('/bar/foo') - ->willReturn([null, 'foo']); - - $this->view->expects($this->any()) ->method('getFileInfo') ->with('/bar/foo') - ->willReturn($this->getFileInfo(['permissions' => \OCP\Constants::PERMISSION_ALL])); + ->willReturn($this->getFileInfo(['permissions' => \OCP\Constants::PERMISSION_ALL], 'foo')); $node = $this->createTestNode($root, $this->view, '/bar/foo'); $node->touch(100); |