diff --git a/apps/encryption/lib/Command/FixEncryptedVersion.php b/apps/encryption/lib/Command/FixEncryptedVersion.php
index a85a96258fcf4..e12d733d2b927 100644
--- a/apps/encryption/lib/Command/FixEncryptedVersion.php
+++ b/apps/encryption/lib/Command/FixEncryptedVersion.php
@@ -24,6 +24,7 @@
use OC\Files\View;
use OC\HintException;
+use OC\ServerNotAvailableException;
use OCA\Encryption\Util;
use OCP\Files\IRootFolder;
use OCP\IConfig;
@@ -53,6 +54,9 @@ class FixEncryptedVersion extends Command {
/** @var View */
private $view;
+ /** @var bool */
+ private $supportLegacy;
+
public function __construct(
IConfig $config,
ILogger $logger,
@@ -67,6 +71,8 @@ public function __construct(
$this->userManager = $userManager;
$this->util = $util;
$this->view = $view;
+ $this->supportLegacy = false;
+
parent::__construct();
}
@@ -95,6 +101,7 @@ protected function configure(): void {
*/
protected function execute(InputInterface $input, OutputInterface $output): int {
$skipSignatureCheck = $this->config->getSystemValue('encryption_skip_signature_check', false);
+ $this->supportLegacy = $this->config->getSystemValueBool('encryption.legacy_format_support', false);
if ($skipSignatureCheck) {
$output->writeln("Repairing is not possible when \"encryption_skip_signature_check\" is set. Please disable this flag in the configuration.\n");
@@ -187,6 +194,14 @@ private function verifyFileContent($path, OutputInterface $output, $ignoreCorrec
\fclose($handle);
return true;
+ } catch (ServerNotAvailableException $e) {
+ // not a "bad signature" error and likely "legacy cipher" exception
+ // this could mean that the file is maybe not encrypted but the encrypted version is set
+ if (!$this->supportLegacy && $ignoreCorrectEncVersionCall === true) {
+ $output->writeln("Attempting to fix the path: \"$path\"");
+ return $this->correctEncryptedVersion($path, $output, true);
+ }
+ return false;
} catch (HintException $e) {
$this->logger->warning("Issue: " . $e->getMessage());
//If allowOnce is set to false, this becomes recursive.
@@ -202,9 +217,10 @@ private function verifyFileContent($path, OutputInterface $output, $ignoreCorrec
/**
* @param string $path
* @param OutputInterface $output
+ * @param bool $includeZero whether to try zero version for unencrypted file
* @return bool
*/
- private function correctEncryptedVersion($path, OutputInterface $output): bool {
+ private function correctEncryptedVersion($path, OutputInterface $output, bool $includeZero = false): bool {
$fileInfo = $this->view->getFileInfo($path);
if (!$fileInfo) {
$output->writeln("File info not found for file: \"$path\"");
@@ -231,6 +247,17 @@ private function correctEncryptedVersion($path, OutputInterface $output): bool {
// Save original encrypted version so we can restore it if decryption fails with all version
$originalEncryptedVersion = $encryptedVersion;
if ($encryptedVersion >= 0) {
+ if ($includeZero) {
+ // try with zero first
+ $cacheInfo = ['encryptedVersion' => 0, 'encrypted' => 0];
+ $cache->put($fileCache->getPath(), $cacheInfo);
+ $output->writeln("Set the encrypted version to 0 (unencrypted)");
+ if ($this->verifyFileContent($path, $output, false) === true) {
+ $output->writeln("Fixed the file: \"$path\" with version 0 (unencrypted)");
+ return true;
+ }
+ }
+
//test by decrementing the value till 1 and if nothing works try incrementing
$encryptedVersion--;
while ($encryptedVersion > 0) {
diff --git a/apps/encryption/tests/Command/FixEncryptedVersionTest.php b/apps/encryption/tests/Command/FixEncryptedVersionTest.php
index 22ae239aec23d..14143223264dd 100644
--- a/apps/encryption/tests/Command/FixEncryptedVersionTest.php
+++ b/apps/encryption/tests/Command/FixEncryptedVersionTest.php
@@ -244,6 +244,46 @@ public function testVersionIsRestoredToOriginalIfNoFixIsFound() {
$this->assertEquals(15, $encryptedVersion);
}
+ public function testRepairUnencryptedFileWhenVersionIsSet() {
+ $this->util->expects($this->once())->method('isMasterKeyEnabled')
+ ->willReturn(true);
+
+ $view = new View("/" . $this->userId . "/files");
+
+ // create a file, it's encrypted and also the version is set in the database
+ $view->touch('hello.txt');
+
+ $fileInfo1 = $view->getFileInfo('hello.txt');
+
+ $storage1 = $fileInfo1->getStorage();
+ $cache1 = $storage1->getCache();
+ $fileCache1 = $cache1->get($fileInfo1->getId());
+
+ // Now change the encrypted version
+ $cacheInfo = ['encryptedVersion' => 1, 'encrypted' => 1];
+ $cache1->put($fileCache1->getPath(), $cacheInfo);
+
+ $absPath = $view->getLocalFolder(''). '/hello.txt';
+
+ // create unencrypted file on disk, the version stays
+ file_put_contents($absPath, 'hello contents');
+
+ $this->commandTester->execute([
+ 'user' => $this->userId
+ ]);
+
+ $output = $this->commandTester->getDisplay();
+
+ $this->assertStringContainsString("Verifying the content of file \"/$this->userId/files/hello.txt\"
+Attempting to fix the path: \"/$this->userId/files/hello.txt\"
+Set the encrypted version to 0 (unencrypted)
+The file \"/$this->userId/files/hello.txt\" is: OK
+Fixed the file: \"/$this->userId/files/hello.txt\" with version 0 (unencrypted)", $output);
+
+ // the file can be decrypted
+ $this->assertEquals('hello contents', $view->file_get_contents('hello.txt'));
+ }
+
/**
* Test commands with a file path
*/
diff --git a/lib/private/Files/Stream/Encryption.php b/lib/private/Files/Stream/Encryption.php
index 16d2ca3ce97f6..fc75ead1e45b4 100644
--- a/lib/private/Files/Stream/Encryption.php
+++ b/lib/private/Files/Stream/Encryption.php
@@ -213,7 +213,7 @@ protected static function wrapSource($source, $context = [], $protocol = null, $
} else {
$wrapped = fopen($protocol . '://', $mode, false, $context);
}
- } catch (\BadMethodCallException $e) {
+ } catch (\Exception $e) {
stream_wrapper_unregister($protocol);
throw $e;
}