Skip to content

fix(core/layers): handle non-trailing-slash path in native recursive list#7705

Open
tonghuaroot wants to merge 1 commit into
apache:mainfrom
tonghuaroot:fix-simulate-native-recursive-prefix
Open

fix(core/layers): handle non-trailing-slash path in native recursive list#7705
tonghuaroot wants to merge 1 commit into
apache:mainfrom
tonghuaroot:fix-simulate-native-recursive-prefix

Conversation

@tonghuaroot
Copy link
Copy Markdown
Contributor

Which issue does this PR close?

Related to #4256. This PR does not close that issue; it fixes a latent
correctness bug in the recursive-list path that was found while investigating
#4256, and is a prerequisite for any future native WebDAV recursive-list work.

Rationale for this change

SimulateAccessor::simulate_list has three arms. Two of them (the
simulated-recursive arm (true, false, true) and the non-recursive arm
(false, false, _)) already special-case a list path without a trailing slash:
they list the parent directory and wrap the result in a PrefixLister, so that
listing dir/file returns every entry sharing that prefix (for example
dir/file and dir/file2).

The native-recursive arm (_, true, _) did not. It forwarded the path straight
to the backend. On a backend that natively supports recursive list and follows
directory/subtree semantics (a WebDAV server honoring Depth: infinity),
listing dir/file walks the subtree of dir/file and returns only that one
entry, silently dropping prefix-siblings such as dir/file2. The result of a
recursive list then depends on whether the backend's native lister happens to
be prefix-based or subtree-based, which is inconsistent.

The in-memory service did not expose this because its native lister is
prefix-based (scan(prefix) returns every key that starts with the prefix), so
the bug stayed latent until a real WebDAV backend with native Depth: infinity
exercised it.

What changes are included in this PR?

  • In the (_, true, _) arm of simulate_list: when the path has a trailing
    slash, forward to the backend as before; when it does not, list the parent
    directory and wrap the result in a PrefixLister, matching the other two
    arms.
  • A focused regression test (mod tests in simulate.rs) using a mock backend
    that models WebDAV Depth: infinity semantics. It asserts that recursively
    listing dir/file (no trailing slash) returns both dir/file and
    dir/file2. The test fails without this change and passes with it. A second
    case asserts that listing a trailing-slash directory path is unchanged.

Are there any user-facing changes?

Yes, a bug fix: for services that declare native recursive list, calling a
recursive list with a path that has no trailing slash now correctly returns all
entries sharing that path prefix, consistent with non-native-recursive
services. No API changes. No breaking changes.

AI Usage Statement

This change was co-authored with AI assistance. The contributor drove the work:
the bug was found while reproducing #4256 against real WebDAV servers
(nginx, ownCloud, Nextcloud) in Docker, where a recursive list of a
non-trailing-slash path against a native Depth: infinity backend dropped a
sibling entry. The contributor diagnosed the root cause in simulate.rs,
verified the fix and the regression test against the memory service and a real
Nextcloud backend, and reviewed the final implementation. An AI assistant
(Claude) helped implement the fix and draft the regression test and this
description.

…list

The native-recursive arm of SimulateAccessor::simulate_list `(_, true, _)`
forwarded the list path directly to the backend. For a path without a
trailing slash (for example `dir/file`), a backend that natively supports
recursive list (such as a WebDAV server honoring `Depth: infinity`) walks
the subtree of `dir/file` and returns only that entry, silently dropping
prefix-siblings like `dir/file2`.

Both the simulated-recursive arm `(true, false, true)` and the
non-recursive arm `(false, false, _)` already handle this: for a path
without a trailing slash they list the parent directory and wrap the
result in a PrefixLister so that entries sharing the path prefix are
returned. The native-recursive arm now applies the same handling, making
recursive list of a non-trailing-slash path consistent across all
backends regardless of native capability.

The in-memory service tolerated the bug because its native lister treats
the trailing path component as a prefix, so it stayed latent until a real
WebDAV backend with native `Depth: infinity` exercised it. A focused
regression test using a WebDAV-style native-recursive mock backend is
added: it fails before this change (the sibling is dropped) and passes
after it.

Discovered while investigating apache#4256.

Signed-off-by: tonghuaroot <23011166+tonghuaroot@users.noreply.github.com>
@tonghuaroot tonghuaroot requested a review from Xuanwo as a code owner June 7, 2026 09:50
@dosubot dosubot Bot added size:L This PR changes 100-499 lines, ignoring generated files. releases-note/fix The PR fixes a bug or has a title that begins with "fix" labels Jun 7, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

releases-note/fix The PR fixes a bug or has a title that begins with "fix" size:L This PR changes 100-499 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant