Skip to content

Commit a3e8700

Browse files
authored
Merge pull request #57927 from nextcloud/share-target-repair-no-providers-33
[stable33] fix: don't rely on share providers being avaiable in CleanupShareTarget
2 parents 39740e0 + 78b4a8f commit a3e8700

2 files changed

Lines changed: 55 additions & 31 deletions

File tree

apps/files_sharing/lib/Repair/CleanupShareTarget.php

Lines changed: 49 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -11,15 +11,18 @@
1111
use OC\Files\SetupManager;
1212
use OCA\Files_Sharing\ShareTargetValidator;
1313
use OCP\DB\QueryBuilder\IQueryBuilder;
14+
use OCP\Files\IRootFolder;
1415
use OCP\Files\Mount\IMountManager;
1516
use OCP\IDBConnection;
1617
use OCP\IUserManager;
1718
use OCP\Migration\IOutput;
1819
use OCP\Migration\IRepairStep;
19-
use OCP\Share\IManager;
20-
use OCP\Share\IProviderFactory;
2120
use OCP\Share\IShare;
21+
use Psr\Log\LoggerInterface;
2222

23+
/**
24+
* @psalm-type ShareInfo = array{id: string|int, share_type: string, share_with: string, file_source: string, file_target: string}
25+
*/
2326
class CleanupShareTarget implements IRepairStep {
2427
/** we only care about shares with a user target,
2528
* since the underling group/deck/talk share doesn't get moved
@@ -33,12 +36,12 @@ class CleanupShareTarget implements IRepairStep {
3336

3437
public function __construct(
3538
private readonly IDBConnection $connection,
36-
private readonly IManager $shareManager,
37-
private readonly IProviderFactory $shareProviderFactory,
3839
private readonly ShareTargetValidator $shareTargetValidator,
3940
private readonly IUserManager $userManager,
4041
private readonly SetupManager $setupManager,
4142
private readonly IMountManager $mountManager,
43+
private readonly IRootFolder $rootFolder,
44+
private readonly LoggerInterface $logger,
4245
) {
4346
}
4447

@@ -57,12 +60,13 @@ public function run(IOutput $output) {
5760

5861
$lastUser = '';
5962
$userMounts = [];
63+
$userFolder = null;
6064

6165
foreach ($this->getProblemShares() as $shareInfo) {
6266
$recipient = $this->userManager->getExistingUser($shareInfo['share_with']);
63-
$share = $this->shareProviderFactory
64-
->getProviderForType((int)$shareInfo['share_type'])
65-
->getShareById($shareInfo['id'], $recipient->getUID());
67+
if (!$recipient->isEnabled()) {
68+
continue;
69+
}
6670

6771
// since we ordered the share by user, we can reuse the last data until we get to the next user
6872
if ($lastUser !== $recipient->getUID()) {
@@ -71,24 +75,34 @@ public function run(IOutput $output) {
7175
$this->setupManager->tearDown();
7276
$this->setupManager->setupForUser($recipient);
7377
$userMounts = $this->mountManager->getAll();
78+
$userFolder = $this->rootFolder->getUserFolder($recipient->getUID());
7479
}
7580

76-
$oldTarget = $share->getTarget();
81+
$oldTarget = $shareInfo['file_target'];
7782
$newTarget = $this->cleanTarget($oldTarget);
78-
$share->setTarget($newTarget);
79-
$this->shareManager->moveShare($share, $recipient->getUID());
80-
81-
$this->shareTargetValidator->verifyMountPoint(
82-
$recipient,
83-
$share,
84-
$userMounts,
85-
[$share],
86-
);
87-
88-
$oldMountPoint = "/{$recipient->getUID()}/files$oldTarget/";
89-
$newMountPoint = "/{$recipient->getUID()}/files$newTarget/";
90-
$userMounts[$newMountPoint] = $userMounts[$oldMountPoint];
91-
unset($userMounts[$oldMountPoint]);
83+
$absoluteNewTarget = $userFolder->getFullPath($newTarget);
84+
$targetParentNode = $this->rootFolder->get(dirname($absoluteNewTarget));
85+
86+
try {
87+
$absoluteNewTarget = $this->shareTargetValidator->generateUniqueTarget(
88+
(int)$shareInfo['file_source'],
89+
$absoluteNewTarget,
90+
$targetParentNode->getMountPoint(),
91+
$userMounts,
92+
);
93+
$newTarget = $userFolder->getRelativePath($absoluteNewTarget);
94+
95+
$this->moveShare((string)$shareInfo['id'], $newTarget);
96+
97+
$oldMountPoint = "/{$recipient->getUID()}/files$oldTarget/";
98+
$newMountPoint = "/{$recipient->getUID()}/files$newTarget/";
99+
$userMounts[$newMountPoint] = $userMounts[$oldMountPoint];
100+
unset($userMounts[$oldMountPoint]);
101+
} catch (\Exception $e) {
102+
$msg = 'error cleaning up share target: ' . $e->getMessage();
103+
$this->logger->error($msg, ['exception' => $e, 'app' => 'files_sharing']);
104+
$output->warning($msg);
105+
}
92106

93107
$output->advance();
94108
}
@@ -105,19 +119,29 @@ private function countProblemShares(): int {
105119
return (int)$query->executeQuery()->fetchOne();
106120
}
107121

122+
private function moveShare(string $id, string $target) {
123+
// since we only process user-specific shares, we can just move them
124+
// without having to check if we need to create a user-specific override
125+
$query = $this->connection->getQueryBuilder();
126+
$query->update('share')
127+
->set('file_target', $query->createNamedParameter($target))
128+
->where($query->expr()->eq('id', $query->createNamedParameter($id)))
129+
->executeStatement();
130+
}
131+
108132
/**
109-
* @return \Traversable<array{id: string, share_type: string, share_with: string}>
133+
* @return \Traversable<ShareInfo>
110134
*/
111135
private function getProblemShares(): \Traversable {
112136
$query = $this->connection->getQueryBuilder();
113-
$query->select('id', 'share_type', 'share_with')
137+
$query->select('id', 'share_type', 'share_with', 'file_source', 'file_target')
114138
->from('share')
115139
->where($query->expr()->like('file_target', $query->createNamedParameter('% (_) (_)%')))
116140
->andWhere($query->expr()->in('share_type', $query->createNamedParameter(self::USER_SHARE_TYPES, IQueryBuilder::PARAM_INT_ARRAY), IQueryBuilder::PARAM_INT_ARRAY))
117141
->orderBy('share_with')
118142
->addOrderBy('id');
119143
$result = $query->executeQuery();
120-
/** @var \Traversable<array{id: string, share_type: string, share_with: string}> $rows */
144+
/** @var \Traversable<ShareInfo> $rows */
121145
$rows = $result->iterateAssociative();
122146
return $rows;
123147
}

apps/files_sharing/lib/ShareTargetValidator.php

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ public function verifyMountPoint(
8484
}
8585

8686
$newAbsoluteMountPoint = $this->generateUniqueTarget(
87-
$share,
87+
$share->getNodeId(),
8888
Filesystem::normalizePath($absoluteParent . '/' . $mountPoint),
8989
$parentMount,
9090
$allCachedMounts,
@@ -107,8 +107,8 @@ public function verifyMountPoint(
107107
/**
108108
* @param IMountPoint[] $allCachedMounts
109109
*/
110-
private function generateUniqueTarget(
111-
IShare $share,
110+
public function generateUniqueTarget(
111+
int $shareNodeId,
112112
string $absolutePath,
113113
IMountPoint $parentMount,
114114
array $allCachedMounts,
@@ -121,7 +121,7 @@ private function generateUniqueTarget(
121121
$i = 2;
122122
$parentCache = $parentMount->getStorage()->getCache();
123123
$internalPath = $parentMount->getInternalPath($absolutePath);
124-
while ($parentCache->inCache($internalPath) || $this->hasConflictingMount($share, $allCachedMounts, $absolutePath)) {
124+
while ($parentCache->inCache($internalPath) || $this->hasConflictingMount($shareNodeId, $allCachedMounts, $absolutePath)) {
125125
$absolutePath = Filesystem::normalizePath($dir . '/' . $name . ' (' . $i . ')' . $ext);
126126
$internalPath = $parentMount->getInternalPath($absolutePath);
127127
$i++;
@@ -133,13 +133,13 @@ private function generateUniqueTarget(
133133
/**
134134
* @param IMountPoint[] $allCachedMounts
135135
*/
136-
private function hasConflictingMount(IShare $share, array $allCachedMounts, string $absolutePath): bool {
136+
private function hasConflictingMount(int $shareNodeId, array $allCachedMounts, string $absolutePath): bool {
137137
if (!isset($allCachedMounts[$absolutePath . '/'])) {
138138
return false;
139139
}
140140

141141
$mount = $allCachedMounts[$absolutePath . '/'];
142-
if ($mount instanceof SharedMount && $mount->getShare()->getNodeId() === $share->getNodeId()) {
142+
if ($mount instanceof SharedMount && $mount->getShare()->getNodeId() === $shareNodeId) {
143143
// "conflicting" mount is a mount for the current share
144144
return false;
145145
}

0 commit comments

Comments
 (0)