Skip to content

os/v3/puppy: a build root that carries the previous version of the ownership patch refuses every build (the guard asks for the comment, not the call) #975

Closed

Description

@bong-water-water-bong

Found by the cycle-21 image build on 2026-11-04 — the first build from main after #957 merged. The build stopped before it started:

build: REFUSED: 3builddistro still chowns with a bare chown -R, which strips the image's setuid bits

Root cause, measured

container-build.sh patches woof-code/3builddistro idempotently, and the guard for the patch is the marker comment:

if ! grep -q '1bit: ownership before pack' woof-code/3builddistro; then   # <-- comment
    sed -i ... 'sh /recipe/fix-ownership.sh rootfs-complete   # 1bit: ownership before pack' ...
fi

The cycle-16 version of this same patch wrote that same comment on a bare chown:

1116:	chown -R 0:0 rootfs-complete 2>/dev/null || true   # 1bit: ownership before pack
1218:	chown -R 0:0 sandbox3/devx 2>/dev/null || true   # 1bit: ownership before pack

So on any woof-CE clone or build root reused from an earlier cycle the guard matches, the sed is skipped, and the next
assertion correctly refuses. This is not upstream drift, which is what the message says:

$ git -C ~/wt/1bit-puppy-build11/woof-CE status --porcelain woof-code/3builddistro
 M woof-code/3builddistro
$ grep -c '1bit: ownership before pack' ~/wt/1bit-puppy-build11/woof-CE/woof-code/3builddistro
2

Both trees on this box (~/wt/1bit-puppy-build11/woof-CE, the clone, and ~/wt/1bit-puppy-build/woof-CE, the container's
copy) are in that state, so every build refuses until one of them is cleaned by hand — and the hand-clean is the thing
the patch was supposed to make unnecessary.

It fails closed, which is why it is not a shipped defect: the refusal is right, the diagnosis and the cure are not. The
fragile part is that the patch tests for its own history; a future woof-CE refactor that keeps the comment while moving the
call would keep a bare chown beside fix-ownership.sh and only the call-count check would catch it.

Fix

  • guard on the call (grep -q 'sh /recipe/fix-ownership.sh'), so the question is "is this tree patched?" and not "was
    it ever touched?";
  • before inserting, drop every line carrying the marker comment — that line is a previous version of this patch;
  • assert the call count is exactly 2, and refuse any bare chown -R of rootfs-complete / sandbox3/devx that is still
    beside the calls.

Reproducer (~/wt/os3i/c21-ownpatch-repro.sh before.sh after.sh) runs the real extracted patch text against scratch
copies of both tree states:

arm tree result
A before carries the cycle-16 patch rc=1, calls=0 marker=2 barechown=2 — the failure
B after same tree rc=0, calls=2 marker=2 barechown=0, lines are sh /recipe/fix-ownership.sh …
C after ×2 same tree unchanged: idempotent
D after clean upstream file rc=0, calls=2 marker=2 barechown=0 — the normal path is untouched
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions