Skip to content

Commit 3a26dda

Browse files
committed
fix(files): Also restore shares after ownership transfer for object storage
When a file is moved between different storages then the file id is not (always) preserved. This means the file id has to be adjusted for all shares. So in case the file id does not exist anymore we try to find the new file id based on the target path of the transfer and the path suffix of the share. Signed-off-by: Ferdinand Thiessen <opensource@fthiessen.de>
1 parent b4004a2 commit 3a26dda

File tree

1 file changed

+71
-39
lines changed

1 file changed

+71
-39
lines changed

apps/files/lib/Service/OwnershipTransferService.php

Lines changed: 71 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
* @author Roeland Jago Douma <roeland@famdouma.nl>
1313
* @author Sascha Wiswedel <sascha.wiswedel@nextcloud.com>
1414
* @author Tobia De Koninck <LEDfan@users.noreply.github.com>
15+
* @author Ferdinand Thiessen <opensource@fthiessen.de>
1516
*
1617
* @license GNU AGPL version 3 or any later version
1718
*
@@ -33,6 +34,7 @@
3334
namespace OCA\Files\Service;
3435

3536
use Closure;
37+
use OC\Encryption\Manager as EncryptionManager;
3638
use OC\Files\Filesystem;
3739
use OC\Files\View;
3840
use OCA\Files\Exception\TransferOwnershipException;
@@ -41,6 +43,7 @@
4143
use OCP\Files\FileInfo;
4244
use OCP\Files\IHomeStorage;
4345
use OCP\Files\InvalidPathException;
46+
use OCP\Files\IRootFolder;
4447
use OCP\Files\Mount\IMountManager;
4548
use OCP\IUser;
4649
use OCP\IUserManager;
@@ -58,31 +61,13 @@
5861

5962
class OwnershipTransferService {
6063

61-
/** @var IEncryptionManager */
62-
private $encryptionManager;
63-
64-
/** @var IShareManager */
65-
private $shareManager;
66-
67-
/** @var IMountManager */
68-
private $mountManager;
69-
70-
/** @var IUserMountCache */
71-
private $userMountCache;
72-
73-
/** @var IUserManager */
74-
private $userManager;
75-
76-
public function __construct(IEncryptionManager $manager,
77-
IShareManager $shareManager,
78-
IMountManager $mountManager,
79-
IUserMountCache $userMountCache,
80-
IUserManager $userManager) {
81-
$this->encryptionManager = $manager;
82-
$this->shareManager = $shareManager;
83-
$this->mountManager = $mountManager;
84-
$this->userMountCache = $userMountCache;
85-
$this->userManager = $userManager;
64+
public function __construct(
65+
private IEncryptionManager|EncryptionManager $encryptionManager,
66+
private IShareManager $shareManager,
67+
private IMountManager $mountManager,
68+
private IUserMountCache $userMountCache,
69+
private IUserManager $userManager,
70+
) {
8671
}
8772

8873
/**
@@ -95,13 +80,15 @@ public function __construct(IEncryptionManager $manager,
9580
* @throws TransferOwnershipException
9681
* @throws \OC\User\NoUserException
9782
*/
98-
public function transfer(IUser $sourceUser,
83+
public function transfer(
84+
IUser $sourceUser,
9985
IUser $destinationUser,
10086
string $path,
10187
?OutputInterface $output = null,
10288
bool $move = false,
10389
bool $firstLogin = false,
104-
bool $transferIncomingShares = false): void {
90+
bool $transferIncomingShares = false,
91+
): void {
10592
$output = $output ?? new NullOutput();
10693
$sourceUid = $sourceUser->getUID();
10794
$destinationUid = $destinationUser->getUID();
@@ -183,10 +170,12 @@ public function transfer(IUser $sourceUser,
183170
$output
184171
);
185172

173+
$destinationPath = $finalTarget . '/' . $path;
186174
// restore the shares
187175
$this->restoreShares(
188176
$sourceUid,
189177
$destinationUid,
178+
$destinationPath,
190179
$shares,
191180
$output
192181
);
@@ -280,16 +269,35 @@ function (FileInfo $fileInfo) use ($progress) {
280269
}
281270
}
282271

283-
private function collectUsersShares(string $sourceUid,
272+
/**
273+
* @return array<array{share: IShare, suffix: string}>
274+
*/
275+
private function collectUsersShares(
276+
string $sourceUid,
284277
OutputInterface $output,
285278
View $view,
286-
string $path): array {
279+
string $path,
280+
): array {
287281
$output->writeln("Collecting all share information for files and folders of $sourceUid ...");
288282

289283
$shares = [];
290284
$progress = new ProgressBar($output);
291285

292-
foreach ([IShare::TYPE_GROUP, IShare::TYPE_USER, IShare::TYPE_LINK, IShare::TYPE_REMOTE, IShare::TYPE_ROOM, IShare::TYPE_EMAIL, IShare::TYPE_CIRCLE, IShare::TYPE_DECK, IShare::TYPE_SCIENCEMESH] as $shareType) {
286+
$normalizedPath = Filesystem::normalizePath($path);
287+
288+
$supportedShareTypes = [
289+
IShare::TYPE_GROUP,
290+
IShare::TYPE_USER,
291+
IShare::TYPE_LINK,
292+
IShare::TYPE_REMOTE,
293+
IShare::TYPE_ROOM,
294+
IShare::TYPE_EMAIL,
295+
IShare::TYPE_CIRCLE,
296+
IShare::TYPE_DECK,
297+
IShare::TYPE_SCIENCEMESH,
298+
];
299+
300+
foreach ($supportedShareTypes as $shareType) {
293301
$offset = 0;
294302
while (true) {
295303
$sharePage = $this->shareManager->getSharesBy($sourceUid, $shareType, null, true, 50, $offset);
@@ -298,17 +306,17 @@ private function collectUsersShares(string $sourceUid,
298306
break;
299307
}
300308
if ($path !== "$sourceUid/files") {
301-
$sharePage = array_filter($sharePage, function (IShare $share) use ($view, $path) {
309+
$sharePage = array_filter($sharePage, function (IShare $share) use ($view, $normalizedPath) {
302310
try {
303311
$relativePath = $view->getPath($share->getNodeId());
304-
$singleFileTranfer = $view->is_file($path);
312+
$singleFileTranfer = $view->is_file($normalizedPath);
305313
if ($singleFileTranfer) {
306-
return Filesystem::normalizePath($relativePath) === Filesystem::normalizePath($path);
314+
return Filesystem::normalizePath($relativePath) === $normalizedPath;
307315
}
308316

309317
return mb_strpos(
310318
Filesystem::normalizePath($relativePath . '/', false),
311-
Filesystem::normalizePath($path . '/', false)) === 0;
319+
$normalizedPath . '/') === 0;
312320
} catch (\Exception $e) {
313321
return false;
314322
}
@@ -321,7 +329,11 @@ private function collectUsersShares(string $sourceUid,
321329

322330
$progress->finish();
323331
$output->writeln('');
324-
return $shares;
332+
333+
return array_map(fn (IShare $share) => [
334+
'share' => $share,
335+
'suffix' => substr(Filesystem::normalizePath($view->getPath($share->getNodeId())), strlen($normalizedPath)),
336+
], $shares);
325337
}
326338

327339
private function collectIncomingShares(string $sourceUid,
@@ -384,14 +396,22 @@ protected function transferFiles(string $sourceUid,
384396
}
385397
}
386398

387-
private function restoreShares(string $sourceUid,
399+
/**
400+
* @param string $targetLocation New location of the transfered node
401+
* @param array<array{share: IShare, suffix: string}> $shares previously collected share information
402+
*/
403+
private function restoreShares(
404+
string $sourceUid,
388405
string $destinationUid,
406+
string $targetLocation,
389407
array $shares,
390-
OutputInterface $output) {
408+
OutputInterface $output,
409+
) {
391410
$output->writeln("Restoring shares ...");
392411
$progress = new ProgressBar($output, count($shares));
412+
$rootFolder = \OCP\Server::get(IRootFolder::class);
393413

394-
foreach ($shares as $share) {
414+
foreach ($shares as ['share' => $share, 'suffix' => $suffix]) {
395415
try {
396416
if ($share->getShareType() === IShare::TYPE_USER &&
397417
$share->getSharedWith() === $destinationUid) {
@@ -419,7 +439,19 @@ private function restoreShares(string $sourceUid,
419439
// trigger refetching of the node so that the new owner and mountpoint are taken into account
420440
// otherwise the checks on the share update will fail due to the original node not being available in the new user scope
421441
$this->userMountCache->clear();
422-
$share->setNodeId($share->getNode()->getId());
442+
443+
try {
444+
// Try to get the "old" id.
445+
// Normally the ID is preserved,
446+
// but for transferes between different storages the ID might change
447+
$newNodeId = $share->getNode()->getId();
448+
} catch (\OCP\Files\NotFoundException) {
449+
// ID has changed due to transfer between different storages
450+
// Try to get the new ID from the target path and suffix of the share
451+
$node = $rootFolder->get(Filesystem::normalizePath($targetLocation . '/' . $suffix));
452+
$newNodeId = $node->getId();
453+
}
454+
$share->setNodeId($newNodeId);
423455

424456
$this->shareManager->updateShare($share);
425457
}

0 commit comments

Comments
 (0)