From 63863271503384e124e39833d0598e8652d3a380 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Wed, 12 Aug 2015 15:06:59 +0200 Subject: work directly on storages when doing a chunked upload assembly --- lib/private/connector/sabre/file.php | 117 ++++++++++++++++++++++------------- 1 file changed, 74 insertions(+), 43 deletions(-) (limited to 'lib/private/connector/sabre') diff --git a/lib/private/connector/sabre/file.php b/lib/private/connector/sabre/file.php index 1730feee264..a7e529183b6 100644 --- a/lib/private/connector/sabre/file.php +++ b/lib/private/connector/sabre/file.php @@ -35,6 +35,7 @@ namespace OC\Connector\Sabre; use OC\Connector\Sabre\Exception\EntityTooLarge; use OC\Connector\Sabre\Exception\FileLocked; use OC\Connector\Sabre\Exception\UnsupportedMediaType; +use OC\Files\Filesystem; use OCP\Encryption\Exceptions\GenericEncryptionException; use OCP\Files\EntityTooLargeException; use OCP\Files\InvalidContentException; @@ -126,7 +127,7 @@ class File extends Node implements IFile { // because we have no clue about the cause we can only throw back a 500/Internal Server Error throw new Exception('Could not write file contents'); } - list($count, ) = \OC_Helper::streamCopy($data, $target); + list($count,) = \OC_Helper::streamCopy($data, $target); fclose($target); // if content length is sent by client: @@ -148,25 +149,8 @@ class File extends Node implements IFile { try { $view = \OC\Files\Filesystem::getView(); - $run = true; if ($view) { - $hookPath = $view->getRelativePath($this->fileView->getAbsolutePath($this->path)); - - if (!$exists) { - \OC_Hook::emit(\OC\Files\Filesystem::CLASSNAME, \OC\Files\Filesystem::signal_create, array( - \OC\Files\Filesystem::signal_param_path => $hookPath, - \OC\Files\Filesystem::signal_param_run => &$run, - )); - } else { - \OC_Hook::emit(\OC\Files\Filesystem::CLASSNAME, \OC\Files\Filesystem::signal_update, array( - \OC\Files\Filesystem::signal_param_path => $hookPath, - \OC\Files\Filesystem::signal_param_run => &$run, - )); - } - \OC_Hook::emit(\OC\Files\Filesystem::CLASSNAME, \OC\Files\Filesystem::signal_write, array( - \OC\Files\Filesystem::signal_param_path => $hookPath, - \OC\Files\Filesystem::signal_param_run => &$run, - )); + $this->emitPreHooks($exists); } try { @@ -181,11 +165,9 @@ class File extends Node implements IFile { if ($needsPartFile) { // rename to correct path try { - if ($run) { - $renameOkay = $storage->moveFromStorage($partStorage, $internalPartPath, $internalPath); - $fileExists = $storage->file_exists($internalPath); - } - if (!$run || $renameOkay === false || $fileExists === false) { + $renameOkay = $storage->moveFromStorage($partStorage, $internalPartPath, $internalPath); + $fileExists = $storage->file_exists($internalPath); + if ($renameOkay === false || $fileExists === false) { \OCP\Util::writeLog('webdav', 'renaming part file to final file failed', \OCP\Util::ERROR); throw new Exception('Could not rename part file to final file'); } @@ -205,18 +187,7 @@ class File extends Node implements IFile { $this->fileView->getUpdater()->update($this->path); if ($view) { - if (!$exists) { - \OC_Hook::emit(\OC\Files\Filesystem::CLASSNAME, \OC\Files\Filesystem::signal_post_create, array( - \OC\Files\Filesystem::signal_param_path => $hookPath - )); - } else { - \OC_Hook::emit(\OC\Files\Filesystem::CLASSNAME, \OC\Files\Filesystem::signal_post_update, array( - \OC\Files\Filesystem::signal_param_path => $hookPath - )); - } - \OC_Hook::emit(\OC\Files\Filesystem::CLASSNAME, \OC\Files\Filesystem::signal_post_write, array( - \OC\Files\Filesystem::signal_param_path => $hookPath - )); + $this->emitPostHooks($exists); } // allow sync clients to send the mtime along in a header @@ -234,6 +205,43 @@ class File extends Node implements IFile { return '"' . $this->info->getEtag() . '"'; } + private function emitPreHooks($exists) { + $hookPath = Filesystem::getView()->getRelativePath($this->fileView->getAbsolutePath($this->path)); + $run = true; + + if (!$exists) { + \OC_Hook::emit(\OC\Files\Filesystem::CLASSNAME, \OC\Files\Filesystem::signal_create, array( + \OC\Files\Filesystem::signal_param_path => $hookPath, + \OC\Files\Filesystem::signal_param_run => &$run, + )); + } else { + \OC_Hook::emit(\OC\Files\Filesystem::CLASSNAME, \OC\Files\Filesystem::signal_update, array( + \OC\Files\Filesystem::signal_param_path => $hookPath, + \OC\Files\Filesystem::signal_param_run => &$run, + )); + } + \OC_Hook::emit(\OC\Files\Filesystem::CLASSNAME, \OC\Files\Filesystem::signal_write, array( + \OC\Files\Filesystem::signal_param_path => $hookPath, + \OC\Files\Filesystem::signal_param_run => &$run, + )); + } + + private function emitPostHooks($exists) { + $hookPath = Filesystem::getView()->getRelativePath($this->fileView->getAbsolutePath($this->path)); + if (!$exists) { + \OC_Hook::emit(\OC\Files\Filesystem::CLASSNAME, \OC\Files\Filesystem::signal_post_create, array( + \OC\Files\Filesystem::signal_param_path => $hookPath + )); + } else { + \OC_Hook::emit(\OC\Files\Filesystem::CLASSNAME, \OC\Files\Filesystem::signal_post_update, array( + \OC\Files\Filesystem::signal_param_path => $hookPath + )); + } + \OC_Hook::emit(\OC\Files\Filesystem::CLASSNAME, \OC\Files\Filesystem::signal_post_write, array( + \OC\Files\Filesystem::signal_param_path => $hookPath + )); + } + /** * Returns the data * @@ -347,15 +355,30 @@ class File extends Node implements IFile { $needsPartFile = $this->needsPartFile($storage); $partFile = null; + $targetPath = $path . '/' . $info['name']; + /** @var \OC\Files\Storage\Storage $targetStorage */ + list($targetStorage, $targetInternalPath) = $this->fileView->resolvePath($targetPath); + + $exists = $this->fileView->file_exists($targetPath); + try { - $targetPath = $path . '/' . $info['name']; + $this->emitPreHooks($exists); + + $this->changeLock(ILockingProvider::LOCK_EXCLUSIVE); + if ($needsPartFile) { // we first assembly the target file as a part file $partFile = $path . '/' . $info['name'] . '.ocTransferId' . $info['transferid'] . '.part'; - $chunk_handler->file_assemble($partFile); + + + list($partStorage, $partInternalPath) = $this->fileView->resolvePath($partFile); + + + $chunk_handler->file_assemble($partStorage, $partInternalPath, $this->fileView->getAbsolutePath($targetPath)); // here is the final atomic rename - $renameOkay = $this->fileView->rename($partFile, $targetPath); + $renameOkay = $targetStorage->moveFromStorage($partStorage, $partInternalPath, $targetInternalPath); + $fileExists = $this->fileView->file_exists($targetPath); if ($renameOkay === false || $fileExists === false) { \OCP\Util::writeLog('webdav', '\OC\Files\Filesystem::rename() failed', \OCP\Util::ERROR); @@ -364,28 +387,36 @@ class File extends Node implements IFile { // set to null to avoid double-deletion when handling exception // stray part file $partFile = null; - $this->fileView->unlink($targetPath); + $targetStorage->unlink($targetInternalPath); } + $this->changeLock(ILockingProvider::LOCK_SHARED); throw new Exception('Could not rename part file assembled from chunks'); } } else { // assemble directly into the final file - $chunk_handler->file_assemble($targetPath); + $chunk_handler->file_assemble($targetStorage, $targetInternalPath, $this->fileView->getAbsolutePath($targetPath)); } // allow sync clients to send the mtime along in a header $request = \OC::$server->getRequest(); if (isset($request->server['HTTP_X_OC_MTIME'])) { - if ($this->fileView->touch($targetPath, $request->server['HTTP_X_OC_MTIME'])) { + if ($targetStorage->touch($targetInternalPath, $request->server['HTTP_X_OC_MTIME'])) { header('X-OC-MTime: accepted'); } } + $this->changeLock(ILockingProvider::LOCK_SHARED); + + // since we skipped the view we need to scan and emit the hooks ourselves + $this->fileView->getUpdater()->update($this->path); + + $this->emitPostHooks($exists); + $info = $this->fileView->getFileInfo($targetPath); return $info->getEtag(); } catch (\Exception $e) { if ($partFile !== null) { - $this->fileView->unlink($partFile); + $targetStorage->unlink($targetInternalPath); } $this->convertToSabreException($e); } -- cgit v1.2.3