Skip to content

[Bug] A single desynced filecache path makes an entire group folder directory return 500 #61649

Description

@pringelmann

Description

When a folder is moved between two team folders that share one object-store storage, and the moved folder is reachable through a nested cache jail (it was also shared to a user who already has access via the team folder, a "double share"), the move updates the folder's own oc_filecache row but not its children. The children keep the source jail's path prefix.

After that, opening the destination folder returns HTTP 500 for everyone. View::getDirectoryContent() maps each child through $storage->getOwner($child->getPath()), the desynced child resolves to a null path, and getOwner(null) throws a TypeError that aborts the whole PROPFIND. One bad entry makes the entire directory unreadable.

The corruption source is already fixed by #59252 / #53934. But that fix is preventive only: instances corrupted before it stay broken.

Steps to reproduce

On a build with #59252 reverted, primary S3 object store, groupfolders with shared storage:

  1. Create two team folders, TeamA and TeamB, both on the same object store (so they are jails inside one storage).
  2. As a member, create TeamA/Sub with a couple of files.
  3. Share TeamA/Sub with a second user who is also a member of TeamA (the "double share" stacks a share cache jail on the team-folder jail).
  4. As that second user, move Sub out of the share into TeamB.
  5. Open TeamB/Sub. -> 500.

oc_filecache then shows the moved folder under __groupfolders/<TeamB>/… while its children still read __groupfolders/<TeamA>/…. Upgrading past #59252 does not repair these rows, and opening the folder still 500s (confirmed on a patched 32.0.10).

Why I think it happens

CacheJail::getMoveInfo() returned getSourcePath($path) (relative to the inner jail) instead of getUnjailedSourcePath($path) (relative to the base storage). With nested jails the two diverge, so the cache move rewrote the folder row but not its descendants.

Separately, View::getDirectoryContent() calls getOwner() per child with no null guard, so any single unresolvable path throws TypeError: getOwner(): Argument #1 ($path) must be of type string, null given and kills the entire listing instead of just that one entry.

Possible fix

  • getDirectoryContent() should skip and log entries whose path can't be resolved, rather than 500 the whole directory on one bad row.
    - an occ command to find and fix descendant rows whose path prefix doesn't match their parent's jail, for instances corrupted before the fix. Already exists: occ files:repair-tree

Reproduced on 32.0.10 with #59252 reverted; residual 500 confirmed with #59252 in place.

Metadata

Metadata

Assignees

No one assigned

    Type

    Projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions