]> source.dussan.org Git - nextcloud-server.git/commitdiff
make LazyFolder::get not load the real folder if we know the path
authorRobin Appelman <robin@icewind.nl>
Tue, 15 Aug 2023 10:07:54 +0000 (12:07 +0200)
committerRobin Appelman <robin@icewind.nl>
Mon, 4 Sep 2023 16:42:49 +0000 (18:42 +0200)
Signed-off-by: Robin Appelman <robin@icewind.nl>
lib/private/Files/Node/LazyFolder.php
lib/private/Files/Node/LazyRoot.php
lib/private/Files/Node/LazyUserFolder.php
lib/private/Files/Node/Node.php

index 026a16060c4b29c8af332df9a9ac6e2210a93522..2400d8e80974c1b6f321fc13877aef807a349128 100644 (file)
@@ -29,7 +29,9 @@ namespace OC\Files\Node;
 use OC\Files\Utils\PathHelper;
 use OCP\Files\Folder;
 use OCP\Constants;
+use OCP\Files\IRootFolder;
 use OCP\Files\Mount\IMountPoint;
+use OCP\Files\NotPermittedException;
 
 /**
  * Class LazyFolder
@@ -41,23 +43,33 @@ use OCP\Files\Mount\IMountPoint;
  */
 class LazyFolder implements Folder {
        /** @var \Closure(): Folder */
-       private $folderClosure;
-
-       /** @var LazyFolder | null */
-       protected $folder = null;
-
+       private \Closure $folderClosure;
+       protected ?Folder $folder = null;
+       protected IRootFolder $rootFolder;
        protected array $data;
 
        /**
-        * LazyFolder constructor.
-        *
+        * @param IRootFolder $rootFolder
         * @param \Closure(): Folder $folderClosure
+        * @param array $data
         */
-       public function __construct(\Closure $folderClosure, array $data = []) {
+       public function __construct(IRootFolder $rootFolder, \Closure $folderClosure, array $data = []) {
+               $this->rootFolder = $rootFolder;
                $this->folderClosure = $folderClosure;
                $this->data = $data;
        }
 
+       protected function getRootFolder(): IRootFolder {
+               return $this->rootFolder;
+       }
+
+       protected function getRealFolder(): Folder {
+               if ($this->folder === null) {
+                       $this->folder = call_user_func($this->folderClosure);
+               }
+               return $this->folder;
+       }
+
        /**
         * Magic method to first get the real rootFolder and then
         * call $method with $args on it
@@ -67,11 +79,7 @@ class LazyFolder implements Folder {
         * @return mixed
         */
        public function __call($method, $args) {
-               if ($this->folder === null) {
-                       $this->folder = call_user_func($this->folderClosure);
-               }
-
-               return call_user_func_array([$this->folder, $method], $args);
+               return call_user_func_array([$this->getRealFolder(), $method], $args);
        }
 
        /**
@@ -148,7 +156,7 @@ class LazyFolder implements Folder {
         * @inheritDoc
         */
        public function get($path) {
-               return $this->__call(__FUNCTION__, func_get_args());
+               return $this->getRootFolder()->get($this->getFullPath($path));
        }
 
        /**
@@ -408,9 +416,23 @@ class LazyFolder implements Folder {
         * @inheritDoc
         */
        public function getFullPath($path) {
+               if (isset($this->data['path'])) {
+                       $path = PathHelper::normalizePath($path);
+                       if (!$this->isValidPath($path)) {
+                               throw new NotPermittedException('Invalid path "' . $path . '"');
+                       }
+                       return $this->data['path'] . $path;
+               }
                return $this->__call(__FUNCTION__, func_get_args());
        }
 
+       public function isValidPath($path) {
+               if (!str_starts_with($path, '/')) {
+                       $path = '/' . $path;
+               }
+               return !(str_contains($path, '/../') || strrchr($path, '/') === '/..');
+       }
+
        /**
         * @inheritDoc
         */
index c01b9fdbb839c402cc1c947cdcb65d547403e1dc..ce140124f559a936d6f3e2578eff0ac90e23f0ba 100644 (file)
@@ -33,9 +33,18 @@ use OCP\Files\IRootFolder;
  * @package OC\Files\Node
  */
 class LazyRoot extends LazyFolder implements IRootFolder {
-       /**
-        * @inheritDoc
-        */
+       public function __construct(\Closure $folderClosure, array $data = []) {
+               parent::__construct($this, $folderClosure, $data);
+       }
+
+       protected function getRootFolder(): IRootFolder {
+               $folder = $this->getRealFolder();
+               if (!$folder instanceof IRootFolder) {
+                       throw new \Exception('Lazy root folder closure didn\'t return a root folder');
+               }
+               return $folder;
+       }
+
        public function getUserFolder($userId) {
                return $this->__call(__FUNCTION__, func_get_args());
        }
index 7093f5ebd8f3221c6055d1245217dac78d1ac987..503b0af89214e0e5e04375e02f72db11d076e8f9 100644 (file)
@@ -34,19 +34,17 @@ use OCP\IUser;
 use Psr\Log\LoggerInterface;
 
 class LazyUserFolder extends LazyFolder {
-       private IRootFolder $root;
        private IUser $user;
        private string $path;
        private IMountManager $mountManager;
 
        public function __construct(IRootFolder $rootFolder, IUser $user, IMountManager $mountManager) {
-               $this->root = $rootFolder;
                $this->user = $user;
                $this->mountManager = $mountManager;
                $this->path = '/' . $user->getUID() . '/files';
-               parent::__construct(function () use ($user): Folder {
+               parent::__construct($rootFolder, function () use ($user): Folder {
                        try {
-                               $node = $this->root->get($this->path);
+                               $node = $this->getRootFolder()->get($this->path);
                                if ($node instanceof File) {
                                        $e = new \RuntimeException();
                                        \OCP\Server::get(LoggerInterface::class)->error('User root storage is not a folder: ' . $this->path, [
@@ -56,10 +54,10 @@ class LazyUserFolder extends LazyFolder {
                                }
                                return $node;
                        } catch (NotFoundException $e) {
-                               if (!$this->root->nodeExists('/' . $user->getUID())) {
-                                       $this->root->newFolder('/' . $user->getUID());
+                               if (!$this->getRootFolder()->nodeExists('/' . $user->getUID())) {
+                                       $this->getRootFolder()->newFolder('/' . $user->getUID());
                                }
-                               return $this->root->newFolder($this->path);
+                               return $this->getRootFolder()->newFolder($this->path);
                        }
                }, [
                        'path' => $this->path,
@@ -71,7 +69,7 @@ class LazyUserFolder extends LazyFolder {
        }
 
        public function get($path) {
-               return $this->root->get('/' . $this->user->getUID() . '/files/' . ltrim($path, '/'));
+               return $this->getRootFolder()->get('/' . $this->user->getUID() . '/files/' . ltrim($path, '/'));
        }
 
        /**
@@ -79,7 +77,7 @@ class LazyUserFolder extends LazyFolder {
         * @return \OCP\Files\Node[]
         */
        public function getById($id) {
-               return $this->root->getByIdInPath((int)$id, $this->getPath());
+               return $this->getRootFolder()->getByIdInPath((int)$id, $this->getPath());
        }
 
        public function getMountPoint() {
index bc96a116da726f8668aabfc1d5f3bdeb11e2376b..4f466dde532bd8613064404f2e1665926125bdfa 100644 (file)
@@ -304,7 +304,7 @@ class Node implements INode {
                        ];
 
                        // and create lazy folder with it instead of always querying
-                       $this->parent = new LazyFolder(function () use ($newPath) {
+                       $this->parent = new LazyFolder($this->root, function () use ($newPath) {
                                return $this->root->get($newPath);
                        }, $parentData);
                }