Skip to content

Commit 3e12e1e

Browse files
committed
fix: rework move into object store to better preserve fileids
Signed-off-by: Robin Appelman <robin@icewind.nl>
1 parent f8a59b5 commit 3e12e1e

1 file changed

Lines changed: 55 additions & 19 deletions

File tree

lib/private/Files/ObjectStore/ObjectStoreStorage.php

Lines changed: 55 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -601,32 +601,68 @@ public function moveFromStorage(IStorage $sourceStorage, $sourceInternalPath, $t
601601
if (!$sourceCacheEntry) {
602602
$sourceCacheEntry = $sourceCache->get($sourceInternalPath);
603603
}
604-
if ($sourceCacheEntry->getMimeType() === FileInfo::MIMETYPE_FOLDER) {
605-
$this->mkdir($targetInternalPath);
606-
foreach ($sourceCache->getFolderContentsById($sourceCacheEntry->getId()) as $child) {
607-
$this->moveFromStorage($sourceStorage, $child->getPath(), $targetInternalPath . '/' . $child->getName(), $child);
608-
}
604+
605+
$this->copyObjects($sourceStorage, $sourceCache, $sourceCacheEntry);
606+
if ($sourceStorage->instanceOfStorage(ObjectStoreStorage::class)) {
607+
/** @var ObjectStoreStorage $sourceStorage */
608+
$sourceStorage->setPreserveCacheOnDelete(true);
609+
}
610+
if ($sourceCacheEntry->getMimeType() === ICacheEntry::DIRECTORY_MIMETYPE) {
609611
$sourceStorage->rmdir($sourceInternalPath);
610-
$sourceStorage->getCache()->remove($sourceInternalPath);
611612
} else {
612-
$sourceStream = $sourceStorage->fopen($sourceInternalPath, 'r');
613-
if (!$sourceStream) {
614-
return false;
615-
}
616-
// move the cache entry before the contents so that we have the correct fileid/urn for the target
617-
$this->getCache()->moveFromCache($sourceCache, $sourceInternalPath, $targetInternalPath);
618-
try {
619-
$this->writeStream($targetInternalPath, $sourceStream, $sourceCacheEntry->getSize());
620-
} catch (\Exception $e) {
621-
// restore the cache entry
622-
$sourceCache->moveFromCache($this->getCache(), $targetInternalPath, $sourceInternalPath);
623-
throw $e;
624-
}
625613
$sourceStorage->unlink($sourceInternalPath);
626614
}
615+
if ($sourceStorage->instanceOfStorage(ObjectStoreStorage::class)) {
616+
/** @var ObjectStoreStorage $sourceStorage */
617+
$sourceStorage->setPreserveCacheOnDelete(false);
618+
}
619+
$this->getCache()->moveFromCache($sourceCache, $sourceInternalPath, $targetInternalPath);
620+
627621
return true;
628622
}
629623

624+
/**
625+
* Copy the object(s) of a file or folder into this storage, without touching the cache
626+
*/
627+
private function copyObjects(IStorage $sourceStorage, ICache $sourceCache, ICacheEntry $sourceCacheEntry) {
628+
$copiedFiles = [];
629+
try {
630+
foreach ($this->getAllChildObjects($sourceCache, $sourceCacheEntry) as $file) {
631+
$sourceStream = $sourceStorage->fopen($file->getPath(), 'r');
632+
if (!$sourceStream) {
633+
throw new \Exception("Failed to open source file {$file->getPath()} ({$file->getId()})");
634+
}
635+
$this->objectStore->writeObject($this->getURN($file->getId()), $sourceStream, $file->getMimeType());
636+
if (is_resource($sourceStream)) {
637+
fclose($sourceStream);
638+
}
639+
$copiedFiles[] = $file->getId();
640+
}
641+
} catch (\Exception $e) {
642+
foreach ($copiedFiles as $fileId) {
643+
try {
644+
$this->objectStore->deleteObject($this->getURN($fileId));
645+
} catch (\Exception $e) {
646+
// ignore
647+
}
648+
}
649+
throw $e;
650+
}
651+
}
652+
653+
/**
654+
* @return \Iterator<ICacheEntry>
655+
*/
656+
private function getAllChildObjects(ICache $cache, ICacheEntry $entry): \Iterator {
657+
if ($entry->getMimeType() === FileInfo::MIMETYPE_FOLDER) {
658+
foreach ($cache->getFolderContentsById($entry->getId()) as $child) {
659+
yield from $this->getAllChildObjects($cache, $child);
660+
}
661+
} else {
662+
yield $entry;
663+
}
664+
}
665+
630666
public function copy($source, $target): bool {
631667
$source = $this->normalizePath($source);
632668
$target = $this->normalizePath($target);

0 commit comments

Comments
 (0)