Skip to content

Conversation

@salmart-dev
Copy link
Contributor

@salmart-dev salmart-dev commented Aug 15, 2025

Summary

Sabre has an interface INodeByPath that allows nodes to fetch a specific node by its path. This was not implemented in our Directory class, leading to any file query to fetch all parent nodes until the file node was reached. This PR makes Directory implement that interface, extends makes the NC Node class extend the Node class from Sabre (required step).

Before:
Screenshot From 2025-08-15 18-52-24

After:
Screenshot From 2025-08-15 18-50-51

Checklist

@salmart-dev salmart-dev self-assigned this Aug 15, 2025
@salmart-dev salmart-dev force-pushed the fix/directoryAsINodeByPath branch from cad0ea3 to 87d9b88 Compare August 15, 2025 16:12
@salmart-dev salmart-dev force-pushed the fix/directoryAsINodeByPath branch from f67982e to c54b5c9 Compare August 21, 2025 16:28
@come-nc come-nc requested a review from icewind1991 August 25, 2025 09:05
@salmart-dev salmart-dev force-pushed the fix/directoryAsINodeByPath branch from c54b5c9 to cf952ce Compare September 10, 2025 11:02
@github-actions
Copy link
Contributor

Possible performance regression detected

Show Output
An unhandled exception has been thrown:
TypeError: array_map(): Argument #2 ($array) must be of type array, null given in /home/runner/work/server/server/apps/profiler/lib/Command/Compare.php:35
Stack trace:
#0 /home/runner/work/server/server/apps/profiler/lib/Command/Compare.php(35): array_map()
#1 /home/runner/work/server/server/3rdparty/symfony/console/Command/Command.php(326): OCAProfilerCommandCompare->execute()
#2 /home/runner/work/server/server/core/Command/Base.php(218): SymfonyComponentConsoleCommandCommand->run()
#3 /home/runner/work/server/server/3rdparty/symfony/console/Application.php(1078): OCCoreCommandBase->run()
#4 /home/runner/work/server/server/3rdparty/symfony/console/Application.php(324): SymfonyComponentConsoleApplication->doRunCommand()
#5 /home/runner/work/server/server/3rdparty/symfony/console/Application.php(175): SymfonyComponentConsoleApplication->doRun()
#6 /home/runner/work/server/server/lib/private/Console/Application.php(187): SymfonyComponentConsoleApplication->run()
#7 /home/runner/work/server/server/console.php(91): OCConsoleApplication->run()
#8 /home/runner/work/server/server/occ(33): require_once('...')
#9 {main}

1 similar comment
@github-actions
Copy link
Contributor

Possible performance regression detected

Show Output
An unhandled exception has been thrown:
TypeError: array_map(): Argument #2 ($array) must be of type array, null given in /home/runner/work/server/server/apps/profiler/lib/Command/Compare.php:35
Stack trace:
#0 /home/runner/work/server/server/apps/profiler/lib/Command/Compare.php(35): array_map()
#1 /home/runner/work/server/server/3rdparty/symfony/console/Command/Command.php(326): OCAProfilerCommandCompare->execute()
#2 /home/runner/work/server/server/core/Command/Base.php(218): SymfonyComponentConsoleCommandCommand->run()
#3 /home/runner/work/server/server/3rdparty/symfony/console/Application.php(1078): OCCoreCommandBase->run()
#4 /home/runner/work/server/server/3rdparty/symfony/console/Application.php(324): SymfonyComponentConsoleApplication->doRunCommand()
#5 /home/runner/work/server/server/3rdparty/symfony/console/Application.php(175): SymfonyComponentConsoleApplication->doRun()
#6 /home/runner/work/server/server/lib/private/Console/Application.php(187): SymfonyComponentConsoleApplication->run()
#7 /home/runner/work/server/server/console.php(91): OCConsoleApplication->run()
#8 /home/runner/work/server/server/occ(33): require_once('...')
#9 {main}

@salmart-dev

This comment was marked as outdated.

@salmart-dev salmart-dev added 3. to review Waiting for reviews and removed 2. developing Work in progress labels Sep 10, 2025
@github-actions
Copy link
Contributor

