Merge pull request #53665 from nextcloud/fix/catch-exception-in-encrypt-all

fix(encryption): Catch exceptions in encrypt-all command and continue
This commit is contained in:
Côme Chilliet 2025-07-03 16:28:23 +02:00 committed by GitHub
commit 1ded359d7e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 60 additions and 15 deletions

View file

@ -20,6 +20,7 @@ use OCP\L10N\IFactory;
use OCP\Mail\Headers\AutoSubmitted;
use OCP\Mail\IMailer;
use OCP\Security\ISecureRandom;
use Psr\Log\LoggerInterface;
use Symfony\Component\Console\Helper\ProgressBar;
use Symfony\Component\Console\Helper\QuestionHelper;
use Symfony\Component\Console\Helper\Table;
@ -50,6 +51,7 @@ class EncryptAll {
protected IFactory $l10nFactory,
protected QuestionHelper $questionHelper,
protected ISecureRandom $secureRandom,
protected LoggerInterface $logger,
) {
// store one time passwords for the users
$this->userPasswords = [];
@ -207,9 +209,22 @@ class EncryptAll {
} else {
$progress->setMessage("encrypt files for user $userCount: $path");
$progress->advance();
if ($this->encryptFile($path) === false) {
$progress->setMessage("encrypt files for user $userCount: $path (already encrypted)");
try {
if ($this->encryptFile($path) === false) {
$progress->setMessage("encrypt files for user $userCount: $path (already encrypted)");
$progress->advance();
}
} catch (\Exception $e) {
$progress->setMessage("Failed to encrypt path $path: " . $e->getMessage());
$progress->advance();
$this->logger->error(
'Failed to encrypt path {path}',
[
'user' => $uid,
'path' => $path,
'exception' => $e,
]
);
}
}
}
@ -234,7 +249,14 @@ class EncryptAll {
$target = $path . '.encrypted.' . time();
try {
$this->rootView->copy($source, $target);
$copySuccess = $this->rootView->copy($source, $target);
if ($copySuccess === false) {
/* Copy failed, abort */
if ($this->rootView->file_exists($target)) {
$this->rootView->unlink($target);
}
throw new \Exception('Copy failed for ' . $source);
}
$this->rootView->rename($target, $source);
} catch (DecryptionFailedException $e) {
if ($this->rootView->file_exists($target)) {

View file

@ -23,6 +23,7 @@ use OCP\Mail\IMailer;
use OCP\Security\ISecureRandom;
use OCP\UserInterface;
use PHPUnit\Framework\MockObject\MockObject;
use Psr\Log\LoggerInterface;
use Symfony\Component\Console\Formatter\OutputFormatterInterface;
use Symfony\Component\Console\Helper\ProgressBar;
use Symfony\Component\Console\Helper\QuestionHelper;
@ -46,6 +47,7 @@ class EncryptAllTest extends TestCase {
protected \Symfony\Component\Console\Output\OutputInterface&MockObject $outputInterface;
protected UserInterface&MockObject $userInterface;
protected ISecureRandom&MockObject $secureRandom;
protected LoggerInterface&MockObject $logger;
protected EncryptAll $encryptAll;
@ -76,6 +78,7 @@ class EncryptAllTest extends TestCase {
->disableOriginalConstructor()->getMock();
$this->userInterface = $this->getMockBuilder(UserInterface::class)
->disableOriginalConstructor()->getMock();
$this->logger = $this->createMock(LoggerInterface::class);
/**
* We need format method to return a string
@ -106,12 +109,13 @@ class EncryptAllTest extends TestCase {
$this->l,
$this->l10nFactory,
$this->questionHelper,
$this->secureRandom
$this->secureRandom,
$this->logger,
);
}
public function testEncryptAll(): void {
/** @var EncryptAll | \PHPUnit\Framework\MockObject\MockObject $encryptAll */
/** @var EncryptAll&MockObject $encryptAll */
$encryptAll = $this->getMockBuilder(EncryptAll::class)
->setConstructorArgs(
[
@ -125,7 +129,8 @@ class EncryptAllTest extends TestCase {
$this->l,
$this->l10nFactory,
$this->questionHelper,
$this->secureRandom
$this->secureRandom,
$this->logger,
]
)
->onlyMethods(['createKeyPairs', 'encryptAllUsersFiles', 'outputPasswords'])
@ -140,7 +145,7 @@ class EncryptAllTest extends TestCase {
}
public function testEncryptAllWithMasterKey(): void {
/** @var EncryptAll | \PHPUnit\Framework\MockObject\MockObject $encryptAll */
/** @var EncryptAll&MockObject $encryptAll */
$encryptAll = $this->getMockBuilder(EncryptAll::class)
->setConstructorArgs(
[
@ -154,7 +159,8 @@ class EncryptAllTest extends TestCase {
$this->l,
$this->l10nFactory,
$this->questionHelper,
$this->secureRandom
$this->secureRandom,
$this->logger,
]
)
->onlyMethods(['createKeyPairs', 'encryptAllUsersFiles', 'outputPasswords'])
@ -170,7 +176,7 @@ class EncryptAllTest extends TestCase {
}
public function testCreateKeyPairs(): void {
/** @var EncryptAll | \PHPUnit\Framework\MockObject\MockObject $encryptAll */
/** @var EncryptAll&MockObject $encryptAll */
$encryptAll = $this->getMockBuilder(EncryptAll::class)
->setConstructorArgs(
[
@ -184,7 +190,8 @@ class EncryptAllTest extends TestCase {
$this->l,
$this->l10nFactory,
$this->questionHelper,
$this->secureRandom
$this->secureRandom,
$this->logger,
]
)
->onlyMethods(['setupUserFS', 'generateOneTimePassword'])
@ -234,7 +241,8 @@ class EncryptAllTest extends TestCase {
$this->l,
$this->l10nFactory,
$this->questionHelper,
$this->secureRandom
$this->secureRandom,
$this->logger,
]
)
->onlyMethods(['encryptUsersFiles'])
@ -275,7 +283,8 @@ class EncryptAllTest extends TestCase {
$this->l,
$this->l10nFactory,
$this->questionHelper,
$this->secureRandom
$this->secureRandom,
$this->logger,
]
)
->onlyMethods(['encryptFile', 'setupUserFS'])

View file

@ -23,6 +23,7 @@ use OCP\Encryption\IManager;
use OCP\Encryption\Keys\IStorage;
use OCP\Files;
use OCP\Files\Cache\ICacheEntry;
use OCP\Files\GenericFileException;
use OCP\Files\Mount\IMountPoint;
use OCP\Files\Storage;
use Psr\Log\LoggerInterface;
@ -685,12 +686,16 @@ class Encryption extends Wrapper {
try {
$source = $sourceStorage->fopen($sourceInternalPath, 'r');
$target = $this->fopen($targetInternalPath, 'w');
[, $result] = Files::streamCopy($source, $target, true);
if ($source === false || $target === false) {
$result = false;
} else {
[, $result] = Files::streamCopy($source, $target, true);
}
} finally {
if (is_resource($source)) {
if (isset($source) && $source !== false) {
fclose($source);
}
if (is_resource($target)) {
if (isset($target) && $target !== false) {
fclose($target);
}
}
@ -740,6 +745,9 @@ class Encryption extends Wrapper {
public function hash(string $type, string $path, bool $raw = false): string|false {
$fh = $this->fopen($path, 'rb');
if ($fh === false) {
return false;
}
$ctx = hash_init($type);
hash_update_stream($ctx, $fh);
fclose($fh);
@ -764,6 +772,9 @@ class Encryption extends Wrapper {
$firstBlock = '';
if ($this->storage->is_file($path)) {
$handle = $this->storage->fopen($path, 'r');
if ($handle === false) {
return '';
}
$firstBlock = fread($handle, $this->util->getHeaderSize());
fclose($handle);
}
@ -894,6 +905,9 @@ class Encryption extends Wrapper {
public function writeStream(string $path, $stream, ?int $size = null): int {
// always fall back to fopen
$target = $this->fopen($path, 'w');
if ($target === false) {
throw new GenericFileException("Failed to open $path for writing");
}
[$count, $result] = Files::streamCopy($stream, $target, true);
fclose($stream);
fclose($target);