flock changes. Work in progress.

This commit is contained in:
ringmaster 2014-05-20 17:44:57 -04:00 committed by Thomas Müller
parent 3a1994d001
commit 5365ae416e
7 changed files with 259 additions and 1 deletions

View File

@ -103,5 +103,4 @@ abstract class StreamWrapper extends Common {
public function stat($path) {
return stat($this->constructUrl($path));
}
}

View File

@ -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;
}
}
}

View File

@ -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;
}
}

View File

@ -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';

48
lib/public/files/lock.php Normal file
View File

@ -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;
}
}

View File

@ -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";
}
}

View File

@ -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
*