Skip to content

Commit d7aa1c0

Browse files
authored
Merge pull request #21983 from nextcloud/backport/21628/stable19
[stable19] fix moving files from external storage to object store trashbin
2 parents 93225bc + 193b3ea commit d7aa1c0

5 files changed

Lines changed: 54 additions & 30 deletions

File tree

apps/files_trashbin/lib/Trashbin.php

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@
4444
namespace OCA\Files_Trashbin;
4545

4646
use OC\Files\Filesystem;
47+
use OC\Files\ObjectStore\ObjectStoreStorage;
4748
use OC\Files\View;
4849
use OCA\Files_Trashbin\AppInfo\Application;
4950
use OCA\Files_Trashbin\Command\Expire;
@@ -278,12 +279,22 @@ public static function move2trash($file_path, $ownerOnly = false) {
278279
/** @var \OC\Files\Storage\Storage $sourceStorage */
279280
[$sourceStorage, $sourceInternalPath] = $ownerView->resolvePath('/files/' . $ownerPath);
280281

282+
283+
if ($trashStorage->file_exists($trashInternalPath)) {
284+
$trashStorage->unlink($trashInternalPath);
285+
}
286+
287+
$connection = \OC::$server->getDatabaseConnection();
288+
$connection->beginTransaction();
289+
$trashStorage->getUpdater()->renameFromStorage($sourceStorage, $sourceInternalPath, $trashInternalPath);
290+
281291
try {
282292
$moveSuccessful = true;
283-
if ($trashStorage->file_exists($trashInternalPath)) {
284-
$trashStorage->unlink($trashInternalPath);
293+
294+
// when moving within the same object store, the cache update done above is enough to move the file
295+
if (!($trashStorage->instanceOfStorage(ObjectStoreStorage::class) && $trashStorage->getId() === $sourceStorage->getId())) {
296+
$trashStorage->moveFromStorage($sourceStorage, $sourceInternalPath, $trashInternalPath);
285297
}
286-
$trashStorage->moveFromStorage($sourceStorage, $sourceInternalPath, $trashInternalPath);
287298
} catch (\OCA\Files_Trashbin\Exceptions\CopyRecursiveException $e) {
288299
$moveSuccessful = false;
289300
if ($trashStorage->file_exists($trashInternalPath)) {
@@ -298,10 +309,11 @@ public static function move2trash($file_path, $ownerOnly = false) {
298309
} else {
299310
$sourceStorage->unlink($sourceInternalPath);
300311
}
312+
$connection->rollBack();
301313
return false;
302314
}
303315

304-
$trashStorage->getUpdater()->renameFromStorage($sourceStorage, $sourceInternalPath, $trashInternalPath);
316+
$connection->commit();
305317

306318
if ($moveSuccessful) {
307319
$query = \OC_DB::prepare("INSERT INTO `*PREFIX*files_trash` (`id`,`timestamp`,`location`,`user`) VALUES (?,?,?,?)");

lib/private/Files/Cache/Updater.php

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828

2929
namespace OC\Files\Cache;
3030

31+
use OC\Files\FileInfo;
3132
use OCP\Files\Cache\ICacheEntry;
3233
use OCP\Files\Cache\IUpdater;
3334
use OCP\Files\Storage\IStorage;
@@ -187,7 +188,9 @@ public function renameFromStorage(IStorage $sourceStorage, $source, $target) {
187188
$sourceUpdater = $sourceStorage->getUpdater();
188189
$sourcePropagator = $sourceStorage->getPropagator();
189190

190-
if ($sourceCache->inCache($source)) {
191+
$sourceInfo = $sourceCache->get($source);
192+
193+
if ($sourceInfo !== false) {
191194
if ($this->cache->inCache($target)) {
192195
$this->cache->remove($target);
193196
}
@@ -197,13 +200,17 @@ public function renameFromStorage(IStorage $sourceStorage, $source, $target) {
197200
} else {
198201
$this->cache->moveFromCache($sourceCache, $source, $target);
199202
}
200-
}
201203

202-
if (pathinfo($source, PATHINFO_EXTENSION) !== pathinfo($target, PATHINFO_EXTENSION)) {
203-
// handle mime type change
204-
$mimeType = $this->storage->getMimeType($target);
205-
$fileId = $this->cache->getId($target);
206-
$this->cache->update($fileId, ['mimetype' => $mimeType]);
204+
$sourceExtension = pathinfo($source, PATHINFO_EXTENSION);
205+
$targetExtension = pathinfo($target, PATHINFO_EXTENSION);
206+
$targetIsTrash = preg_match("/d\d+/", $targetExtension);
207+
208+
if ($sourceExtension !== $targetExtension && $sourceInfo->getMimeType() !== FileInfo::MIMETYPE_FOLDER && !$targetIsTrash) {
209+
// handle mime type change
210+
$mimeType = $this->storage->getMimeType($target);
211+
$fileId = $this->cache->getId($target);
212+
$this->cache->update($fileId, ['mimetype' => $mimeType]);
213+
}
207214
}
208215

209216
if ($sourceCache instanceof Cache) {

lib/private/Files/Storage/Common.php

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@
5353
use OC\Files\Storage\Wrapper\Wrapper;
5454
use OCP\Files\EmptyFileNameException;
5555
use OCP\Files\FileNameTooLongException;
56+
use OCP\Files\GenericFileException;
5657
use OCP\Files\InvalidCharacterInPathException;
5758
use OCP\Files\InvalidDirectoryException;
5859
use OCP\Files\InvalidPathException;
@@ -620,18 +621,19 @@ public function copyFromStorage(IStorage $sourceStorage, $sourceInternalPath, $t
620621
}
621622
} else {
622623
$source = $sourceStorage->fopen($sourceInternalPath, 'r');
623-
// TODO: call fopen in a way that we execute again all storage wrappers
624-
// to avoid that we bypass storage wrappers which perform important actions
625-
// for this operation. Same is true for all other operations which
626-
// are not the same as the original one.Once this is fixed we also
627-
// need to adjust the encryption wrapper.
628-
$target = $this->fopen($targetInternalPath, 'w');
629-
[, $result] = \OC_Helper::streamCopy($source, $target);
624+
$result = false;
625+
if ($source) {
626+
try {
627+
$this->writeStream($targetInternalPath, $source);
628+
$result = true;
629+
} catch (\Exception $e) {
630+
\OC::$server->getLogger()->logException($e, ['level' => ILogger::WARN, 'message' => 'Failed to copy stream to storage']);
631+
}
632+
}
633+
630634
if ($result and $preserveMtime) {
631635
$this->touch($targetInternalPath, $sourceStorage->filemtime($sourceInternalPath));
632636
}
633-
fclose($source);
634-
fclose($target);
635637

636638
if (!$result) {
637639
// delete partially written target file
@@ -858,10 +860,13 @@ public function needsPartFile() {
858860
public function writeStream(string $path, $stream, int $size = null): int {
859861
$target = $this->fopen($path, 'w');
860862
if (!$target) {
861-
return 0;
863+
throw new GenericFileException("Failed to open $path for writing");
862864
}
863865
try {
864866
[$count, $result] = \OC_Helper::streamCopy($stream, $target);
867+
if (!$result) {
868+
throw new GenericFileException("Failed to copy stream");
869+
}
865870
} finally {
866871
fclose($target);
867872
fclose($stream);

lib/private/Files/Storage/Local.php

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@
4444
use OC\Files\Storage\Wrapper\Jail;
4545
use OCP\Constants;
4646
use OCP\Files\ForbiddenException;
47+
use OCP\Files\GenericFileException;
4748
use OCP\Files\Storage\IStorage;
4849
use OCP\ILogger;
4950

@@ -555,6 +556,11 @@ public function moveFromStorage(IStorage $sourceStorage, $sourceInternalPath, $t
555556
}
556557

557558
public function writeStream(string $path, $stream, int $size = null): int {
558-
return (int)file_put_contents($this->getSourcePath($path), $stream);
559+
$result = file_put_contents($this->getSourcePath($path), $stream);
560+
if ($result === false) {
561+
throw new GenericFileException("Failed write steam to $path");
562+
} else {
563+
return $result;
564+
}
559565
}
560566
}

tests/lib/Files/ViewTest.php

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@
1313
use OC\Files\Mount\MountPoint;
1414
use OC\Files\Storage\Common;
1515
use OC\Files\Storage\Temporary;
16-
use OC\Files\Stream\Quota;
1716
use OC\Files\View;
1817
use OCP\Constants;
1918
use OCP\Files\Config\IMountProvider;
@@ -1167,13 +1166,8 @@ private function doTestCopyRenameFail($operation) {
11671166
->setMethods(['fopen'])
11681167
->getMock();
11691168

1170-
$storage2->expects($this->any())
1171-
->method('fopen')
1172-
->willReturnCallback(function ($path, $mode) use ($storage2) {
1173-
/** @var \PHPUnit_Framework_MockObject_MockObject | \OC\Files\Storage\Temporary $storage2 */
1174-
$source = fopen($storage2->getSourcePath($path), $mode);
1175-
return Quota::wrap($source, 9);
1176-
});
1169+
$storage2->method('writeStream')
1170+
->willReturn(0);
11771171

11781172
$storage1->mkdir('sub');
11791173
$storage1->file_put_contents('foo.txt', '0123456789ABCDEFGH');

0 commit comments

Comments
 (0)