]> source.dussan.org Git - nextcloud-server.git/commitdiff
[testing] Allow to lock a file without the lock being killed at the end of the reques...
authorJoas Schilling <nickvergessen@gmx.de>
Mon, 30 May 2016 09:16:14 +0000 (11:16 +0200)
committerThomas Müller <DeepDiver1975@users.noreply.github.com>
Mon, 30 May 2016 09:16:14 +0000 (11:16 +0200)
* Allow to lock a file without the lock being killed at the end of the request

* Make DB locking detectable

.gitignore
apps/testing/appinfo/routes.php
apps/testing/locking/fakedblockingprovider.php [new file with mode: 0644]
apps/testing/locking/provisioning.php [new file with mode: 0644]

index 73f57989a8da17779ff9065b123df5bad1e293ce..e7ced4b393f231514640b030cf52478b3e923b8f 100644 (file)
@@ -22,6 +22,7 @@
 !/apps/user_ldap
 !/apps/provisioning_api
 !/apps/systemtags
+!/apps/testing
 !/apps/updatenotification
 /apps/files_external/3rdparty/irodsphp/PHPUnitTest
 /apps/files_external/3rdparty/irodsphp/web
index 6684d486360df9e57e46a6abd4f7ab7a13b7abcf..619daf40da9d60b3f924569243789fbbf3da0b05 100644 (file)
@@ -22,6 +22,7 @@
 namespace OCA\Testing\AppInfo;
 
 use OCA\Testing\Config;
