diff options
Diffstat (limited to 'apps/files_sharing/lib/Cache.php')
-rw-r--r-- | apps/files_sharing/lib/Cache.php | 199 |
1 files changed, 199 insertions, 0 deletions
diff --git a/apps/files_sharing/lib/Cache.php b/apps/files_sharing/lib/Cache.php new file mode 100644 index 00000000000..f9042fc0765 --- /dev/null +++ b/apps/files_sharing/lib/Cache.php @@ -0,0 +1,199 @@ +<?php + +/** + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only + */ +namespace OCA\Files_Sharing; + +use OC\Files\Cache\CacheDependencies; +use OC\Files\Cache\FailedCache; +use OC\Files\Cache\Wrapper\CacheJail; +use OC\Files\Search\SearchBinaryOperator; +use OC\Files\Search\SearchComparison; +use OC\Files\Storage\Wrapper\Jail; +use OC\User\DisplayNameCache; +use OCP\Files\Cache\ICache; +use OCP\Files\Cache\ICacheEntry; +use OCP\Files\Search\ISearchBinaryOperator; +use OCP\Files\Search\ISearchComparison; +use OCP\Files\Search\ISearchOperator; +use OCP\Files\StorageNotAvailableException; +use OCP\Share\IShare; + +/** + * Metadata cache for shared files + * + * don't use this class directly if you need to get metadata, use \OC\Files\Filesystem::getFileInfo instead + */ +class Cache extends CacheJail { + private bool $rootUnchanged = true; + private ?string $ownerDisplayName = null; + private $numericId; + private DisplayNameCache $displayNameCache; + + /** + * @param SharedStorage $storage + */ + public function __construct( + private $storage, + private ICacheEntry $sourceRootInfo, + CacheDependencies $dependencies, + private IShare $share, + ) { + $this->numericId = $this->sourceRootInfo->getStorageId(); + $this->displayNameCache = $dependencies->getDisplayNameCache(); + + parent::__construct( + null, + '', + $dependencies, + ); + } + + protected function getRoot() { + if ($this->root === '') { + $absoluteRoot = $this->sourceRootInfo->getPath(); + + // the sourceRootInfo path is the absolute path of the folder in the "real" storage + // in the case where a folder is shared from a Jail we need to ensure that the share Jail + // has its root set relative to the source Jail + $currentStorage = $this->storage->getSourceStorage(); + if ($currentStorage->instanceOfStorage(Jail::class)) { + /** @var Jail $currentStorage */ + $absoluteRoot = $currentStorage->getJailedPath($absoluteRoot); + } + $this->root = $absoluteRoot ?? ''; + } + return $this->root; + } + + public function getGetUnjailedRoot(): string { + return $this->sourceRootInfo->getPath(); + } + + public function getCache(): ICache { + if (is_null($this->cache)) { + $sourceStorage = $this->storage->getSourceStorage(); + if ($sourceStorage) { + $this->cache = $sourceStorage->getCache(); + } else { + // don't set $this->cache here since sourceStorage will be set later + return new FailedCache(); + } + } + return $this->cache; + } + + public function getNumericStorageId() { + if (isset($this->numericId)) { + return $this->numericId; + } else { + return -1; + } + } + + public function get($file) { + if ($this->rootUnchanged && ($file === '' || $file === $this->sourceRootInfo->getId())) { + return $this->formatCacheEntry(clone $this->sourceRootInfo, ''); + } + return parent::get($file); + } + + public function update($id, array $data) { + $this->rootUnchanged = false; + parent::update($id, $data); + } + + public function insert($file, array $data) { + $this->rootUnchanged = false; + return parent::insert($file, $data); + } + + public function remove($file) { + $this->rootUnchanged = false; + parent::remove($file); + } + + public function moveFromCache(ICache $sourceCache, $sourcePath, $targetPath) { + $this->rootUnchanged = false; + return parent::moveFromCache($sourceCache, $sourcePath, $targetPath); + } + + protected function formatCacheEntry($entry, $path = null) { + if (is_null($path)) { + $path = $entry['path'] ?? ''; + $entry['path'] = $this->getJailedPath($path); + } else { + $entry['path'] = $path; + } + + try { + if (isset($entry['permissions'])) { + $entry['permissions'] &= $this->share->getPermissions(); + } else { + $entry['permissions'] = $this->storage->getPermissions($entry['path']); + } + + if ($this->share->getNodeId() === $entry['fileid']) { + $entry['name'] = basename($this->share->getTarget()); + } + } catch (StorageNotAvailableException $e) { + // thrown by FailedStorage e.g. when the sharer does not exist anymore + // (IDE may say the exception is never thrown – false negative) + $sharePermissions = 0; + } + $entry['uid_owner'] = $this->share->getShareOwner(); + $entry['displayname_owner'] = $this->getOwnerDisplayName(); + if ($path === '') { + $entry['is_share_mount_point'] = true; + } + return $entry; + } + + private function getOwnerDisplayName() { + if (!$this->ownerDisplayName) { + $uid = $this->share->getShareOwner(); + $this->ownerDisplayName = $this->displayNameCache->getDisplayName($uid) ?? $uid; + } + return $this->ownerDisplayName; + } + + /** + * remove all entries for files that are stored on the storage from the cache + */ + public function clear() { + // Not a valid action for Shared Cache + } + + public function getQueryFilterForStorage(): ISearchOperator { + $storageFilter = \OC\Files\Cache\Cache::getQueryFilterForStorage(); + + // Do the normal jail behavior for non files + if ($this->storage->getItemType() !== 'file') { + return $this->addJailFilterQuery($storageFilter); + } + + // for single file shares we don't need to do the LIKE + return new SearchBinaryOperator( + ISearchBinaryOperator::OPERATOR_AND, + [ + $storageFilter, + new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'path', $this->getGetUnjailedRoot()), + ] + ); + } + + public function getCacheEntryFromSearchResult(ICacheEntry $rawEntry): ?ICacheEntry { + if ($rawEntry->getStorageId() === $this->getNumericStorageId()) { + return parent::getCacheEntryFromSearchResult($rawEntry); + } else { + return null; + } + } + + public function markRootChanged(): void { + $this->rootUnchanged = false; + } +} |