Skip to content

Commit 3e2cb59

Browse files
icewind1991AndyScherzinger
authored andcommitted
fix: rework move into object store to better preserve fileids
Signed-off-by: Robin Appelman <robin@icewind.nl>
1 parent fa602e8 commit 3e2cb59

File tree

1 file changed

+55
-13
lines changed

1 file changed

+55
-13
lines changed

lib/private/Files/ObjectStore/ObjectStoreStorage.php

Lines changed: 55 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -614,26 +614,68 @@ public function moveFromStorage(IStorage $sourceStorage, $sourceInternalPath, $t
614614
if (!$sourceCacheEntry) {
615615
$sourceCacheEntry = $sourceCache->get($sourceInternalPath);
616616
}
617-
if ($sourceCacheEntry->getMimeType() === FileInfo::MIMETYPE_FOLDER) {
618-
foreach ($sourceCache->getFolderContents($sourceInternalPath) as $child) {
619-
$this->moveFromStorage($sourceStorage, $child->getPath(), $targetInternalPath . '/' . $child->getName());
620-
}
617+
618+
$this->copyObjects($sourceStorage, $sourceCache, $sourceCacheEntry);
619+
if ($sourceStorage->instanceOfStorage(ObjectStoreStorage::class)) {
620+
/** @var ObjectStoreStorage $sourceStorage */
621+
$sourceStorage->setPreserveCacheOnDelete(true);
622+
}
623+
if ($sourceCacheEntry->getMimeType() === ICacheEntry::DIRECTORY_MIMETYPE) {
621624
$sourceStorage->rmdir($sourceInternalPath);
622625
} else {
623-
// move the cache entry before the contents so that we have the correct fileid/urn for the target
624-
$this->getCache()->moveFromCache($sourceCache, $sourceInternalPath, $targetInternalPath);
625-
try {
626-
$this->writeStream($targetInternalPath, $sourceStorage->fopen($sourceInternalPath, 'r'), $sourceCacheEntry->getSize());
627-
} catch (\Exception $e) {
628-
// restore the cache entry
629-
$sourceCache->moveFromCache($this->getCache(), $targetInternalPath, $sourceInternalPath);
630-
throw $e;
631-
}
632626
$sourceStorage->unlink($sourceInternalPath);
633627
}
628+
if ($sourceStorage->instanceOfStorage(ObjectStoreStorage::class)) {
629+
/** @var ObjectStoreStorage $sourceStorage */
630+
$sourceStorage->setPreserveCacheOnDelete(false);
631+
}
632+
$this->getCache()->moveFromCache($sourceCache, $sourceInternalPath, $targetInternalPath);
633+
634634
return true;
635635
}
636636

637+
/**
638+
* Copy the object(s) of a file or folder into this storage, without touching the cache
639+
*/
640+
private function copyObjects(IStorage $sourceStorage, ICache $sourceCache, ICacheEntry $sourceCacheEntry) {
641+
$copiedFiles = [];
642+
try {
643+
foreach ($this->getAllChildObjects($sourceCache, $sourceCacheEntry) as $file) {
644+
$sourceStream = $sourceStorage->fopen($file->getPath(), 'r');
645+
if (!$sourceStream) {
646+
throw new \Exception("Failed to open source file {$file->getPath()} ({$file->getId()})");
647+
}
648+
$this->objectStore->writeObject($this->getURN($file->getId()), $sourceStream, $file->getMimeType());
649+
if (is_resource($sourceStream)) {
650+
fclose($sourceStream);
651+
}
652+
$copiedFiles[] = $file->getId();
653+
}
654+
} catch (\Exception $e) {
655+
foreach ($copiedFiles as $fileId) {
656+
try {
657+
$this->objectStore->deleteObject($this->getURN($fileId));
658+
} catch (\Exception $e) {
659+
// ignore
660+
}
661+
}
662+
throw $e;
663+
}
664+
}
665+
666+
/**
667+
* @return \Iterator<ICacheEntry>
668+
*/
669+
private function getAllChildObjects(ICache $cache, ICacheEntry $entry): \Iterator {
670+
if ($entry->getMimeType() === FileInfo::MIMETYPE_FOLDER) {
671+
foreach ($cache->getFolderContentsById($entry->getId()) as $child) {
672+
yield from $this->getAllChildObjects($cache, $child);
673+
}
674+
} else {
675+
yield $entry;
676+
}
677+
}
678+
637679
public function copy($source, $target) {
638680
$source = $this->normalizePath($source);
639681
$target = $this->normalizePath($target);

0 commit comments

Comments
 (0)