From 694032c54c85baf0bd8e37dfa907c9f136efc01b Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Thu, 5 Nov 2020 16:30:05 +0100 Subject: [PATCH] use in objectstore copy Signed-off-by: Robin Appelman --- lib/private/Files/ObjectStore/Azure.php | 4 ++ .../Files/ObjectStore/ObjectStoreStorage.php | 64 ++++++++++++++++++- .../Files/ObjectStore/S3ObjectTrait.php | 4 ++ .../Files/ObjectStore/StorageObjectStore.php | 4 ++ lib/private/Files/ObjectStore/Swift.php | 12 +++- lib/public/Files/ObjectStore/IObjectStore.php | 8 +++ .../ObjectStore/FailDeleteObjectStore.php | 4 ++ .../ObjectStore/FailWriteObjectStore.php | 4 ++ .../lib/Files/ObjectStore/ObjectStoreTest.php | 16 +++++ 9 files changed, 115 insertions(+), 5 deletions(-) diff --git a/lib/private/Files/ObjectStore/Azure.php b/lib/private/Files/ObjectStore/Azure.php index 0b65a6b80e..2ef13d60c5 100644 --- a/lib/private/Files/ObjectStore/Azure.php +++ b/lib/private/Files/ObjectStore/Azure.php @@ -130,4 +130,8 @@ class Azure implements IObjectStore { } } } + + public function copyObject($from, $to) { + $this->getBlobClient()->copyBlob($this->containerName, $to, $this->containerName, $from); + } } diff --git a/lib/private/Files/ObjectStore/ObjectStoreStorage.php b/lib/private/Files/ObjectStore/ObjectStoreStorage.php index e675064eb1..e855c16661 100644 --- a/lib/private/Files/ObjectStore/ObjectStoreStorage.php +++ b/lib/private/Files/ObjectStore/ObjectStoreStorage.php @@ -33,10 +33,15 @@ use Icewind\Streams\CallbackWrapper; use Icewind\Streams\CountWrapper; use Icewind\Streams\IteratorDirectory; use OC\Files\Cache\CacheEntry; +use OC\Files\Storage\PolyFill\CopyDirectory; +use OCP\Files\Cache\ICacheEntry; +use OCP\Files\FileInfo; use OCP\Files\NotFoundException; use OCP\Files\ObjectStore\IObjectStore; class ObjectStoreStorage extends \OC\Files\Storage\Common { + use CopyDirectory; + /** * @var \OCP\Files\ObjectStore\IObjectStore $objectStore */ @@ -319,7 +324,7 @@ class ObjectStoreStorage extends \OC\Files\Storage\Common { } else { return false; } - // no break + // no break case 'w': case 'wb': case 'w+': @@ -474,7 +479,7 @@ class ObjectStoreStorage extends \OC\Files\Storage\Common { if ($size === null) { $countStream = CountWrapper::wrap($stream, function ($writtenSize) use ($fileId, &$size) { $this->getCache()->update($fileId, [ - 'size' => $writtenSize + 'size' => $writtenSize, ]); $size = $writtenSize; }); @@ -523,4 +528,59 @@ class ObjectStoreStorage extends \OC\Files\Storage\Common { public function getObjectStore(): IObjectStore { return $this->objectStore; } + + public function copy($path1, $path2) { + $path1 = $this->normalizePath($path1); + $path2 = $this->normalizePath($path2); + + $cache = $this->getCache(); + $sourceEntry = $cache->get($path1); + if (!$sourceEntry) { + throw new NotFoundException('Source object not found'); + } + + $this->copyInner($sourceEntry, $path2); + + return true; + } + + private function copyInner(ICacheEntry $sourceEntry, string $to) { + $cache = $this->getCache(); + + if ($sourceEntry->getMimeType() === FileInfo::MIMETYPE_FOLDER) { + if ($cache->inCache($to)) { + $cache->remove($to); + } + $this->mkdir($to); + + foreach ($cache->getFolderContentsById($sourceEntry->getId()) as $child) { + $this->copyInner($child, $to . '/' . $child->getName()); + } + } else { + $this->copyFile($sourceEntry, $to); + } + } + + private function copyFile(ICacheEntry $sourceEntry, string $to) { + $cache = $this->getCache(); + + $sourceUrn = $this->getURN($sourceEntry->getId()); + + $cache->copyFromCache($cache, $sourceEntry, $to); + $targetEntry = $cache->get($to); + + if (!$targetEntry) { + throw new \Exception('Target not in cache after copy'); + } + + $targetUrn = $this->getURN($targetEntry->getId()); + + try { + $this->objectStore->copyObject($sourceUrn, $targetUrn); + } catch (\Exception $e) { + $cache->remove($to); + + throw $e; + } + } } diff --git a/lib/private/Files/ObjectStore/S3ObjectTrait.php b/lib/private/Files/ObjectStore/S3ObjectTrait.php index a390c6b4c7..80b8a6f132 100644 --- a/lib/private/Files/ObjectStore/S3ObjectTrait.php +++ b/lib/private/Files/ObjectStore/S3ObjectTrait.php @@ -124,4 +124,8 @@ trait S3ObjectTrait { public function objectExists($urn) { return $this->getConnection()->doesObjectExist($this->bucket, $urn); } + + public function copyObject($from, $to) { + $this->getConnection()->copy($this->getBucket(), $from, $this->getBucket(), $to); + } } diff --git a/lib/private/Files/ObjectStore/StorageObjectStore.php b/lib/private/Files/ObjectStore/StorageObjectStore.php index a7551385b3..acf4675895 100644 --- a/lib/private/Files/ObjectStore/StorageObjectStore.php +++ b/lib/private/Files/ObjectStore/StorageObjectStore.php @@ -93,4 +93,8 @@ class StorageObjectStore implements IObjectStore { public function objectExists($urn) { return $this->storage->file_exists($urn); } + + public function copyObject($from, $to) { + $this->storage->copy($from, $to); + } } diff --git a/lib/private/Files/ObjectStore/Swift.php b/lib/private/Files/ObjectStore/Swift.php index 5ee924c9de..1b0888b070 100644 --- a/lib/private/Files/ObjectStore/Swift.php +++ b/lib/private/Files/ObjectStore/Swift.php @@ -87,13 +87,13 @@ class Swift implements IObjectStore { if (filesize($tmpFile) < SWIFT_SEGMENT_SIZE) { $this->getContainer()->createObject([ 'name' => $urn, - 'stream' => stream_for($handle) + 'stream' => stream_for($handle), ]); } else { $this->getContainer()->createLargeObject([ 'name' => $urn, 'stream' => stream_for($handle), - 'segmentSize' => SWIFT_SEGMENT_SIZE + 'segmentSize' => SWIFT_SEGMENT_SIZE, ]); } } @@ -114,7 +114,7 @@ class Swift implements IObjectStore { 'stream' => true, 'headers' => [ 'X-Auth-Token' => $tokenId, - 'Cache-Control' => 'no-cache' + 'Cache-Control' => 'no-cache', ], ] ); @@ -149,4 +149,10 @@ class Swift implements IObjectStore { public function objectExists($urn) { return $this->getContainer()->objectExists($urn); } + + public function copyObject($from, $to) { + $this->getContainer()->getObject($from)->copy([ + 'destination' => $this->getContainer()->name . '/' . $to + ]); + } } diff --git a/lib/public/Files/ObjectStore/IObjectStore.php b/lib/public/Files/ObjectStore/IObjectStore.php index 4925959d6c..e9d948682f 100644 --- a/lib/public/Files/ObjectStore/IObjectStore.php +++ b/lib/public/Files/ObjectStore/IObjectStore.php @@ -73,4 +73,12 @@ interface IObjectStore { * @since 16.0.0 */ public function objectExists($urn); + + /** + * @param string $from the unified resource name used to identify the source object + * @param string $to the unified resource name used to identify the target object + * @return void + * @since 21.0.0 + */ + public function copyObject($from, $to); } diff --git a/tests/lib/Files/ObjectStore/FailDeleteObjectStore.php b/tests/lib/Files/ObjectStore/FailDeleteObjectStore.php index 1a3477090b..c755657faf 100644 --- a/tests/lib/Files/ObjectStore/FailDeleteObjectStore.php +++ b/tests/lib/Files/ObjectStore/FailDeleteObjectStore.php @@ -51,4 +51,8 @@ class FailDeleteObjectStore implements IObjectStore { public function objectExists($urn) { return $this->objectStore->objectExists($urn); } + + public function copyObject($from, $to) { + $this->objectStore->copyObject($from, $to); + } } diff --git a/tests/lib/Files/ObjectStore/FailWriteObjectStore.php b/tests/lib/Files/ObjectStore/FailWriteObjectStore.php index ad2350ea36..b9c8751fda 100644 --- a/tests/lib/Files/ObjectStore/FailWriteObjectStore.php +++ b/tests/lib/Files/ObjectStore/FailWriteObjectStore.php @@ -52,4 +52,8 @@ class FailWriteObjectStore implements IObjectStore { public function objectExists($urn) { return $this->objectStore->objectExists($urn); } + + public function copyObject($from, $to) { + $this->objectStore->copyObject($from, $to); + } } diff --git a/tests/lib/Files/ObjectStore/ObjectStoreTest.php b/tests/lib/Files/ObjectStore/ObjectStoreTest.php index 9300a9bdef..4ec44eb410 100644 --- a/tests/lib/Files/ObjectStore/ObjectStoreTest.php +++ b/tests/lib/Files/ObjectStore/ObjectStoreTest.php @@ -108,4 +108,20 @@ abstract class ObjectStoreTest extends TestCase { $this->assertFalse($instance->objectExists('2')); } + + public function testCopy() { + $stream = $this->stringToStream('foobar'); + + $instance = $this->getInstance(); + + $instance->writeObject('source', $stream); + + $this->assertFalse($instance->objectExists('target')); + + $instance->copyObject('source', 'target'); + + $this->assertTrue($instance->objectExists('target')); + + $this->assertEquals('foobar', stream_get_contents($instance->readObject('target'))); + } }