diff options
Diffstat (limited to 'lib/private/files')
-rw-r--r-- | lib/private/files/cache/cache.php | 29 | ||||
-rw-r--r-- | lib/private/files/cache/homecache.php | 14 | ||||
-rw-r--r-- | lib/private/files/fileinfo.php | 7 | ||||
-rw-r--r-- | lib/private/files/storage/common.php | 43 | ||||
-rw-r--r-- | lib/private/files/storage/localtempfiletrait.php | 85 | ||||
-rw-r--r-- | lib/private/files/storage/wrapper/encryption.php | 181 | ||||
-rw-r--r-- | lib/private/files/storage/wrapper/quota.php | 5 | ||||
-rw-r--r-- | lib/private/files/stream/encryption.php | 126 | ||||
-rw-r--r-- | lib/private/files/view.php | 22 |
9 files changed, 327 insertions, 185 deletions
diff --git a/lib/private/files/cache/cache.php b/lib/private/files/cache/cache.php index 9a785d071f7..c5e118946e5 100644 --- a/lib/private/files/cache/cache.php +++ b/lib/private/files/cache/cache.php @@ -152,7 +152,7 @@ class Cache { $params = array($file); } $sql = 'SELECT `fileid`, `storage`, `path`, `parent`, `name`, `mimetype`, `mimepart`, `size`, `mtime`, - `storage_mtime`, `encrypted`, `unencrypted_size`, `etag`, `permissions` + `storage_mtime`, `encrypted`, `etag`, `permissions` FROM `*PREFIX*filecache` ' . $where; $result = \OC_DB::executeAudited($sql, $params); $data = $result->fetchRow(); @@ -175,7 +175,6 @@ class Cache { $data['mtime'] = (int)$data['mtime']; $data['storage_mtime'] = (int)$data['storage_mtime']; $data['encrypted'] = (bool)$data['encrypted']; - $data['unencrypted_size'] = 0 + $data['unencrypted_size']; $data['storage'] = $this->storageId; $data['mimetype'] = $this->getMimetype($data['mimetype']); $data['mimepart'] = $this->getMimetype($data['mimepart']); @@ -208,7 +207,7 @@ class Cache { public function getFolderContentsById($fileId) { if ($fileId > -1) { $sql = 'SELECT `fileid`, `storage`, `path`, `parent`, `name`, `mimetype`, `mimepart`, `size`, `mtime`, - `storage_mtime`, `encrypted`, `unencrypted_size`, `etag`, `permissions` + `storage_mtime`, `encrypted`, `etag`, `permissions` FROM `*PREFIX*filecache` WHERE `parent` = ? ORDER BY `name` ASC'; $result = \OC_DB::executeAudited($sql, array($fileId)); $files = $result->fetchAll(); @@ -218,10 +217,6 @@ class Cache { if ($file['storage_mtime'] == 0) { $file['storage_mtime'] = $file['mtime']; } - if ($file['encrypted'] or ($file['unencrypted_size'] > 0 and $file['mimetype'] === 'httpd/unix-directory')) { - $file['encrypted_size'] = $file['size']; - $file['size'] = $file['unencrypted_size']; - } $file['permissions'] = (int)$file['permissions']; $file['mtime'] = (int)$file['mtime']; $file['storage_mtime'] = (int)$file['storage_mtime']; @@ -325,7 +320,7 @@ class Cache { */ function buildParts(array $data) { $fields = array( - 'path', 'parent', 'name', 'mimetype', 'size', 'mtime', 'storage_mtime', 'encrypted', 'unencrypted_size', + 'path', 'parent', 'name', 'mimetype', 'size', 'mtime', 'storage_mtime', 'encrypted', 'etag', 'permissions'); $params = array(); $queryParts = array(); @@ -521,7 +516,7 @@ class Cache { $sql = ' SELECT `fileid`, `storage`, `path`, `parent`, `name`, `mimetype`, `mimepart`, `size`, `mtime`, `encrypted`, - `unencrypted_size`, `etag`, `permissions` + `etag`, `permissions` FROM `*PREFIX*filecache` WHERE `storage` = ? AND `name` ILIKE ?'; $result = \OC_DB::executeAudited($sql, @@ -549,7 +544,7 @@ class Cache { } else { $where = '`mimepart` = ?'; } - $sql = 'SELECT `fileid`, `storage`, `path`, `parent`, `name`, `mimetype`, `mimepart`, `size`, `mtime`, `encrypted`, `unencrypted_size`, `etag`, `permissions` + $sql = 'SELECT `fileid`, `storage`, `path`, `parent`, `name`, `mimetype`, `mimepart`, `size`, `mtime`, `encrypted`, `etag`, `permissions` FROM `*PREFIX*filecache` WHERE ' . $where . ' AND `storage` = ?'; $mimetype = $this->getMimetypeId($mimetype); $result = \OC_DB::executeAudited($sql, array($mimetype, $this->getNumericStorageId())); @@ -574,7 +569,7 @@ class Cache { public function searchByTag($tag, $userId) { $sql = 'SELECT `fileid`, `storage`, `path`, `parent`, `name`, ' . '`mimetype`, `mimepart`, `size`, `mtime`, ' . - '`encrypted`, `unencrypted_size`, `etag`, `permissions` ' . + '`encrypted`, `etag`, `permissions` ' . 'FROM `*PREFIX*filecache` `file`, ' . '`*PREFIX*vcategory_to_object` `tagmap`, ' . '`*PREFIX*vcategory` `tag` ' . @@ -638,17 +633,15 @@ class Cache { } if (isset($entry['mimetype']) && $entry['mimetype'] === 'httpd/unix-directory') { $id = $entry['fileid']; - $sql = 'SELECT SUM(`size`) AS f1, MIN(`size`) AS f2, ' . - 'SUM(`unencrypted_size`) AS f3 ' . + $sql = 'SELECT SUM(`size`) AS f1, MIN(`size`) AS f2 ' . 'FROM `*PREFIX*filecache` ' . 'WHERE `parent` = ? AND `storage` = ?'; $result = \OC_DB::executeAudited($sql, array($id, $this->getNumericStorageId())); if ($row = $result->fetchRow()) { $result->closeCursor(); - list($sum, $min, $unencryptedSum) = array_values($row); + list($sum, $min) = array_values($row); $sum = 0 + $sum; $min = 0 + $min; - $unencryptedSum = 0 + $unencryptedSum; if ($min === -1) { $totalSize = $min; } else { @@ -658,15 +651,9 @@ class Cache { if ($entry['size'] !== $totalSize) { $update['size'] = $totalSize; } - if (!isset($entry['unencrypted_size']) or $entry['unencrypted_size'] !== $unencryptedSum) { - $update['unencrypted_size'] = $unencryptedSum; - } if (count($update) > 0) { $this->update($id, $update); } - if ($totalSize !== -1 and $unencryptedSum > 0) { - $totalSize = $unencryptedSum; - } } else { $result->closeCursor(); } diff --git a/lib/private/files/cache/homecache.php b/lib/private/files/cache/homecache.php index aa075d41221..1b85462d615 100644 --- a/lib/private/files/cache/homecache.php +++ b/lib/private/files/cache/homecache.php @@ -48,26 +48,18 @@ class HomeCache extends Cache { } if ($entry && $entry['mimetype'] === 'httpd/unix-directory') { $id = $entry['fileid']; - $sql = 'SELECT SUM(`size`) AS f1, ' . - 'SUM(`unencrypted_size`) AS f2 FROM `*PREFIX*filecache` ' . + $sql = 'SELECT SUM(`size`) AS f1 ' . + 'FROM `*PREFIX*filecache` ' . 'WHERE `parent` = ? AND `storage` = ? AND `size` >= 0'; $result = \OC_DB::executeAudited($sql, array($id, $this->getNumericStorageId())); if ($row = $result->fetchRow()) { $result->closeCursor(); - list($sum, $unencryptedSum) = array_values($row); + list($sum) = array_values($row); $totalSize = 0 + $sum; - $unencryptedSize = 0 + $unencryptedSum; $entry['size'] += 0; - if (!isset($entry['unencrypted_size'])) { - $entry['unencrypted_size'] = 0; - } - $entry['unencrypted_size'] += 0; if ($entry['size'] !== $totalSize) { $this->update($id, array('size' => $totalSize)); } - if ($entry['unencrypted_size'] !== $unencryptedSize) { - $this->update($id, array('unencrypted_size' => $unencryptedSize)); - } } } return $totalSize; diff --git a/lib/private/files/fileinfo.php b/lib/private/files/fileinfo.php index 03aad56e103..82c8f3de690 100644 --- a/lib/private/files/fileinfo.php +++ b/lib/private/files/fileinfo.php @@ -170,13 +170,6 @@ class FileInfo implements \OCP\Files\FileInfo, \ArrayAccess { /** * @return int */ - public function getUnencryptedSize() { - return isset($this->data['unencrypted_size']) ? $this->data['unencrypted_size'] : 0; - } - - /** - * @return int - */ public function getPermissions() { return $this->data['permissions']; } diff --git a/lib/private/files/storage/common.php b/lib/private/files/storage/common.php index 9fef53fa95a..ed85d3c07cc 100644 --- a/lib/private/files/storage/common.php +++ b/lib/private/files/storage/common.php @@ -56,6 +56,9 @@ use OCP\Files\ReservedWordException; * in classes which extend it, e.g. $this->stat() . */ abstract class Common implements Storage { + + use LocalTempFileTrait; + protected $cache; protected $scanner; protected $watcher; @@ -63,11 +66,6 @@ abstract class Common implements Storage { protected $mountOptions = []; - /** - * @var string[] - */ - protected $cachedFiles = array(); - public function __construct($parameters) { } @@ -247,27 +245,6 @@ abstract class Common implements Storage { return $this->getCachedFile($path); } - /** - * @param string $path - * @return string - */ - protected function toTmpFile($path) { //no longer in the storage api, still useful here - $source = $this->fopen($path, 'r'); - if (!$source) { - return false; - } - if ($pos = strrpos($path, '.')) { - $extension = substr($path, $pos); - } else { - $extension = ''; - } - $tmpFile = \OC_Helper::tmpFile($extension); - $target = fopen($tmpFile, 'w'); - \OC_Helper::streamCopy($source, $target); - fclose($target); - return $tmpFile; - } - public function getLocalFolder($path) { $baseDir = \OC_Helper::tmpFolder(); $this->addLocalFolder($path, $baseDir); @@ -451,20 +428,6 @@ abstract class Common implements Storage { } /** - * @param string $path - */ - protected function getCachedFile($path) { - if (!isset($this->cachedFiles[$path])) { - $this->cachedFiles[$path] = $this->toTmpFile($path); - } - return $this->cachedFiles[$path]; - } - - protected function removeCachedFile($path) { - unset($this->cachedFiles[$path]); - } - - /** * Check if the storage is an instance of $class or is a wrapper for a storage that is an instance of $class * * @param string $class diff --git a/lib/private/files/storage/localtempfiletrait.php b/lib/private/files/storage/localtempfiletrait.php new file mode 100644 index 00000000000..444e4e2e89e --- /dev/null +++ b/lib/private/files/storage/localtempfiletrait.php @@ -0,0 +1,85 @@ +<?php +/** + * @author Thomas Müller <thomas.mueller@tmit.eu> + * + * @copyright Copyright (c) 2015, ownCloud, Inc. + * @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/> + * + */ + +namespace OC\Files\Storage; + +use OC\Files\Cache\Cache; +use OC\Files\Cache\Scanner; +use OC\Files\Filesystem; +use OC\Files\Cache\Watcher; +use OCP\Files\FileNameTooLongException; +use OCP\Files\InvalidCharacterInPathException; +use OCP\Files\InvalidPathException; +use OCP\Files\ReservedWordException; + +/** + * Storage backend class for providing common filesystem operation methods + * which are not storage-backend specific. + * + * \OC\Files\Storage\Common is never used directly; it is extended by all other + * storage backends, where its methods may be overridden, and additional + * (backend-specific) methods are defined. + * + * Some \OC\Files\Storage\Common methods call functions which are first defined + * in classes which extend it, e.g. $this->stat() . + */ +trait LocalTempFileTrait { + + /** + * @var string[] + */ + protected $cachedFiles = array(); + + /** + * @param string $path + */ + protected function getCachedFile($path) { + if (!isset($this->cachedFiles[$path])) { + $this->cachedFiles[$path] = $this->toTmpFile($path); + } + return $this->cachedFiles[$path]; + } + + protected function removeCachedFile($path) { + unset($this->cachedFiles[$path]); + } + + /** + * @param string $path + * @return string + */ + protected function toTmpFile($path) { //no longer in the storage api, still useful here + $source = $this->fopen($path, 'r'); + if (!$source) { + return false; + } + if ($pos = strrpos($path, '.')) { + $extension = substr($path, $pos); + } else { + $extension = ''; + } + $tmpFile = \OC_Helper::tmpFile($extension); + $target = fopen($tmpFile, 'w'); + \OC_Helper::streamCopy($source, $target); + fclose($target); + return $tmpFile; + } +} diff --git a/lib/private/files/storage/wrapper/encryption.php b/lib/private/files/storage/wrapper/encryption.php index 0e70c99c8d7..5245fe4cc45 100644 --- a/lib/private/files/storage/wrapper/encryption.php +++ b/lib/private/files/storage/wrapper/encryption.php @@ -24,9 +24,13 @@ namespace OC\Files\Storage\Wrapper; use OC\Encryption\Exceptions\ModuleDoesNotExistsException; +use OC\Files\Storage\LocalTempFileTrait; +use OCP\Files\Mount\IMountPoint; class Encryption extends Wrapper { + use LocalTempFileTrait; + /** @var string */ private $mountPoint; @@ -45,11 +49,18 @@ class Encryption extends Wrapper { /** @var array */ private $unencryptedSize; + /** @var \OC\Encryption\File */ + private $fileHelper; + + /** @var IMountPoint */ + private $mount; + /** * @param array $parameters * @param \OC\Encryption\Manager $encryptionManager * @param \OC\Encryption\Util $util * @param \OC\Log $logger + * @param \OC\Encryption\File $fileHelper * @param string $uid user who perform the read/write operation (null for public access) */ public function __construct( @@ -57,14 +68,17 @@ class Encryption extends Wrapper { \OC\Encryption\Manager $encryptionManager = null, \OC\Encryption\Util $util = null, \OC\Log $logger = null, + \OC\Encryption\File $fileHelper = null, $uid = null ) { $this->mountPoint = $parameters['mountPoint']; + $this->mount = $parameters['mount']; $this->encryptionManager = $encryptionManager; $this->util = $util; $this->logger = $logger; $this->uid = $uid; + $this->fileHelper = $fileHelper; $this->unencryptedSize = array(); parent::__construct($parameters); } @@ -77,27 +91,18 @@ class Encryption extends Wrapper { * @return int */ public function filesize($path) { - $size = 0; $fullPath = $this->getFullPath($path); - - $encryptedSize = $this->storage->filesize($path); + $size = $this->storage->filesize($path); $info = $this->getCache()->get($path); - if($encryptedSize > 0 && $info['encrypted']) { - $size = $info['unencrypted_size']; - if ($size <= 0) { - $encryptionModule = $this->getEncryptionModule($path); - if ($encryptionModule) { - $size = $encryptionModule->calculateUnencryptedSize($fullPath); - $this->getCache()->update($info['fileid'], array('unencrypted_size' => $size)); - } - } - } else if (isset($this->unencryptedSize[$fullPath]) && isset($info['fileid'])) { + if (isset($this->unencryptedSize[$fullPath])) { $size = $this->unencryptedSize[$fullPath]; + } + + if (isset($info['fileid'])) { $info['encrypted'] = true; - $info['unencrypted_size'] = $size; - $info['size'] = $encryptedSize; + $info['size'] = $size; $this->getCache()->put($path, $info); } @@ -112,23 +117,18 @@ class Encryption extends Wrapper { */ public function file_get_contents($path) { - $data = null; $encryptionModule = $this->getEncryptionModule($path); if ($encryptionModule) { - - $handle = $this->fopen($path, 'r'); - - if (is_resource($handle)) { - while (!feof($handle)) { - $data .= fread($handle, $this->util->getBlockSize()); - } + $handle = $this->fopen($path, "r"); + if (!$handle) { + return false; } - } else { - $data = $this->storage->file_get_contents($path); + $data = stream_get_contents($handle); + fclose($handle); + return $data; } - - return $data; + return $this->storage->file_get_contents($path); } /** @@ -141,8 +141,9 @@ class Encryption extends Wrapper { public function file_put_contents($path, $data) { // file put content will always be translated to a stream write $handle = $this->fopen($path, 'w'); - fwrite($handle, $data); - return fclose($handle); + $written = fwrite($handle, $data); + fclose($handle); + return $written; } /** @@ -158,8 +159,8 @@ class Encryption extends Wrapper { $encryptionModule = $this->getEncryptionModule($path); if ($encryptionModule) { - $keyStorage = \OC::$server->getEncryptionKeyStorage($encryptionModule->getId()); - $keyStorage->deleteAllFileKeys($this->getFullPath($path)); + $keyStorage = $this->getKeyStorage($encryptionModule->getId()); + $keyStorage->deleteAllFileKeys($this->getFullPath($path)); } return $this->storage->unlink($path); @@ -177,17 +178,14 @@ class Encryption extends Wrapper { return $this->storage->rename($path1, $path2); } - $fullPath1 = $this->getFullPath($path1); - list($owner, $source) = $this->util->getUidAndFilename($fullPath1); + $source = $this->getFullPath($path1); $result = $this->storage->rename($path1, $path2); if ($result) { - $fullPath2 = $this->getFullPath($path2); - $systemWide = $this->util->isSystemWideMountPoint($this->mountPoint); - list(, $target) = $this->util->getUidAndFilename($fullPath2); + $target = $this->getFullPath($path2); $encryptionModule = $this->getEncryptionModule($path2); if ($encryptionModule) { - $keyStorage = \OC::$server->getEncryptionKeyStorage($encryptionModule->getId()); - $keyStorage->renameKeys($source, $target, $owner, $systemWide); + $keyStorage = $this->getKeyStorage($encryptionModule->getId()); + $keyStorage->renameKeys($source, $target); } } @@ -202,9 +200,22 @@ class Encryption extends Wrapper { * @return bool */ public function copy($path1, $path2) { - // todo copy encryption keys, get users with access to the file and reencrypt - // or is this to encryption module specific? Then we can hand this over - return $this->storage->copy($path1, $path2); + if ($this->util->isExcluded($path1)) { + return $this->storage->rename($path1, $path2); + } + + $source = $this->getFullPath($path1); + $result = $this->storage->copy($path1, $path2); + if ($result) { + $target = $this->getFullPath($path2); + $encryptionModule = $this->getEncryptionModule($path2); + if ($encryptionModule) { + $keyStorage = $this->getKeyStorage($encryptionModule->getId()); + $keyStorage->copyKeys($source, $target); + } + } + + return $result; } /** @@ -223,7 +234,17 @@ class Encryption extends Wrapper { $encryptionModuleId = $this->util->getEncryptionModuleId($header); $size = $unencryptedSize = 0; - if ($this->file_exists($path)) { + $targetExists = $this->file_exists($path); + $targetIsEncrypted = false; + if ($targetExists) { + // in case the file exists we require the explicit module as + // specified in the file header - otherwise we need to fail hard to + // prevent data loss on client side + if (!empty($encryptionModuleId)) { + $targetIsEncrypted = true; + $encryptionModule = $this->encryptionManager->getEncryptionModule($encryptionModuleId); + } + $size = $this->storage->filesize($path); $unencryptedSize = $this->filesize($path); } @@ -254,10 +275,18 @@ class Encryption extends Wrapper { '" not found, file will be stored unencrypted'); } + // encryption disabled on write of new file and write to existing unencrypted file -> don't encrypt + $encEnabled = $this->encryptionManager->isEnabled(); + if (!$encEnabled || !$this->mount->getOption('encrypt', true)) { + if (!$targetExists || !$targetIsEncrypted) { + $shouldEncrypt = false; + } + } + if($shouldEncrypt === true && !$this->util->isExcluded($fullPath) && $encryptionModule !== null) { $source = $this->storage->fopen($path, $mode); $handle = \OC\Files\Stream\Encryption::wrap($source, $path, $fullPath, $header, - $this->uid, $encryptionModule, $this->storage, $this, $this->util, $mode, + $this->uid, $encryptionModule, $this->storage, $this, $this->util, $this->fileHelper, $mode, $size, $unencryptedSize); return $handle; } else { @@ -266,6 +295,57 @@ class Encryption extends Wrapper { } /** + * 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 + * + * @param string $path + * @return string + */ + public function getLocalFile($path) { + return $this->getCachedFile($path); + } + + /** + * Returns the wrapped storage's value for isLocal() + * + * @return bool wrapped storage's isLocal() value + */ + public function isLocal() { + return false; + } + + /** + * see http://php.net/manual/en/function.stat.php + * only the following keys are required in the result: size and mtime + * + * @param string $path + * @return array + */ + public function stat($path) { + $stat = $this->storage->stat($path); + $fileSize = $this->filesize($path); + $stat['size'] = $fileSize; + $stat[7] = $fileSize; + return $stat; + } + + /** + * see http://php.net/manual/en/function.hash.php + * + * @param string $type + * @param string $path + * @param bool $raw + * @return string + */ + public function hash($type, $path, $raw = false) { + $fh = $this->fopen($path, 'rb'); + $ctx = hash_init($type); + hash_update_stream($ctx, $fh); + fclose($fh); + return hash_final($ctx, $raw); + } + + /** * return full path, including mount point * * @param string $path relative to mount point @@ -295,7 +375,9 @@ class Encryption extends Wrapper { * read encryption module needed to read/write the file located at $path * * @param string $path - * @return \OCP\Encryption\IEncryptionModule|null + * @return null|\OCP\Encryption\IEncryptionModule + * @throws ModuleDoesNotExistsException + * @throws \Exception */ protected function getEncryptionModule($path) { $encryptionModule = null; @@ -305,7 +387,7 @@ class Encryption extends Wrapper { try { $encryptionModule = $this->encryptionManager->getEncryptionModule($encryptionModuleId); } catch (ModuleDoesNotExistsException $e) { - $this->logger->critical('Encryption module defined in "' . $path . '" mot loaded!'); + $this->logger->critical('Encryption module defined in "' . $path . '" not loaded!'); throw $e; } } @@ -316,4 +398,13 @@ class Encryption extends Wrapper { $this->unencryptedSize[$path] = $unencryptedSize; } + /** + * @param string $encryptionModuleId + * @return \OCP\Encryption\Keys\IStorage + */ + protected function getKeyStorage($encryptionModuleId) { + $keyStorage = \OC::$server->getEncryptionKeyStorage($encryptionModuleId); + return $keyStorage; + } + } diff --git a/lib/private/files/storage/wrapper/quota.php b/lib/private/files/storage/wrapper/quota.php index 34bd2589947..3c0fda98dad 100644 --- a/lib/private/files/storage/wrapper/quota.php +++ b/lib/private/files/storage/wrapper/quota.php @@ -60,11 +60,6 @@ class Quota extends Wrapper { $cache = $this->getCache(); $data = $cache->get($path); if (is_array($data) and isset($data['size'])) { - if (isset($data['unencrypted_size']) - && $data['unencrypted_size'] > 0 - ) { - return $data['unencrypted_size']; - } return $data['size']; } else { return \OCP\Files\FileInfo::SPACE_NOT_COMPUTED; diff --git a/lib/private/files/stream/encryption.php b/lib/private/files/stream/encryption.php index ddef9067bad..b4e06c99943 100644 --- a/lib/private/files/stream/encryption.php +++ b/lib/private/files/stream/encryption.php @@ -31,6 +31,9 @@ class Encryption extends Wrapper { /** @var \OC\Encryption\Util */ protected $util; + /** @var \OC\Encryption\File */ + protected $file; + /** @var \OCP\Encryption\IEncryptionModule */ protected $encryptionModule; @@ -43,6 +46,9 @@ class Encryption extends Wrapper { /** @var string */ protected $internalPath; + /** @var string */ + protected $cache; + /** @var integer */ protected $size; @@ -72,13 +78,16 @@ class Encryption extends Wrapper { /** * user who perform the read/write operation null for public access * - * @var string + * @var string */ protected $uid; /** @var bool */ protected $readOnly; + /** @var bool */ + protected $writeFlag; + /** @var array */ protected $expectedContextProperties; @@ -91,6 +100,7 @@ class Encryption extends Wrapper { 'encryptionModule', 'header', 'uid', + 'file', 'util', 'size', 'unencryptedSize', @@ -106,11 +116,12 @@ class Encryption extends Wrapper { * @param string $internalPath relative to mount point * @param string $fullPath relative to data/ * @param array $header - * @param sting $uid + * @param string $uid * @param \OCP\Encryption\IEncryptionModule $encryptionModule * @param \OC\Files\Storage\Storage $storage - * @param OC\Files\Storage\Wrapper\Encryption $encStorage + * @param \OC\Files\Storage\Wrapper\Encryption $encStorage * @param \OC\Encryption\Util $util + * @param \OC\Encryption\File $file * @param string $mode * @param int $size * @param int $unencryptedSize @@ -119,9 +130,15 @@ class Encryption extends Wrapper { * @throws \BadMethodCallException */ public static function wrap($source, $internalPath, $fullPath, array $header, - $uid, \OCP\Encryption\IEncryptionModule $encryptionModule, - \OC\Files\Storage\Storage $storage, \OC\Files\Storage\Wrapper\Encryption $encStorage, - \OC\Encryption\Util $util, $mode, $size, $unencryptedSize) { + $uid, + \OCP\Encryption\IEncryptionModule $encryptionModule, + \OC\Files\Storage\Storage $storage, + \OC\Files\Storage\Wrapper\Encryption $encStorage, + \OC\Encryption\Util $util, + \OC\Encryption\File $file, + $mode, + $size, + $unencryptedSize) { $context = stream_context_create(array( 'ocencryption' => array( @@ -133,6 +150,7 @@ class Encryption extends Wrapper { 'header' => $header, 'uid' => $uid, 'util' => $util, + 'file' => $file, 'size' => $size, 'unencryptedSize' => $unencryptedSize, 'encryptionStorage' => $encStorage @@ -180,7 +198,7 @@ class Encryption extends Wrapper { $context = parent::loadContext($name); foreach ($this->expectedContextProperties as $property) { - if (isset($context[$property])) { + if (array_key_exists($property, $context)) { $this->{$property} = $context[$property]; } else { throw new \BadMethodCallException('Invalid context, "' . $property . '" options not set'); @@ -194,6 +212,8 @@ class Encryption extends Wrapper { $this->loadContext('ocencryption'); $this->position = 0; + $this->cache = ''; + $this->writeFlag = false; $this->unencryptedBlockSize = $this->encryptionModule->getUnencryptedBlockSize(); if ( @@ -216,7 +236,7 @@ class Encryption extends Wrapper { $sharePath = dirname($path); } - $accessList = $this->util->getSharingUsersArray($sharePath); + $accessList = $this->file->getAccessList($sharePath); $this->newHeader = $this->encryptionModule->begin($this->fullPath, $this->uid, $this->header, $accessList); return true; @@ -228,25 +248,26 @@ class Encryption extends Wrapper { $result = ''; // skip the header if we read the file from the beginning - if ($this->position === 0 && !empty($this->header)) { - parent::stream_read($this->util->getBlockSize()); + if ($this->position === 0) { + parent::stream_read($this->util->getHeaderSize()); } +// $count = min($count, $this->unencryptedSize - $this->position); while ($count > 0) { $remainingLength = $count; // update the cache of the current block - $data = parent::stream_read($this->util->getBlockSize()); - $decrypted = $this->encryptionModule->decrypt($data); + $this->readCache(); // determine the relative position in the current block $blockPosition = ($this->position % $this->unencryptedBlockSize); // if entire read inside current block then only position needs to be updated if ($remainingLength < ($this->unencryptedBlockSize - $blockPosition)) { - $result .= substr($decrypted, $blockPosition, $remainingLength); + $result .= substr($this->cache, $blockPosition, $remainingLength); $this->position += $remainingLength; $count = 0; - // otherwise remainder of current block is fetched, the block is flushed and the position updated + // otherwise remainder of current block is fetched, the block is flushed and the position updated } else { - $result .= substr($decrypted, $blockPosition); + $result .= substr($this->cache, $blockPosition); + $this->flush(); $this->position += ($this->unencryptedBlockSize - $blockPosition); $count -= ($this->unencryptedBlockSize - $blockPosition); } @@ -259,6 +280,7 @@ class Encryption extends Wrapper { if ($this->position === 0) { $this->writeHeader(); + $this->size+=$this->util->getHeaderSize(); } $length = 0; @@ -266,9 +288,8 @@ class Encryption extends Wrapper { while (strlen($data) > 0) { $remainingLength = strlen($data); - // read current block - $currentBlock = parent::stream_read($this->util->getBlockSize()); - $decrypted = $this->encryptionModule->decrypt($currentBlock, $this->uid); + // set the cache to the current 6126 block + $this->readCache(); // for seekable streams the pointer is moved back to the beginning of the encrypted block // flush will start writing there when the position moves to another block @@ -277,7 +298,10 @@ class Encryption extends Wrapper { $resultFseek = parent::stream_seek($positionInFile); // only allow writes on seekable streams, or at the end of the encrypted stream - if ($resultFseek || $positionInFile === $this->size) { + if (!($this->readOnly) && ($resultFseek || $positionInFile === $this->size)) { + + // switch the writeFlag so flush() will write the block + $this->writeFlag = true; // determine the relative position in the current block $blockPosition = ($this->position % $this->unencryptedBlockSize); @@ -285,28 +309,22 @@ class Encryption extends Wrapper { // if so, overwrite existing data (if any) // update position and liberate $data if ($remainingLength < ($this->unencryptedBlockSize - $blockPosition)) { - $decrypted = substr($decrypted, 0, $blockPosition) - . $data . substr($decrypted, $blockPosition + $remainingLength); - $encrypted = $this->encryptionModule->encrypt($decrypted); - parent::stream_write($encrypted); + $this->cache = substr($this->cache, 0, $blockPosition) + . $data . substr($this->cache, $blockPosition + $remainingLength); $this->position += $remainingLength; $length += $remainingLength; $data = ''; - // if $data doens't fit the current block, the fill the current block and reiterate - // after the block is filled, it is flushed and $data is updatedxxx + // if $data doesn't fit the current block, the fill the current block and reiterate + // after the block is filled, it is flushed and $data is updatedxxx } else { - $decrypted = substr($decrypted, 0, $blockPosition) . + $this->cache = substr($this->cache, 0, $blockPosition) . substr($data, 0, $this->unencryptedBlockSize - $blockPosition); - $encrypted = $this->encryptionModule->encrypt($decrypted); - parent::stream_write($encrypted); + $this->flush(); $this->position += ($this->unencryptedBlockSize - $blockPosition); - $this->size = max($this->size, $this->stream_tell()); $length += ($this->unencryptedBlockSize - $blockPosition); $data = substr($data, $this->unencryptedBlockSize - $blockPosition); } } else { - $encrypted = $this->encryptionModule->encrypt($data); - parent::stream_write($encrypted); $data = ''; } } @@ -345,7 +363,11 @@ class Encryption extends Wrapper { $newFilePosition = floor($newPosition / $this->unencryptedBlockSize) * $this->util->getBlockSize() + $this->util->getHeaderSize(); + $oldFilePosition = parent::stream_tell(); if (parent::stream_seek($newFilePosition)) { + parent::stream_seek($oldFilePosition); + $this->flush(); + parent::stream_seek($newFilePosition); $this->position = $newPosition; $return = true; } @@ -355,13 +377,6 @@ class Encryption extends Wrapper { public function stream_close() { $this->flush(); - return parent::stream_close(); - } - - /** - * tell encryption module that we are done and write remaining data to the file - */ - protected function flush() { $remainingData = $this->encryptionModule->end($this->fullPath); if ($this->readOnly === false) { if(!empty($remainingData)) { @@ -369,17 +384,50 @@ class Encryption extends Wrapper { } $this->encryptionStorage->updateUnencryptedSize($this->fullPath, $this->unencryptedSize); } + return parent::stream_close(); } + /** + * write block to file + */ + protected function flush() { + // write to disk only when writeFlag was set to 1 + if ($this->writeFlag) { + // Disable the file proxies so that encryption is not + // automatically attempted when the file is written to disk - + // we are handling that separately here and we don't want to + // get into an infinite loop + $encrypted = $this->encryptionModule->encrypt($this->cache); + parent::stream_write($encrypted); + $this->writeFlag = false; + $this->size = max($this->size, parent::stream_tell()); + } + // always empty the cache (otherwise readCache() will not fill it with the new block) + $this->cache = ''; + } + + /** + * read block to file + */ + protected function readCache() { + // cache should always be empty string when this function is called + // don't try to fill the cache when trying to write at the end of the unencrypted file when it coincides with new block + if ($this->cache === '' && !($this->position === $this->unencryptedSize && ($this->position % $this->unencryptedBlockSize) === 0)) { + // Get the data from the file handle + $data = parent::stream_read($this->util->getBlockSize()); + $this->cache = $this->encryptionModule->decrypt($data); + } + } /** * write header at beginning of encrypted file * + * @return integer * @throws EncryptionHeaderKeyExistsException if header key is already in use */ private function writeHeader() { $header = $this->util->createHeader($this->newHeader, $this->encryptionModule); - parent::stream_write($header); + return parent::stream_write($header); } } diff --git a/lib/private/files/view.php b/lib/private/files/view.php index f8ec4a0eb43..0f371bbc5ea 100644 --- a/lib/private/files/view.php +++ b/lib/private/files/view.php @@ -515,8 +515,7 @@ class View { public function file_put_contents($path, $data) { if (is_resource($data)) { //not having to deal with streams in file_put_contents makes life easier $absolutePath = Filesystem::normalizePath($this->getAbsolutePath($path)); - if (\OC_FileProxy::runPreProxies('file_put_contents', $absolutePath, $data) - and Filesystem::isValidPath($path) + if (Filesystem::isValidPath($path) and !Filesystem::isFileBlacklisted($path) ) { $path = $this->getRelativePath($absolutePath); @@ -537,7 +536,6 @@ class View { if ($this->shouldEmitHooks($path) && $result !== false) { $this->emit_file_hooks_post($exists, $path); } - \OC_FileProxy::runPostProxies('file_put_contents', $absolutePath, $count); return $result; } else { return false; @@ -591,8 +589,7 @@ class View { $absolutePath1 = Filesystem::normalizePath($this->getAbsolutePath($path1)); $absolutePath2 = Filesystem::normalizePath($this->getAbsolutePath($path2)); if ( - \OC_FileProxy::runPreProxies('rename', $absolutePath1, $absolutePath2) - and Filesystem::isValidPath($path2) + Filesystem::isValidPath($path2) and Filesystem::isValidPath($path1) and !Filesystem::isFileBlacklisted($path2) ) { @@ -635,14 +632,12 @@ class View { $sourceMountPoint = $mount->getMountPoint(); $result = $mount->moveMount($absolutePath2); $manager->moveMount($sourceMountPoint, $mount->getMountPoint()); - \OC_FileProxy::runPostProxies('rename', $absolutePath1, $absolutePath2); } else { $result = false; } } elseif ($mp1 == $mp2) { if ($storage1) { $result = $storage1->rename($internalPath1, $internalPath2); - \OC_FileProxy::runPostProxies('rename', $absolutePath1, $absolutePath2); } else { $result = false; } @@ -718,8 +713,7 @@ class View { $absolutePath1 = Filesystem::normalizePath($this->getAbsolutePath($path1)); $absolutePath2 = Filesystem::normalizePath($this->getAbsolutePath($path2)); if ( - \OC_FileProxy::runPreProxies('copy', $absolutePath1, $absolutePath2) - and Filesystem::isValidPath($path2) + Filesystem::isValidPath($path2) and Filesystem::isValidPath($path1) and !Filesystem::isFileBlacklisted($path2) ) { @@ -927,7 +921,7 @@ class View { public function hash($type, $path, $raw = false) { $postFix = (substr($path, -1, 1) === '/') ? '/' : ''; $absolutePath = Filesystem::normalizePath($this->getAbsolutePath($path)); - if (\OC_FileProxy::runPreProxies('hash', $absolutePath) && Filesystem::isValidPath($path)) { + if (Filesystem::isValidPath($path)) { $path = $this->getRelativePath($absolutePath); if ($path == null) { return false; @@ -942,7 +936,6 @@ class View { list($storage, $internalPath) = Filesystem::resolvePath($absolutePath . $postFix); if ($storage) { $result = $storage->hash($type, $internalPath, $raw); - $result = \OC_FileProxy::runPostProxies('hash', $absolutePath, $result); return $result; } } @@ -975,8 +968,7 @@ class View { private function basicOperation($operation, $path, $hooks = array(), $extraParam = null) { $postFix = (substr($path, -1, 1) === '/') ? '/' : ''; $absolutePath = Filesystem::normalizePath($this->getAbsolutePath($path)); - if (\OC_FileProxy::runPreProxies($operation, $absolutePath, $extraParam) - and Filesystem::isValidPath($path) + if (Filesystem::isValidPath($path) and !Filesystem::isFileBlacklisted($path) ) { $path = $this->getRelativePath($absolutePath); @@ -993,8 +985,6 @@ class View { $result = $storage->$operation($internalPath); } - $result = \OC_FileProxy::runPostProxies($operation, $this->getAbsolutePath($path), $result); - if (in_array('delete', $hooks) and $result) { $this->updater->remove($path); } @@ -1168,8 +1158,6 @@ class View { $data['permissions'] |= \OCP\Constants::PERMISSION_DELETE; } - $data = \OC_FileProxy::runPostProxies('getFileInfo', $path, $data); - return new FileInfo($path, $storage, $internalPath, $data, $mount); } |