aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLouis Chemineau <louis@chmn.me>2025-04-01 11:27:02 +0200
committerLouis <louis@chmn.me>2025-04-16 16:18:42 +0200
commit9bed21a8d722df3f3a818cc6778fa3b59061b685 (patch)
treeeea1c52402b3377176f6dd119df79ecdaa7e1172
parent3a1880d8cee60a414173f49d08b93e8000e9bc56 (diff)
downloadnextcloud-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.php13
-rw-r--r--tests/lib/Files/Storage/StoragesTest.php18
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');