summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--apps/files_sharing/lib/Controller/ShareAPIController.php33
-rw-r--r--apps/files_sharing/lib/SharedStorage.php41
-rw-r--r--apps/files_sharing/tests/ApiTest.php3
-rw-r--r--apps/files_sharing/tests/Controller/ShareAPIControllerTest.php44
-rw-r--r--apps/files_versions/lib/Storage.php5
-rw-r--r--lib/composer/composer/autoload_classmap.php1
-rw-r--r--lib/composer/composer/autoload_static.php1
-rw-r--r--lib/private/Files/Config/MountProviderCollection.php4
-rw-r--r--lib/private/Files/Mount/Manager.php19
-rw-r--r--lib/private/Files/Node/Folder.php78
-rw-r--r--lib/private/Files/Node/LazyFolder.php12
-rw-r--r--lib/private/Files/Node/LazyRoot.php4
-rw-r--r--lib/private/Files/Node/LazyUserFolder.php26
-rw-r--r--lib/private/Files/Node/Node.php40
-rw-r--r--lib/private/Files/Node/Root.php85
-rw-r--r--lib/private/Files/SetupManager.php98
-rw-r--r--lib/private/Files/Utils/PathHelper.php71
-rw-r--r--lib/public/Files/Config/IMountProviderCollection.php4
-rw-r--r--lib/public/Files/IRootFolder.php13
-rw-r--r--tests/lib/Files/Node/FileTest.php8
-rw-r--r--tests/lib/Files/Node/FolderTest.php36
-rw-r--r--tests/lib/Files/Node/NodeTest.php36
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);