diff options
Diffstat (limited to 'lib/private/Files/Storage/Wrapper/Encryption.php')
-rw-r--r-- | lib/private/Files/Storage/Wrapper/Encryption.php | 450 |
1 files changed, 151 insertions, 299 deletions
diff --git a/lib/private/Files/Storage/Wrapper/Encryption.php b/lib/private/Files/Storage/Wrapper/Encryption.php index e68e1f65048..58bd4dfddcf 100644 --- a/lib/private/Files/Storage/Wrapper/Encryption.php +++ b/lib/private/Files/Storage/Wrapper/Encryption.php @@ -1,57 +1,29 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Arthur Schiwon <blizzz@arthur-schiwon.de> - * @author Bjoern Schiessle <bjoern@schiessle.org> - * @author Björn Schießle <bjoern@schiessle.org> - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author J0WI <J0WI@users.noreply.github.com> - * @author jknockaert <jasper@knockaert.nl> - * @author Joas Schilling <coding@schilljs.com> - * @author Lukas Reschke <lukas@statuscode.ch> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Piotr M <mrow4a@yahoo.com> - * @author Robin Appelman <robin@icewind.nl> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * @author Thomas Müller <thomas.mueller@tmit.eu> - * @author Tigran Mkrtchyan <tigran.mkrtchyan@desy.de> - * @author Vincent Petry <vincent@nextcloud.com> - * @author Richard Steinmetz <richard@steinmetz.cloud> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program 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, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ - namespace OC\Files\Storage\Wrapper; use OC\Encryption\Exceptions\ModuleDoesNotExistsException; -use OC\Encryption\Update; use OC\Encryption\Util; use OC\Files\Cache\CacheEntry; use OC\Files\Filesystem; use OC\Files\Mount\Manager; use OC\Files\ObjectStore\ObjectStoreStorage; +use OC\Files\Storage\Common; use OC\Files\Storage\LocalTempFileTrait; use OC\Memcache\ArrayCache; use OCP\Cache\CappedMemoryCache; -use OCP\Encryption\Exceptions\GenericEncryptionException; +use OCP\Encryption\Exceptions\InvalidHeaderException; use OCP\Encryption\IFile; 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; @@ -59,90 +31,42 @@ use Psr\Log\LoggerInterface; class Encryption extends Wrapper { use LocalTempFileTrait; - /** @var string */ - private $mountPoint; - - /** @var \OC\Encryption\Util */ - private $util; - - /** @var \OCP\Encryption\IManager */ - private $encryptionManager; - - private LoggerInterface $logger; - - /** @var string */ - private $uid; - - /** @var array */ - protected $unencryptedSize; - - /** @var \OCP\Encryption\IFile */ - private $fileHelper; - - /** @var IMountPoint */ - private $mount; - - /** @var IStorage */ - private $keyStorage; - - /** @var Update */ - private $update; - - /** @var Manager */ - private $mountManager; - - /** @var array remember for which path we execute the repair step to avoid recursions */ - private $fixUnencryptedSizeOf = []; - - /** @var ArrayCache */ - private $arrayCache; - + private string $mountPoint; + protected array $unencryptedSize = []; + private IMountPoint $mount; + /** for which path we execute the repair step to avoid recursions */ + private array $fixUnencryptedSizeOf = []; /** @var CappedMemoryCache<bool> */ private CappedMemoryCache $encryptedPaths; - - private $enabled = true; + private bool $enabled = true; /** * @param array $parameters */ public function __construct( - $parameters, - ?IManager $encryptionManager = null, - ?Util $util = null, - ?LoggerInterface $logger = null, - ?IFile $fileHelper = null, - $uid = null, - ?IStorage $keyStorage = null, - ?Update $update = null, - ?Manager $mountManager = null, - ?ArrayCache $arrayCache = null + array $parameters, + private IManager $encryptionManager, + private Util $util, + private LoggerInterface $logger, + private IFile $fileHelper, + private ?string $uid, + private IStorage $keyStorage, + private Manager $mountManager, + private ArrayCache $arrayCache, ) { $this->mountPoint = $parameters['mountPoint']; $this->mount = $parameters['mount']; - $this->encryptionManager = $encryptionManager; - $this->util = $util; - $this->logger = $logger; - $this->uid = $uid; - $this->fileHelper = $fileHelper; - $this->keyStorage = $keyStorage; - $this->unencryptedSize = []; - $this->update = $update; - $this->mountManager = $mountManager; - $this->arrayCache = $arrayCache; $this->encryptedPaths = new CappedMemoryCache(); parent::__construct($parameters); } - /** - * see https://www.php.net/manual/en/function.filesize.php - * The result for filesize when called on a folder is required to be 0 - */ - public function filesize($path): false|int|float { + public function filesize(string $path): int|float|false { $fullPath = $this->getFullPath($path); $info = $this->getCache()->get($path); if ($info === false) { - return false; + /* Pass call to wrapped storage, it may be a special file like a part file */ + return $this->storage->filesize($path); } if (isset($this->unencryptedSize[$fullPath])) { $size = $this->unencryptedSize[$fullPath]; @@ -180,11 +104,6 @@ class Encryption extends Wrapper { return $this->storage->filesize($path); } - /** - * @param string $path - * @param array $data - * @return array - */ private function modifyMetaData(string $path, array $data): array { $fullPath = $this->getFullPath($path); $info = $this->getCache()->get($path); @@ -208,7 +127,7 @@ class Encryption extends Wrapper { return $data; } - public function getMetaData($path) { + public function getMetaData(string $path): ?array { $data = $this->storage->getMetaData($path); if (is_null($data)) { return null; @@ -216,24 +135,18 @@ class Encryption extends Wrapper { return $this->modifyMetaData($path, $data); } - public function getDirectoryContent($directory): \Traversable { + public function getDirectoryContent(string $directory): \Traversable { $parent = rtrim($directory, '/'); foreach ($this->getWrapperStorage()->getDirectoryContent($directory) as $data) { yield $this->modifyMetaData($parent . '/' . $data['name'], $data); } } - /** - * see https://www.php.net/manual/en/function.file_get_contents.php - * - * @param string $path - * @return string|false - */ - public function file_get_contents($path) { + public function file_get_contents(string $path): string|false { $encryptionModule = $this->getEncryptionModule($path); if ($encryptionModule) { - $handle = $this->fopen($path, "r"); + $handle = $this->fopen($path, 'r'); if (!$handle) { return false; } @@ -244,14 +157,7 @@ class Encryption extends Wrapper { return $this->storage->file_get_contents($path); } - /** - * see https://www.php.net/manual/en/function.file_put_contents.php - * - * @param string $path - * @param mixed $data - * @return int|false - */ - public function file_put_contents($path, $data) { + public function file_put_contents(string $path, mixed $data): int|float|false { // file put content will always be translated to a stream write $handle = $this->fopen($path, 'w'); if (is_resource($handle)) { @@ -263,13 +169,7 @@ class Encryption extends Wrapper { return false; } - /** - * see https://www.php.net/manual/en/function.unlink.php - * - * @param string $path - * @return bool - */ - public function unlink($path) { + public function unlink(string $path): bool { $fullPath = $this->getFullPath($path); if ($this->util->isExcluded($fullPath)) { return $this->storage->unlink($path); @@ -283,21 +183,14 @@ class Encryption extends Wrapper { return $this->storage->unlink($path); } - /** - * see https://www.php.net/manual/en/function.rename.php - * - * @param string $source - * @param string $target - * @return bool - */ - public function rename($source, $target) { + public function rename(string $source, string $target): bool { $result = $this->storage->rename($source, $target); - if ($result && + if ($result // versions always use the keys from the original file, so we can skip // this step for versions - $this->isVersion($target) === false && - $this->encryptionManager->isEnabled()) { + && $this->isVersion($target) === false + && $this->encryptionManager->isEnabled()) { $sourcePath = $this->getFullPath($source); if (!$this->util->isExcluded($sourcePath)) { $targetPath = $this->getFullPath($target); @@ -315,18 +208,12 @@ class Encryption extends Wrapper { return $result; } - /** - * see https://www.php.net/manual/en/function.rmdir.php - * - * @param string $path - * @return bool - */ - public function rmdir($path) { + public function rmdir(string $path): bool { $result = $this->storage->rmdir($path); $fullPath = $this->getFullPath($path); - if ($result && - $this->util->isExcluded($fullPath) === false && - $this->encryptionManager->isEnabled() + if ($result + && $this->util->isExcluded($fullPath) === false + && $this->encryptionManager->isEnabled() ) { $this->keyStorage->deleteAllFileKeys($fullPath); } @@ -334,20 +221,14 @@ class Encryption extends Wrapper { return $result; } - /** - * check if a file can be read - * - * @param string $path - * @return bool - */ - public function isReadable($path) { + public function isReadable(string $path): bool { $isReadable = true; $metaData = $this->getMetaData($path); if ( - !$this->is_dir($path) && - isset($metaData['encrypted']) && - $metaData['encrypted'] === true + !$this->is_dir($path) + && isset($metaData['encrypted']) + && $metaData['encrypted'] === true ) { $fullPath = $this->getFullPath($path); $module = $this->getEncryptionModule($path); @@ -357,13 +238,7 @@ class Encryption extends Wrapper { return $this->storage->isReadable($path) && $isReadable; } - /** - * see https://www.php.net/manual/en/function.copy.php - * - * @param string $source - * @param string $target - */ - public function copy($source, $target): bool { + public function copy(string $source, string $target): bool { $sourcePath = $this->getFullPath($source); if ($this->util->isExcluded($sourcePath)) { @@ -376,16 +251,7 @@ class Encryption extends Wrapper { return $this->copyFromStorage($this, $source, $target); } - /** - * see https://www.php.net/manual/en/function.fopen.php - * - * @param string $path - * @param string $mode - * @return resource|bool - * @throws GenericEncryptionException - * @throws ModuleDoesNotExistsException - */ - public function fopen($path, $mode) { + public function fopen(string $path, string $mode) { // check if the file is stored in the array cache, this means that we // copy a file over to the versions folder, in this case we don't want to // decrypt it @@ -438,10 +304,8 @@ class Encryption extends Wrapper { // if we update a encrypted file with a un-encrypted one we change the db flag if ($targetIsEncrypted && $encryptionEnabled === false) { $cache = $this->storage->getCache(); - if ($cache) { - $entry = $cache->get($path); - $cache->update($entry->getId(), ['encrypted' => 0]); - } + $entry = $cache->get($path); + $cache->update($entry->getId(), ['encrypted' => 0]); } if ($encryptionEnabled) { // if $encryptionModuleId is empty, the default module will be used @@ -456,7 +320,7 @@ class Encryption extends Wrapper { if (!empty($encryptionModuleId)) { $encryptionModule = $this->encryptionManager->getEncryptionModule($encryptionModuleId); $shouldEncrypt = true; - } elseif (empty($encryptionModuleId) && $info['encrypted'] === true) { + } elseif ($info !== false && $info['encrypted'] === true) { // we come from a old installation. No header and/or no module defined // but the file is encrypted. In this case we need to use the // OC_DEFAULT_MODULE to read the file @@ -482,6 +346,16 @@ class Encryption extends Wrapper { if ($shouldEncrypt === true && $encryptionModule !== null) { $this->encryptedPaths->set($this->util->stripPartialFileExtension($path), true); $headerSize = $this->getHeaderSize($path); + if ($mode === 'r' && $headerSize === 0) { + $firstBlock = $this->readFirstBlock($path); + if (!$firstBlock) { + throw new InvalidHeaderException("Unable to get header block for $path"); + } elseif (!str_starts_with($firstBlock, Util::HEADER_START)) { + throw new InvalidHeaderException("Unable to get header size for $path, file doesn't start with encryption header"); + } else { + throw new InvalidHeaderException("Unable to get header size for $path, even though file does start with encryption header"); + } + } $source = $this->storage->fopen($path, $mode); if (!is_resource($source)) { return false; @@ -499,7 +373,7 @@ class Encryption extends Wrapper { /** - * perform some plausibility checks if the the unencrypted size is correct. + * perform some plausibility checks if the unencrypted size is correct. * If not, we calculate the correct unencrypted size and return it * * @param string $path internal path relative to the storage root @@ -511,9 +385,9 @@ class Encryption extends Wrapper { $size = $this->storage->filesize($path); $result = $unencryptedSize; - if ($unencryptedSize < 0 || - ($size > 0 && $unencryptedSize === $size) || - $unencryptedSize > $size + if ($unencryptedSize < 0 + || ($size > 0 && $unencryptedSize === $size) + || $unencryptedSize > $size ) { // check if we already calculate the unencrypted size for the // given path to avoid recursions @@ -537,10 +411,8 @@ class Encryption extends Wrapper { * @param string $path internal path relative to the storage root * @param int $size size of the physical file * @param int $unencryptedSize size of the unencrypted file - * - * @return int calculated unencrypted size */ - protected function fixUnencryptedSize(string $path, int $size, int $unencryptedSize): int { + protected function fixUnencryptedSize(string $path, int $size, int $unencryptedSize): int|float { $headerSize = $this->getHeaderSize($path); $header = $this->getHeader($path); $encryptionModule = $this->getEncryptionModule($path); @@ -609,12 +481,10 @@ class Encryption extends Wrapper { // write to cache if applicable $cache = $this->storage->getCache(); - if ($cache) { - $entry = $cache->get($path); - $cache->update($entry['fileid'], [ - 'unencrypted_size' => $newUnencryptedSize - ]); - } + $entry = $cache->get($path); + $cache->update($entry['fileid'], [ + 'unencrypted_size' => $newUnencryptedSize + ]); return $newUnencryptedSize; } @@ -628,7 +498,7 @@ class Encryption extends Wrapper { * This is required as stream_read only returns smaller chunks of data when the stream fetches from a * remote storage over the internet and it does not care about the given $blockSize. * - * @param handle the stream to read from + * @param resource $handle the stream to read from * @param int $blockSize Length of requested data block in bytes * @return string Data fetched from stream. */ @@ -646,19 +516,12 @@ class Encryption extends Wrapper { return $data; } - /** - * @param Storage\IStorage $sourceStorage - * @param string $sourceInternalPath - * @param string $targetInternalPath - * @param bool $preserveMtime - * @return bool - */ public function moveFromStorage( Storage\IStorage $sourceStorage, - $sourceInternalPath, - $targetInternalPath, - $preserveMtime = true - ) { + string $sourceInternalPath, + string $targetInternalPath, + $preserveMtime = true, + ): bool { if ($sourceStorage === $this) { return $this->rename($sourceInternalPath, $targetInternalPath); } @@ -675,31 +538,34 @@ class Encryption extends Wrapper { $result = $this->copyBetweenStorage($sourceStorage, $sourceInternalPath, $targetInternalPath, $preserveMtime, true); if ($result) { - if ($sourceStorage->is_dir($sourceInternalPath)) { - $result &= $sourceStorage->rmdir($sourceInternalPath); - } else { - $result &= $sourceStorage->unlink($sourceInternalPath); + $setPreserveCacheOnDelete = $sourceStorage->instanceOfStorage(ObjectStoreStorage::class) && !$this->instanceOfStorage(ObjectStoreStorage::class); + if ($setPreserveCacheOnDelete) { + /** @var ObjectStoreStorage $sourceStorage */ + $sourceStorage->setPreserveCacheOnDelete(true); + } + try { + if ($sourceStorage->is_dir($sourceInternalPath)) { + $result = $sourceStorage->rmdir($sourceInternalPath); + } else { + $result = $sourceStorage->unlink($sourceInternalPath); + } + } finally { + if ($setPreserveCacheOnDelete) { + /** @var ObjectStoreStorage $sourceStorage */ + $sourceStorage->setPreserveCacheOnDelete(false); + } } } return $result; } - - /** - * @param Storage\IStorage $sourceStorage - * @param string $sourceInternalPath - * @param string $targetInternalPath - * @param bool $preserveMtime - * @param bool $isRename - * @return bool - */ public function copyFromStorage( Storage\IStorage $sourceStorage, - $sourceInternalPath, - $targetInternalPath, + string $sourceInternalPath, + string $targetInternalPath, $preserveMtime = false, - $isRename = false - ) { + $isRename = false, + ): bool { // TODO clean this up once the underlying moveFromStorage in OC\Files\Storage\Wrapper\Common is fixed: // - call $this->storage->copyFromStorage() instead of $this->copyBetweenStorage // - copy the file cache update from $this->copyBetweenStorage to this method @@ -711,20 +577,14 @@ class Encryption extends Wrapper { /** * Update the encrypted cache version in the database - * - * @param Storage\IStorage $sourceStorage - * @param string $sourceInternalPath - * @param string $targetInternalPath - * @param bool $isRename - * @param bool $keepEncryptionVersion */ private function updateEncryptedVersion( Storage\IStorage $sourceStorage, - $sourceInternalPath, - $targetInternalPath, - $isRename, - $keepEncryptionVersion - ) { + string $sourceInternalPath, + string $targetInternalPath, + bool $isRename, + bool $keepEncryptionVersion, + ): void { $isEncrypted = $this->encryptionManager->isEnabled() && $this->shouldEncrypt($targetInternalPath); $cacheInformation = [ 'encrypted' => $isEncrypted, @@ -764,26 +624,19 @@ class Encryption extends Wrapper { /** * copy file between two storages - * - * @param Storage\IStorage $sourceStorage - * @param string $sourceInternalPath - * @param string $targetInternalPath - * @param bool $preserveMtime - * @param bool $isRename - * @return bool * @throws \Exception */ private function copyBetweenStorage( Storage\IStorage $sourceStorage, - $sourceInternalPath, - $targetInternalPath, - $preserveMtime, - $isRename - ) { + string $sourceInternalPath, + string $targetInternalPath, + bool $preserveMtime, + bool $isRename, + ): bool { // for versions we have nothing to do, because versions should always use the // key from the original file. Just create a 1:1 copy and done - if ($this->isVersion($targetInternalPath) || - $this->isVersion($sourceInternalPath)) { + if ($this->isVersion($targetInternalPath) + || $this->isVersion($sourceInternalPath)) { // remember that we try to create a version so that we can detect it during // fopen($sourceInternalPath) and by-pass the encryption in order to // create a 1:1 copy of the file @@ -806,9 +659,8 @@ class Encryption extends Wrapper { // first copy the keys that we reuse the existing file key on the target location // and don't create a new one which would break versions for example. - $mount = $this->mountManager->findByStorageId($sourceStorage->getId()); - if (count($mount) === 1) { - $mountPoint = $mount[0]->getMountPoint(); + if ($sourceStorage->instanceOfStorage(Common::class) && $sourceStorage->getMountOption('mount_point')) { + $mountPoint = $sourceStorage->getMountOption('mount_point'); $source = $mountPoint . '/' . $sourceInternalPath; $target = $this->getFullPath($targetInternalPath); $this->copyKeys($source, $target); @@ -824,9 +676,9 @@ class Encryption extends Wrapper { $result = true; } if (is_resource($dh)) { - while ($result and ($file = readdir($dh)) !== false) { + while ($result && ($file = readdir($dh)) !== false) { if (!Filesystem::isIgnoredDir($file)) { - $result &= $this->copyFromStorage($sourceStorage, $sourceInternalPath . '/' . $file, $targetInternalPath . '/' . $file, false, $isRename); + $result = $this->copyFromStorage($sourceStorage, $sourceInternalPath . '/' . $file, $targetInternalPath . '/' . $file, $preserveMtime, $isRename); } } } @@ -834,12 +686,16 @@ class Encryption extends Wrapper { try { $source = $sourceStorage->fopen($sourceInternalPath, 'r'); $target = $this->fopen($targetInternalPath, 'w'); - [, $result] = \OC_Helper::streamCopy($source, $target); + 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); } } @@ -858,7 +714,7 @@ class Encryption extends Wrapper { return (bool)$result; } - public function getLocalFile($path) { + public function getLocalFile(string $path): string|false { if ($this->encryptionManager->isEnabled()) { $cachedFile = $this->getCachedFile($path); if (is_string($cachedFile)) { @@ -868,14 +724,14 @@ class Encryption extends Wrapper { return $this->storage->getLocalFile($path); } - public function isLocal() { + public function isLocal(): bool { if ($this->encryptionManager->isEnabled()) { return false; } return $this->storage->isLocal(); } - public function stat($path) { + public function stat(string $path): array|false { $stat = $this->storage->stat($path); if (!$stat) { return false; @@ -887,8 +743,11 @@ class Encryption extends Wrapper { return $stat; } - public function hash($type, $path, $raw = false) { + 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); @@ -901,21 +760,21 @@ class Encryption extends Wrapper { * @param string $path relative to mount point * @return string full path including mount point */ - protected function getFullPath($path) { + protected function getFullPath(string $path): string { return Filesystem::normalizePath($this->mountPoint . '/' . $path); } /** * read first block of encrypted file, typically this will contain the * encryption header - * - * @param string $path - * @return string */ - protected function readFirstBlock($path) { + protected function readFirstBlock(string $path): string { $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); } @@ -924,11 +783,8 @@ class Encryption extends Wrapper { /** * return header size of given file - * - * @param string $path - * @return int */ - protected function getHeaderSize($path) { + protected function getHeaderSize(string $path): int { $headerSize = 0; $realFile = $this->util->stripPartialFileExtension($path); if ($this->storage->is_file($realFile)) { @@ -945,11 +801,8 @@ class Encryption extends Wrapper { /** * read header from file - * - * @param string $path - * @return array */ - protected function getHeader($path) { + protected function getHeader(string $path): array { $realFile = $this->util->stripPartialFileExtension($path); $exists = $this->storage->is_file($realFile); if ($exists) { @@ -981,12 +834,10 @@ class Encryption extends Wrapper { /** * read encryption module needed to read/write the file located at $path * - * @param string $path - * @return null|\OCP\Encryption\IEncryptionModule * @throws ModuleDoesNotExistsException * @throws \Exception */ - protected function getEncryptionModule($path) { + protected function getEncryptionModule(string $path): ?\OCP\Encryption\IEncryptionModule { $encryptionModule = null; $header = $this->getHeader($path); $encryptionModuleId = $this->util->getEncryptionModuleId($header); @@ -1002,11 +853,7 @@ class Encryption extends Wrapper { return $encryptionModule; } - /** - * @param string $path - * @param int $unencryptedSize - */ - public function updateUnencryptedSize($path, $unencryptedSize) { + public function updateUnencryptedSize(string $path, int|float $unencryptedSize): void { $this->unencryptedSize[$path] = $unencryptedSize; } @@ -1015,9 +862,8 @@ class Encryption extends Wrapper { * * @param string $source path relative to data/ * @param string $target path relative to data/ - * @return bool */ - protected function copyKeys($source, $target) { + protected function copyKeys(string $source, string $target): bool { if (!$this->util->isExcluded($source)) { return $this->keyStorage->copyKeys($source, $target); } @@ -1027,22 +873,16 @@ class Encryption extends Wrapper { /** * check if path points to a files version - * - * @param $path - * @return bool */ - protected function isVersion($path) { + protected function isVersion(string $path): bool { $normalized = Filesystem::normalizePath($path); return substr($normalized, 0, strlen('/files_versions/')) === '/files_versions/'; } /** * check if the given storage should be encrypted or not - * - * @param $path - * @return bool */ - protected function shouldEncrypt($path) { + protected function shouldEncrypt(string $path): bool { $fullPath = $this->getFullPath($path); $mountPointConfig = $this->mount->getOption('encrypt', true); if ($mountPointConfig === false) { @@ -1065,7 +905,10 @@ class Encryption extends Wrapper { public function writeStream(string $path, $stream, ?int $size = null): int { // always fall back to fopen $target = $this->fopen($path, 'w'); - [$count, $result] = \OC_Helper::streamCopy($stream, $target); + if ($target === false) { + throw new GenericFileException("Failed to open $path for writing"); + } + [$count, $result] = Files::streamCopy($stream, $target, true); fclose($stream); fclose($target); @@ -1084,11 +927,20 @@ class Encryption extends Wrapper { /** * Allow temporarily disabling the wrapper - * - * @param bool $enabled - * @return void */ public function setEnabled(bool $enabled): void { $this->enabled = $enabled; } + + /** + * Check if the on-disk data for a file has a valid encrypted header + * + * @param string $path + * @return bool + */ + public function hasValidHeader(string $path): bool { + $firstBlock = $this->readFirstBlock($path); + $header = $this->util->parseRawHeader($firstBlock); + return (count($header) > 0); + } } |