diff options
Diffstat (limited to 'lib/private/Files/Node/Node.php')
-rw-r--r-- | lib/private/Files/Node/Node.php | 212 |
1 files changed, 121 insertions, 91 deletions
diff --git a/lib/private/Files/Node/Node.php b/lib/private/Files/Node/Node.php index c8975154059..5dbdc4054bf 100644 --- a/lib/private/Files/Node/Node.php +++ b/lib/private/Files/Node/Node.php @@ -1,65 +1,45 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Arthur Schiwon <blizzz@arthur-schiwon.de> - * @author Bernhard Posselt <dev@bernhard-posselt.com> - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Joas Schilling <coding@schilljs.com> - * @author Julius Härtl <jus@bitgrid.net> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Robin Appelman <robin@icewind.nl> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * @author Vincent Petry <vincent@nextcloud.com> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * 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, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Files\Node; use OC\Files\Filesystem; use OC\Files\Mount\MoveableMount; use OC\Files\Utils\PathHelper; +use OCP\EventDispatcher\GenericEvent; +use OCP\EventDispatcher\IEventDispatcher; use OCP\Files\FileInfo; use OCP\Files\InvalidPathException; +use OCP\Files\IRootFolder; +use OCP\Files\Node as INode; use OCP\Files\NotFoundException; use OCP\Files\NotPermittedException; use OCP\Lock\LockedException; -use Symfony\Component\EventDispatcher\GenericEvent; +use OCP\PreConditionNotMetException; -// FIXME: this class really should be abstract -class Node implements \OCP\Files\Node { +// FIXME: this class really should be abstract (+1) +class Node implements INode { /** * @var \OC\Files\View $view */ protected $view; - /** - * @var \OC\Files\Node\Root $root - */ - protected $root; + protected IRootFolder $root; /** - * @var string $path + * @var string $path Absolute path to the node (e.g. /admin/files/folder/file) */ protected $path; - /** - * @var \OCP\Files\FileInfo - */ - protected $fileInfo; + protected ?FileInfo $fileInfo; + + protected ?INode $parent; + + private bool $infoHasSubMountsIncluded; /** * @param \OC\Files\View $view @@ -67,18 +47,23 @@ class Node implements \OCP\Files\Node { * @param string $path * @param FileInfo $fileInfo */ - public function __construct($root, $view, $path, $fileInfo = null) { + public function __construct(IRootFolder $root, $view, $path, $fileInfo = null, ?INode $parent = null, bool $infoHasSubMountsIncluded = true) { + if (Filesystem::normalizePath($view->getRoot()) !== '/') { + throw new PreConditionNotMetException('The view passed to the node should not have any fake root set'); + } $this->view = $view; $this->root = $root; $this->path = $path; $this->fileInfo = $fileInfo; + $this->parent = $parent; + $this->infoHasSubMountsIncluded = $infoHasSubMountsIncluded; } /** * Creates a Node of the same type that represents a non-existing path * * @param string $path path - * @return string non-existing node class + * @return Node non-existing node * @throws \Exception */ protected function createNonExistingNode($path) { @@ -92,17 +77,23 @@ class Node implements \OCP\Files\Node { * @throws InvalidPathException * @throws NotFoundException */ - public function getFileInfo() { - if (!Filesystem::isValidPath($this->path)) { - throw new InvalidPathException(); - } + public function getFileInfo(bool $includeMountPoint = true) { if (!$this->fileInfo) { - $fileInfo = $this->view->getFileInfo($this->path); + if (!Filesystem::isValidPath($this->path)) { + throw new InvalidPathException(); + } + $fileInfo = $this->view->getFileInfo($this->path, $includeMountPoint); + $this->infoHasSubMountsIncluded = $includeMountPoint; if ($fileInfo instanceof FileInfo) { $this->fileInfo = $fileInfo; } else { throw new NotFoundException(); } + } elseif ($includeMountPoint && !$this->infoHasSubMountsIncluded && $this instanceof Folder) { + if ($this->fileInfo instanceof \OC\Files\FileInfo) { + $this->view->addSubMounts($this->fileInfo); + } + $this->infoHasSubMountsIncluded = true; } return $this->fileInfo; } @@ -110,12 +101,22 @@ class Node implements \OCP\Files\Node { /** * @param string[] $hooks */ - protected function sendHooks($hooks, array $args = null) { + protected function sendHooks($hooks, ?array $args = null) { $args = !empty($args) ? $args : [$this]; - $dispatcher = \OC::$server->getEventDispatcher(); + /** @var IEventDispatcher $dispatcher */ + $dispatcher = \OC::$server->get(IEventDispatcher::class); foreach ($hooks as $hook) { - $this->root->emit('\OC\Files', $hook, $args); - $dispatcher->dispatch('\OCP\Files::' . $hook, new GenericEvent($args)); + if (method_exists($this->root, 'emit')) { + $this->root->emit('\OC\Files', $hook, $args); + } + + if (in_array($hook, ['preWrite', 'postWrite', 'preCreate', 'postCreate', 'preTouch', 'postTouch', 'preDelete', 'postDelete'], true)) { + $event = new GenericEvent($args[0]); + } else { + $event = new GenericEvent($args); + } + + $dispatcher->dispatch('\OCP\Files::' . $hook, $event); } } @@ -157,7 +158,7 @@ class Node implements \OCP\Files\Node { public function getStorage() { $storage = $this->getMountPoint()->getStorage(); if (!$storage) { - throw new \Exception("No storage for node"); + throw new \Exception('No storage for node'); } return $storage; } @@ -173,7 +174,7 @@ class Node implements \OCP\Files\Node { * @return string */ public function getInternalPath() { - return $this->getFileInfo()->getInternalPath(); + return $this->getFileInfo(false)->getInternalPath(); } /** @@ -182,7 +183,7 @@ class Node implements \OCP\Files\Node { * @throws NotFoundException */ public function getId() { - return $this->getFileInfo()->getId(); + return $this->getFileInfo(false)->getId() ?? -1; } /** @@ -203,11 +204,11 @@ class Node implements \OCP\Files\Node { /** * @param bool $includeMounts - * @return int + * @return int|float * @throws InvalidPathException * @throws NotFoundException */ - public function getSize($includeMounts = true) { + public function getSize($includeMounts = true): int|float { return $this->getFileInfo()->getSize($includeMounts); } @@ -226,7 +227,7 @@ class Node implements \OCP\Files\Node { * @throws NotFoundException */ public function getPermissions() { - return $this->getFileInfo()->getPermissions(); + return $this->getFileInfo(false)->getPermissions(); } /** @@ -235,7 +236,7 @@ class Node implements \OCP\Files\Node { * @throws NotFoundException */ public function isReadable() { - return $this->getFileInfo()->isReadable(); + return $this->getFileInfo(false)->isReadable(); } /** @@ -244,7 +245,7 @@ class Node implements \OCP\Files\Node { * @throws NotFoundException */ public function isUpdateable() { - return $this->getFileInfo()->isUpdateable(); + return $this->getFileInfo(false)->isUpdateable(); } /** @@ -253,7 +254,7 @@ class Node implements \OCP\Files\Node { * @throws NotFoundException */ public function isDeletable() { - return $this->getFileInfo()->isDeletable(); + return $this->getFileInfo(false)->isDeletable(); } /** @@ -262,7 +263,7 @@ class Node implements \OCP\Files\Node { * @throws NotFoundException */ public function isShareable() { - return $this->getFileInfo()->isShareable(); + return $this->getFileInfo(false)->isShareable(); } /** @@ -271,18 +272,38 @@ class Node implements \OCP\Files\Node { * @throws NotFoundException */ public function isCreatable() { - return $this->getFileInfo()->isCreatable(); + return $this->getFileInfo(false)->isCreatable(); } - /** - * @return Node - */ - public function getParent() { - $newPath = dirname($this->path); - if ($newPath === '' || $newPath === '.' || $newPath === '/') { - return $this->root; + public function getParent(): INode|IRootFolder { + if ($this->parent === null) { + $newPath = dirname($this->path); + if ($newPath === '' || $newPath === '.' || $newPath === '/') { + return $this->root; + } + + // Manually fetch the parent if the current node doesn't have a file info yet + try { + $fileInfo = $this->getFileInfo(); + } catch (NotFoundException) { + $this->parent = $this->root->get($newPath); + /** @var \OCP\Files\Folder $this->parent */ + return $this->parent; + } + + // gather the metadata we already know about our parent + $parentData = [ + 'path' => $newPath, + 'fileid' => $fileInfo->getParentId(), + ]; + + // and create lazy folder with it instead of always querying + $this->parent = new LazyFolder($this->root, function () use ($newPath) { + return $this->root->get($newPath); + }, $parentData); } - return $this->root->get($newPath); + + return $this->parent; } /** @@ -307,52 +328,46 @@ class Node implements \OCP\Files\Node { * @return bool */ public function isValidPath($path) { - if (!$path || $path[0] !== '/') { - $path = '/' . $path; - } - if (strstr($path, '/../') || strrchr($path, '/') === '/..') { - return false; - } - return true; + return Filesystem::isValidPath($path); } public function isMounted() { - return $this->getFileInfo()->isMounted(); + return $this->getFileInfo(false)->isMounted(); } public function isShared() { - return $this->getFileInfo()->isShared(); + return $this->getFileInfo(false)->isShared(); } public function getMimeType() { - return $this->getFileInfo()->getMimetype(); + return $this->getFileInfo(false)->getMimetype(); } public function getMimePart() { - return $this->getFileInfo()->getMimePart(); + return $this->getFileInfo(false)->getMimePart(); } public function getType() { - return $this->getFileInfo()->getType(); + return $this->getFileInfo(false)->getType(); } public function isEncrypted() { - return $this->getFileInfo()->isEncrypted(); + return $this->getFileInfo(false)->isEncrypted(); } public function getMountPoint() { - return $this->getFileInfo()->getMountPoint(); + return $this->getFileInfo(false)->getMountPoint(); } public function getOwner() { - return $this->getFileInfo()->getOwner(); + return $this->getFileInfo(false)->getOwner(); } public function getChecksum() { } public function getExtension(): string { - return $this->getFileInfo()->getExtension(); + return $this->getFileInfo(false)->getExtension(); } /** @@ -381,7 +396,7 @@ class Node implements \OCP\Files\Node { /** * @param string $targetPath - * @return \OC\Files\Node\Node + * @return INode * @throws InvalidPathException * @throws NotFoundException * @throws NotPermittedException if copy not allowed or failed @@ -407,7 +422,7 @@ class Node implements \OCP\Files\Node { /** * @param string $targetPath - * @return \OC\Files\Node\Node + * @return INode * @throws InvalidPathException * @throws NotFoundException * @throws NotPermittedException if move not allowed or failed @@ -417,11 +432,14 @@ class Node implements \OCP\Files\Node { $targetPath = $this->normalizePath($targetPath); $parent = $this->root->get(dirname($targetPath)); if ( - $parent instanceof Folder and - $this->isValidPath($targetPath) and - ( - $parent->isCreatable() || - ($parent->getInternalPath() === '' && $parent->getMountPoint() instanceof MoveableMount) + ($parent instanceof Folder) + && $this->isValidPath($targetPath) + && ( + $parent->isCreatable() + || ( + $parent->getInternalPath() === '' + && ($parent->getMountPoint() instanceof MoveableMount) + ) ) ) { $nonExisting = $this->createNonExistingNode($targetPath); @@ -456,4 +474,16 @@ class Node implements \OCP\Files\Node { public function getUploadTime(): int { return $this->getFileInfo()->getUploadTime(); } + + public function getParentId(): int { + return $this->fileInfo->getParentId(); + } + + /** + * @inheritDoc + * @return array<string, int|string|bool|float|string[]|int[]> + */ + public function getMetadata(): array { + return $this->fileInfo->getMetadata(); + } } |