diff options
author | Vincent Petry <pvince81@owncloud.com> | 2015-04-08 18:56:20 +0300 |
---|---|---|
committer | Vincent Petry <pvince81@owncloud.com> | 2015-06-17 17:07:01 +0300 |
commit | 2972585ba14ca243f18d02bd9f23ebbcf86af496 (patch) | |
tree | 5a698abad38e3bf85e59e90f3630101287a5b9e1 /apps | |
parent | 6ea7e89649896763b0f8bb927cadee3f38eb3a36 (diff) |
Fix version rollback to keep fileid
Diffstat (limited to 'apps')
-rw-r--r-- | apps/files_versions/lib/storage.php | 42 | ||||
-rw-r--r-- | apps/files_versions/tests/versions.php | 96 |
2 files changed, 134 insertions, 4 deletions
diff --git a/apps/files_versions/lib/storage.php b/apps/files_versions/lib/storage.php index 60a4c463fd5..11e693b212f 100644 --- a/apps/files_versions/lib/storage.php +++ b/apps/files_versions/lib/storage.php @@ -243,11 +243,16 @@ class Storage { } /** - * rollback to an old version of a file. + * Rollback to an old version of a file. + * + * @param string $file file name + * @param int $revision revision timestamp */ public static function rollback($file, $revision) { if(\OCP\Config::getSystemValue('files_versions', Storage::DEFAULTENABLED)=='true') { + // add expected leading slash + $file = '/' . ltrim($file, '/'); list($uid, $filename) = self::getUidAndFilename($file); $users_view = new \OC\Files\View('/'.$uid); $files_view = new \OC\Files\View('/'.\OCP\User::getUser().'/files'); @@ -270,12 +275,11 @@ class Storage { } // rollback - if( @$users_view->rename('files_versions'.$filename.'.v'.$revision, 'files'.$filename) ) { + if (self::copyFileContents($users_view, 'files_versions' . $filename . '.v' . $revision, 'files' . $filename)) { $files_view->touch($file, $revision); Storage::expire($file); return true; - - }else if ( $versionCreated ) { + } else if ($versionCreated) { self::deleteVersion($users_view, $version); } } @@ -283,6 +287,36 @@ class Storage { } + /** + * Stream copy file contents from $path1 to $path2 + * + * @param \OC\Files\View $view view to use for copying + * @param string $path1 source file to copy + * @param string $path2 target file + * + * @return bool true for success, false otherwise + */ + private static function copyFileContents($view, $path1, $path2) { + list($storage1, $internalPath1) = $view->resolvePath($path1); + list($storage2, $internalPath2) = $view->resolvePath($path2); + + if ($storage1 === $storage2) { + return $storage1->rename($internalPath1, $internalPath2); + } + $source = $storage1->fopen($internalPath1, 'r'); + $target = $storage2->fopen($internalPath2, 'w'); + // FIXME: might need to use part file to avoid concurrent writes + // (this would be an issue anyway when renaming/restoring cross-storage) + list(, $result) = \OC_Helper::streamCopy($source, $target); + fclose($source); + fclose($target); + + if ($result !== false) { + $storage1->unlink($internalPath1); + } + + return ($result !== false); + } /** * get a list of all available versions of a file in descending chronological order diff --git a/apps/files_versions/tests/versions.php b/apps/files_versions/tests/versions.php index cf0ffb320e2..f0a408bae6a 100644 --- a/apps/files_versions/tests/versions.php +++ b/apps/files_versions/tests/versions.php @@ -22,6 +22,8 @@ require_once __DIR__ . '/../appinfo/app.php'; +use OC\Files\Storage\Temporary; + /** * Class Test_Files_versions * this class provide basic files versions test @@ -406,6 +408,100 @@ class Test_Files_Versioning extends \Test\TestCase { $this->rootView->deleteAll(self::USERS_VERSIONS_ROOT . '/subfolder'); } + public function testRestoreSameStorage() { + \OC\Files\Filesystem::mkdir('sub'); + $this->doTestRestore(); + } + + public function testRestoreCrossStorage() { + $storage2 = new Temporary(array()); + \OC\Files\Filesystem::mount($storage2, array(), self::TEST_VERSIONS_USER . '/files/sub'); + + $this->doTestRestore(); + } + + private function doTestRestore() { + $filePath = self::TEST_VERSIONS_USER . '/files/sub/test.txt'; + $this->rootView->file_put_contents($filePath, 'test file'); + + $t0 = $this->rootView->filemtime($filePath); + + // not exactly the same timestamp as the file + $t1 = time() - 60; + // second version is two weeks older + $t2 = $t1 - 60 * 60 * 24 * 14; + + // create some versions + $v1 = self::USERS_VERSIONS_ROOT . '/sub/test.txt.v' . $t1; + $v2 = self::USERS_VERSIONS_ROOT . '/sub/test.txt.v' . $t2; + + $this->rootView->mkdir(self::USERS_VERSIONS_ROOT . '/sub'); + $this->rootView->file_put_contents($v1, 'version1'); + $this->rootView->file_put_contents($v2, 'version2'); + + $oldVersions = \OCA\Files_Versions\Storage::getVersions( + self::TEST_VERSIONS_USER, '/sub/test.txt' + ); + + $this->assertCount(2, $oldVersions); + + $this->assertEquals('test file', $this->rootView->file_get_contents($filePath)); + $info1 = $this->rootView->getFileInfo($filePath); + + \OCA\Files_Versions\Storage::rollback('sub/test.txt', $t2); + + $this->assertEquals('version2', $this->rootView->file_get_contents($filePath)); + $info2 = $this->rootView->getFileInfo($filePath); + + $this->assertNotEquals( + $info2['etag'], + $info1['etag'], + 'Etag must change after rolling back version' + ); + $this->assertEquals( + $info2['fileid'], + $info1['fileid'], + 'File id must not change after rolling back version' + ); + $this->assertEquals( + $info2['mtime'], + $t2, + 'Restored file has mtime from version' + ); + + $newVersions = \OCA\Files_Versions\Storage::getVersions( + self::TEST_VERSIONS_USER, '/sub/test.txt' + ); + + $this->assertTrue( + $this->rootView->file_exists(self::USERS_VERSIONS_ROOT . '/sub/test.txt.v' . $t0), + 'A version file was created for the file before restoration' + ); + $this->assertTrue( + $this->rootView->file_exists($v1), + 'Untouched version file is still there' + ); + $this->assertFalse( + $this->rootView->file_exists($v2), + 'Restored version file gone from files_version folder' + ); + + $this->assertCount(2, $newVersions, 'Additional version created'); + + $this->assertTrue( + isset($newVersions[$t0 . '#' . 'test.txt']), + 'A version was created for the file before restoration' + ); + $this->assertTrue( + isset($newVersions[$t1 . '#' . 'test.txt']), + 'Untouched version is still there' + ); + $this->assertFalse( + isset($newVersions[$t2 . '#' . 'test.txt']), + 'Restored version is not in the list any more' + ); + } + /** * @param string $user * @param bool $create |