Repository navigation
fix: a delta layer is the final tree diff against its parent - #18
Merged
Merged
Conversation
bt-layer packed a child layer as the two deck upper directories one after the other, root.build's then root.patched's. A file the first stage created and the second deleted is content in one and a whiteout in the other, so the tarball carried its bytes and then the whiteout: the tree a layer unpacks to was right and the bytes were still published. That is how the postgresql layer of 2026-10-08 carried the snakeoil key and certificate ssl-cert makes at installation and removelists-final takes out; layer_ audit_keys reads the final tree, where the whiteout had already hidden them, so it passed. layer_delta_lists computes the members from the final tree and the parent instead: every path either upper holds that root.patched still has, packed from root.patched; a whiteout only for a path the parent has and the final tree does not; an opaque directory over a parent directory from its upper, so the mark is kept. A file created and deleted inside the layer leaves nothing. The lists are NUL separated and sorted, and tar reads them with --null --no-recursion, so the tarball stays reproducible (the -u re-export test is byte identical). bt-layer passes the parent's rootfs and fails when the members cannot be listed. tests/layer: a fake key created in stage 1 and whited out in stage 2 is neither named nor present byte for byte in a real tarball; a parent file removed is a whiteout; a file removed by stage 1 and re-created by stage 2 is packed once; an opaque directory comes from its upper; the refusals; the real predicates on what an unprivileged run can make. The fake product's final tree now holds what its uppers add, as an overlay mount shows it. Opaque marks are read or the build stops: layer_is_opaque returns 2 when getfattr is missing or fails with anything but "No such attribute", and layer_delta_lists refuses to run without getfattr (package attr, now in docs/setup and in CI). Before, a getfattr error read as "not opaque" and a parent file the layer deleted came back. No test of the lists follows a symlink: layer_exists_in and layer_real_dir_in walk REL component by component with lstat semantics, so a stage 1 file under a directory stage 2 replaced with a link to the build host is not packed through the link, and a parent link is neither a whiteout target nor a directory to be opaque over.
marcos-mendez
force-pushed
the
fix/delta-layer-final-diff
branch
from
October 8, 2026 01:53
8fb6fe7 to
627edcf
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.
A child layer was packed as the two deck upper directories in turn (root.build, then root.patched). A file created in stage 1 and deleted in stage 2 is content in one and a whiteout in the other, so the tarball shipped its bytes followed by the whiteout. The unpacked tree was right; the bytes were still published. This is how the postgresql layer of 2026-10-08 (not published) carried the snakeoil key ssl-cert makes;
layer_audit_keysreads the final tree, where the whiteout already hides it.layer_delta_listscomputes the members from the final tree and the parent: paths either upper holds thatroot.patchedstill has (packed fromroot.patched); a whiteout only for a path the parent has and the final tree lacks; an opaque directory over a parent directory taken from its upper so the mark survives. Created-then-deleted leaves nothing.--null --no-recursion: reproducible (the-ure-export stays byte identical).bt-layerpasses the parent rootfs and fails if the members cannot be listed.Tests (
tests/layer, section 10a): a fake key created in stage 1 and whited out in stage 2 is neither named nor present byte for byte in a real tarball; parent file removed = whiteout; file re-created packed once; opaque directory from its upper; refusals; the real predicates.layer-lib99.54 percent locally (gate 99); the one new uncovered line is a loop'sdone.