Skip to content

Commit 247ec7e

Browse files
authored
Merge pull request #2797 from nextcloud/backport/2784/stable33
[stable33] fix: do not fail moves when the old location cannot be resolved
2 parents fc2eb85 + 719068c commit 247ec7e

2 files changed

Lines changed: 87 additions & 14 deletions

File tree

lib/FilesHooks.php

Lines changed: 28 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ class FilesHooks {
4343

4444
/** @var string|bool */
4545
protected $moveCase = false;
46-
/** @var array */
46+
/** @var array|null */
4747
protected $oldAccessList;
4848
/** @var string */
4949
protected $oldParentPath;
@@ -275,22 +275,30 @@ public function fileMove($oldPath, $newPath) {
275275
$this->moveCase = 'moveCross';
276276
}
277277

278-
[$this->oldParentPath, $this->oldParentOwner, $this->oldParentId] = $this->getSourcePathAndOwner($oldDir);
279-
if ($this->oldParentId === 0) {
280-
// Could not find the file for the owner ...
281-
$this->moveCase = false;
282-
return;
283-
}
278+
try {
279+
[$this->oldParentPath, $this->oldParentOwner, $this->oldParentId] = $this->getSourcePathAndOwner($oldDir);
280+
if ($this->oldParentId === 0) {
281+
// Could not find the file for the owner ...
282+
$this->moveCase = false;
283+
return;
284+
}
284285

285-
$oldAccessList = $this->getUserPathsFromPath($this->oldParentPath, $this->oldParentOwner);
286+
$oldAccessList = $this->getUserPathsFromPath($this->oldParentPath, $this->oldParentOwner);
286287

287-
// file can be shared using GroupFolders, including ACL check
288-
if ($this->config->getSystemValueBool('activity_use_cached_mountpoints', false)) {
289-
[, , $oldFileId] = $this->getSourcePathAndOwner($oldPath);
290-
$oldAccessList['users'] = array_merge($oldAccessList['users'], $this->getAffectedUsersFromCachedMounts($oldFileId));
291-
}
288+
// file can be shared using GroupFolders, including ACL check
289+
if ($this->config->getSystemValueBool('activity_use_cached_mountpoints', false)) {
290+
[, , $oldFileId] = $this->getSourcePathAndOwner($oldPath);
291+
$oldAccessList['users'] = array_merge($oldAccessList['users'], $this->getAffectedUsersFromCachedMounts($oldFileId));
292+
}
292293

293-
$this->oldAccessList = $oldAccessList;
294+
$this->oldAccessList = $oldAccessList;
295+
} catch (NotFoundException $e) {
296+
// The old location cannot be resolved (e.g. inconsistent mount or file
297+
// cache state), so fileMovePost() would have no valid data to build
298+
// activities from. Skip it instead of failing the move half-way.
299+
$this->logger->warning('Could not resolve the old location of "' . $oldPath . '", no move activities will be created', ['exception' => $e]);
300+
$this->moveCase = false;
301+
}
294302
}
295303

296304

@@ -392,6 +400,12 @@ protected function fileRenaming($oldPath, $newPath) {
392400
* @param string $newPath
393401
*/
394402
protected function fileMoving($oldPath, $newPath) {
403+
if (!is_array($this->oldAccessList)) {
404+
// fileMove() could not collect the old access list, so there is no
405+
// base to compute the activities from
406+
return;
407+
}
408+
395409
$dirName = dirname($newPath);
396410
$fileName = basename($newPath);
397411
$oldFileName = basename($oldPath);

tests/FilesHooksTest.php

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -481,6 +481,65 @@ public function testAddNotificationsForFileAction(array $filterUsers, bool $moun
481481
$this->assertEquals($addCalls, array_slice($receivedActivities, 0, count($addCalls)));
482482
}
483483

484+
public function testFileMoveCollectsOldAccessList(): void {
485+
$filesHooks = $this->getFilesHooks([
486+
'getSourcePathAndOwner',
487+
'getUserPathsFromPath',
488+
]);
489+
490+
$filesHooks->expects($this->once())
491+
->method('getSourcePathAndOwner')
492+
->with('/folder')
493+
->willReturn(['/folder', 'owner', 23]);
494+
$filesHooks->expects($this->once())
495+
->method('getUserPathsFromPath')
496+
->with('/folder', 'owner')
497+
->willReturn(['users' => ['user' => '/folder'], 'remotes' => []]);
498+
499+
$filesHooks->fileMove('/folder/file.txt', '/target/file.txt');
500+
501+
$this->assertSame('moveCross', self::invokePrivate($filesHooks, 'moveCase'));
502+
$this->assertSame(['users' => ['user' => '/folder'], 'remotes' => []], self::invokePrivate($filesHooks, 'oldAccessList'));
503+
}
504+
505+
public function testFileMoveOldPathNotResolvable(): void {
506+
$filesHooks = $this->getFilesHooks([
507+
'getSourcePathAndOwner',
508+
'getUserPathsFromPath',
509+
'fileRenaming',
510+
'fileMoving',
511+
]);
512+
513+
$filesHooks->expects($this->once())
514+
->method('getSourcePathAndOwner')
515+
->with('/folder')
516+
->willThrowException(new NotFoundException('File with id "1337" has not been found.'));
517+
$filesHooks->expects($this->never())
518+
->method('getUserPathsFromPath');
519+
$filesHooks->expects($this->never())
520+
->method('fileRenaming');
521+
$filesHooks->expects($this->never())
522+
->method('fileMoving');
523+
524+
$filesHooks->fileMove('/folder/file.txt', '/target/file.txt');
525+
526+
$this->assertFalse(self::invokePrivate($filesHooks, 'moveCase'));
527+
528+
// the post hook must be a no-op instead of failing on the missing state
529+
$filesHooks->fileMovePost('/folder/file.txt', '/target/file.txt');
530+
}
531+
532+
public function testFileMovingWithoutOldAccessList(): void {
533+
$filesHooks = $this->getFilesHooks([
534+
'getSourcePathAndOwner',
535+
]);
536+
537+
$filesHooks->expects($this->never())
538+
->method('getSourcePathAndOwner');
539+
540+
self::invokePrivate($filesHooks, 'fileMoving', ['/folder/file.txt', '/target/file.txt']);
541+
}
542+
484543
private function getNodeMock(int $fileId = 1337, string $path = 'path', bool $isFile = true): Node&MockObject {
485544
if ($isFile) {
486545
$node = $this->createMock(File::class);

0 commit comments

Comments
 (0)