Keep /var's package-shipped files, and pin BOOTC_REF #6

Merged
sebastian merged 2 commits from fix/var-files-and-bootc-pin into main 2026-08-24 22:14:45 +02:00
Owner

Two fixes, independent of each other, one commit each.

1. /var files were being dropped

bootc-rootfs.sh transcribed /var into tmpfiles as directories (d) and
symlinks (L) and emitted nothing for regular files — which a tmpfiles entry
cannot fill anyway. Step 3 then deletes them.

This isn't confined to the bootc upgrade path the generator was written for.
Step 3 empties the /var that ships in the image, so a fresh
bootc install has nothing to materialise either. bootc container lint
doesn't catch it — var-tmpfiles is about directories.

The fix copies the content to /usr/share/factory/var at build time and emits
C entries, whose bare argument resolves to exactly that path. C only copies
when the destination is absent, so a file a host has since edited is left alone.

Which files count is asked of dpkg rather than read off the tree: a .list
entry is content some package installed, and build residue (apt lists, dpkg's
own logs, /var/backups) belongs to no package and is skipped without an
exception list to maintain.

On scope — smaller than the /var tree suggests. Of ~350 .list entries
under /var on a desktop root, ~228 are symlinks — all of
/var/lib/ghostscript/CMap points into /usr/share/poppler, already covered
by the L entries — and ~100 are directories. What was actually being lost is
the couple of dozen regular files left over:

  • /var/lib/dictionaries-common/hunspell/* — how hunspell dictionaries are
    found at all
  • /var/lib/usbutils/usb.ids
  • /var/lib/xkb/README.compiled
  • tex-common's fmtutil/font-map fragments, on an image that installs TeX

A few hundred KiB. Worth it at that size because the failure mode is a file
that is simply not there, on a system with no apt install --reinstall to put
it back — and because the file case is now covered for whatever gets added to
the Containerfile later, rather than being correct by accident for today's
package list.

Verification

Replayed a generated conf with systemd-tmpfiles --root --create over an
emptied /var, against a synthetic root mimicking the state at step 2:

  • content and modes restored (0644, 0640 checked)
  • directory modes preserved (0750 case)
  • unowned build residue skipped — /var/log/dpkg.log absent, /var/log present
  • /var/lib/dpkg symlink intact, and not followed into /usr
  • no duplicate paths across d/L/C entries
  • idempotent, and a host-edited file left untouched on a second run

Not built end-to-end — no trixie builder here — so the real check is one
publish.yml run plus grep -c '^C ' /usr/lib/tmpfiles.d/zz-os4-var.conf on
the result.

2. BOOTC_REF was unpinned

It defaulted to empty, which bootc-build.sh resolves to whatever
bootc-dev/bootc has most recently released. publish.yml builds every other day
and passes no build-arg, so an unattended build was free to replace the thing
that mounts the root filesystem with a version nobody had booted.

Pinned to v1.16.9 — the newest release as of now, and also the one the
README's source references were read against (the bootupd/systemd-boot branch
in install.rs, the kargs handling in bootc_composefs/boot.rs), so the docs
and the binary now describe the same program. Empty still means "newest
release" for testing what upstream has moved to.


Nothing here touches the /opt, AppArmor or composefs work. README.md is
updated alongside both changes.

Two fixes, independent of each other, one commit each. ## 1. `/var` files were being dropped `bootc-rootfs.sh` transcribed `/var` into tmpfiles as directories (`d`) and symlinks (`L`) and emitted nothing for regular files — which a tmpfiles entry cannot fill anyway. Step 3 then deletes them. This isn't confined to the `bootc upgrade` path the generator was written for. Step 3 empties the `/var` that ships **in the image**, so a fresh `bootc install` has nothing to materialise either. `bootc container lint` doesn't catch it — `var-tmpfiles` is about directories. The fix copies the content to `/usr/share/factory/var` at build time and emits `C` entries, whose bare argument resolves to exactly that path. `C` only copies when the destination is absent, so a file a host has since edited is left alone. Which files count is asked of dpkg rather than read off the tree: a `.list` entry is content some package installed, and build residue (apt lists, dpkg's own logs, `/var/backups`) belongs to no package and is skipped without an exception list to maintain. **On scope — smaller than the `/var` tree suggests.** Of ~350 `.list` entries under `/var` on a desktop root, ~228 are *symlinks* — all of `/var/lib/ghostscript/CMap` points into `/usr/share/poppler`, already covered by the `L` entries — and ~100 are directories. What was actually being lost is the couple of dozen regular files left over: - `/var/lib/dictionaries-common/hunspell/*` — how hunspell dictionaries are found at all - `/var/lib/usbutils/usb.ids` - `/var/lib/xkb/README.compiled` - `tex-common`'s `fmtutil`/font-map fragments, on an image that installs TeX A few hundred KiB. Worth it at that size because the failure mode is a file that is simply not there, on a system with no `apt install --reinstall` to put it back — and because the file case is now covered for whatever gets added to the Containerfile later, rather than being correct by accident for today's package list. ### Verification Replayed a generated conf with `systemd-tmpfiles --root --create` over an emptied `/var`, against a synthetic root mimicking the state at step 2: - content and modes restored (`0644`, `0640` checked) - directory modes preserved (`0750` case) - unowned build residue skipped — `/var/log/dpkg.log` absent, `/var/log` present - `/var/lib/dpkg` symlink intact, and not followed into `/usr` - no duplicate paths across `d`/`L`/`C` entries - idempotent, and a host-edited file left untouched on a second run Not built end-to-end — no trixie builder here — so the real check is one `publish.yml` run plus `grep -c '^C ' /usr/lib/tmpfiles.d/zz-os4-var.conf` on the result. ## 2. `BOOTC_REF` was unpinned It defaulted to empty, which `bootc-build.sh` resolves to whatever bootc-dev/bootc has most recently released. `publish.yml` builds every other day and passes no build-arg, so an unattended build was free to replace the thing that mounts the root filesystem with a version nobody had booted. Pinned to `v1.16.9` — the newest release as of now, and also the one the README's source references were read against (the bootupd/systemd-boot branch in `install.rs`, the kargs handling in `bootc_composefs/boot.rs`), so the docs and the binary now describe the same program. Empty still means "newest release" for testing what upstream has moved to. --- Nothing here touches the `/opt`, AppArmor or composefs work. `README.md` is updated alongside both changes.
bootc-rootfs.sh transcribed /var into tmpfiles as directories and symlinks and
emitted nothing for regular files -- which a tmpfiles entry cannot fill anyway.
Step 3 then deletes them. Not only on the upgrade path the generator was
written for: it empties the /var that ships in the image, so a fresh
`bootc install` has nothing to materialise either. `bootc container lint` does
not catch it; var-tmpfiles is about directories.

Copy the content to /usr/share/factory/var at build time and emit `C` entries,
whose bare argument resolves to exactly that path. `C` copies only when the
destination is absent, so a file a host has since edited is left alone.

Which files count is asked of dpkg rather than read off the tree: a .list entry
is content some package installed, and build residue -- apt lists, dpkg's logs,
/var/backups -- belongs to no package and is skipped without an exception list
to maintain.

The set is smaller than the /var tree suggests. Of ~350 .list entries under
/var on a desktop root, ~228 are symlinks -- all of /var/lib/ghostscript/CMap
points into /usr/share/poppler, already covered by the L entries -- and ~100
are directories. What was being lost is the couple of dozen regular files left
over: the hunspell dispatch tables under /var/lib/dictionaries-common, which is
how hunspell dictionaries are found at all; usb.ids; /var/lib/xkb's compiled
cache marker; tex-common's fmtutil and font-map fragments. A few hundred KiB.

Worth it at that size because the failure mode is a file that is simply not
there, on a system with no `apt install --reinstall` to put it back -- and
because the file case is now covered for whatever is added to the Containerfile
later, rather than correct by accident for today's package list.

Verified by replaying a generated conf with `systemd-tmpfiles --root --create`
over an emptied /var: content and modes restored, directory modes preserved,
unowned residue skipped, the /var/lib/dpkg symlink intact, idempotent, and a
host-edited file left untouched on a second run.
Pin BOOTC_REF, so a timer cannot change the boot path
All checks were successful
Publish / build (pull_request) Successful in 23m26s
Publish / build (push) Successful in 27m30s
f5e0127622
BOOTC_REF defaulted to empty, which bootc-build.sh resolves to whatever
bootc-dev/bootc has most recently released. publish.yml builds every other day
and passes no build-arg, so an unattended build was free to replace the thing
that mounts the root filesystem with a version nobody had booted.

Pin it to v1.16.9 -- also the release the README's source references were read
against, the bootupd/systemd-boot branch in install.rs and the kargs handling
in bootc_composefs/boot.rs, so the documentation and the binary now describe
the same program. Empty still means "newest release", which is useful for
testing what upstream has moved to; bumping the pin is a commit, which is where
a change to the boot path belongs.
sebastian deleted branch fix/var-files-and-bootc-pin 2026-08-24 22:14:45 +02:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
sebastian/os4!6
No description provided.