Skip to content

Share-types preload lists a team folder a second time (DefaultShareProvider::getSharesInFolderInternal, ownerless mounts) #66251

Open

Description

@SylvainLegrand

⚠️ 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 calls getDirectoryListing() 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-workspace pay ~6 file-cache lookups per child folder (WorkspaceService::getFile): +4.0 s on this folder during the same period. The browser only asks for rich-workspace-flat and is not affected. Worked around with text workspace_available=false.

Steps to reproduce

  1. Team folder (groupfolders), one folder with ~4 000 sub-folders, no shares.
  2. PROPFIND Depth 1 with the Files app body, then the same body without oc:share-types and nc:sharees.
  3. 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

No one assigned

    Labels

    0. Needs triagePending check for reproducibility or if it fits our roadmapbug

    Type

    Projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions