aboutsummaryrefslogtreecommitdiffstats
path: root/lib/private/Files/Node
diff options
context:
space:
mode:
Diffstat (limited to 'lib/private/Files/Node')
-rw-r--r--lib/private/Files/Node/Folder.php72
-rw-r--r--lib/private/Files/Node/HookConnector.php8
-rw-r--r--lib/private/Files/Node/LazyFolder.php10
-rw-r--r--lib/private/Files/Node/LazyUserFolder.php2
-rw-r--r--lib/private/Files/Node/Node.php15
-rw-r--r--lib/private/Files/Node/NonExistingFile.php8
-rw-r--r--lib/private/Files/Node/NonExistingFolder.php8
-rw-r--r--lib/private/Files/Node/Root.php49
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);
}
}
}