diff options
author | Robin Appelman <robin@icewind.nl> | 2022-03-28 18:47:17 +0200 |
---|---|---|
committer | Robin Appelman <robin@icewind.nl> | 2022-04-04 15:40:16 +0200 |
commit | 44a8ebdc1fa796d4f7dc453c58561b64902ca12f (patch) | |
tree | b2dc883aa3c8d8ff36c89cda9be16f0aad242bfc | |
parent | 700444e21801002cc24093229d8ac7714d2e1486 (diff) | |
download | nextcloud-server-44a8ebdc1fa796d4f7dc453c58561b64902ca12f.tar.gz nextcloud-server-44a8ebdc1fa796d4f7dc453c58561b64902ca12f.zip |
optimize getById on LazyUserFolder to not require a full fs setup
Signed-off-by: Robin Appelman <robin@icewind.nl>
-rw-r--r-- | lib/private/Files/Node/Folder.php | 65 | ||||
-rw-r--r-- | lib/private/Files/Node/LazyFolder.php | 12 | ||||
-rw-r--r-- | lib/private/Files/Node/LazyRoot.php | 4 | ||||
-rw-r--r-- | lib/private/Files/Node/LazyUserFolder.php | 26 | ||||
-rw-r--r-- | lib/private/Files/Node/Root.php | 83 | ||||
-rw-r--r-- | lib/public/Files/IRootFolder.php | 13 |
6 files changed, 121 insertions, 82 deletions
diff --git a/lib/private/Files/Node/Folder.php b/lib/private/Files/Node/Folder.php index 3c40ee455fa..d058805b20e 100644 --- a/lib/private/Files/Node/Folder.php +++ b/lib/private/Files/Node/Folder.php @@ -333,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/Root.php b/lib/private/Files/Node/Root.php index 84493551003..7592d4caf37 100644 --- a/lib/private/Files/Node/Root.php +++ b/lib/private/Files/Node/Root.php @@ -44,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; @@ -216,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) { @@ -229,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) { @@ -404,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/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); } |