summaryrefslogtreecommitdiffstats
path: root/tests
diff options
context:
space:
mode:
authorVincent Petry <pvince81@owncloud.com>2015-06-12 18:50:49 +0200
committerVincent Petry <pvince81@owncloud.com>2015-06-12 18:52:18 +0200
commit4497aa4c68051ff75d1587dc1b01676926dfb466 (patch)
treee298734f2f3ed0abcc19e45db284f6c1e2ca427a /tests
parentbb0ea6336df84d391c0d972a56f27077f632948c (diff)
downloadnextcloud-server-4497aa4c68051ff75d1587dc1b01676926dfb466.tar.gz
nextcloud-server-4497aa4c68051ff75d1587dc1b01676926dfb466.zip
Webdav PUT small file lock must be shared during hooks
Fixed code path for Webdav PUT of small files to use shared locks during hook execution, and exclusive during the file operation This makes it possible for versions to be copied by accessing the file in a post_write hook.
Diffstat (limited to 'tests')
-rw-r--r--tests/lib/connector/sabre/file.php80
-rw-r--r--tests/lib/testcase.php35
2 files changed, 115 insertions, 0 deletions
diff --git a/tests/lib/connector/sabre/file.php b/tests/lib/connector/sabre/file.php
index 6602a2df24f..834698d90cb 100644
--- a/tests/lib/connector/sabre/file.php
+++ b/tests/lib/connector/sabre/file.php
@@ -404,4 +404,84 @@ class File extends \Test\TestCase {
$params[Filesystem::signal_param_path]
);
}
+
+ /**
+ * Test whether locks are set before and after the operation
+ */
+ public function testPutLocking() {
+ $view = new \OC\Files\View('/' . $this->user . '/files/');
+
+ $path = 'test-locking.txt';
+ $info = new \OC\Files\FileInfo(
+ '/' . $this->user . '/files/' . $path,
+ null,
+ null,
+ ['permissions' => \OCP\Constants::PERMISSION_ALL],
+ null
+ );
+
+ $file = new \OC\Connector\Sabre\File($view, $info);
+
+ $this->assertFalse(
+ $this->isFileLocked($view, $path, \OCP\Lock\ILockingProvider::LOCK_SHARED),
+ 'File unlocked before put'
+ );
+ $this->assertFalse(
+ $this->isFileLocked($view, $path, \OCP\Lock\ILockingProvider::LOCK_EXCLUSIVE),
+ 'File unlocked before put'
+ );
+
+ $wasLockedPre = false;
+ $wasLockedPost = false;
+ $eventHandler = $this->getMockBuilder('\stdclass')
+ ->setMethods(['writeCallback', 'postWriteCallback'])
+ ->getMock();
+
+ // both pre and post hooks might need access to the file,
+ // so only shared lock is acceptable
+ $eventHandler->expects($this->once())
+ ->method('writeCallback')
+ ->will($this->returnCallback(
+ function() use ($view, $path, &$wasLockedPre){
+ $wasLockedPre = $this->isFileLocked($view, $path, \OCP\Lock\ILockingProvider::LOCK_SHARED);
+ $wasLockedPre = $wasLockedPre && !$this->isFileLocked($view, $path, \OCP\Lock\ILockingProvider::LOCK_EXCLUSIVE);
+ }
+ ));
+ $eventHandler->expects($this->once())
+ ->method('postWriteCallback')
+ ->will($this->returnCallback(
+ function() use ($view, $path, &$wasLockedPost){
+ $wasLockedPost = $this->isFileLocked($view, $path, \OCP\Lock\ILockingProvider::LOCK_SHARED);
+ $wasLockedPost = $wasLockedPost && !$this->isFileLocked($view, $path, \OCP\Lock\ILockingProvider::LOCK_EXCLUSIVE);
+ }
+ ));
+
+ \OCP\Util::connectHook(
+ Filesystem::CLASSNAME,
+ Filesystem::signal_write,
+ $eventHandler,
+ 'writeCallback'
+ );
+ \OCP\Util::connectHook(
+ Filesystem::CLASSNAME,
+ Filesystem::signal_post_write,
+ $eventHandler,
+ 'postWriteCallback'
+ );
+
+ $this->assertNotEmpty($file->put($this->getStream('test data')));
+
+ $this->assertTrue($wasLockedPre, 'File was locked during pre-hooks');
+ $this->assertTrue($wasLockedPost, 'File was locked during post-hooks');
+
+ $this->assertFalse(
+ $this->isFileLocked($view, $path, \OCP\Lock\ILockingProvider::LOCK_SHARED),
+ 'File unlocked after put'
+ );
+ $this->assertFalse(
+ $this->isFileLocked($view, $path, \OCP\Lock\ILockingProvider::LOCK_EXCLUSIVE),
+ 'File unlocked after put'
+ );
+ }
+
}
diff --git a/tests/lib/testcase.php b/tests/lib/testcase.php
index 8551edab71f..d385a903d1e 100644
--- a/tests/lib/testcase.php
+++ b/tests/lib/testcase.php
@@ -242,4 +242,39 @@ abstract class TestCase extends \PHPUnit_Framework_TestCase {
\OC_Util::setupFS($user);
}
}
+
+ /**
+ * Check if the given path is locked with a given type
+ *
+ * @param \OC\Files\View $view view
+ * @param string $path path to check
+ * @param int $type lock type
+ *
+ * @return boolean true if the file is locked with the
+ * given type, false otherwise
+ */
+ protected function isFileLocked($view, $path, $type) {
+ // Note: this seems convoluted but is necessary because
+ // the format of the lock key depends on the storage implementation
+ // (in our case mostly md5)
+
+ if ($type === \OCP\Lock\ILockingProvider::LOCK_SHARED) {
+ // to check if the file has a shared lock, try acquiring an exclusive lock
+ $checkType = \OCP\Lock\ILockingProvider::LOCK_EXCLUSIVE;
+ } else {
+ // a shared lock cannot be set if exclusive lock is in place
+ $checkType = \OCP\Lock\ILockingProvider::LOCK_SHARED;
+ }
+ try {
+ $view->lockFile($path, $checkType);
+ // no exception, which means the lock of $type is not set
+ // clean up
+ $view->unlockFile($path, $checkType);
+ return false;
+ } catch (\OCP\Lock\LockedException $e) {
+ // we could not acquire the counter-lock, which means
+ // the lock of $type was in place
+ return true;
+ }
+ }
}