RepairInvalidShares.php 2.7 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495
  1. <?php
  2. /**
  3. * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
  4. * SPDX-FileCopyrightText: 2016 ownCloud, Inc.
  5. * SPDX-License-Identifier: AGPL-3.0-only
  6. */
  7. namespace OC\Repair;
  8. use OCP\IConfig;
  9. use OCP\IDBConnection;
  10. use OCP\Migration\IOutput;
  11. use OCP\Migration\IRepairStep;
  12. /**
  13. * Repairs shares with invalid data
  14. */
  15. class RepairInvalidShares implements IRepairStep {
  16. public const CHUNK_SIZE = 200;
  17. public function __construct(
  18. protected IConfig $config,
  19. protected IDBConnection $connection,
  20. ) {
  21. }
  22. public function getName() {
  23. return 'Repair invalid shares';
  24. }
  25. /**
  26. * Adjust file share permissions
  27. */
  28. private function adjustFileSharePermissions(IOutput $out) {
  29. $mask = \OCP\Constants::PERMISSION_READ | \OCP\Constants::PERMISSION_UPDATE | \OCP\Constants::PERMISSION_SHARE;
  30. $builder = $this->connection->getQueryBuilder();
  31. $permsFunc = $builder->expr()->bitwiseAnd('permissions', $mask);
  32. $builder
  33. ->update('share')
  34. ->set('permissions', $permsFunc)
  35. ->where($builder->expr()->eq('item_type', $builder->expr()->literal('file')))
  36. ->andWhere($builder->expr()->neq('permissions', $permsFunc));
  37. $updatedEntries = $builder->executeStatement();
  38. if ($updatedEntries > 0) {
  39. $out->info('Fixed file share permissions for ' . $updatedEntries . ' shares');
  40. }
  41. }
  42. /**
  43. * Remove shares where the parent share does not exist anymore
  44. */
  45. private function removeSharesNonExistingParent(IOutput $out) {
  46. $deletedEntries = 0;
  47. $query = $this->connection->getQueryBuilder();
  48. $query->select('s1.parent')
  49. ->from('share', 's1')
  50. ->where($query->expr()->isNotNull('s1.parent'))
  51. ->andWhere($query->expr()->isNull('s2.id'))
  52. ->leftJoin('s1', 'share', 's2', $query->expr()->eq('s1.parent', 's2.id'))
  53. ->groupBy('s1.parent')
  54. ->setMaxResults(self::CHUNK_SIZE);
  55. $deleteQuery = $this->connection->getQueryBuilder();
  56. $deleteQuery->delete('share')
  57. ->where($deleteQuery->expr()->eq('parent', $deleteQuery->createParameter('parent')));
  58. $deletedInLastChunk = self::CHUNK_SIZE;
  59. while ($deletedInLastChunk === self::CHUNK_SIZE) {
  60. $deletedInLastChunk = 0;
  61. $result = $query->executeQuery();
  62. while ($row = $result->fetch()) {
  63. $deletedInLastChunk++;
  64. $deletedEntries += $deleteQuery->setParameter('parent', (int)$row['parent'])
  65. ->executeStatement();
  66. }
  67. $result->closeCursor();
  68. }
  69. if ($deletedEntries) {
  70. $out->info('Removed ' . $deletedEntries . ' shares where the parent did not exist');
  71. }
  72. }
  73. public function run(IOutput $out) {
  74. $ocVersionFromBeforeUpdate = $this->config->getSystemValueString('version', '0.0.0');
  75. if (version_compare($ocVersionFromBeforeUpdate, '12.0.0.11', '<')) {
  76. $this->adjustFileSharePermissions($out);
  77. }
  78. $this->removeSharesNonExistingParent($out);
  79. }
  80. }