From 2e57fe93e4abf7608265c25fec414528ae882187 Mon Sep 17 00:00:00 2001 From: Vincent Petry Date: Wed, 7 Jan 2015 21:21:51 +0100 Subject: [PATCH] Disable part files for OC ext storage backend + s2s backend When uploading files to an OC ext storage backend or when using server to server sharing storage, part files aren't needed because the backend already has its own part files and takes care of the final atomic rename operation. This also fixes issues when using two encrypted ownCloud instances where one mounts the other either as external storage (ownCloud backend) or through server to server sharing. --- lib/private/connector/sabre/file.php | 87 +++++++++++++++++++--------- 1 file changed, 60 insertions(+), 27 deletions(-) diff --git a/lib/private/connector/sabre/file.php b/lib/private/connector/sabre/file.php index 76ebe3ed9f..cd05db8e12 100644 --- a/lib/private/connector/sabre/file.php +++ b/lib/private/connector/sabre/file.php @@ -74,8 +74,16 @@ class OC_Connector_Sabre_File extends OC_Connector_Sabre_Node implements \Sabre\ return $this->createFileChunked($data); } - // mark file as partial while uploading (ignored by the scanner) - $partFilePath = $this->path . '.ocTransferId' . rand() . '.part'; + list($storage, $internalPath) = $this->fileView->resolvePath($this->path); + $needsPartFile = $this->needsPartFile($storage); + + if ($needsPartFile) { + // mark file as partial while uploading (ignored by the scanner) + $partFilePath = $this->path . '.ocTransferId' . rand() . '.part'; + } else { + // upload file directly as the final path + $partFilePath = $this->path; + } try { $putOkay = $this->fileView->file_put_contents($partFilePath, $data); @@ -123,19 +131,21 @@ class OC_Connector_Sabre_File extends OC_Connector_Sabre_Node implements \Sabre\ } } - // rename to correct path - try { - $renameOkay = $this->fileView->rename($partFilePath, $this->path); - $fileExists = $this->fileView->file_exists($this->path); - if ($renameOkay === false || $fileExists === false) { - \OC_Log::write('webdav', '\OC\Files\Filesystem::rename() failed', \OC_Log::ERROR); - $this->fileView->unlink($partFilePath); - throw new \Sabre\DAV\Exception('Could not rename part file to final file'); + if ($needsPartFile) { + // rename to correct path + try { + $renameOkay = $this->fileView->rename($partFilePath, $this->path); + $fileExists = $this->fileView->file_exists($this->path); + if ($renameOkay === false || $fileExists === false) { + \OC_Log::write('webdav', '\OC\Files\Filesystem::rename() failed', \OC_Log::ERROR); + $this->fileView->unlink($partFilePath); + throw new \Sabre\DAV\Exception('Could not rename part file to final file'); + } + } + catch (\OCP\Files\LockNotAcquiredException $e) { + // the file is currently being written to by another process + throw new OC_Connector_Sabre_Exception_FileLocked($e->getMessage(), $e->getCode(), $e); } - } - catch (\OCP\Files\LockNotAcquiredException $e) { - // the file is currently being written to by another process - throw new OC_Connector_Sabre_Exception_FileLocked($e->getMessage(), $e->getCode(), $e); } // allow sync clients to send the mtime along in a header @@ -267,23 +277,31 @@ class OC_Connector_Sabre_File extends OC_Connector_Sabre_Node implements \Sabre\ } if ($chunk_handler->isComplete()) { + list($storage, $internalPath) = $this->fileView->resolvePath($path); + $needsPartFile = $this->needsPartFile($storage); try { - // we first assembly the target file as a part file - $partFile = $path . '/' . $info['name'] . '.ocTransferId' . $info['transferid'] . '.part'; - $chunk_handler->file_assemble($partFile); + 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); - // here is the final atomic rename - $targetPath = $path . '/' . $info['name']; - $renameOkay = $this->fileView->rename($partFile, $targetPath); - $fileExists = $this->fileView->file_exists($targetPath); - if ($renameOkay === false || $fileExists === false) { - \OC_Log::write('webdav', '\OC\Files\Filesystem::rename() failed', \OC_Log::ERROR); - // only delete if an error occurred and the target file was already created - if ($fileExists) { - $this->fileView->unlink($targetPath); + // here is the final atomic rename + $targetPath = $path . '/' . $info['name']; + $renameOkay = $this->fileView->rename($partFile, $targetPath); + $fileExists = $this->fileView->file_exists($targetPath); + if ($renameOkay === false || $fileExists === false) { + \OC_Log::write('webdav', '\OC\Files\Filesystem::rename() failed', \OC_Log::ERROR); + // only delete if an error occurred and the target file was already created + if ($fileExists) { + $this->fileView->unlink($targetPath); + } + throw new \Sabre\DAV\Exception('Could not rename part file assembled from chunks'); } - throw new \Sabre\DAV\Exception('Could not rename part file assembled from chunks'); + } else { + // assemble directly into the final file + $partFile = $path . '/' . $info['name']; + $chunk_handler->file_assemble($partFile); } // allow sync clients to send the mtime along in a header @@ -303,4 +321,19 @@ class OC_Connector_Sabre_File extends OC_Connector_Sabre_Node implements \Sabre\ return null; } + + /** + * Returns whether a part file is needed for the given storage + * or whether the file can be assembled/uploaded directly on the + * target storage. + * + * @param \OCP\Files\Storage $storage storage to check + * @param bool true if the storage needs part file handling + */ + private function needsPartFile($storage) { + // TODO: in the future use ChunkHandler provided by storage + // and/or add method on Storage called "needsPartFile()" + return !$storage->instanceOfStorage('OCA\Files_Sharing\External\Storage') && + !$storage->instanceOfStorage('OC\Files\Storage\OwnCloud'); + } }