镜像站点 · 本页由第三方 GitHub 只读镜像提供,非 GitHub 官方站点,不接受任何登录或凭据输入。前往 github.com
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
845b780
feat(IFileAccess#getByAncestorInStorage): Add new method to retrieve …
marcelklehr Mar 31, 2025
3eef614
feat(IFileAccess#getMounts): Add new method to retrieve all distinct …
marcelklehr Mar 31, 2025
611d83a
fix: Fix psalm issues
marcelklehr Mar 31, 2025
d8c6f8d
fix(FileAccess): Address review comments
marcelklehr Apr 1, 2025
5689af5
fix(FileAccess): Run cs:fix
marcelklehr Apr 1, 2025
d67c877
fix(FileAccess): Add tests
marcelklehr Apr 1, 2025
10cc430
fix(FileAccess#getByAncestorInStorage): Use a subquery to fix tests
marcelklehr Apr 3, 2025
34b3f75
fix(FileAccess#getDistinctMounts): Order results deterministically
marcelklehr Apr 4, 2025
3941622
fix(FileAccessTest): Make sure path_hash is not NULL
marcelklehr Apr 4, 2025
895160a
fix(FileAccessTest): Do not use LIMIT in subquery
marcelklehr Apr 4, 2025
26f6013
fix(FileAccessTest): Make it work on sharded instance
marcelklehr Apr 4, 2025
131125b
fix(FileAccessTest): Adress review comments
marcelklehr Apr 10, 2025
7e98698
fix(FileAccess*): Adress review comments
julien-nc May 28, 2025
28dc4a2
fix(FileAccess): exclude trashbin nodes on the oc_filecache query, th…
julien-nc Jun 2, 2025
a2aeec0
fix(FileAccess*): Adress review comments
julien-nc Jun 3, 2025
cb221c8
fix(FileAccess*): Change the way home dir root is found, remove the e…
julien-nc Jun 3, 2025
3a96f8e
fix(FileAccess*): fix tests
julien-nc Jun 3, 2025
43be97d
fix(FileAccess): Use one param for rewriting home dirs and excluding …
marcelklehr Jul 7, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
125 changes: 125 additions & 0 deletions lib/private/Files/Cache/FileAccess.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

use OC\FilesMetadata\FilesMetadataManager;
use OC\SystemConfig;
use OCP\DB\Exception;
use OCP\DB\QueryBuilder\IQueryBuilder;
use OCP\Files\Cache\IFileAccess;
use OCP\Files\IMimeTypeLoader;
Expand Down Expand Up @@ -94,4 +95,128 @@ public function getByFileIdsInStorage(array $fileIds, int $storageId): array {
$rows = $query->executeQuery()->fetchAll();
return $this->rowsToEntries($rows);
}

public function getByAncestorInStorage(int $storageId, int $folderId, int $fileIdCursor = 0, int $maxResults = 100, array $mimeTypeIds = [], bool $endToEndEncrypted = true, bool $serverSideEncrypted = true): \Generator {
$qb = $this->getQuery();
$qb->select('path')
->from('filecache')
->where($qb->expr()->eq('fileid', $qb->createNamedParameter($folderId, IQueryBuilder::PARAM_INT)));
$result = $qb->executeQuery();
/** @var array{path:string}|false $root */
$root = $result->fetch();
$result->closeCursor();

if ($root === false) {
throw new Exception('Could not fetch storage root');
}

$qb = $this->getQuery();

$path = $root['path'] === '' ? '' : $root['path'] . '/';

$qb->selectDistinct('*')
->from('filecache', 'f')
->where($qb->expr()->like('f.path', $qb->createNamedParameter($this->connection->escapeLikeParameter($path) . '%')))
->andWhere($qb->expr()->eq('f.storage', $qb->createNamedParameter($storageId)))
->andWhere($qb->expr()->gt('f.fileid', $qb->createNamedParameter($fileIdCursor, IQueryBuilder::PARAM_INT)));

if (!$endToEndEncrypted) {
// End to end encrypted files are descendants of a folder with encrypted=1
// Use a subquery to check the `encrypted` status of the parent folder
$subQuery = $this->getQuery()->select('p.encrypted')
->from('filecache', 'p')
->andWhere($qb->expr()->eq('p.fileid', 'f.parent'))
->getSQL();

$qb->andWhere(
$qb->expr()->eq($qb->createFunction(sprintf('(%s)', $subQuery)), $qb->createNamedParameter(0, IQueryBuilder::PARAM_INT))
Comment thread
provokateurin marked this conversation as resolved.
Resolved
);
}

if (!$serverSideEncrypted) {
// Server side encrypted files have encrypted=1 directly
$qb->andWhere($qb->expr()->eq('f.encrypted', $qb->createNamedParameter(0, IQueryBuilder::PARAM_INT)));
}

if (count($mimeTypeIds) > 0) {
$qb->andWhere($qb->expr()->in('f.mimetype', $qb->createNamedParameter($mimeTypeIds, IQueryBuilder::PARAM_INT_ARRAY)));
}

if ($maxResults !== 0) {
$qb->setMaxResults($maxResults);
}
$qb->orderBy('f.fileid', 'ASC');
$files = $qb->executeQuery();

while (
/** @var array */
$row = $files->fetch()
) {
yield Cache::cacheEntryFromData($row, $this->mimeTypeLoader);
}

$files->closeCursor();
}

public function getDistinctMounts(array $mountProviders = [], bool $onlyUserFilesMounts = true): \Generator {
$qb = $this->connection->getQueryBuilder();
$qb->selectDistinct(['root_id', 'storage_id', 'mount_provider_class'])
->from('mounts');
if ($onlyUserFilesMounts) {
$qb->andWhere(
$qb->expr()->orX(
$qb->expr()->like('mount_point', $qb->createNamedParameter('/%/files/%')),
$qb->expr()->in('mount_provider_class', $qb->createNamedParameter([
\OC\Files\Mount\LocalHomeMountProvider::class,
\OC\Files\Mount\ObjectHomeMountProvider::class,
], IQueryBuilder::PARAM_STR_ARRAY))
)
);
}
if (count($mountProviders) > 0) {
$qb->andWhere($qb->expr()->in('mount_provider_class', $qb->createNamedParameter($mountProviders, IQueryBuilder::PARAM_STR_ARRAY)));
}
$qb->orderBy('root_id', 'ASC');
$result = $qb->executeQuery();

while (
/** @var array{storage_id:int, root_id:int,mount_provider_class:string} $row */
$row = $result->fetch()
) {
$storageId = (int)$row['storage_id'];
$rootId = (int)$row['root_id'];
$overrideRoot = $rootId;
// LocalHomeMountProvider is the default provider for user home directories
// ObjectHomeMountProvider is the home directory provider for when S3 primary storage is used
if ($onlyUserFilesMounts && in_array($row['mount_provider_class'], [
\OC\Files\Mount\LocalHomeMountProvider::class,
\OC\Files\Mount\ObjectHomeMountProvider::class,
Comment thread
marcelklehr marked this conversation as resolved.
Resolved
], true)) {
// Only crawl files, not cache or trashbin
$qb = $this->getQuery();
try {
$qb->select('fileid')
->from('filecache')
->where($qb->expr()->eq('storage', $qb->createNamedParameter($storageId, IQueryBuilder::PARAM_INT)))
->andWhere($qb->expr()->eq('parent', $qb->createNamedParameter($rootId, IQueryBuilder::PARAM_INT)))
->andWhere($qb->expr()->eq('path', $qb->createNamedParameter('files')));
/** @var array|false $root */
$root = $qb->executeQuery()->fetch();
if ($root !== false) {
$overrideRoot = (int)$root['fileid'];
}
} catch (Exception $e) {
$this->logger->error('Could not fetch home storage files root for storage ' . $storageId, ['exception' => $e]);
continue;
}
}
// Reference to root_id is still necessary even if we have the overridden_root_id, because storage_id and root_id uniquely identify a mount
yield [
'storage_id' => $storageId,
'root_id' => $rootId,
Comment thread
marcelklehr marked this conversation as resolved.
Resolved
'overridden_root' => $overrideRoot,
];
}
$result->closeCursor();
}
}
32 changes: 32 additions & 0 deletions lib/public/Files/Cache/IFileAccess.php
Original file line number Diff line number Diff line change
Expand Up @@ -79,4 +79,36 @@ public function getByFileIds(array $fileIds): array;
* @since 29.0.0
*/
public function getByFileIdsInStorage(array $fileIds, int $storageId): array;

/**
* Retrieves files stored in a specific storage that have a specified ancestor in the file hierarchy.
* Allows filtering by mime types, encryption status, and limits the number of results.
*
* @param int $storageId The ID of the storage to search within.
* @param int $folderId The file ID of the ancestor to base the search on.
* @param int $fileIdCursor The last processed file ID. Only files with a higher ID will be included. Defaults to 0.
* @param int $maxResults The maximum number of results to retrieve. If set to 0, all matching files will be retrieved.
* @param list<int> $mimeTypeIds An array of mime types to filter the results. If empty, no mime type filtering will be applied.
* @param bool $endToEndEncrypted Whether to include EndToEndEncrypted files
* @param bool $serverSideEncrypted Whether to include ServerSideEncrypted files
* @return \Generator<ICacheEntry> A generator yielding matching files as cache entries.
* @throws \OCP\DB\Exception
*
* @since 32.0.0
*/
Comment thread
julien-nc marked this conversation as resolved.
Resolved
public function getByAncestorInStorage(int $storageId, int $folderId, int $fileIdCursor = 0, int $maxResults = 100, array $mimeTypeIds = [], bool $endToEndEncrypted = true, bool $serverSideEncrypted = true): \Generator;

/**
* Retrieves a list of all distinct mounts.
* Allows filtering by specific mount providers.
* Optionally rewrites home directory root paths to avoid cache and trashbin.
*
* @param list<string> $mountProviders An array of mount provider class names to filter. If empty, all providers will be included.
* @param bool $onlyUserFilesMounts Whether to rewrite the root IDs for home directories to only include user files and to only consider mounts with mount points in the user files.
* @return \Generator<array{storage_id: int, root_id: int, overridden_root: int}> A generator yielding mount configurations as an array containing 'storage_id', 'root_id', and 'override_root'.
* @throws \OCP\DB\Exception
*
* @since 32.0.0
*/
public function getDistinctMounts(array $mountProviders = [], bool $onlyUserFilesMounts = true): \Generator;
}
Loading