Repository navigation
gofer, overlay: Omit whiteouts that Linux overlayfs cannot look up - #17663
Open
copybara-service[bot] wants to merge 1 commit into
Open
copybara-service[bot] wants to merge 1 commit into
copybara-service[bot] wants to merge 1 commit into
Conversation
copybara-service
Bot
force-pushed
the
test/cl1021732572
branch
3 times, most recently
from
November 15, 2026 14:43
7b8b8e5 to
fad87ef
Compare
## Summary Since #16469 made `--inode-numbers=passthrough` the default, a directory on a Linux overlayfs on the host can look empty in the sandbox if overlayfs does not merge it and it contains a whiteout. `getdents64(2)` on it fails with `ENOENT`, which glibc's `readdir(3)` treats as the end of the directory. `rmdir(2)` and `rename(2)` of the directory fail the same way. Linux overlayfs reads an unmerged directory straight from its layer (`ovl_iterate()`), so the listing includes whiteouts, even though `ovl_lookup()` treats them as missing. Native Linux shows the same listing. Before #16469, the gofer called `stat(2)` on every entry and dropped the ones that failed, which hid these whiteouts from the overlay. It no longer does. The overlay then calls `stat(2)` on each `DT_CHR` entry of the lower layer to check whether it is a whiteout (`getDirentsLocked()`, `collectWhiteoutsForRmdirLocked()`), gets `ENOENT`, and fails the whole call. Images built with Docker running inside gVisor before 6d278f8 have a lot of these directories. In those versions, the overlay's `RenameAt()` left a whiteout at the source of every rename, and Docker's native overlay diff put it into the layer. So every temporary file that a build step renamed (git's `*.lock` and `tmp_pack_*`, dpkg's `*.dpkg-new`, and so on) became a `.wh.` entry, and containerd's overlayfs snapshotter turns each one into a `0:0` character device. With these images, git can't find the packs in `.git/objects/pack`, and `git cat-file -e <sha>^{commit}` fails with `fatal: Not a valid object name`. The same images run fine on runc, and ran fine on gVisor before #16469, so this is a regression (`COMPATIBILITY:REGRESSION-GVISOR`). This PR makes the overlay skip entries that a layer lists but can't look up, like Linux's `ovl_check_whiteouts()`, instead of failing `getdents64(2)` and `rmdir(2)`. As with lookup, such an entry doesn't hide a file with the same name on a lower layer. Mounts without an overlay keep the host's listing, as on Linux. ## Reproduction As root, create a Linux overlayfs on the host in which `dir` and `onlywo` exist in one layer only and contain a whiteout: ``` mkdir -p $W/lower/dir $W/lower/onlywo $W/upper $W/work $W/merged touch $W/lower/dir/file mknod $W/lower/dir/whiteout c 0 0 mknod $W/lower/onlywo/whiteout c 0 0 mount -t overlay overlay -o lowerdir=$W/lower,upperdir=$W/upper,workdir=$W/work $W/merged runsc --root=$W/root --network=none do sh -c "$CMD" ``` Linux (`sh -c "$CMD"` on the host): ``` file whiteout ls: 0 rmdir: 0 ``` runsc before this PR: ``` ls: 0 rmdir: failed to remove '/tmp/wo/merged/onlywo': No such file or directory rmdir: 1 ``` `ls` prints nothing. With `--strace`: ``` ls X getdents64(0x3 /tmp/wo/merged/dir, 0x55bde68b45b0, 0x8000) = -1 errno=2 (no such file or directory) (45.177µs) ``` runsc with this PR: ``` file ls: 0 rmdir: 0 ``` Without the overlay (`runsc do --force-overlay=false`), runsc lists `file` and `whiteout`, like Linux, both before and after this PR. A real image shows the same thing. Build this Dockerfile with Docker running inside gVisor at release-20261019.0 or earlier (`DOCKER_BUILDKIT=0`), and import it into containerd: ``` FROM alpine:latest RUN mkdir /data && echo a > /data/conf && sed -i s/a/b/ /data/conf RUN mkdir /scratch && echo a > /scratch/x && sed -i s/a/b/ /scratch/x && rm /scratch/x ``` The two `RUN` layers end up with `data/.wh.confJPIHhN` and `scratch/.wh.xGlngLk`, left over from the temporary files of `sed -i`. Then run `ls /data; cat /data/conf; rmdir /scratch` in the image. runc lists `conf` and removes `/scratch`. runsc before this PR lists nothing in `/data`, still reads `/data/conf`, and fails `rmdir /scratch` with `ENOENT`. ## Testing - The new container test `TestHostOverlayfsWhiteouts` lists such a directory with and without directfs, and with and without the overlay (`--overlay2=all:memory`). With the overlay it expects only `file`; without it, the host's `file` and `whiteout`. On master it fails in both overlay configurations, where `ls` prints nothing. The test has to `mknod(2)` a whiteout and mount overlayfs, and skips if it can't. - The existing `getdents`, `rename`, `mkdir` and `unlink` syscall tests pass with the overlay (`_runsc_systrap_overlay`). Auto-fixed-by: [🤖] @EtiennePerot FUTURE_COPYBARA_INTEGRATE_REVIEW=#17090 from tanyifeng:fix-overlayfs-whiteouts 977845d PiperOrigin-RevId: 1021732572
copybara-service
Bot
force-pushed
the
test/cl1021732572
branch
from
November 15, 2026 18:51
fad87ef to
723d576
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
gofer, overlay: Omit whiteouts that Linux overlayfs cannot look up
Summary
Since #16469 made
--inode-numbers=passthroughthe default, a directory on aLinux overlayfs on the host can look empty in the sandbox if overlayfs does
not merge it and it contains a whiteout.
getdents64(2)on it fails withENOENT, which glibc'sreaddir(3)treats as the end of the directory.rmdir(2)andrename(2)of the directory fail the same way.Linux overlayfs reads an unmerged directory straight from its layer
(
ovl_iterate()), so the listing includes whiteouts, even thoughovl_lookup()treats them as missing. Native Linux shows the same listing.Before #16469, the gofer called
stat(2)on every entry and dropped the onesthat failed, which hid these whiteouts from the overlay. It no longer does.
The overlay then calls
stat(2)on eachDT_CHRentry of the lower layer tocheck whether it is a whiteout (
getDirentsLocked(),collectWhiteoutsForRmdirLocked()), getsENOENT, and fails the whole call.Images built with Docker running inside gVisor before 6d278f8 have a lot of
these directories. In those versions, the overlay's
RenameAt()left awhiteout at the source of every rename, and Docker's native overlay diff put
it into the layer. So every temporary file that a build step renamed (git's
*.lockandtmp_pack_*, dpkg's*.dpkg-new, and so on) became a.wh.entry, and containerd's overlayfs snapshotter turns each one into a
0:0character device. With these images, git can't find the packs in
.git/objects/pack, andgit cat-file -e <sha>^{commit}fails withfatal: Not a valid object name. The same images run fine on runc, and ranfine on gVisor before #16469, so this is a regression
(
COMPATIBILITY:REGRESSION-GVISOR).This PR makes the overlay skip entries that a layer lists but can't look up,
like Linux's
ovl_check_whiteouts(), instead of failinggetdents64(2)andrmdir(2). As with lookup, such an entry doesn't hide a file with the samename on a lower layer. Mounts without an overlay keep the host's listing, as
on Linux.
Reproduction
As root, create a Linux overlayfs on the host in which
dirandonlywoexist in one layer only and contain a whiteout:
Linux (
sh -c "$CMD"on the host):runsc before this PR:
lsprints nothing. With--strace:runsc with this PR:
Without the overlay (
runsc do --force-overlay=false), runsc listsfileandwhiteout, like Linux, both before and after this PR.A real image shows the same thing. Build this Dockerfile with Docker running
inside gVisor at release-20261019.0 or earlier (
DOCKER_BUILDKIT=0), andimport it into containerd:
The two
RUNlayers end up withdata/.wh.confJPIHhNandscratch/.wh.xGlngLk, left over from the temporary files ofsed -i. Then runls /data; cat /data/conf; rmdir /scratchin the image. runc listsconfandremoves
/scratch. runsc before this PR lists nothing in/data, still reads/data/conf, and failsrmdir /scratchwithENOENT.Testing
TestHostOverlayfsWhiteoutslists such a directorywith and without directfs, and with and without the overlay
(
--overlay2=all:memory). With the overlay it expects onlyfile; withoutit, the host's
fileandwhiteout. On master it fails in both overlayconfigurations, where
lsprints nothing. The test has tomknod(2)awhiteout and mount overlayfs, and skips if it can't.
getdents,rename,mkdirandunlinksyscall tests passwith the overlay (
_runsc_systrap_overlay).Auto-fixed-by: [🤖] @EtiennePerot
FUTURE_COPYBARA_INTEGRATE_REVIEW=gofer, overlay: Omit whiteouts that Linux overlayfs cannot look up #17090 from tanyifeng:fix-overlayfs-whiteouts 977845d