diff options
author | ringmaster <epithet@gmail.com> | 2014-05-21 01:44:57 +0400 |
---|---|---|
committer | Thomas Müller <thomas.mueller@tmit.eu> | 2014-06-04 09:55:44 +0400 |
commit | 5365ae416ec1a2c1dd868707cbb01f50bbe9027d (patch) | |
tree | 09b8a89846594eedbb14adb4f5ae31204205c912 /lib | |
parent | 3a1994d00149bea6d554c96a3bdee3126cfb17b8 (diff) |
flock changes. Work in progress.
Diffstat (limited to 'lib')
-rw-r--r-- | lib/private/files/storage/local.php | 14 | ||||
-rw-r--r-- | lib/private/files/storage/wrapper/lockingwrapper.php | 139 | ||||
-rwxr-xr-x | lib/private/util.php | 10 | ||||
-rw-r--r-- | lib/public/files/lock.php | 48 | ||||
-rw-r--r-- | lib/public/files/locknotacquiredexception.php | 47 | ||||
-rw-r--r-- | lib/public/files/storage.php | 1 |
6 files changed, 259 insertions, 0 deletions
diff --git a/lib/private/files/storage/local.php b/lib/private/files/storage/local.php index e33747bbd52..565f9202d5c 100644 --- a/lib/private/files/storage/local.php +++ b/lib/private/files/storage/local.php @@ -279,5 +279,19 @@ if (\OC_Util::runningOnWindows()) { public function isLocal() { return true; } + + /** + * Acquire a lock on a file + */ + public function getLock($path, $lockType) { + return true; + } + + /** + * Release an existing lock + */ + public function releaseLock($path) { + return true; + } } } diff --git a/lib/private/files/storage/wrapper/lockingwrapper.php b/lib/private/files/storage/wrapper/lockingwrapper.php new file mode 100644 index 00000000000..04a325940bf --- /dev/null +++ b/lib/private/files/storage/wrapper/lockingwrapper.php @@ -0,0 +1,139 @@ +<?php + +/** + * Copyright (c) 2013 ownCloud, Inc. + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ + +namespace OC\Files\Storage\Wrapper; + +use OC\Files\Filesystem; +use OCP\Files\LockNotAcquiredException; +use OCP\Files\Lock; + +/** + * Class LockingWrapper + * A Storage Wrapper used to lock files at the system level + * @package OC\Files\Storage\Wrapper + * + * Notes: Does the $locks array need to be global to all LockingWrapper instances, such as in the case of two paths + * that point to the same physical file? Otherwise accessing the file from a different path the second time would show + * the file as locked, even though this process is the one locking it. + */ +class LockingWrapper extends Wrapper { + + /** @var array $locks Holds an array of lock instances indexed by path for this storage */ + protected $locks = array(); + + /** + * Acquire a lock on a file + * @param string $path Path to file, relative to this storage + * @param integer $lockType A Lock class constant, Lock::READ/Lock::WRITE + * @return bool|\OCP\Files\Lock Lock instance on success, false on failure + */ + protected function getLock($path, $lockType){ + $path = Filesystem::normalizePath($path); + if(!isset($this->locks[$path])) { + $this->locks[$path] = new Lock($path); + } + $this->locks[$path]->addLock($lockType); + return $this->locks[$path]; + } + + /** + * Release an existing lock + * @param string $path Path to file, relative to this storage + * @return bool true on success, false on failure + */ + protected function releaseLock($path, $lockType, $releaseAll = false){ + $path = Filesystem::normalizePath($path); + if(isset($this->locks[$path])) { + if($releaseAll) { + return $this->locks[$path]->releaseAll(); + } + else { + return $this->locks[$path]->release($lockType); + } + } + return true; + } + + /** + * see http://php.net/manual/en/function.file_get_contents.php + * @param string $path + * @return string + * @throws \Exception + */ + public function file_get_contents($path) { + try { + if (!$this->getLock($path, Lock::READ)) { + throw new LockNotAcquiredException($path, Lock::READ); + } + $result = $this->storage->file_get_contents($path); + } + catch(\Exception $originalException) { + // Need to release the lock before more operations happen in upstream exception handlers + $this->releaseLock($path, Lock::READ); + throw $originalException; + } + return $result; + } + + public function file_put_contents($path, $data) { + try { + if (!$this->getLock($path, Lock::WRITE)) { + throw new LockNotAcquiredException($path, Lock::WRITE); + } + $result = $this->storage->file_put_contents($path, $data); + } + catch(\Exception $originalException) { + // Release lock, throw original exception + $this->releaseLock($path, Lock::WRITE); + throw $originalException; + } + return $result; + } + + + public function copy($path1, $path2) { + try { + if (!$this->getLock($path1, Lock::READ)) { + throw new LockNotAcquiredException($path1, Lock::READ); + } + if (!$this->getLock($path2, Lock::WRITE)) { + throw new LockNotAcquiredException($path2, Lock::WRITE); + } + $result = $this->storage->copy($path1, $path2); + } + catch(\Exception $originalException) { + // Release locks, throw original exception + $this->releaseLock($path1, Lock::READ); + $this->releaseLock($path2, Lock::WRITE); + throw $originalException; + } + return $result; + } + + public function rename($path1, $path2) { + try { + if (!$this->getLock($path1, Lock::READ)) { + throw new LockNotAcquiredException($path1, Lock::READ); + } + if (!$this->getLock($path2, Lock::WRITE)) { + throw new LockNotAcquiredException($path2, Lock::WRITE); + } + $result = $this->storage->rename($path1, $path2); + } + catch(\Exception $originalException) { + // Release locks, throw original exception + $this->releaseLock($path1, Lock::READ); + $this->releaseLock($path2, Lock::WRITE); + throw $originalException; + } + return $result; + } + + +}
\ No newline at end of file diff --git a/lib/private/util.php b/lib/private/util.php index 306e37b9478..8cc48c03462 100755 --- a/lib/private/util.php +++ b/lib/private/util.php @@ -71,6 +71,16 @@ class OC_Util { return $storage; }); + // Set up flock + \OC\Files\Filesystem::addStorageWrapper(function($mountPoint, /** @var \OC\Files\Storage\Storage|null $storage */ $storage){ + // lock files on all local storage + if ($storage instanceof \OC\Files\Storage\Storage && $storage->isLocal()) { + return new \OC\Files\Storage\Wrapper\LockingWrapper(array('storage' => $storage)); + } else { + return $storage; + } + }); + $userDir = '/'.$user.'/files'; $userRoot = OC_User::getHome($user); $userDirectory = $userRoot . '/files'; diff --git a/lib/public/files/lock.php b/lib/public/files/lock.php new file mode 100644 index 00000000000..fb1e4b9f1c5 --- /dev/null +++ b/lib/public/files/lock.php @@ -0,0 +1,48 @@ +<?php +/** + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE + * License as published by the Free Software Foundation; either + * version 3 of the License, or any later version. + * + * This library 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 along with this library. If not, see <http://www.gnu.org/licenses/>. + * + */ + +namespace OCP\Files; + +/** + * Class Lock + * @package OC\Files + */ +class Lock { + const READ = 1; + const WRITE = 2; + + /** @var string $path Filename of the file as represented in storage */ + protected $path; + + public function __construct($path) { + $this->path = $path; + } + + public function addLock($lockType) { + // This class is a stub/base for classes that implement locks + // We don't actually care what kind of lock we're queuing here + } + + /** + * Release locks on handles and files + */ + public function release($lockType) { + return true; + } + +}
\ No newline at end of file diff --git a/lib/public/files/locknotacquiredexception.php b/lib/public/files/locknotacquiredexception.php new file mode 100644 index 00000000000..9fb70e7cbe2 --- /dev/null +++ b/lib/public/files/locknotacquiredexception.php @@ -0,0 +1,47 @@ +<?php +/** + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE + * License as published by the Free Software Foundation; either + * version 3 of the License, or any later version. + * + * This library 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 along with this library. If not, see <http://www.gnu.org/licenses/>. + * + */ + +/** + * Public interface of ownCloud for apps to use. + * Files/LockNotAcquiredException class + */ + +// use OCP namespace for all classes that are considered public. +// This means that they should be used by apps instead of the internal ownCloud classes +namespace OCP\Files; + +/** + * Exception for a file that is locked + */ +class LockNotAcquiredException extends \Exception { + /** @var string $path The path that could not be locked */ + public $path; + + /** @var integer $lockType The type of the lock that was attempted */ + public $lockType; + + public function __construct($path, $lockType, $code = 0, \Exception $previous = null) { + $message = \OC_L10N::get('core')->t('Could not obtain lock type %d on "%s".', array($lockType, $path)); + parent::__construct($message, $code, $previous); + } + + // custom string representation of object + public function __toString() { + return __CLASS__ . ": [{$this->code}]: {$this->message}\n"; + } +}
\ No newline at end of file diff --git a/lib/public/files/storage.php b/lib/public/files/storage.php index 323d20db564..758cbb27377 100644 --- a/lib/public/files/storage.php +++ b/lib/public/files/storage.php @@ -35,6 +35,7 @@ namespace OCP\Files; * All paths passed to the storage are relative to the storage and should NOT have a leading slash. */ interface Storage { + /** * $parameters is a free form array with the configuration options needed to construct the storage * |