Repository navigation
Description
⚠️ This issue respects the following points: ⚠️
- This is not a troubleshooting question, general support matter, or webserver/proxy problem, but likely a bug (if unsure, ask the Community Help Forum).
- This issue is not already reported on Github OR solved at the Community Help Forum (I've searched!).
- I'm using a maintained major version of Nextcloud Server and tested against the latest patch level. (Supported major versions and current patch levels).
- I agree to follow Nextcloud's Code of Conduct.
- I've tried my best to provide clear reproduction steps that someone unfamiliar with this bug could use to reproduce it.
Bug description
For a folder inside a team folder (groupfolders), every PROPFIND (Depth 1) that asks for oc:share-types / nc:sharees lists the folder twice: once for the response, once more in the share preload, although the folder contains no share at all. The preload therefore costs exactly one extra directory listing, whatever that costs.
Cause (code reading, 34.0.3): SharesPlugin::preloadCollection() → Manager::getSharesInFolder(). For an ownerless mount (IShareOwnerlessMount, i.e. team folders), it calls getAllSharesInFolder() on every provider implementing IShareProviderSupportsAllSharesInFolder. DefaultShareProvider::getSharesInFolderInternal() then calls $node->getDirectoryListing() (lib/private/Share20/DefaultShareProvider.php:676) only to find child mount roots (getInternalPath() === ''), with the existing comment // todo? maybe get these from the oc_mounts table.
Measured in-process (same request path, folder with 4 141 children, ACL enabled, no shares, median of 5):
| Call | Time | SQL queries |
|---|---|---|
Folder::getDirectoryListing() |
0.053 s | 6 |
IManager::getSharesInFolder($uid, $folder, true) |
0.054 s | 9 |
The preload returns 0 shares and costs one listing.
Why it matters: the duplicated listing doubles any per-listing cost. On our instance the listing was temporarily slow (about 0.65 s, caused locally by outdated database statistics for the ACL rules table — fixed on our side); during that period the two share properties cost +0.66 s at the web server:
| Body (web server, slow-listing period) | TTFB |
|---|---|
| Files app body | 1.648 s |
same, without oc:share-types + nc:sharees |
0.985 s |
| default properties only | 0.869 s |
| Body (web server, normal listing) | TTFB |
|---|---|
| Files app body | 0.617 s |
same, without oc:share-types + nc:sharees |
0.467 s |
| default properties only | 0.337 s |
With a normal listing the two share properties still cost +0.15 s of a 0.62 s request (24 %).
Related (same pattern, worked around locally)
- files_reminders 1.7.0:
PropFindPlugin::preloadCollection()→ReminderService::cacheFolder()also callsgetDirectoryListing()again just to write one cache entry per child (measured +1.09 s during the slow-listing period). Worked around by disabling the app (no reminders in use). - text 8.0.0: clients requesting the non-flat
nc:rich-workspacepay ~6 file-cache lookups per child folder (WorkspaceService::getFile): +4.0 s on this folder during the same period. The browser only asks forrich-workspace-flatand is not affected. Worked around withtext workspace_available=false.
Steps to reproduce
- Team folder (groupfolders), one folder with ~4 000 sub-folders, no shares.
- PROPFIND Depth 1 with the Files app body, then the same body without
oc:share-typesandnc:sharees. - Compare: the two share properties add roughly the cost of one full listing.
Expected behavior
One listing per request. As the todo says: get the child mount roots from the mount cache (oc_mounts) or the mount manager (IMountManager::findIn()) instead of getDirectoryListing(); or let SharesPlugin pass the children Sabre has already loaded.
Nextcloud Server version
34
Operating system
None
PHP engine version
PHP 8.4
Web server
Other
Database engine version
PostgreSQL
Is this bug present after an update or on a fresh install?
Fresh Nextcloud Server install
Are you using the Nextcloud Server Encryption module?
Encryption is Disabled
What user-backends are you using?
- Default user-backend (database)
- LDAP/ Active Directory
- SSO - SAML
- Other
Configuration report
List of activated Apps
Nextcloud Signing status
Nextcloud Logs
Additional info
Versions: Nextcloud 34.0.3 — groupfolders 22.0.6, files_reminders 1.7.0, text 8.0.0; PHP 8.4 FPM, PostgreSQL 16.
Proposed fix (running on our production instance since 2026-10-29)
Take the child mount roots from the mount manager instead of listing the folder a second time (as the existing todo suggests). The mount manager is the one View::getDirectoryContent() already used to build the listing of the same request, so findIn() does not set up anything new.
--- a/lib/private/Share20/DefaultShareProvider.php
+++ b/lib/private/Share20/DefaultShareProvider.php
@@ -672,13 +672,15 @@
}
}
- // todo? maybe get these from the oc_mounts table
- $childMountNodes = array_filter($node->getDirectoryListing(), function (Node $node): bool {
- return $node->getInternalPath() === '';
- });
- $childMountRootIds = array_map(function (Node $node): int {
- return $node->getId();
- }, $childMountNodes);
+ // Child mount roots come from the mount manager: no second directory listing.
+ $folderPath = rtrim($node->getPath(), '/');
+ $childMountRootIds = [];
+ foreach (\OCP\Server::get(\OCP\Files\Mount\IMountManager::class)->findIn($folderPath) as $childMount) {
+ $childRootId = (int)$childMount->getStorageRootId();
+ if ($childRootId > 0 && dirname(rtrim($childMount->getMountPoint(), '/')) === $folderPath) {
+ $childMountRootIds[] = $childRootId;
+ }
+ }
$qb->innerJoin('s', 'filecache', 'f', $qb->expr()->eq('s.file_source', 'f.fileid'));(Upstream, IMountManager would rather be injected than fetched with \OCP\Server::get(); kept minimal here so the patch applies on 34.0.3.)
Only direct children are kept (dirname(mount point) === folder), which is what getInternalPath() === '' selected among the listed children.
Checks before deployment (34.0.3.2, in-process, original and patched class side by side, same user, median of 5):
| Folder | Original | Patched | Shares found |
|---|---|---|---|
| team folder, 4 136 children, no share | 0.0652 s / 7 SQL | 0.0002 s / 1 SQL | 0 = 0 |
| team folder with one link share below | 0.0010 s / 3 SQL | 0.0002 s / 1 SQL | 1 = 1 |
| user root (6 child mount roots) | 0.0006 s / 2 SQL | 0.0003 s / 1 SQL | 0 = 0 |
Child mount roots found by both methods on the user root: 6 = 6. Every existing share (6 on the instance: user, link, email; personal folders and team folders) is still returned by the preload for its parent folder.
Result at the web server (PROPFIND Depth 1 on the 4 136-children folder, loopback, median TTFB of 5):
| Body | Before | After |
|---|---|---|
| Files app body | 0.642 s | 0.489 s |
same, with nc:rich-workspace for each child |
0.631 s | 0.491 s |
| same, without reminders properties | 0.627 s | 0.484 s |
| default properties only (control, no share properties) | 0.360 s | 0.344 s |
The two share properties now cost about what producing their values costs, instead of one more full listing. No error in the server log since the change. Happy to turn this into a PR if the approach suits you.
Metadata
Metadata
Assignees
Labels
Type
Projects
- StatusShow more project fieldsTo triage
Activity