Speculative work, agreed 2026-09-17 as "note it for later". Arises from the /usr/obj shadowing bug on radagast — full diagnosis at hosts/radagast/source-upgrade. Fits the wider "own our critical software" theme (CVE → review → build), except here the ambition is to fix upstream rather than just build it.
2.2.2-0ubuntu9.4; upstream is on 2.3.x. Some or all of this may already be fixed or reported. Search the openzfs/zfs issue tracker and git log before investing effort.zroot. Should be a few lines: create a pool, create parent and child datasets, give the child a local mountpoint that the parent's mount will cover, then zfs mount -a. If it reproduces off a handful of truncated files, it is cheap to iterate on and it is also the basis of any ZTS test a PR would need.Observed: with a shadowed dataset, zpool export zroot failed, and so did zpool export -f zroot — cannot unmount '/mnt/radagast/zroot/usr/obj': no such pool or dataset. The pool could not be exported at all until every covering mount was unwound by hand, deepest first. That is a real robustness bug: -f ought to mean forced, and the pool was left in a state the tools could not leave.
Proposed fix: export already knows every mount belonging to the pool. Instead of one pass that aborts on first failure, unmount what succeeds and loop while progress is being made — a fixed point. Unmounting a coverer releases whatever it covered, so a second pass succeeds. Self-contained, no new interfaces, easy to test.
After zfs inherit mountpoint zroot/usr/obj made the child's path a genuine child of the parent's path, FreeBSD 11.4 mounted them in the correct order — but Linux OpenZFS 2.2.2 still mounted the child first, visible in /proc/self/mountinfo as a mount whose parent ID was the host root filesystem rather than the pool's root dataset. If that reproduces, it is an ordering bug in the parallel mounter, and it is the one worth fixing, because it is the thing that actually causes the damage. Not yet diagnosed — treat as a lead, not a finding. OpenZFS 2.x mounts in parallel; the question is whether the dependency ordering (parent dataset before child) is genuinely enforced or only implied by sorting mountpoint strings.
zfs unmount zroot/usr/obj on a mounted-but-shadowed dataset reports no such pool or dataset. The dataset exists and is mounted; only its mountpoint path is unreachable. It should say so — something naming the covering mount and suggesting it be unmounted first. Small, uncontroversial, and would have saved most of the time spent on this.
ZFS has everything it needs at zfs set mountpoint time to say that the result will be shadowed. It stayed silent for nine years on radagast while builds risked landing on the wrong dataset. Genuinely useful, but upstreams are rightly resistant to new warnings on common paths, so expect bikeshedding about whether it belongs at set time, mount time, or in zpool status.
umount2() takes only a path, and a path addresses the topmost mount there; there is no unmount-by-mount-ID. The coverer must go first. This is kernel mount semantics, not an OpenZFS defect — an initial guess that it could be fixed by unmounting via mount handle was wrong.df reporting the covered dataset for an unreachable path — that is coreutils longest-prefix matching the mount table, and affects every filesystem.