wrap the entire put operation in a read lock

This commit is contained in:
Robin Appelman 2015-07-22 15:28:56 +02:00 committed by Thomas Müller
parent 209abaadbb
commit e612d3123f
4 changed files with 98 additions and 9 deletions

View File

@ -30,6 +30,7 @@ namespace OC\Connector\Sabre;
use OC\Connector\Sabre\Exception\InvalidPath;
use OC\Connector\Sabre\Exception\FileLocked;
use OCP\Lock\ILockingProvider;
use OCP\Lock\LockedException;
use Sabre\DAV\Exception\Locked;
@ -110,6 +111,7 @@ class Directory extends \OC\Connector\Sabre\Node
// using a dummy FileInfo is acceptable here since it will be refreshed after the put is complete
$info = new \OC\Files\FileInfo($path, null, null, array(), null);
$node = new \OC\Connector\Sabre\File($this->fileView, $info);
$node->acquireLock(ILockingProvider::LOCK_SHARED);
return $node->put($data);
} catch (\OCP\Files\StorageNotAvailableException $e) {
throw new \Sabre\DAV\Exception\ServiceUnavailable($e->getMessage());

View File

@ -114,12 +114,6 @@ class File extends Node implements IFile {
$partFilePath = $this->path;
}
try {
$this->fileView->lockFile($this->path, ILockingProvider::LOCK_SHARED);
} catch (LockedException $e) {
throw new FileLocked($e->getMessage(), $e->getCode(), $e);
}
// the part file and target file might be on a different storage in case of a single file storage (e.g. single file share)
/** @var \OC\Files\Storage\Storage $partStorage */
list($partStorage, $internalPartPath) = $this->fileView->resolvePath($partFilePath);
@ -176,7 +170,7 @@ class File extends Node implements IFile {
}
try {
$this->fileView->changeLock($this->path, ILockingProvider::LOCK_EXCLUSIVE);
$this->changeLock(ILockingProvider::LOCK_EXCLUSIVE);
} catch (LockedException $e) {
if ($needsPartFile) {
$partStorage->unlink($internalPartPath);
@ -202,7 +196,7 @@ class File extends Node implements IFile {
}
try {
$this->fileView->changeLock($this->path, ILockingProvider::LOCK_SHARED);
$this->changeLock(ILockingProvider::LOCK_SHARED);
} catch (LockedException $e) {
throw new FileLocked($e->getMessage(), $e->getCode(), $e);
}
@ -233,7 +227,6 @@ class File extends Node implements IFile {
}
}
$this->refreshInfo();
$this->fileView->unlockFile($this->path, ILockingProvider::LOCK_SHARED);
} catch (StorageNotAvailableException $e) {
throw new ServiceUnavailable("Failed to check file size: " . $e->getMessage());
}

View File

@ -0,0 +1,93 @@
<?php
/**
* @author Robin Appelman <icewind@owncloud.com>
*
* @copyright Copyright (c) 2015, 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 OC\Connector\Sabre;
use OC\Connector\Sabre\Exception\FileLocked;
use OCP\Lock\ILockingProvider;
use OCP\Lock\LockedException;
use Sabre\DAV\Exception\NotFound;
use \Sabre\DAV\PropFind;
use \Sabre\DAV\PropPatch;
use Sabre\DAV\ServerPlugin;
use Sabre\DAV\Tree;
use Sabre\HTTP\RequestInterface;
use Sabre\HTTP\ResponseInterface;
class LockPlugin extends ServerPlugin {
/**
* Reference to main server object
*
* @var \Sabre\DAV\Server
*/
private $server;
/**
* @var \Sabre\DAV\Tree
*/
private $tree;
/**
* @param \Sabre\DAV\Tree $tree tree
*/
public function __construct(Tree $tree) {
$this->tree = $tree;
}
/**
* {@inheritdoc}
*/
public function initialize(\Sabre\DAV\Server $server) {
$this->server = $server;
$this->server->on('beforeMethod', [$this, 'getLock'], 50);
$this->server->on('afterMethod', [$this, 'releaseLock'], 50);
}
public function getLock(RequestInterface $request) {
// we cant listen on 'beforeMethod:PUT' due to order of operations with setting up the tree
// so instead we limit ourselves to the PUT method manually
if ($request->getMethod() !== 'PUT') {
return;
}
try {
$node = $this->tree->getNodeForPath($request->getPath());
} catch (NotFound $e) {
return;
}
if ($node instanceof Node) {
try {
$node->acquireLock(ILockingProvider::LOCK_SHARED);
} catch (LockedException $e) {
throw new FileLocked($e->getMessage(), $e->getCode(), $e);
}
}
}
public function releaseLock(RequestInterface $request) {
if ($request->getMethod() !== 'PUT') {
return;
}
$node = $this->tree->getNodeForPath($request->getPath());
if ($node instanceof Node) {
$node->releaseLock(ILockingProvider::LOCK_SHARED);
}
}
}

View File

@ -70,6 +70,7 @@ class ServerFactory {
$server->addPlugin(new \OC\Connector\Sabre\FilesPlugin($objectTree));
$server->addPlugin(new \OC\Connector\Sabre\MaintenancePlugin($this->config));
$server->addPlugin(new \OC\Connector\Sabre\ExceptionLoggerPlugin('webdav', $this->logger));
$server->addPlugin(new \OC\Connector\Sabre\LockPlugin($objectTree));
// wait with registering these until auth is handled and the filesystem is setup
$server->on('beforeMethod', function () use ($server, $objectTree, $viewCallBack) {