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:
- Create two team folders,
TeamA and TeamB, both on the same object store (so they are jails inside one storage).
- As a member, create
TeamA/Sub with a couple of files.
- 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).
- As that second user, move
Sub out of the share into TeamB.
- 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.
Reproduced on 32.0.10 with #59252 reverted; residual 500 confirmed with #59252 in place.
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_filecacherow 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, andgetOwner(null)throws aTypeErrorthat 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:
TeamAandTeamB, both on the same object store (so they are jails inside one storage).TeamA/Subwith a couple of files.TeamA/Subwith a second user who is also a member ofTeamA(the "double share" stacks a share cache jail on the team-folder jail).Subout of the share intoTeamB.TeamB/Sub. -> 500.oc_filecachethen 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()returnedgetSourcePath($path)(relative to the inner jail) instead ofgetUnjailedSourcePath($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()callsgetOwner()per child with no null guard, so any single unresolvable path throwsTypeError: getOwner(): Argument #1 ($path) must be of type string, null givenand 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.occcommand to find and fix descendant rows whose path prefix doesn't match their parent's jail, for instances corrupted before the fix.Reproduced on 32.0.10 with #59252 reverted; residual 500 confirmed with #59252 in place.