diff --git a/apps/files_external/lib/streamwrapper.php b/apps/files_external/lib/streamwrapper.php index 44bd9a0161..d749f62afe 100644 --- a/apps/files_external/lib/streamwrapper.php +++ b/apps/files_external/lib/streamwrapper.php @@ -103,5 +103,4 @@ abstract class StreamWrapper extends Common { public function stat($path) { return stat($this->constructUrl($path)); } - } diff --git a/lib/private/files/storage/local.php b/lib/private/files/storage/local.php index e33747bbd5..565f9202d5 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 0000000000..04a325940b --- /dev/null +++ b/lib/private/files/storage/wrapper/lockingwrapper.php @@ -0,0 +1,139 @@ +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 306e37b947..8cc48c0346 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 0000000000..fb1e4b9f1c --- /dev/null +++ b/lib/public/files/lock.php @@ -0,0 +1,48 @@ +. + * + */ + +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 0000000000..9fb70e7cbe --- /dev/null +++ b/lib/public/files/locknotacquiredexception.php @@ -0,0 +1,47 @@ +. + * + */ + +/** + * 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 323d20db56..758cbb2737 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 *