Cache.php 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194
  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 OCA\Files_Sharing;
  8. use OC\Files\Cache\CacheDependencies;
  9. use OC\Files\Cache\FailedCache;
  10. use OC\Files\Cache\Wrapper\CacheJail;
  11. use OC\Files\Search\SearchBinaryOperator;
  12. use OC\Files\Search\SearchComparison;
  13. use OC\Files\Storage\Wrapper\Jail;
  14. use OC\User\DisplayNameCache;
  15. use OCP\Files\Cache\ICache;
  16. use OCP\Files\Cache\ICacheEntry;
  17. use OCP\Files\Search\ISearchBinaryOperator;
  18. use OCP\Files\Search\ISearchComparison;
  19. use OCP\Files\Search\ISearchOperator;
  20. use OCP\Files\StorageNotAvailableException;
  21. use OCP\Share\IShare;
  22. /**
  23. * Metadata cache for shared files
  24. *
  25. * don't use this class directly if you need to get metadata, use \OC\Files\Filesystem::getFileInfo instead
  26. */
  27. class Cache extends CacheJail {
  28. private bool $rootUnchanged = true;
  29. private ?string $ownerDisplayName = null;
  30. private $numericId;
  31. private DisplayNameCache $displayNameCache;
  32. /**
  33. * @param SharedStorage $storage
  34. */
  35. public function __construct(
  36. private $storage,
  37. private ICacheEntry $sourceRootInfo,
  38. CacheDependencies $dependencies,
  39. private IShare $share,
  40. ) {
  41. $this->numericId = $this->sourceRootInfo->getStorageId();
  42. $this->displayNameCache = $dependencies->getDisplayNameCache();
  43. parent::__construct(
  44. null,
  45. '',
  46. $dependencies,
  47. );
  48. }
  49. protected function getRoot() {
  50. if ($this->root === '') {
  51. $absoluteRoot = $this->sourceRootInfo->getPath();
  52. // the sourceRootInfo path is the absolute path of the folder in the "real" storage
  53. // in the case where a folder is shared from a Jail we need to ensure that the share Jail
  54. // has its root set relative to the source Jail
  55. $currentStorage = $this->storage->getSourceStorage();
  56. if ($currentStorage->instanceOfStorage(Jail::class)) {
  57. /** @var Jail $currentStorage */
  58. $absoluteRoot = $currentStorage->getJailedPath($absoluteRoot);
  59. }
  60. $this->root = $absoluteRoot;
  61. }
  62. return $this->root;
  63. }
  64. protected function getGetUnjailedRoot() {
  65. return $this->sourceRootInfo->getPath();
  66. }
  67. public function getCache(): ICache {
  68. if (is_null($this->cache)) {
  69. $sourceStorage = $this->storage->getSourceStorage();
  70. if ($sourceStorage) {
  71. $this->cache = $sourceStorage->getCache();
  72. } else {
  73. // don't set $this->cache here since sourceStorage will be set later
  74. return new FailedCache();
  75. }
  76. }
  77. return $this->cache;
  78. }
  79. public function getNumericStorageId() {
  80. if (isset($this->numericId)) {
  81. return $this->numericId;
  82. } else {
  83. return -1;
  84. }
  85. }
  86. public function get($file) {
  87. if ($this->rootUnchanged && ($file === '' || $file === $this->sourceRootInfo->getId())) {
  88. return $this->formatCacheEntry(clone $this->sourceRootInfo, '');
  89. }
  90. return parent::get($file);
  91. }
  92. public function update($id, array $data) {
  93. $this->rootUnchanged = false;
  94. parent::update($id, $data);
  95. }
  96. public function insert($file, array $data) {
  97. $this->rootUnchanged = false;
  98. return parent::insert($file, $data);
  99. }
  100. public function remove($file) {
  101. $this->rootUnchanged = false;
  102. parent::remove($file);
  103. }
  104. public function moveFromCache(ICache $sourceCache, $sourcePath, $targetPath) {
  105. $this->rootUnchanged = false;
  106. return parent::moveFromCache($sourceCache, $sourcePath, $targetPath);
  107. }
  108. protected function formatCacheEntry($entry, $path = null) {
  109. if (is_null($path)) {
  110. $path = $entry['path'] ?? '';
  111. $entry['path'] = $this->getJailedPath($path);
  112. } else {
  113. $entry['path'] = $path;
  114. }
  115. try {
  116. if (isset($entry['permissions'])) {
  117. $entry['permissions'] &= $this->share->getPermissions();
  118. } else {
  119. $entry['permissions'] = $this->storage->getPermissions($entry['path']);
  120. }
  121. if ($this->share->getNodeId() === $entry['fileid']) {
  122. $entry['name'] = basename($this->share->getTarget());
  123. }
  124. } catch (StorageNotAvailableException $e) {
  125. // thrown by FailedStorage e.g. when the sharer does not exist anymore
  126. // (IDE may say the exception is never thrown – false negative)
  127. $sharePermissions = 0;
  128. }
  129. $entry['uid_owner'] = $this->share->getShareOwner();
  130. $entry['displayname_owner'] = $this->getOwnerDisplayName();
  131. if ($path === '') {
  132. $entry['is_share_mount_point'] = true;
  133. }
  134. return $entry;
  135. }
  136. private function getOwnerDisplayName() {
  137. if (!$this->ownerDisplayName) {
  138. $uid = $this->share->getShareOwner();
  139. $this->ownerDisplayName = $this->displayNameCache->getDisplayName($uid) ?? $uid;
  140. }
  141. return $this->ownerDisplayName;
  142. }
  143. /**
  144. * remove all entries for files that are stored on the storage from the cache
  145. */
  146. public function clear() {
  147. // Not a valid action for Shared Cache
  148. }
  149. public function getQueryFilterForStorage(): ISearchOperator {
  150. $storageFilter = \OC\Files\Cache\Cache::getQueryFilterForStorage();
  151. // Do the normal jail behavior for non files
  152. if ($this->storage->getItemType() !== 'file') {
  153. return $this->addJailFilterQuery($storageFilter);
  154. }
  155. // for single file shares we don't need to do the LIKE
  156. return new SearchBinaryOperator(
  157. ISearchBinaryOperator::OPERATOR_AND,
  158. [
  159. $storageFilter,
  160. new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'path', $this->getGetUnjailedRoot()),
  161. ]
  162. );
  163. }
  164. public function getCacheEntryFromSearchResult(ICacheEntry $rawEntry): ?ICacheEntry {
  165. if ($rawEntry->getStorageId() === $this->getNumericStorageId()) {
  166. return parent::getCacheEntryFromSearchResult($rawEntry);
  167. } else {
  168. return null;
  169. }
  170. }
  171. }