Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 25 additions & 1 deletion apps/files/lib/Command/TransferOwnership.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
use OC\Encryption\Manager;
use OC\Files\Filesystem;
use OC\Files\View;
use OC\HintException;
use OC\Share20\ProviderFactory;
use OCP\Files\FileInfo;
use OCP\Files\Mount\IMountManager;
Expand All @@ -40,6 +41,8 @@
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\EventDispatcher\GenericEvent;

class TransferOwnership extends Command {

Expand All @@ -61,6 +64,9 @@ class TransferOwnership extends Command {
/** @var ProviderFactory */
private $shareProviderFactory;

/** @var EventDispatcherInterface */
private $eventDispatcher;

/** @var bool */
private $filesExist = false;

Expand All @@ -85,13 +91,19 @@ class TransferOwnership extends Command {
/** @var string */
private $finalTarget;

public function __construct(IUserManager $userManager, IManager $shareManager, IMountManager $mountManager, Manager $encryptionManager, ILogger $logger, ProviderFactory $shareProviderFactory) {
private $disableSignatureCheck = false;

public function __construct(IUserManager $userManager, IManager $shareManager,
IMountManager $mountManager, Manager $encryptionManager,
ILogger $logger, ProviderFactory $shareProviderFactory,
EventDispatcherInterface $eventDispatcher) {
$this->userManager = $userManager;
$this->shareManager = $shareManager;
$this->mountManager = $mountManager;
$this->encryptionManager = $encryptionManager;
$this->logger = $logger;
$this->shareProviderFactory = $shareProviderFactory;
$this->eventDispatcher = $eventDispatcher;
parent::__construct();
}

Expand Down Expand Up @@ -287,6 +299,18 @@ protected function transfer(OutputInterface $output) {
}
}

$this->eventDispatcher->addListener('files.aftersignaturemismatch', function (GenericEvent $event) use ($output) {
if ($event->hasArgument('fileName')) {
$output->writeln("<error>The file affected by signature mismatch: " . $event->getArgument('fileName') . "</error>");
}
if (!$event->hasArgument('retryWithIgnoreSignature')) {
$this->disableSignatureCheck = true;
} else {
$this->disableSignatureCheck = false;
}
$event->setArgument('retryWithIgnoreSignature', $this->disableSignatureCheck);
}, 20);

$view->rename($sourcePath, $this->finalTarget);

if (!\is_dir("$this->sourceUser/files")) {
Expand Down
55 changes: 53 additions & 2 deletions lib/private/Files/Storage/Wrapper/Encryption.php
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
use OC\Files\Filesystem;
use OC\Files\Mount\Manager;
use OC\Files\Storage\LocalTempFileTrait;
use OC\HintException;
use OC\Memcache\ArrayCache;
use OCP\Encryption\Exceptions\GenericEncryptionException;
use OCP\Encryption\IFile;
Expand All @@ -42,6 +43,8 @@
use OCP\Files\Storage;
use OCP\ILogger;
use OCP\Files\Cache\ICacheEntry;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\EventDispatcher\GenericEvent;

class Encryption extends Wrapper {
use LocalTempFileTrait;
Expand Down Expand Up @@ -85,6 +88,9 @@ class Encryption extends Wrapper {
/** @var ArrayCache */
private $arrayCache;

/** @var EventDispatcherInterface */
private $eventDispatcher;

/** @var array which has information of sourcePath during rename operation */
private $sourcePath;

Expand Down Expand Up @@ -112,7 +118,8 @@ public function __construct(
IStorage $keyStorage = null,
Update $update = null,
Manager $mountManager = null,
ArrayCache $arrayCache = null
ArrayCache $arrayCache = null,
EventDispatcherInterface $eventDispatcher = null
) {
$this->mountPoint = $parameters['mountPoint'];
$this->mount = $parameters['mount'];
Expand All @@ -126,6 +133,7 @@ public function __construct(
$this->update = $update;
$this->mountManager = $mountManager;
$this->arrayCache = $arrayCache;
$this->eventDispatcher = ($eventDispatcher === null) ? \OC::$server->getEventDispatcher() : $eventDispatcher;
parent::__construct($parameters);
}

Expand Down Expand Up @@ -782,7 +790,7 @@ private function copyBetweenStorage(Storage\IStorage $sourceStorage, $sourceInte
unset($this->sourcePath[$targetInternalPath]);
}
$target = $this->fopen($targetInternalPath, 'w');
list(, $result) = \OC_Helper::streamCopy($source, $target);
$result = $this->copyFile($source, $target, $sourceInternalPath);
\fclose($source);
\fclose($target);
} catch (\Exception $e) {
Expand All @@ -806,6 +814,49 @@ private function copyBetweenStorage(Storage\IStorage $sourceStorage, $sourceInte
return (bool)$result;
}

/**
* This method copies the file from the source to the target
*
* When the file is copied, the source file is read. And while decryption
* any signature mismatch happens, then the file is tried to copy again
* by disabling signature check. A custom symfony event is triggered here.
*
* @param resource $source
* @param resource $target
* @param string|null $sourceFilePath
* @throws HintException, thrown when the second attempt to copy fails
*/
private function copyFile($source, $target, $sourceFilePath = null) {
$retry = false;
do {
try {
$signatureMismatchEvent = new GenericEvent("signaturemismatch", []);
if ($retry === true) {
\fseek($source, 0);
}
list(, $result) = \OC_Helper::streamCopy($source, $target);
if ($retry === true) {
unset($signatureMismatchEvent['fileName']);
$signatureMismatchEvent->setArguments(['retryWithIgnoreSignature' => false]);
$this->eventDispatcher->dispatch('files.aftersignaturemismatch', $signatureMismatchEvent);
}
break;
} catch (HintException $e) {
$this->logger->warning("The file with signature mismatch is " . $sourceFilePath);
$this->logger->logException($e);
if ($retry === true) {
throw $e;
} else {
$retry = true;
}
$signatureMismatchEvent->setArguments(['fileName' => $sourceFilePath]);
$this->eventDispatcher->dispatch('files.aftersignaturemismatch', $signatureMismatchEvent);
}
} while ($retry === true);

return $result;
}

/**
* 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
Expand Down
8 changes: 7 additions & 1 deletion tests/lib/Files/Storage/Wrapper/EncryptionTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
use OC\Files\View;
use OC\User\Manager;
use OCP\Files\Storage\IStorage;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Test\Files\Storage\Storage;
use OC\Files\Storage\Wrapper\Encryption;
use OC\Files\Cache\Cache;
Expand Down Expand Up @@ -101,6 +102,9 @@ class EncryptionTest extends Storage {
/** @var \OC\Memcache\ArrayCache | \PHPUnit_Framework_MockObject_MockObject */
private $arrayCache;

/** @var EventDispatcherInterface | \PHPUnit_Framework_MockObject_MockObject */
private $eventDispatcherInterface;

/** @var integer dummy unencrypted size */
private $dummySize = -1;

Expand Down Expand Up @@ -187,7 +191,9 @@ protected function setUp() {
'mountPoint' => '/',
'mount' => $this->mount
],
$this->encryptionManager, $this->util, $this->logger, $this->file, null, $this->keyStore, $this->update, $this->mountManager, $this->arrayCache
$this->encryptionManager, $this->util, $this->logger,
$this->file, null, $this->keyStore, $this->update,
$this->mountManager, $this->arrayCache, $this->eventDispatcherInterface
]
)
->setMethods(['getMetaData', 'getCache', 'getEncryptionModule'])
Expand Down