Merge pull request #15628 from owncloud/enc_fix_rename

Encryption - fix moving/copying files between storages
This commit is contained in:
Björn Schießle 2015-05-06 15:03:59 +02:00
commit 6633514a98
3 changed files with 119 additions and 17 deletions

View File

@ -545,6 +545,11 @@ abstract class Common implements Storage {
} }
} else { } else {
$source = $sourceStorage->fopen($sourceInternalPath, 'r'); $source = $sourceStorage->fopen($sourceInternalPath, 'r');
// TODO: call fopen in a way that we execute again all storage wrappers
// to avoid that we bypass storage wrappers which perform important actions
// for this operation. Same is true for all other operations which
// are not the same as the original one.Once this is fixed we also
// need to adjust the encryption wrapper.
$target = $this->fopen($targetInternalPath, 'w'); $target = $this->fopen($targetInternalPath, 'w');
list(, $result) = \OC_Helper::streamCopy($source, $target); list(, $result) = \OC_Helper::streamCopy($source, $target);
if ($result and $preserveMtime) { if ($result and $preserveMtime) {

View File

@ -61,19 +61,20 @@ class Encryption extends Wrapper {
/** @var IMountPoint */ /** @var IMountPoint */
private $mount; private $mount;
/** @var \OCP\Encryption\Keys\IStorage */
/** @var IStorage */
private $keyStorage; private $keyStorage;
/** @var \OC\Encryption\Update */ /** @var Update */
private $update; private $update;
/** /**
* @param array $parameters * @param array $parameters
* @param \OCP\Encryption\IManager $encryptionManager * @param IManager $encryptionManager
* @param \OC\Encryption\Util $util * @param Util $util
* @param \OCP\ILogger $logger * @param ILogger $logger
* @param \OCP\Encryption\IFile $fileHelper * @param IFile $fileHelper
* @param string $uid user who perform the read/write operation (null for public access) * @param string $uid
* @param IStorage $keyStorage * @param IStorage $keyStorage
* @param Update $update * @param Update $update
*/ */
@ -365,6 +366,101 @@ class Encryption extends Wrapper {
} }
} }
/**
* @param \OCP\Files\Storage $sourceStorage
* @param string $sourceInternalPath
* @param string $targetInternalPath
* @param bool $preserveMtime
* @return bool
*/
public function moveFromStorage(\OCP\Files\Storage $sourceStorage, $sourceInternalPath, $targetInternalPath, $preserveMtime = true) {
// TODO clean this up once the underlying moveFromStorage in OC\Files\Storage\Wrapper\Common is fixed:
// - call $this->storage->moveFromStorage() instead of $this->copyBetweenStorage
// - copy the file cache update from $this->copyBetweenStorage to this method
// - remove $this->copyBetweenStorage
$result = $this->copyBetweenStorage($sourceStorage, $sourceInternalPath, $targetInternalPath, $preserveMtime, true);
if ($result) {
if ($sourceStorage->is_dir($sourceInternalPath)) {
$result &= $sourceStorage->rmdir($sourceInternalPath);
} else {
$result &= $sourceStorage->unlink($sourceInternalPath);
}
}
return $result;
}
/**
* @param \OCP\Files\Storage $sourceStorage
* @param string $sourceInternalPath
* @param string $targetInternalPath
* @param bool $preserveMtime
* @return bool
*/
public function copyFromStorage(\OCP\Files\Storage $sourceStorage, $sourceInternalPath, $targetInternalPath, $preserveMtime = false) {
// TODO clean this up once the underlying moveFromStorage in OC\Files\Storage\Wrapper\Common is fixed:
// - call $this->storage->moveFromStorage() instead of $this->copyBetweenStorage
// - copy the file cache update from $this->copyBetweenStorage to this method
// - remove $this->copyBetweenStorage
return $this->copyBetweenStorage($sourceStorage, $sourceInternalPath, $targetInternalPath, $preserveMtime, false);
}
/**
* copy file between two storages
*
* @param \OCP\Files\Storage $sourceStorage
* @param string $sourceInternalPath
* @param string $targetInternalPath
* @param bool $preserveMtime
* @param bool $isRename
* @return bool
*/
private function copyBetweenStorage(\OCP\Files\Storage $sourceStorage, $sourceInternalPath, $targetInternalPath, $preserveMtime, $isRename) {
if ($sourceStorage->is_dir($sourceInternalPath)) {
$dh = $sourceStorage->opendir($sourceInternalPath);
$result = $this->mkdir($targetInternalPath);
if (is_resource($dh)) {
while ($result and ($file = readdir($dh)) !== false) {
if (!Filesystem::isIgnoredDir($file)) {
$result &= $this->copyFromStorage($sourceStorage, $sourceInternalPath . '/' . $file, $targetInternalPath . '/' . $file);
}
}
}
} else {
$source = $sourceStorage->fopen($sourceInternalPath, 'r');
$target = $this->fopen($targetInternalPath, 'w');
list(, $result) = \OC_Helper::streamCopy($source, $target);
fclose($source);
fclose($target);
if($result) {
if ($preserveMtime) {
$this->touch($targetInternalPath, $sourceStorage->filemtime($sourceInternalPath));
}
$isEncrypted = $this->mount->getOption('encrypt', true) ? 1 : 0;
// in case of a rename we need to manipulate the source cache because
// this information will be kept for the new target
if ($isRename) {
$sourceStorage->getCache()->put($sourceInternalPath, ['encrypted' => $isEncrypted]);
} else {
$this->getCache()->put($targetInternalPath, ['encrypted' => $isEncrypted]);
}
} else {
// delete partially written target file
$this->unlink($targetInternalPath);
// delete cache entry that was created by fopen
$this->getCache()->remove($targetInternalPath);
}
}
return (bool)$result;
}
/** /**
* get the path to a local version of the file. * get the path to a local version of the file.
* The local version of the file can be temporary and doesn't have to be persistent across requests * The local version of the file can be temporary and doesn't have to be persistent across requests

View File

@ -106,15 +106,17 @@ class Encryption extends \Test\Files\Storage\Storage {
->willReturn(['encrypted' => false]); ->willReturn(['encrypted' => false]);
$this->instance = $this->getMockBuilder('\OC\Files\Storage\Wrapper\Encryption') $this->instance = $this->getMockBuilder('\OC\Files\Storage\Wrapper\Encryption')
->setConstructorArgs([ ->setConstructorArgs(
[ [
'storage' => $this->sourceStorage, [
'root' => 'foo', 'storage' => $this->sourceStorage,
'mountPoint' => '/', 'root' => 'foo',
'mount' => $mount 'mountPoint' => '/',
], 'mount' => $mount
$this->encryptionManager, $this->util, $logger, $file, null, $this->keyStore, $this->update ],
]) $this->encryptionManager, $this->util, $logger, $file, null, $this->keyStore, $this->update
]
)
->setMethods(['getMetaData', 'getCache']) ->setMethods(['getMetaData', 'getCache'])
->getMock(); ->getMock();
@ -125,7 +127,6 @@ class Encryption extends \Test\Files\Storage\Storage {
$this->instance->expects($this->any()) $this->instance->expects($this->any())
->method('getCache') ->method('getCache')
->willReturn($this->cache); ->willReturn($this->cache);
} }
/** /**
@ -198,7 +199,7 @@ class Encryption extends \Test\Files\Storage\Storage {
* @param boolean $copyKeysReturn * @param boolean $copyKeysReturn
* @param boolean $shouldUpdate * @param boolean $shouldUpdate
*/ */
public function testCopy($source, public function testCopyEncryption($source,
$target, $target,
$encryptionEnabled, $encryptionEnabled,
$copyKeysReturn, $copyKeysReturn,