diff options
author | Vincent Petry <vincent@nextcloud.com> | 2021-03-15 19:45:30 +0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-03-15 19:45:30 +0300 |
commit | e559afb8d409f75fdf9a216428d858d08aa1ee03 (patch) | |
tree | afea41e1f4cf5f9f25070e819ef9637267c63ec0 /lib/private/Files/Cache | |
parent | f512705f8f3ba7ff676b139bbfc00dcf6d277bd1 (diff) | |
parent | 6ecf33bfe7ef719cd979de5b29fc1da02e255632 (diff) |
Merge pull request #25136 from nextcloud/cachejail-search-filter
do cachejail search filtering in sql
Diffstat (limited to 'lib/private/Files/Cache')
-rw-r--r-- | lib/private/Files/Cache/Cache.php | 2 | ||||
-rw-r--r-- | lib/private/Files/Cache/QuerySearchHelper.php | 5 | ||||
-rw-r--r-- | lib/private/Files/Cache/Wrapper/CacheJail.php | 113 |
3 files changed, 103 insertions, 17 deletions
diff --git a/lib/private/Files/Cache/Cache.php b/lib/private/Files/Cache/Cache.php index 2513abd525f..840523c1890 100644 --- a/lib/private/Files/Cache/Cache.php +++ b/lib/private/Files/Cache/Cache.php @@ -121,7 +121,7 @@ class Cache implements ICache { $this->querySearchHelper = new QuerySearchHelper($this->mimetypeLoader); } - private function getQueryBuilder() { + protected function getQueryBuilder() { return new CacheQueryBuilder( $this->connection, \OC::$server->getSystemConfig(), diff --git a/lib/private/Files/Cache/QuerySearchHelper.php b/lib/private/Files/Cache/QuerySearchHelper.php index da1f5729d3c..fc4b582cbce 100644 --- a/lib/private/Files/Cache/QuerySearchHelper.php +++ b/lib/private/Files/Cache/QuerySearchHelper.php @@ -166,6 +166,9 @@ class QuerySearchHelper { $field = 'tag.category'; } elseif ($field === 'fileid') { $field = 'file.fileid'; + } elseif ($field === 'path' && $type === ISearchComparison::COMPARE_EQUAL) { + $field = 'path_hash'; + $value = md5((string)$value); } return [$field, $value, $type]; } @@ -175,6 +178,7 @@ class QuerySearchHelper { 'mimetype' => 'string', 'mtime' => 'integer', 'name' => 'string', + 'path' => 'string', 'size' => 'integer', 'tagname' => 'string', 'favorite' => 'boolean', @@ -184,6 +188,7 @@ class QuerySearchHelper { 'mimetype' => ['eq', 'like'], 'mtime' => ['eq', 'gt', 'lt', 'gte', 'lte'], 'name' => ['eq', 'like'], + 'path' => ['eq', 'like'], 'size' => ['eq', 'gt', 'lt', 'gte', 'lte'], 'tagname' => ['eq', 'like'], 'favorite' => ['eq'], diff --git a/lib/private/Files/Cache/Wrapper/CacheJail.php b/lib/private/Files/Cache/Wrapper/CacheJail.php index 6c1c17be028..fe3124301ba 100644 --- a/lib/private/Files/Cache/Wrapper/CacheJail.php +++ b/lib/private/Files/Cache/Wrapper/CacheJail.php @@ -29,8 +29,13 @@ namespace OC\Files\Cache\Wrapper; use OC\Files\Cache\Cache; +use OC\Files\Search\SearchBinaryOperator; +use OC\Files\Search\SearchComparison; use OC\Files\Search\SearchQuery; +use OCP\DB\QueryBuilder\IQueryBuilder; use OCP\Files\Cache\ICacheEntry; +use OCP\Files\Search\ISearchBinaryOperator; +use OCP\Files\Search\ISearchComparison; use OCP\Files\Search\ISearchQuery; /** @@ -41,6 +46,7 @@ class CacheJail extends CacheWrapper { * @var string */ protected $root; + protected $unjailedRoot; /** * @param \OCP\Files\Cache\ICache $cache @@ -49,12 +55,29 @@ class CacheJail extends CacheWrapper { public function __construct($cache, $root) { parent::__construct($cache); $this->root = $root; + $this->connection = \OC::$server->getDatabaseConnection(); + $this->mimetypeLoader = \OC::$server->getMimeTypeLoader(); + + if ($cache instanceof CacheJail) { + $this->unjailedRoot = $cache->getSourcePath($root); + } else { + $this->unjailedRoot = $root; + } } protected function getRoot() { return $this->root; } + /** + * Get the root path with any nested jails resolved + * + * @return string + */ + protected function getGetUnjailedRoot() { + return $this->unjailedRoot; + } + protected function getSourcePath($path) { if ($path === '') { return $this->getRoot(); @@ -65,16 +88,20 @@ class CacheJail extends CacheWrapper { /** * @param string $path + * @param null|string $root * @return null|string the jailed path or null if the path is outside the jail */ - protected function getJailedPath($path) { - if ($this->getRoot() === '') { + protected function getJailedPath(string $path, string $root = null) { + if ($root === null) { + $root = $this->getRoot(); + } + if ($root === '') { return $path; } - $rootLength = strlen($this->getRoot()) + 1; - if ($path === $this->getRoot()) { + $rootLength = strlen($root) + 1; + if ($path === $root) { return ''; - } elseif (substr($path, 0, $rootLength) === $this->getRoot() . '/') { + } elseif (substr($path, 0, $rootLength) === $root . '/') { return substr($path, $rootLength); } else { return null; @@ -92,11 +119,6 @@ class CacheJail extends CacheWrapper { return $entry; } - protected function filterCacheEntry($entry) { - $rootLength = strlen($this->getRoot()) + 1; - return $rootLength === 1 || ($entry['path'] === $this->getRoot()) || (substr($entry['path'], 0, $rootLength) === $this->getRoot() . '/'); - } - /** * get the stored metadata of a file or folder * @@ -209,9 +231,10 @@ class CacheJail extends CacheWrapper { } private function formatSearchResults($results) { - $results = array_filter($results, [$this, 'filterCacheEntry']); - $results = array_values($results); - return array_map([$this, 'formatCacheEntry'], $results); + return array_map(function ($entry) { + $entry['path'] = $this->getJailedPath($entry['path'], $this->getGetUnjailedRoot()); + return $entry; + }, $results); } /** @@ -221,7 +244,29 @@ class CacheJail extends CacheWrapper { * @return array an array of file data */ public function search($pattern) { - $results = $this->getCache()->search($pattern); + // normalize pattern + $pattern = $this->normalize($pattern); + + if ($pattern === '%%') { + return []; + } + + $query = $this->getQueryBuilder(); + $query->selectFileCache() + ->whereStorageId() + ->andWhere($query->expr()->orX( + $query->expr()->like('path', $query->createNamedParameter($this->getGetUnjailedRoot() . '/%')), + $query->expr()->eq('path_hash', $query->createNamedParameter(md5($this->getGetUnjailedRoot()))), + )) + ->andWhere($query->expr()->iLike('name', $query->createNamedParameter($pattern))); + + $result = $query->execute(); + $files = $result->fetchAll(); + $result->closeCursor(); + + $results = array_map(function (array $data) { + return self::cacheEntryFromData($data, $this->mimetypeLoader); + }, $files); return $this->formatSearchResults($results); } @@ -232,12 +277,48 @@ class CacheJail extends CacheWrapper { * @return array */ public function searchByMime($mimetype) { - $results = $this->getCache()->searchByMime($mimetype); + $mimeId = $this->mimetypeLoader->getId($mimetype); + + $query = $this->getQueryBuilder(); + $query->selectFileCache() + ->whereStorageId() + ->andWhere($query->expr()->orX( + $query->expr()->like('path', $query->createNamedParameter($this->getGetUnjailedRoot() . '/%')), + $query->expr()->eq('path_hash', $query->createNamedParameter(md5($this->getGetUnjailedRoot()))), + )); + + if (strpos($mimetype, '/')) { + $query->andWhere($query->expr()->eq('mimetype', $query->createNamedParameter($mimeId, IQueryBuilder::PARAM_INT))); + } else { + $query->andWhere($query->expr()->eq('mimepart', $query->createNamedParameter($mimeId, IQueryBuilder::PARAM_INT))); + } + + $result = $query->execute(); + $files = $result->fetchAll(); + $result->closeCursor(); + + $results = array_map(function (array $data) { + return self::cacheEntryFromData($data, $this->mimetypeLoader); + }, $files); return $this->formatSearchResults($results); } public function searchQuery(ISearchQuery $query) { - $simpleQuery = new SearchQuery($query->getSearchOperation(), 0, 0, $query->getOrder(), $query->getUser()); + $prefixFilter = new SearchComparison( + ISearchComparison::COMPARE_LIKE, + 'path', + $this->getGetUnjailedRoot() . '/%' + ); + $rootFilter = new SearchComparison( + ISearchComparison::COMPARE_EQUAL, + 'path', + $this->getGetUnjailedRoot() + ); + $operation = new SearchBinaryOperator( + ISearchBinaryOperator::OPERATOR_AND, + [new SearchBinaryOperator(ISearchBinaryOperator::OPERATOR_OR, [$prefixFilter, $rootFilter]) , $query->getSearchOperation()] + ); + $simpleQuery = new SearchQuery($operation, 0, 0, $query->getOrder(), $query->getUser()); $results = $this->getCache()->searchQuery($simpleQuery); $results = $this->formatSearchResults($results); |