diff options
author | Robin Appelman <robin@icewind.nl> | 2019-09-25 19:17:06 +0200 |
---|---|---|
committer | Backportbot <backportbot-noreply@rullzer.com> | 2019-09-26 13:54:44 +0000 |
commit | 1048bc5102dc605f1f17d3f28a04b9505f7443a2 (patch) | |
tree | d865c9e8044e93d9455efcea81fc98ffd21ab8e2 | |
parent | c937c8f1753b111086e28afa396f3abd5776b1db (diff) | |
download | nextcloud-server-1048bc5102dc605f1f17d3f28a04b9505f7443a2.tar.gz nextcloud-server-1048bc5102dc605f1f17d3f28a04b9505f7443a2.zip |
handle moveFromStorage within the same storage even when storage wrappers are applied to the source storage
the target storage doesn't need additional handling for wrappers as the wrappers implementation of moveFromStorage already deals with that
Any storage based on local storage isn't affected by this as local storage already has it's own way of handling with this
Signed-off-by: Robin Appelman <robin@icewind.nl>
-rw-r--r-- | lib/private/Files/Storage/Common.php | 30 | ||||
-rw-r--r-- | lib/private/Files/Storage/Wrapper/Jail.php | 8 | ||||
-rw-r--r-- | tests/lib/Files/Storage/CommonTest.php | 117 |
3 files changed, 134 insertions, 21 deletions
diff --git a/lib/private/Files/Storage/Common.php b/lib/private/Files/Storage/Common.php index 4e95c594cfa..2dbaf619c05 100644 --- a/lib/private/Files/Storage/Common.php +++ b/lib/private/Files/Storage/Common.php @@ -46,6 +46,8 @@ use OC\Files\Cache\Scanner; use OC\Files\Cache\Updater; use OC\Files\Filesystem; use OC\Files\Cache\Watcher; +use OC\Files\Storage\Wrapper\Jail; +use OC\Files\Storage\Wrapper\Wrapper; use OCP\Files\EmptyFileNameException; use OCP\Files\FileNameTooLongException; use OCP\Files\InvalidCharacterInPathException; @@ -636,13 +638,39 @@ abstract class Common implements Storage, ILockingStorage, IWriteStreamStorage { } /** + * Check if a storage is the same as the current one, including wrapped storages + * + * @param IStorage $storage + * @return bool + */ + private function isSameStorage(IStorage $storage): bool { + while ($storage->instanceOfStorage(Wrapper::class)) { + /** + * @var Wrapper $sourceStorage + */ + $storage = $storage->getWrapperStorage(); + } + + return $storage === $this; + } + + /** * @param IStorage $sourceStorage * @param string $sourceInternalPath * @param string $targetInternalPath * @return bool */ public function moveFromStorage(IStorage $sourceStorage, $sourceInternalPath, $targetInternalPath) { - if ($sourceStorage === $this) { + if ($this->isSameStorage($sourceStorage)) { + // resolve any jailed paths + while ($sourceStorage->instanceOfStorage(Jail::class)) { + /** + * @var Jail $sourceStorage + */ + $sourceInternalPath = $sourceStorage->getUnjailedPath($sourceInternalPath); + $sourceStorage = $sourceStorage->getUnjailedStorage(); + } + return $this->rename($sourceInternalPath, $targetInternalPath); } diff --git a/lib/private/Files/Storage/Wrapper/Jail.php b/lib/private/Files/Storage/Wrapper/Jail.php index 3dc2024d912..35bf8449fd7 100644 --- a/lib/private/Files/Storage/Wrapper/Jail.php +++ b/lib/private/Files/Storage/Wrapper/Jail.php @@ -62,6 +62,14 @@ class Jail extends Wrapper { } } + /** + * This is separate from Wrapper::getWrapperStorage so we can get the jailed storage consistently even if the jail is inside another wrapper + */ + public function getUnjailedStorage() { + return $this->storage; + } + + public function getJailedPath($path) { $root = rtrim($this->rootPath, '/') . '/'; diff --git a/tests/lib/Files/Storage/CommonTest.php b/tests/lib/Files/Storage/CommonTest.php index 38faa9b0b21..f7be996e5ea 100644 --- a/tests/lib/Files/Storage/CommonTest.php +++ b/tests/lib/Files/Storage/CommonTest.php @@ -1,27 +1,31 @@ <?php /** -* ownCloud -* -* @author Robin Appelman -* @copyright 2012 Robin Appelman icewind@owncloud.com -* -* This library is free software; you can redistribute it and/or -* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE -* License as published by the Free Software Foundation; either -* version 3 of the License, or any later version. -* -* This library 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 along with this library. If not, see <http://www.gnu.org/licenses/>. -* -*/ + * ownCloud + * + * @author Robin Appelman + * @copyright 2012 Robin Appelman icewind@owncloud.com + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE + * License as published by the Free Software Foundation; either + * version 3 of the License, or any later version. + * + * This library 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 along with this library. If not, see <http://www.gnu.org/licenses/>. + * + */ namespace Test\Files\Storage; +use OC\Files\Storage\Wrapper\Jail; +use OC\Files\Storage\Wrapper\Wrapper; +use PHPUnit\Framework\MockObject\MockObject; + /** * Class CommonTest * @@ -34,15 +38,88 @@ class CommonTest extends Storage { * @var string tmpDir */ private $tmpDir; + protected function setUp() { parent::setUp(); $this->tmpDir = \OC::$server->getTempManager()->getTemporaryFolder(); - $this->instance=new \OC\Files\Storage\CommonTest(array('datadir'=>$this->tmpDir)); + $this->instance = new \OC\Files\Storage\CommonTest(['datadir' => $this->tmpDir]); } protected function tearDown() { \OC_Helper::rmdirr($this->tmpDir); parent::tearDown(); } + + public function testMoveFromStorageWrapped() { + /** @var \OC\Files\Storage\CommonTest|MockObject $instance */ + $instance = $this->getMockBuilder(\OC\Files\Storage\CommonTest::class) + ->setMethods(['copyFromStorage', 'rmdir', 'unlink']) + ->setConstructorArgs([['datadir' => $this->tmpDir]]) + ->getMock(); + $instance->method('copyFromStorage') + ->willThrowException(new \Exception('copy')); + + $source = new Wrapper([ + 'storage' => $instance, + ]); + + $instance->file_put_contents('foo.txt', 'bar'); + $instance->moveFromStorage($source, 'foo.txt', 'bar.txt'); + $this->assertTrue($instance->file_exists('bar.txt')); + } + + public function testMoveFromStorageJailed() { + /** @var \OC\Files\Storage\CommonTest|MockObject $instance */ + $instance = $this->getMockBuilder(\OC\Files\Storage\CommonTest::class) + ->setMethods(['copyFromStorage', 'rmdir', 'unlink']) + ->setConstructorArgs([['datadir' => $this->tmpDir]]) + ->getMock(); + $instance->method('copyFromStorage') + ->willThrowException(new \Exception('copy')); + + $source = new Jail([ + 'storage' => $instance, + 'root' => 'foo' + ]); + $source = new Wrapper([ + 'storage' => $source + ]); + + $instance->mkdir('foo'); + $instance->file_put_contents('foo/foo.txt', 'bar'); + $instance->moveFromStorage($source, 'foo.txt', 'bar.txt'); + $this->assertTrue($instance->file_exists('bar.txt')); + } + + public function testMoveFromStorageNestedJail() { + /** @var \OC\Files\Storage\CommonTest|MockObject $instance */ + $instance = $this->getMockBuilder(\OC\Files\Storage\CommonTest::class) + ->setMethods(['copyFromStorage', 'rmdir', 'unlink']) + ->setConstructorArgs([['datadir' => $this->tmpDir]]) + ->getMock(); + $instance->method('copyFromStorage') + ->willThrowException(new \Exception('copy')); + + $source = new Jail([ + 'storage' => $instance, + 'root' => 'foo' + ]); + $source = new Wrapper([ + 'storage' => $source + ]); + $source = new Jail([ + 'storage' => $source, + 'root' => 'bar' + ]); + $source = new Wrapper([ + 'storage' => $source + ]); + + $instance->mkdir('foo'); + $instance->mkdir('foo/bar'); + $instance->file_put_contents('foo/bar/foo.txt', 'bar'); + $instance->moveFromStorage($source, 'foo.txt', 'bar.txt'); + $this->assertTrue($instance->file_exists('bar.txt')); + } } |