+use OCA\Testing\Locking\Provisioning;
 use OCP\API;
 
 $config = new Config(
@@ -44,3 +45,17 @@ API::register(
        'testing',
        API::ADMIN_AUTH
 );
+
+$locking = new Provisioning(
+       \OC::$server->getLockingProvider(),
+       \OC::$server->getDatabaseConnection(),
+       \OC::$server->getConfig(),
+       \OC::$server->getRequest()
+);
+API::register('get', '/apps/testing/api/v1/lockprovisioning', [$locking, 'isLockingEnabled'], 'files_lockprovisioning', API::ADMIN_AUTH);
+API::register('get', '/apps/testing/api/v1/lockprovisioning/{type}/{user}', [$locking, 'isLocked'], 'files_lockprovisioning', API::ADMIN_AUTH);
+API::register('post', '/apps/testing/api/v1/lockprovisioning/{type}/{user}', [$locking, 'acquireLock'], 'files_lockprovisioning', API::ADMIN_AUTH);
+API::register('put', '/apps/testing/api/v1/lockprovisioning/{type}/{user}', [$locking, 'changeLock'], 'files_lockprovisioning', API::ADMIN_AUTH);
+API::register('delete', '/apps/testing/api/v1/lockprovisioning/{type}/{user}', [$locking, 'releaseLock'], 'files_lockprovisioning', API::ADMIN_AUTH);
+API::register('delete', '/apps/testing/api/v1/lockprovisioning/{type}', [$locking, 'releaseAll'], 'files_lockprovisioning', API::ADMIN_AUTH);
+API::register('delete', '/apps/testing/api/v1/lockprovisioning', [$locking, 'releaseAll'], 'files_lockprovisioning', API::ADMIN_AUTH);
diff --git a/apps/testing/locking/fakedblockingprovider.php b/apps/testing/locking/fakedblockingprovider.php
new file mode 100644 (file)
index 0000000..fd090e0
--- /dev/null
@@ -0,0 +1,69 @@
+<?php
+/**
+ * @author Joas Schilling <nickvergessen@owncloud.com>
+ *
+ * @copyright Copyright (c) 2016, 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 OCA\Testing\Locking;
+
+use OCP\AppFramework\Utility\ITimeFactory;
+use OCP\IDBConnection;
+use OCP\ILogger;
+
+class FakeDBLockingProvider extends \OC\Lock\DBLockingProvider {
+       // Lock for 10 hours just to be sure
+       const TTL = 36000;
+
+       /**
+        * Need a new child, because parent::connection is private instead of protected...
+        * @var IDBConnection
+        */
+       protected $db;
+
+       /**
+        * @param \OCP\IDBConnection $connection
+        * @param \OCP\ILogger $logger
+        * @param \OCP\AppFramework\Utility\ITimeFactory $timeFactory
+        */
+       public function __construct(IDBConnection $connection, ILogger $logger, ITimeFactory $timeFactory) {
+               parent::__construct($connection, $logger, $timeFactory);
+               $this->db = $connection;
+       }
+
+
+       /**
+        * @param string $path
+        * @param int $type self::LOCK_SHARED or self::LOCK_EXCLUSIVE
+        */
+       public function releaseLock($path, $type) {
+               // we DONT keep shared locks till the end of the request
+               if ($type === self::LOCK_SHARED) {
+                       $this->db->executeUpdate(
+                               'UPDATE `*PREFIX*file_locks` SET `lock` = 0 WHERE `key` = ? AND `lock` = 1',
+                               [$path]
+                       );
+               }
+
+               parent::releaseLock($path, $type);
+       }
+
+       public function __destruct() {
+               // Prevent cleaning up at the end of the live time.
+               // parent::__destruct();
+       }
+}
diff --git a/apps/testing/locking/provisioning.php b/apps/testing/locking/provisioning.php
new file mode 100644 (file)
index 0000000..55a1be0
--- /dev/null
@@ -0,0 +1,227 @@
+<?php
+/**
+ * @author Joas Schilling <nickvergessen@owncloud.com>
+ *
+ * @copyright Copyright (c) 2016, 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 OCA\Testing\Locking;
+
+use OC\Lock\DBLockingProvider;
+use OC\Lock\MemcacheLockingProvider;
+use OC\User\NoUserException;
+use OCP\AppFramework\Http;
+use OCP\Files\NotFoundException;
+use OCP\IConfig;
+use OCP\IDBConnection;
+use OCP\IRequest;
+use OCP\Lock\ILockingProvider;
+use OCP\Lock\LockedException;
+
+class Provisioning {
+
+       /** @var ILockingProvider */
+       protected $lockingProvider;
+
+       /** @var IDBConnection */
+       protected $connection;
+
+       /** @var IConfig */
+       protected $config;
+
+       /** @var IRequest */
+       protected $request;
+
+       /**
+        * @param ILockingProvider $lockingProvider
+        * @param IDBConnection $connection
+        * @param IConfig $config
+        * @param IRequest $request
+        */
+       public function __construct(ILockingProvider $lockingProvider, IDBConnection $connection, IConfig $config, IRequest $request) {
+               $this->lockingProvider = $lockingProvider;
+               $this->connection = $connection;
+               $this->config = $config;
+               $this->request = $request;
+       }
+
+       /**
+        * @return ILockingProvider
+        */
+       protected function getLockingProvider() {
+               if ($this->lockingProvider instanceof DBLockingProvider) {
+                       return \OC::$server->query('OCA\Testing\Locking\FakeDBLockingProvider');
+               } else {
+                       throw new \RuntimeException('Lock provisioning is only possible using the DBLockingProvider');
+               }
+       }
+
+       /**
+        * @param array $parameters
+        * @return int
+        */
+       protected function getType($parameters) {
+               return isset($parameters['type']) ? (int) $parameters['type'] : 0;
+       }
+
+       /**
+        * @param array $parameters
+        * @return int
+        */
+       protected function getPath($parameters) {
+               $node = \OC::$server->getRootFolder()
+                       ->getUserFolder($parameters['user'])
+                       ->get($this->request->getParam('path'));
+               return 'files/' . md5($node->getStorage()->getId() . '::' . trim($node->getInternalPath(), '/'));
+       }
+
+       /**
+        * @return \OC_OCS_Result
+        */
+       public function isLockingEnabled() {
+               try {
+                       $this->getLockingProvider();
+                       return new \OC_OCS_Result(null, 100);
+               } catch (\RuntimeException $e) {
+                       return new \OC_OCS_Result(null, Http::STATUS_NOT_IMPLEMENTED, $e->getMessage());
+               }
+       }
+
+       /**
+        * @param array $parameters
+        * @return \OC_OCS_Result
+        */
+       public function acquireLock(array $parameters) {
+               try {
+                       $path = $this->getPath($parameters);
+               } catch (NoUserException $e) {
+                       return new \OC_OCS_Result(null, Http::STATUS_NOT_FOUND, 'User not found');
+               } catch (NotFoundException $e) {
+                       return new \OC_OCS_Result(null, Http::STATUS_NOT_FOUND, 'Path not found');
+               }
+               $type = $this->getType($parameters);
+
+               $lockingProvider = $this->getLockingProvider();
+
+               try {
+                       $lockingProvider->acquireLock($path, $type);
+                       $this->config->setAppValue('testing', 'locking_' . $path, $type);
+                       return new \OC_OCS_Result(null, 100);
+               } catch (LockedException $e) {
+                       return new \OC_OCS_Result(null, Http::STATUS_LOCKED);
+               }
+       }
+
+       /**
+        * @param array $parameters
+        * @return \OC_OCS_Result
+        */
+       public function changeLock(array $parameters) {
+               try {
+                       $path = $this->getPath($parameters);
+               } catch (NoUserException $e) {
+                       return new \OC_OCS_Result(null, Http::STATUS_NOT_FOUND, 'User not found');
+               } catch (NotFoundException $e) {
+                       return new \OC_OCS_Result(null, Http::STATUS_NOT_FOUND, 'Path not found');
+               }
+               $type = $this->getType($parameters);
+
+               $lockingProvider = $this->getLockingProvider();
+
+               try {
+                       $lockingProvider->changeLock($path, $type);
+                       $this->config->setAppValue('testing', 'locking_' . $path, $type);
+                       return new \OC_OCS_Result(null, 100);
+               } catch (LockedException $e) {
+                       return new \OC_OCS_Result(null, Http::STATUS_LOCKED);
+               }
+       }
+
+       /**
+        * @param array $parameters
+        * @return \OC_OCS_Result
+        */
+       public function releaseLock(array $parameters) {
+               try {
+                       $path = $this->getPath($parameters);
+               } catch (NoUserException $e) {
+                       return new \OC_OCS_Result(null, Http::STATUS_NOT_FOUND, 'User not found');
+               } catch (NotFoundException $e) {
+                       return new \OC_OCS_Result(null, Http::STATUS_NOT_FOUND, 'Path not found');
+               }
+               $type = $this->getType($parameters);
+
+               $lockingProvider = $this->getLockingProvider();
+
+               try {
+                       $lockingProvider->releaseLock($path, $type);
+                       $this->config->deleteAppValue('testing', 'locking_' . $path);
+                       return new \OC_OCS_Result(null, 100);
+               } catch (LockedException $e) {
+                       return new \OC_OCS_Result(null, Http::STATUS_LOCKED);
+               }
+       }
+
+       /**
+        * @param array $parameters
+        * @return \OC_OCS_Result
+        */
+       public function isLocked(array $parameters) {
+               try {
+                       $path = $this->getPath($parameters);
+               } catch (NoUserException $e) {
+                       return new \OC_OCS_Result(null, Http::STATUS_NOT_FOUND, 'User not found');
+               } catch (NotFoundException $e) {
+                       return new \OC_OCS_Result(null, Http::STATUS_NOT_FOUND, 'Path not found');
+               }
+               $type = $this->getType($parameters);
+
+               $lockingProvider = $this->getLockingProvider();
+
+               if ($lockingProvider->isLocked($path, $type)) {
+                       return new \OC_OCS_Result(null, 100);
+               }
+
+               return new \OC_OCS_Result(null, Http::STATUS_LOCKED);
+       }
+
+       /**
+        * @param array $parameters
+        * @return \OC_OCS_Result
+        */
+       public function releaseAll(array $parameters) {
+               $type = $this->getType($parameters);
+
+               $lockingProvider = $this->getLockingProvider();
+
+               foreach ($this->config->getAppKeys('testing') as $lock) {
+                       if (strpos($lock, 'locking_') === 0) {
+                               $path = substr($lock, strlen('locking_'));
+
+                               if ($type === ILockingProvider::LOCK_EXCLUSIVE && $this->config->getAppValue('testing', $lock) == ILockingProvider::LOCK_EXCLUSIVE) {
+                                       $lockingProvider->releaseLock($path, $this->config->getAppValue('testing', $lock));
+                               } else if ($type === ILockingProvider::LOCK_SHARED && $this->config->getAppValue('testing', $lock) == ILockingProvider::LOCK_SHARED) {
+                                       $lockingProvider->releaseLock($path, $this->config->getAppValue('testing', $lock));
+                               } else {
+                                       $lockingProvider->releaseLock($path, $this->config->getAppValue('testing', $lock));
+                               }
+                       }
+               }
+
+               return new \OC_OCS_Result(null, 100);
+       }
+}