RepairInvalidShares.php 2.9 KB

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