diff options
Diffstat (limited to 'lib/private/Files/Node')
-rw-r--r-- | lib/private/Files/Node/Folder.php | 72 | ||||
-rw-r--r-- | lib/private/Files/Node/HookConnector.php | 8 | ||||
-rw-r--r-- | lib/private/Files/Node/LazyFolder.php | 10 | ||||
-rw-r--r-- | lib/private/Files/Node/LazyUserFolder.php | 2 | ||||
-rw-r--r-- | lib/private/Files/Node/Node.php | 15 | ||||
-rw-r--r-- | lib/private/Files/Node/NonExistingFile.php | 8 | ||||
-rw-r--r-- | lib/private/Files/Node/NonExistingFolder.php | 8 | ||||
-rw-r--r-- | lib/private/Files/Node/Root.php | 49 |
8 files changed, 118 insertions, 54 deletions
diff --git a/lib/private/Files/Node/Folder.php b/lib/private/Files/Node/Folder.php index 51b205d545b..7453b553119 100644 --- a/lib/private/Files/Node/Folder.php +++ b/lib/private/Files/Node/Folder.php @@ -1,4 +1,5 @@ <?php + /** * SPDX-FileCopyrightText: 2022 Nextcloud GmbH and Nextcloud contributors * SPDX-FileCopyrightText: 2016 ownCloud, Inc. @@ -12,6 +13,7 @@ use OC\Files\Search\SearchComparison; use OC\Files\Search\SearchOrder; use OC\Files\Search\SearchQuery; use OC\Files\Utils\PathHelper; +use OC\User\LazyUser; use OCP\Files\Cache\ICacheEntry; use OCP\Files\FileInfo; use OCP\Files\Mount\IMountPoint; @@ -26,6 +28,9 @@ use OCP\Files\Search\ISearchQuery; use OCP\IUserManager; class Folder extends Node implements \OCP\Files\Folder { + + private ?IUserManager $userManager = null; + /** * Creates a Folder that represents a non-existing path * @@ -99,26 +104,15 @@ class Folder extends Node implements \OCP\Files\Folder { } } - /** - * Get the node at $path - * - * @param string $path - * @return \OC\Files\Node\Node - * @throws \OCP\Files\NotFoundException - */ public function get($path) { return $this->root->get($this->getFullPath($path)); } - /** - * @param string $path - * @return bool - */ public function nodeExists($path) { try { $this->get($path); return true; - } catch (NotFoundException $e) { + } catch (NotFoundException|NotPermittedException) { return false; } } @@ -133,8 +127,21 @@ class Folder extends Node implements \OCP\Files\Folder { $fullPath = $this->getFullPath($path); $nonExisting = new NonExistingFolder($this->root, $this->view, $fullPath); $this->sendHooks(['preWrite', 'preCreate'], [$nonExisting]); - if (!$this->view->mkdir($fullPath) && !$this->view->is_dir($fullPath)) { - throw new NotPermittedException('Could not create folder "' . $fullPath . '"'); + if (!$this->view->mkdir($fullPath)) { + // maybe another concurrent process created the folder already + if (!$this->view->is_dir($fullPath)) { + throw new NotPermittedException('Could not create folder "' . $fullPath . '"'); + } else { + // we need to ensure we don't return before the concurrent request has finished updating the cache + $tries = 5; + while (!$this->view->getFileInfo($fullPath)) { + if ($tries < 1) { + throw new NotPermittedException('Could not create folder "' . $fullPath . '", folder exists but unable to get cache entry'); + } + usleep(5 * 1000); + $tries--; + } + } } $parent = dirname($fullPath) === $this->getPath() ? $this : null; $node = new Folder($this->root, $this->view, $fullPath, null, $parent); @@ -218,9 +225,9 @@ class Folder extends Node implements \OCP\Files\Folder { }, array_values($resultsPerCache), array_keys($resultsPerCache))); // don't include this folder in the results - $files = array_filter($files, function (FileInfo $file) { + $files = array_values(array_filter($files, function (FileInfo $file) { return $file->getPath() !== $this->getPath(); - }); + })); // since results were returned per-cache, they are no longer fully sorted $order = $query->getOrder(); @@ -245,7 +252,26 @@ class Folder extends Node implements \OCP\Files\Folder { $cacheEntry['internalPath'] = $cacheEntry['path']; $cacheEntry['path'] = rtrim($appendRoot . $cacheEntry->getPath(), '/'); $subPath = $cacheEntry['path'] !== '' ? '/' . $cacheEntry['path'] : ''; - return new \OC\Files\FileInfo($this->path . $subPath, $mount->getStorage(), $cacheEntry['internalPath'], $cacheEntry, $mount); + $storage = $mount->getStorage(); + + $owner = null; + $ownerId = $storage->getOwner($cacheEntry['internalPath']); + if ($ownerId !== false) { + // Cache the user manager (for performance) + if ($this->userManager === null) { + $this->userManager = \OCP\Server::get(IUserManager::class); + } + $owner = new LazyUser($ownerId, $this->userManager); + } + + return new \OC\Files\FileInfo( + $this->path . $subPath, + $storage, + $cacheEntry['internalPath'], + $cacheEntry, + $mount, + $owner, + ); } /** @@ -289,7 +315,7 @@ class Folder extends Node implements \OCP\Files\Folder { } public function getFirstNodeById(int $id): ?\OCP\Files\Node { - return current($this->getById($id)) ?: null; + return $this->root->getFirstNodeByIdInPath($id, $this->getPath()); } public function getAppDataDirectoryName(): string { @@ -399,7 +425,7 @@ class Folder extends Node implements \OCP\Files\Folder { $filterNonRecentFiles = new SearchComparison( ISearchComparison::COMPARE_GREATER_THAN, 'mtime', - strtotime("-2 week") + strtotime('-2 week') ); if ($offset === 0 && $limit <= 100) { $query = new SearchQuery( @@ -435,4 +461,12 @@ class Folder extends Node implements \OCP\Files\Folder { return $this->search($query); } + + public function verifyPath($fileName, $readonly = false): void { + $this->view->verifyPath( + $this->getPath(), + $fileName, + $readonly, + ); + } } diff --git a/lib/private/Files/Node/HookConnector.php b/lib/private/Files/Node/HookConnector.php index 8fd2ffa3369..1149951174c 100644 --- a/lib/private/Files/Node/HookConnector.php +++ b/lib/private/Files/Node/HookConnector.php @@ -39,7 +39,7 @@ class HookConnector { private IRootFolder $root, private View $view, private IEventDispatcher $dispatcher, - private LoggerInterface $logger + private LoggerInterface $logger, ) { } @@ -171,7 +171,7 @@ class HookConnector { public function copy($arguments) { $source = $this->getNodeForPath($arguments['oldpath']); - $target = $this->getNodeForPath($arguments['newpath']); + $target = $this->getNodeForPath($arguments['newpath'], $source instanceof Folder); $this->root->emit('\OC\Files', 'preCopy', [$source, $target]); $this->dispatcher->dispatch('\OCP\Files::preCopy', new GenericEvent([$source, $target])); @@ -203,7 +203,7 @@ class HookConnector { $this->dispatcher->dispatchTyped($event); } - private function getNodeForPath(string $path): Node { + private function getNodeForPath(string $path, bool $isDir = false): Node { $info = Filesystem::getView()->getFileInfo($path); if (!$info) { $fullPath = Filesystem::getView()->getAbsolutePath($path); @@ -212,7 +212,7 @@ class HookConnector { } else { $info = null; } - if (Filesystem::is_dir($path)) { + if ($isDir || Filesystem::is_dir($path)) { return new NonExistingFolder($this->root, $this->view, $fullPath, $info); } else { return new NonExistingFile($this->root, $this->view, $fullPath, $info); diff --git a/lib/private/Files/Node/LazyFolder.php b/lib/private/Files/Node/LazyFolder.php index 83ed7e534a7..37b1efa0fad 100644 --- a/lib/private/Files/Node/LazyFolder.php +++ b/lib/private/Files/Node/LazyFolder.php @@ -134,9 +134,6 @@ class LazyFolder implements Folder { $this->__call(__FUNCTION__, func_get_args()); } - /** - * @inheritDoc - */ public function get($path) { return $this->getRootFolder()->get($this->getFullPath($path)); } @@ -422,9 +419,6 @@ class LazyFolder implements Folder { return $this->__call(__FUNCTION__, func_get_args()); } - /** - * @inheritDoc - */ public function nodeExists($path) { return $this->__call(__FUNCTION__, func_get_args()); } @@ -567,4 +561,8 @@ class LazyFolder implements Folder { public function getMetadata(): array { return $this->data['metadata'] ?? $this->__call(__FUNCTION__, func_get_args()); } + + public function verifyPath($fileName, $readonly = false): void { + $this->__call(__FUNCTION__, func_get_args()); + } } diff --git a/lib/private/Files/Node/LazyUserFolder.php b/lib/private/Files/Node/LazyUserFolder.php index f9908b9b7b6..77479c2fa5e 100644 --- a/lib/private/Files/Node/LazyUserFolder.php +++ b/lib/private/Files/Node/LazyUserFolder.php @@ -59,7 +59,7 @@ class LazyUserFolder extends LazyFolder { } $mountPoint = $this->mountManager->find('/' . $this->user->getUID()); if (is_null($mountPoint)) { - throw new \Exception("No mountpoint for user folder"); + throw new \Exception('No mountpoint for user folder'); } return $mountPoint; } diff --git a/lib/private/Files/Node/Node.php b/lib/private/Files/Node/Node.php index e7f533e73a9..5dbdc4054bf 100644 --- a/lib/private/Files/Node/Node.php +++ b/lib/private/Files/Node/Node.php @@ -158,7 +158,7 @@ class Node implements INode { 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; } @@ -432,11 +432,14 @@ class Node implements INode { $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); diff --git a/lib/private/Files/Node/NonExistingFile.php b/lib/private/Files/Node/NonExistingFile.php index d154876432e..66ec2e6c040 100644 --- a/lib/private/Files/Node/NonExistingFile.php +++ b/lib/private/Files/Node/NonExistingFile.php @@ -38,6 +38,14 @@ class NonExistingFile extends File { } } + public function getInternalPath() { + if ($this->fileInfo) { + return parent::getInternalPath(); + } else { + return $this->getParent()->getMountPoint()->getInternalPath($this->getPath()); + } + } + public function stat() { throw new NotFoundException(); } diff --git a/lib/private/Files/Node/NonExistingFolder.php b/lib/private/Files/Node/NonExistingFolder.php index 5650c99fe73..4489fdaf010 100644 --- a/lib/private/Files/Node/NonExistingFolder.php +++ b/lib/private/Files/Node/NonExistingFolder.php @@ -38,6 +38,14 @@ class NonExistingFolder extends Folder { } } + public function getInternalPath() { + if ($this->fileInfo) { + return parent::getInternalPath(); + } else { + return $this->getParent()->getMountPoint()->getInternalPath($this->getPath()); + } + } + public function stat() { throw new NotFoundException(); } diff --git a/lib/private/Files/Node/Root.php b/lib/private/Files/Node/Root.php index 5fb4013cfc9..76afca9dee8 100644 --- a/lib/private/Files/Node/Root.php +++ b/lib/private/Files/Node/Root.php @@ -28,6 +28,7 @@ use OCP\ICache; use OCP\ICacheFactory; use OCP\IUser; use OCP\IUserManager; +use OCP\Server; use Psr\Log\LoggerInterface; /** @@ -170,12 +171,6 @@ class Root extends Folder implements IRootFolder { $this->mountManager->remove($mount); } - /** - * @param string $path - * @return Node - * @throws \OCP\Files\NotPermittedException - * @throws \OCP\Files\NotFoundException - */ public function get($path) { $path = $this->normalizePath($path); if ($this->isValidPath($path)) { @@ -389,13 +384,17 @@ class Root extends Folder implements IRootFolder { // scope the cache by user, so we don't return nodes for different users if ($this->user) { $cachedPath = $this->pathByIdCache->get($this->user->getUID() . '::' . $id); - if ($cachedPath && str_starts_with($path, $cachedPath)) { + if ($cachedPath && str_starts_with($cachedPath, $path)) { // getting the node by path is significantly cheaper than finding it by id - $node = $this->get($cachedPath); - // by validating that the cached path still has the requested fileid we can work around the need to invalidate the cached path - // if the cached path is invalid or a different file now we fall back to the uncached logic - if ($node && $node->getId() === $id) { - return $node; + try { + $node = $this->get($cachedPath); + // by validating that the cached path still has the requested fileid we can work around the need to invalidate the cached path + // if the cached path is invalid or a different file now we fall back to the uncached logic + if ($node && $node->getId() === $id) { + return $node; + } + } catch (NotFoundException|NotPermittedException) { + // The file may be moved but the old path still in cache } } } @@ -416,7 +415,7 @@ class Root extends Folder implements IRootFolder { */ public function getByIdInPath(int $id, string $path): array { $mountCache = $this->getUserMountCache(); - if (strpos($path, '/', 1) > 0) { + if ($path !== '' && strpos($path, '/', 1) > 0) { [, $user] = explode('/', $path); } else { $user = null; @@ -459,7 +458,7 @@ class Root extends Folder implements IRootFolder { if ($folder instanceof Folder) { return $folder->getByIdInRootMount($id); } else { - throw new \Exception("getByIdInPath with non folder"); + throw new \Exception('getByIdInPath with non folder'); } } return []; @@ -477,9 +476,23 @@ class Root extends Folder implements IRootFolder { $pathRelativeToMount = substr($internalPath, strlen($rootInternalPath)); $pathRelativeToMount = ltrim($pathRelativeToMount, '/'); $absolutePath = rtrim($mount->getMountPoint() . $pathRelativeToMount, '/'); + $storage = $mount->getStorage(); + if ($storage === null) { + return null; + } + $ownerId = $storage->getOwner($pathRelativeToMount); + if ($ownerId !== false) { + $owner = Server::get(IUserManager::class)->get($ownerId); + } else { + $owner = null; + } return $this->createNode($absolutePath, new FileInfo( - $absolutePath, $mount->getStorage(), $cacheEntry->getPath(), $cacheEntry, $mount, - \OC::$server->getUserManager()->get($mount->getStorage()->getOwner($pathRelativeToMount)) + $absolutePath, + $storage, + $cacheEntry->getPath(), + $cacheEntry, + $mount, + $owner, )); }, $mountsContainingFile); @@ -513,9 +526,9 @@ class Root extends Folder implements IRootFolder { $isDir = $info->getType() === FileInfo::TYPE_FOLDER; $view = new View(''); if ($isDir) { - return new Folder($this, $view, $path, $info, $parent); + return new Folder($this, $view, $fullPath, $info, $parent); } else { - return new File($this, $view, $path, $info, $parent); + return new File($this, $view, $fullPath, $info, $parent); } } } |