Possible performance regression detected

Show Output
An unhandled exception has been thrown:
TypeError: array_map(): Argument #2 ($array) must be of type array, null given in /home/runner/actions-runner/_work/server/server/apps/profiler/lib/Command/Compare.php:35
Stack trace:
#0 /home/runner/actions-runner/_work/server/server/apps/profiler/lib/Command/Compare.php(35): array_map()
#1 /home/runner/actions-runner/_work/server/server/3rdparty/symfony/console/Command/Command.php(326): OCAProfilerCommandCompare->execute()
#2 /home/runner/actions-runner/_work/server/server/core/Command/Base.php(218): SymfonyComponentConsoleCommandCommand->run()
#3 /home/runner/actions-runner/_work/server/server/3rdparty/symfony/console/Application.php(1078): OCCoreCommandBase->run()
#4 /home/runner/actions-runner/_work/server/server/3rdparty/symfony/console/Application.php(324): SymfonyComponentConsoleApplication->doRunCommand()
#5 /home/runner/actions-runner/_work/server/server/3rdparty/symfony/console/Application.php(175): SymfonyComponentConsoleApplication->doRun()
#6 /home/runner/actions-runner/_work/server/server/lib/private/Console/Application.php(187): SymfonyComponentConsoleApplication->run()
#7 /home/runner/actions-runner/_work/server/server/console.php(91): OCConsoleApplication->run()
#8 /home/runner/actions-runner/_work/server/server/occ(33): require_once('...')
#9 {main}

@github-actions
Copy link
Contributor

Possible performance regression detected

Show Output
An unhandled exception has been thrown:
TypeError: array_map(): Argument #2 ($array) must be of type array, null given in /home/runner/work/server/server/apps/profiler/lib/Command/Compare.php:35
Stack trace:
#0 /home/runner/work/server/server/apps/profiler/lib/Command/Compare.php(35): array_map()
#1 /home/runner/work/server/server/3rdparty/symfony/console/Command/Command.php(326): OCAProfilerCommandCompare->execute()
#2 /home/runner/work/server/server/core/Command/Base.php(218): SymfonyComponentConsoleCommandCommand->run()
#3 /home/runner/work/server/server/3rdparty/symfony/console/Application.php(1078): OCCoreCommandBase->run()
#4 /home/runner/work/server/server/3rdparty/symfony/console/Application.php(324): SymfonyComponentConsoleApplication->doRunCommand()
#5 /home/runner/work/server/server/3rdparty/symfony/console/Application.php(175): SymfonyComponentConsoleApplication->doRun()
#6 /home/runner/work/server/server/lib/private/Console/Application.php(187): SymfonyComponentConsoleApplication->run()
#7 /home/runner/work/server/server/console.php(91): OCConsoleApplication->run()
#8 /home/runner/work/server/server/occ(33): require_once('...')
#9 {main}

@salmart-dev salmart-dev marked this pull request as ready for review September 10, 2025 14:04
@salmart-dev salmart-dev requested a review from a team as a code owner September 10, 2025 14:04
@salmart-dev salmart-dev requested review from Altahrim and sorbaugh and removed request for a team September 10, 2025 14:04
@salmart-dev salmart-dev changed the title Add INodeByPath to Directory [wip] Add INodeByPath to Directory Sep 10, 2025
@salmart-dev salmart-dev marked this pull request as draft September 15, 2025 16:38
@salmart-dev salmart-dev changed the title Add INodeByPath to Directory [WIP] Add INodeByPath to Directory Sep 15, 2025
@salmart-dev
Copy link
Contributor Author

I converted the PR back to a draft because I ran into an issue with access control rules.

Problem statement

Apps that rely on path navigation to perform checks that block an action could be broken with this PR.

Example

The files_accesscontrol app relies on each node's name to match for names that have been blocked. With this PR, the intermediate nodes in a path may never be navigated to, creating an issue where a file deep in the tree can be accessed, although one of the skipped nodes is marked as not readable through the app.

In the current implementation, the app relies on the fact that isReadable gets called in every Node, which in turn triggers the check for the file name in the app.

