Skip to content

Commit 8fa7607

Browse files
susnuxbackportbot[bot]
authored andcommitted
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 <[email protected]>
1 parent 20275f5 commit 8fa7607

File tree

1 file changed

+73
-38
lines changed

1 file changed

+73
-38
lines changed

apps/files/lib/Service/OwnershipTransferService.php

Lines changed: 73 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
* @author Roeland Jago Douma <[email protected]>
1313
* @author Sascha Wiswedel <[email protected]>
1414
* @author Tobia De Koninck <[email protected]>
15+
* @author Ferdinand Thiessen <[email protected]>
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,16 @@
5861

5962
class OwnershipTransferService {
6063

61-
/** @var IEncryptionManager */
62-
private $encryptionManager;
64+
private IEncryptionManager|EncryptionManager $encryptionManager;
6365

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;
66+
public function __construct(
67+
IEncryptionManager $encryptionManager,
68+
private IShareManager $shareManager,
69+
private IMountManager $mountManager,
70+
private IUserMountCache $userMountCache,
71+
private IUserManager $userManager,
72+
) {
73+
$this->encryptionManager = $encryptionManager;
8674
}
8775

8876
/**
@@ -95,13 +83,15 @@ public function __construct(IEncryptionManager $manager,
9583
* @throws TransferOwnershipException
9684
* @throws \OC\User\NoUserException
9785
*/
98-
public function transfer(IUser $sourceUser,
86+
public function transfer(
87+
IUser $sourceUser,
9988
IUser $destinationUser,
10089
string $path,
10190
?OutputInterface $output = null,
10291
bool $move = false,
10392
bool $firstLogin = false,
104-
bool $transferIncomingShares = false): void {
93+
bool $transferIncomingShares = false,
94+
): void {
10595
$output = $output ?? new NullOutput();
10696
$sourceUid = $sourceUser->getUID();
10797
$destinationUid = $destinationUser->getUID();
@@ -183,10 +173,12 @@ public function transfer(IUser $sourceUser,
183173
$output
184174
);
185175

176+
$destinationPath = $finalTarget . '/' . $path;
186177
// restore the shares
187178
$this->restoreShares(
188179
$sourceUid,
189180
$destinationUid,
181+
$destinationPath,
190182
$shares,
191183
$output
192184
);
@@ -280,16 +272,35 @@ function (FileInfo $fileInfo) use ($progress) {
280272
}
281273
}
282274

283-
private function collectUsersShares(string $sourceUid,
275+
/**
276+
* @return array<array{share: IShare, suffix: string}>
277+
*/
278+
private function collectUsersShares(
279+
string $sourceUid,
284280
OutputInterface $output,
285281
View $view,
286-
string $path): array {
282+
string $path,
283+
): array {
287284
$output->writeln("Collecting all share information for files and folders of $sourceUid ...");
288285

289286
$shares = [];
290287
$progress = new ProgressBar($output);
291288

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) {
289+
$normalizedPath = Filesystem::normalizePath($path);
290+
291+
$supportedShareTypes = [
292+
IShare::TYPE_GROUP,
293+
IShare::TYPE_USER,
294+
IShare::TYPE_LINK,
295+
IShare::TYPE_REMOTE,
296+
IShare::TYPE_ROOM,
297+
IShare::TYPE_EMAIL,
298+
IShare::TYPE_CIRCLE,
299+
IShare::TYPE_DECK,
300+
IShare::TYPE_SCIENCEMESH,
301+
];
302+
303+
foreach ($supportedShareTypes as $shareType) {
293304
$offset = 0;
294305
while (true) {
295306
$sharePage = $this->shareManager->getSharesBy($sourceUid, $shareType, null, true, 50, $offset);
@@ -298,17 +309,17 @@ private function collectUsersShares(string $sourceUid,
298309
break;
299310
}
300311
if ($path !== "$sourceUid/files") {
301-
$sharePage = array_filter($sharePage, function (IShare $share) use ($view, $path) {
312+
$sharePage = array_filter($sharePage, function (IShare $share) use ($view, $normalizedPath) {
302313
try {
303314
$relativePath = $view->getPath($share->getNodeId());
304-
$singleFileTranfer = $view->is_file($path);
315+
$singleFileTranfer = $view->is_file($normalizedPath);
305316
if ($singleFileTranfer) {
306-
return Filesystem::normalizePath($relativePath) === Filesystem::normalizePath($path);
317+
return Filesystem::normalizePath($relativePath) === $normalizedPath;
307318
}
308319

309320
return mb_strpos(
310321
Filesystem::normalizePath($relativePath . '/', false),
311-
Filesystem::normalizePath($path . '/', false)) === 0;
322+
$normalizedPath . '/') === 0;
312323
} catch (\Exception $e) {
313324
return false;
314325
}
@@ -321,7 +332,11 @@ private function collectUsersShares(string $sourceUid,
321332

322333
$progress->finish();
323334
$output->writeln('');
324-
return $shares;
335+
336+
return array_map(fn (IShare $share) => [
337+
'share' => $share,
338+
'suffix' => substr(Filesystem::normalizePath($view->getPath($share->getNodeId())), strlen($normalizedPath)),
339+
], $shares);
325340
}
326341

327342
private function collectIncomingShares(string $sourceUid,
@@ -384,14 +399,22 @@ protected function transferFiles(string $sourceUid,
384399
}
385400
}
386401

387-
private function restoreShares(string $sourceUid,
402+
/**
403+
* @param string $targetLocation New location of the transfered node
404+
* @param array<array{share: IShare, suffix: string}> $shares previously collected share information
405+
*/
406+
private function restoreShares(
407+
string $sourceUid,
388408
string $destinationUid,
409+
string $targetLocation,
389410
array $shares,
390-
OutputInterface $output) {
411+
OutputInterface $output,
412+
):void {
391413
$output->writeln("Restoring shares ...");
392414
$progress = new ProgressBar($output, count($shares));
415+
$rootFolder = \OCP\Server::get(IRootFolder::class);
393416

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

424459
$this->shareManager->updateShare($share);
425460
}

0 commit comments

Comments
 (0)