diff options
author | Vincent Petry <pvince81@owncloud.com> | 2014-03-04 15:53:08 +0400 |
---|---|---|
committer | Vincent Petry <pvince81@owncloud.com> | 2014-03-04 15:53:08 +0400 |
commit | 722b81627c302f24c714f297878722a10496a444 (patch) | |
tree | 23bbbc48b634c3e1cbe82cd53f77b9bc8bed1fb0 /apps/files_encryption | |
parent | 7e2659f017f879a57429b390c3a542689a5ba711 (diff) | |
parent | 9cff832282fd8e852db21d37b52e4f628de9d885 (diff) |
Merge pull request #7464 from owncloud/enc_fseek_fallback_stable6
Enc fseek fallback stable6
Diffstat (limited to 'apps/files_encryption')
-rwxr-xr-x | apps/files_encryption/lib/helper.php | 24 | ||||
-rw-r--r-- | apps/files_encryption/lib/stream.php | 38 | ||||
-rw-r--r-- | apps/files_encryption/lib/util.php | 33 | ||||
-rw-r--r-- | apps/files_encryption/tests/stream.php | 39 | ||||
-rwxr-xr-x | apps/files_encryption/tests/util.php | 35 |
5 files changed, 159 insertions, 10 deletions
diff --git a/apps/files_encryption/lib/helper.php b/apps/files_encryption/lib/helper.php index 584935f6139..bb06a57c714 100755 --- a/apps/files_encryption/lib/helper.php +++ b/apps/files_encryption/lib/helper.php @@ -29,6 +29,8 @@ namespace OCA\Encryption; */ class Helper { + private static $tmpFileMapping; // Map tmp files to files in data/user/files + /** * @brief register share related hooks * @@ -426,5 +428,27 @@ class Helper { public static function escapeGlobPattern($path) { return preg_replace('/(\*|\?|\[)/', '[$1]', $path); } + + /** + * @brief remember from which file the tmp file (getLocalFile() call) was created + * @param string $tmpFile path of tmp file + * @param string $originalFile path of the original file relative to data/ + */ + public static function addTmpFileToMapper($tmpFile, $originalFile) { + self::$tmpFileMapping[$tmpFile] = $originalFile; + } + + /** + * @brief get the path of the original file + * @param string $tmpFile path of the tmp file + * @return mixed path of the original file or false + */ + public static function getPathFromTmpFile($tmpFile) { + if (isset(self::$tmpFileMapping[$tmpFile])) { + return self::$tmpFileMapping[$tmpFile]; + } + + return false; + } } diff --git a/apps/files_encryption/lib/stream.php b/apps/files_encryption/lib/stream.php index f54dbc14e9d..b3fa71f5f59 100644 --- a/apps/files_encryption/lib/stream.php +++ b/apps/files_encryption/lib/stream.php @@ -64,6 +64,9 @@ class Stream { private $publicKey; private $encKeyfile; private $newFile; // helper var, we only need to write the keyfile for new files + private $isLocalTmpFile = false; // do we operate on a local tmp file + private $localTmpFile; // path of local tmp file + /** * @var \OC\Files\View */ @@ -91,13 +94,18 @@ class Stream { $this->rootView = new \OC_FilesystemView('/'); } - $this->session = new \OCA\Encryption\Session($this->rootView); $this->privateKey = $this->session->getPrivateKey(); - // rawPath is relative to the data directory - $this->rawPath = \OC\Files\Filesystem::normalizePath(str_replace('crypt://', '', $path)); + $normalizedPath = \OC\Files\Filesystem::normalizePath(str_replace('crypt://', '', $path)); + if ($originalFile = Helper::getPathFromTmpFile($normalizedPath)) { + $this->rawPath = $originalFile; + $this->isLocalTmpFile = true; + $this->localTmpFile = $normalizedPath; + } else { + $this->rawPath = $normalizedPath; + } $this->userId = Helper::getUser($this->rawPath); @@ -141,10 +149,14 @@ class Stream { \OCA\Encryption\Helper::redirectToErrorPage($this->session); } - $this->size = $this->rootView->filesize($this->rawPath, $mode); + $this->size = $this->rootView->filesize($this->rawPath); } - $this->handle = $this->rootView->fopen($this->rawPath, $mode); + if ($this->isLocalTmpFile) { + $this->handle = fopen($this->localTmpFile, $mode); + } else { + $this->handle = $this->rootView->fopen($this->rawPath, $mode); + } \OC_FileProxy::$enabled = $proxyStatus; @@ -167,14 +179,25 @@ class Stream { } /** + * @brief Returns the current position of the file pointer + * @return int position of the file pointer + */ + public function stream_tell() { + return ftell($this->handle); + } + + /** * @param $offset * @param int $whence + * @return bool true if fseek was successful, otherwise false */ public function stream_seek($offset, $whence = SEEK_SET) { $this->flush(); - fseek($this->handle, $offset, $whence); + // this wrapper needs to return "true" for success. + // the fseek call itself returns 0 on succeess + return !fseek($this->handle, $offset, $whence); } @@ -480,7 +503,7 @@ class Stream { if ($this->privateKey === false) { // cleanup - if ($this->meta['mode'] !== 'r' && $this->meta['mode'] !== 'rb') { + if ($this->meta['mode'] !== 'r' && $this->meta['mode'] !== 'rb' && !$this->isLocalTmpFile) { // Disable encryption proxy to prevent recursive calls $proxyStatus = \OC_FileProxy::$enabled; @@ -501,6 +524,7 @@ class Stream { if ( $this->meta['mode'] !== 'r' && $this->meta['mode'] !== 'rb' && + $this->isLocalTmpFile === false && $this->size > 0 && $this->unencryptedSize > 0 ) { diff --git a/apps/files_encryption/lib/util.php b/apps/files_encryption/lib/util.php index 4a04220a1eb..d7f50f508d1 100644 --- a/apps/files_encryption/lib/util.php +++ b/apps/files_encryption/lib/util.php @@ -477,8 +477,22 @@ class Util { // we only need 24 byte from the last chunk $data = ''; $handle = $this->view->fopen($path, 'r'); - if (is_resource($handle) && !fseek($handle, -24, SEEK_END)) { - $data = fgets($handle); + if (is_resource($handle)) { + // suppress fseek warining, we handle the case that fseek doesn't + // work in the else branch + if (@fseek($handle, -24, SEEK_END) === 0) { + $data = fgets($handle); + } else { + // if fseek failed on the storage we create a local copy from the file + // and read this one + fclose($handle); + $localFile = $this->view->getLocalFile($path); + $handle = fopen($localFile, 'r'); + if (is_resource($handle) && fseek($handle, -24, SEEK_END) === 0) { + $data = fgets($handle); + } + } + fclose($handle); } // re-enable proxy @@ -530,7 +544,20 @@ class Util { $lastChunckPos = ($lastChunkNr * 8192); // seek to end - fseek($stream, $lastChunckPos); + if (@fseek($stream, $lastChunckPos) === -1) { + // storage doesn't support fseek, we need a local copy + fclose($stream); + $localFile = $this->view->getLocalFile($path); + Helper::addTmpFileToMapper($localFile, $path); + $stream = fopen('crypt://' . $localFile, "r"); + if (fseek($stream, $lastChunckPos) === -1) { + // if fseek also fails on the local storage, than + // there is nothing we can do + fclose($stream); + \OCP\Util::writeLog('Encryption library', 'couldn\'t determine size of "' . $path, \OCP\Util::ERROR); + return $result; + } + } // get the content of the last chunk $lastChunkContent = fread($stream, $lastChunkSize); diff --git a/apps/files_encryption/tests/stream.php b/apps/files_encryption/tests/stream.php index 5097f409a60..d26cd75648b 100644 --- a/apps/files_encryption/tests/stream.php +++ b/apps/files_encryption/tests/stream.php @@ -180,4 +180,43 @@ class Test_Encryption_Stream extends \PHPUnit_Framework_TestCase { // tear down $view->unlink($filename); } + + /** + * @medium + * @brief test if stream wrapper can read files outside from the data folder + */ + function testStreamFromLocalFile() { + + $filename = '/' . $this->userId . '/files/' . 'tmp-' . time().'.txt'; + + $tmpFilename = "/tmp/" . time() . ".txt"; + + // write an encrypted file + $cryptedFile = $this->view->file_put_contents($filename, $this->dataShort); + + // Test that data was successfully written + $this->assertTrue(is_int($cryptedFile)); + + // create a copy outside of the data folder in /tmp + $proxyStatus = \OC_FileProxy::$enabled; + \OC_FileProxy::$enabled = false; + $encryptedContent = $this->view->file_get_contents($filename); + \OC_FileProxy::$enabled = $proxyStatus; + + file_put_contents($tmpFilename, $encryptedContent); + + \OCA\Encryption\Helper::addTmpFileToMapper($tmpFilename, $filename); + + // try to read the file from /tmp + $handle = fopen("crypt://".$tmpFilename, "r"); + $contentFromTmpFile = stream_get_contents($handle); + + // check if it was successful + $this->assertEquals($this->dataShort, $contentFromTmpFile); + + // clean up + unlink($tmpFilename); + $this->view->unlink($filename); + + } } diff --git a/apps/files_encryption/tests/util.php b/apps/files_encryption/tests/util.php index af422a3c52b..863ea73b22c 100755 --- a/apps/files_encryption/tests/util.php +++ b/apps/files_encryption/tests/util.php @@ -136,6 +136,41 @@ class Test_Encryption_Util extends \PHPUnit_Framework_TestCase { /** * @medium + * @brief test detection of encrypted files + */ + function testIsEncryptedPath() { + + $util = new Encryption\Util($this->view, $this->userId); + + self::loginHelper($this->userId); + + $unencryptedFile = '/tmpUnencrypted-' . time() . '.txt'; + $encryptedFile = '/tmpEncrypted-' . time() . '.txt'; + + // Disable encryption proxy to write a unencrypted file + $proxyStatus = \OC_FileProxy::$enabled; + \OC_FileProxy::$enabled = false; + + $this->view->file_put_contents($this->userId . '/files/' . $unencryptedFile, $this->dataShort); + + // Re-enable proxy - our work is done + \OC_FileProxy::$enabled = $proxyStatus; + + // write a encrypted file + $this->view->file_put_contents($this->userId . '/files/' . $encryptedFile, $this->dataShort); + + // test if both files are detected correctly + $this->assertFalse($util->isEncryptedPath($this->userId . '/files/' . $unencryptedFile)); + $this->assertTrue($util->isEncryptedPath($this->userId . '/files/' . $encryptedFile)); + + // cleanup + $this->view->unlink($this->userId . '/files/' . $unencryptedFile, $this->dataShort); + $this->view->unlink($this->userId . '/files/' . $encryptedFile, $this->dataShort); + + } + + /** + * @medium * @brief test setup of encryption directories */ function testSetupServerSide() { |