diff options
author | Louis Chemineau <louis@chmn.me> | 2025-04-01 11:27:02 +0200 |
---|---|---|
committer | Louis <louis@chmn.me> | 2025-04-16 16:18:42 +0200 |
commit | 9bed21a8d722df3f3a818cc6778fa3b59061b685 (patch) | |
tree | eea1c52402b3377176f6dd119df79ecdaa7e1172 | |
parent | 3a1880d8cee60a414173f49d08b93e8000e9bc56 (diff) | |
download | nextcloud-server-backport/51020/stable30.tar.gz nextcloud-server-backport/51020/stable30.zip |
fix: Transfer ownership with S3 as primarybackport/51020/stable30
When using S3 as primary storage, transferring ownership with the `--move` option fail with the following error:
`SQLSTATE[23000]: Integrity constraint violation: 1062 Duplicate entry '8-45b963397aa40d4a0063e0d85e4fe7a1' for key 'fs_storage_path_hash'`
The `--move` option moves the entire home folder from one account to another.
The error means that the move failed because the destination folder already exist in `oc_filecache`.
- With S3 as primary storage, folders only exists as entries in `oc_filecache`.
- With S3 as primary storage, `moveFromStorage(...)` only moves the cache entry, as nothing needs to be moved on disk. This cache move does not delete potentially pre-existing destination folder.
- With Local storage, `moveFromStorage(...)` calls `rename(...)` which delete pre-existing folder.
- `transfer(...)`: https://github.com/nextcloud/server/blob/687a4d9ac7fcdbd935f81a0def567a1092306f7a/apps/files/lib/Service/OwnershipTransferService.php#L112
- `oneTimeUserSetup(...)`: https://github.com/nextcloud/server/blob/687a4d9ac7fcdbd935f81a0def567a1092306f7a/lib/private/Files/SetupManager.php#L261-L262
- `mkdir(...)`: https://github.com/nextcloud/server/blob/687a4d9ac7fcdbd935f81a0def567a1092306f7a/lib/private/Files/ObjectStore/ObjectStoreStorage.php#L91-L135
- `moveFromStorage(...)`: https://github.com/nextcloud/server/blob/687a4d9ac7fcdbd935f81a0def567a1092306f7a/lib/private/Files/ObjectStore/ObjectStoreStorage.php#L635-L636
Delete pre-existing folder in `moveFromStorage(...)`
Signed-off-by: Louis Chemineau <louis@chmn.me>
-rw-r--r-- | lib/private/Files/ObjectStore/ObjectStoreStorage.php | 13 | ||||
-rw-r--r-- | tests/lib/Files/Storage/StoragesTest.php | 18 |
2 files changed, 30 insertions, 1 deletions
diff --git a/lib/private/Files/ObjectStore/ObjectStoreStorage.php b/lib/private/Files/ObjectStore/ObjectStoreStorage.php index acf0dd492bc..4b7d9cb1429 100644 --- a/lib/private/Files/ObjectStore/ObjectStoreStorage.php +++ b/lib/private/Files/ObjectStore/ObjectStoreStorage.php @@ -602,7 +602,14 @@ class ObjectStoreStorage extends \OC\Files\Storage\Common implements IChunkedFil public function moveFromStorage(IStorage $sourceStorage, $sourceInternalPath, $targetInternalPath, ?ICacheEntry $sourceCacheEntry = null): bool { $sourceCache = $sourceStorage->getCache(); - if ($sourceStorage->instanceOfStorage(ObjectStoreStorage::class) && $sourceStorage->getObjectStore()->getStorageId() === $this->getObjectStore()->getStorageId()) { + if ( + $sourceStorage->instanceOfStorage(ObjectStoreStorage::class) && + $sourceStorage->getObjectStore()->getStorageId() === $this->getObjectStore()->getStorageId() + ) { + if ($this->getCache()->get($targetInternalPath)) { + $this->unlink($targetInternalPath); + $this->getCache()->remove($targetInternalPath); + } $this->getCache()->moveFromCache($sourceCache, $sourceInternalPath, $targetInternalPath); // Do not import any data when source and target bucket are identical. return true; @@ -625,6 +632,10 @@ class ObjectStoreStorage extends \OC\Files\Storage\Common implements IChunkedFil /** @var ObjectStoreStorage $sourceStorage */ $sourceStorage->setPreserveCacheOnDelete(false); } + if ($this->getCache()->get($targetInternalPath)) { + $this->unlink($targetInternalPath); + $this->getCache()->remove($targetInternalPath); + } $this->getCache()->moveFromCache($sourceCache, $sourceInternalPath, $targetInternalPath); return true; diff --git a/tests/lib/Files/Storage/StoragesTest.php b/tests/lib/Files/Storage/StoragesTest.php index d157d288f2c..43587517479 100644 --- a/tests/lib/Files/Storage/StoragesTest.php +++ b/tests/lib/Files/Storage/StoragesTest.php @@ -42,6 +42,24 @@ abstract class StoragesTest extends TestCase { $this->assertEquals('foo', $this->storage1->file_get_contents($target)); } + public function testMoveFileFromStorageWithExistingTarget() { + $source = 'source.txt'; + $target = 'target.txt'; + $this->storage1->file_put_contents($target, 'bar'); + $this->storage2->file_put_contents($source, 'foo'); + + $targetURN = $this->storage1->getURN($this->storage1->getCache()->get($target)->getID()); + $sourceURN = $this->storage2->getURN($this->storage2->getCache()->get($source)->getID()); + + $this->storage1->moveFromStorage($this->storage2, $source, $target); + + $this->assertTrue($this->storage1->file_exists($target), $target . ' was not created in DB'); + $this->assertFalse($this->storage2->file_exists($source), $source . ' still exists in DB'); + $this->assertTrue($this->storage1->getObjectStore()->objectExists($sourceURN), $sourceURN . ' was not created in bucket'); + $this->assertFalse($this->storage1->getObjectStore()->objectExists($targetURN), $targetURN . ' still exists in bucket'); + $this->assertEquals('foo', $this->storage1->file_get_contents($target)); + } + public function testMoveDirectoryFromStorage() { $this->storage2->mkdir('source'); $this->storage2->file_put_contents('source/test1.txt', 'foo'); |