How to reproduce

  1. Create a public share
  2. Add a folder inside the public share
  3. Block access to the directory through a flow with the files_accesscontrol app
  4. Try uploading inside the new folder

Uploading should fail but succeeds instead

Possible solution?

Alter files_accesscontrol

For the case in the example, a fix could be to change files_accesscontrol to stop relying on the assumption that all nodes in a path will be navigated to and compare each part of the exploded path with the given value to block, but I fear this would break other flows and checks.


Can someone come up with ideas on how to address this or other things that may break, so that if we try a new solution I can test it still works?

@salmart-dev salmart-dev force-pushed the fix/directoryAsINodeByPath branch from a1a6278 to cf952ce Compare September 16, 2025 08:08
@salmart-dev salmart-dev force-pushed the fix/directoryAsINodeByPath branch from b5977e7 to 831e780 Compare November 26, 2025 09:06
@salmart-dev salmart-dev force-pushed the refactor/files-sharing branch from 85d8df5 to cfba3f8 Compare November 28, 2025 12:03
Base automatically changed from refactor/files-sharing to master November 28, 2025 14:43
@salmart-dev salmart-dev force-pushed the fix/directoryAsINodeByPath branch 3 times, most recently from a61556d to 3959a22 Compare December 2, 2025 10:22
@salmart-dev salmart-dev force-pushed the fix/directoryAsINodeByPath branch from 3959a22 to 7ae8463 Compare December 2, 2025 11:45
@salmart-dev salmart-dev changed the title [WIP] Add INodeByPath to Directory Add INodeByPath to Directory Dec 2, 2025
@salmart-dev salmart-dev force-pushed the fix/directoryAsINodeByPath branch from 7ae8463 to 82e6de7 Compare December 3, 2025 09:33
@salmart-dev salmart-dev force-pushed the fix/directoryAsINodeByPath branch from 82e6de7 to b2b60b5 Compare December 3, 2025 10:18
@salmart-dev salmart-dev marked this pull request as ready for review December 3, 2025 10:20
@salmart-dev
Copy link
Contributor Author

Had to add a check for !$allowDirectory to skip navigating to the parent which is not readable in the context of a file drop (https://github.com/nextcloud/server/pull/54441/files#diff-bbeea4e9faca76aa9e6901242c70df675c85efe25134b50995bbe78109fb636cR559)

cc. @artonge

@salmart-dev salmart-dev force-pushed the fix/directoryAsINodeByPath branch from 415bccf to 02dd830 Compare December 4, 2025 13:18
@salmart-dev
Copy link
Contributor Author

@icewind1991 this needs another review from you 🙏

The issue with files_accesscontrol has been solved by checking if all parent folders are readable, seems to be working well with group folders as well.

A case that I was expecting to cause trouble with this PR also seems to have the same behaviour on master: within a group folder having a folder with read permissions removed for a specific user, but containing a file with read permissions allowed for the same user. This fails on master and on my PR, so from what I can test, this seems to be working well. The only drawback is that we lose the removal of the N+1 queries for navigating into deep folders, since navigating to the parents to check for readability will load their file infos.

Signed-off-by: Salvatore Martire <[email protected]>
Signed-off-by: Salvatore Martire <[email protected]>
@salmart-dev salmart-dev force-pushed the fix/directoryAsINodeByPath branch from b73efca to 871262d Compare December 10, 2025 17:31
@sorbaugh sorbaugh merged commit a7a64de into master Dec 11, 2025
282 of 308 checks passed
@sorbaugh sorbaugh deleted the fix/directoryAsINodeByPath branch December 11, 2025 13:07
nickvergessen added a commit to nextcloud/files_accesscontrol that referenced this pull request Dec 15, 2025
nickvergessen added a commit to nextcloud/files_accesscontrol that referenced this pull request Dec 15, 2025
nickvergessen added a commit to nextcloud/files_accesscontrol that referenced this pull request Dec 15, 2025
…sponse-code

fix(DAV): Adjust expected response code after nextcloud/server#54441
@salmart-dev salmart-dev mentioned this pull request Dec 22, 2025
7 tasks
@nextcloud-bot nextcloud-bot mentioned this pull request Jan 6, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants