Skip to content

Clean up part2disk loop mappings#136

Open
SeanMooney wants to merge 5 commits into
cirros-dev:mainfrom
SeanMooney:fix/part2disk-loop-cleanup
Open

Clean up part2disk loop mappings#136
SeanMooney wants to merge 5 commits into
cirros-dev:mainfrom
SeanMooney:fix/part2disk-loop-cleanup

Conversation

@SeanMooney

Copy link
Copy Markdown

Summary

  • make bin/part2disk cleanup guard mounted paths instead of unconditionally unmounting them
  • track the loop device returned by kpartx and remove its device-mapper mappings on exit
  • detach the loop device and remove the temporary directory during normal and failure cleanup

Testing

  • reproduced leaked loop devices after an x86_64 Docker build with BOOTTEST=true
  • cleaned the leaked mappings
  • ran part2disk directly inside the Docker build container after this change and confirmed loop0-loop6 were free afterward, with no stale loopNp* mapper entries

The CirrOS build system has historically required a bare Ubuntu 22.04
host with a specific set of system packages installed via system-setup.
This makes it difficult to build CirrOS reproducibly on modern
workstations or in CI environments running a different host OS.

This change introduces a Docker-based build environment that encapsulates
all build dependencies in an ubuntu:22.04 image while preserving the
existing native build-release contract.

Dockerfile installs all packages from bin/system-setup plus the implicit
dependencies (binutils, cpio, kmod, sudo) and the CI extras
(qemu-system-*, cloud-utils, openbios-ppc) in a single layer. It also
sets USER=root so the existing `sudo chown -R $USER:$USER` ownership
handoff remains valid when bin/build-release runs inside the container.
It pre-creates /dev/loop0-7 nodes and declares /cirros/download and
/cirros/ccache as volumes so that build caches persist across runs.

docker-entrypoint.sh validates that the cirros source tree is bind-mounted
at /cirros, loads the loop kernel module (required for bin/bundle's
loopback mounts), and delegates to bin/build-release. The ARCHES
environment variable can be set to limit the build to a subset of
architectures, e.g. ARCHES=x86_64.

bin/build-release also updates the Buildroot download URL from the
defunct buildroot.uclibc.org domain to buildroot.org, which now hosts the
official release archives. The existing sudo-based bundle and chown steps
are intentionally left unchanged so native and GitHub Actions builds keep
working as before.

To build an x86_64 image:

  docker build -t cirros-builder .
  docker run --privileged --rm \
    -e ARCHES=x86_64 \
    -v "$PWD":/cirros \
    -v cirros-dl:/cirros/download \
    -v cirros-ccache:/cirros/ccache \
    cirros-builder daily

The --privileged flag is required because bin/bundle mounts a loopback
partition image during the disk image assembly stage.

Assisted-By: pi claude-sonnet-4-6
Signed-off-by: Sean Mooney <work@seanmooney.info>
CirrOS currently ships a bootable qcow2 image with a mostly empty root
filesystem partition. On first boot the initramfs detects that the labeled
rootfs partition does not contain /sbin/init, mounts it read-write, and
copies the initramfs contents into the partition before switching root.

If that first boot is interrupted, the partition can be left only partly
populated. A later boot may then find /sbin/init and switch into the
partition, but fail when a required shared library or file was never
copied. That presents as PID 1 exiting, followed by a kernel panic.

Keep the traditional minimal qcow2 image for compatibility, but rename it
from the old .img suffix to -minimal.qcow2 so its format is explicit. Add
a second -full.qcow2 image whose root filesystem is pre-populated at build
time from the assembled filesys tarball. The full initramfs is still copied
into /boot so that the early boot environment has the kernel modules
required to find and mount the root device.

This removes the first-boot rootfs copy window for users that choose the
full image while preserving the existing minimal image and initramfs
fallback behavior.

Assisted-By: pi claude-sonnet-4-6
Signed-off-by: Sean Mooney <work@seanmooney.info>
CirrOS root filesystem partition images are currently formatted as ext3.
The previous change makes the partition hold the real root filesystem at
build time, so use ext4 for that filesystem to get the newer ext4 layout
and metadata handling while keeping compatibility with the existing
resize2fs-based grow path.

The initramfs mounts the root device by label without forcing a
filesystem type, so no initramfs change is needed. The generated x86_64
image was also boot-tested under QEMU to confirm GRUB can load the kernel
and initramfs from the ext4 partition and the system reaches the CirrOS
login prompt.

Tested with an x86_64 Docker build and a QEMU boot smoke test. The image
boots to the CirrOS login prompt, mounts /dev/vda1 as the root
filesystem, and does not copy the initramfs to the rootfs partition at
boot.

Assisted-By: pi claude-sonnet-4-6
Signed-off-by: Sean Mooney <work@seanmooney.info>
CirrOS has several focused docs and many small shell scripts that explain parts
of the build, boot, test, and release workflow, but contributors currently need
to reconstruct the full picture from scattered sources. That makes it harder to
understand how Buildroot output, the CirrOS filesystem overlay, downloaded
kernels, grub payloads, image bundling, runtime scripts, datasources, and
release artifacts fit together.

Add a CirrOS From Scratch developer guide as the primary contributor manual. The
guide provides a read-first path, Buildroot primer, staged build walkthrough,
filesystem and boot/runtime flow, runtime script and datasource reference, image
assembly notes, architecture support details, testing and CI guidance,
maintenance workflows, and troubleshooting appendices.

Fold durable context from the older docs into the new guide while keeping stale
commands clearly caveated. The guide maps existing docs, documents current
release artifact names and semantics, describes manual release-test categories,
records maintainer-only publishing context, and calls out stale examples that
should not be copied blindly.

Docker build support is intentionally left out of scope because it is still WIP,
and riscv64 is described as experimental until maintainers decide otherwise.

Assisted-By: pi gpt-5.5
Signed-off-by: Sean Mooney <work@seanmooney.info>
@SeanMooney SeanMooney force-pushed the fix/part2disk-loop-cleanup branch 2 times, most recently from 078101b to 1f75f60 Compare June 2, 2026 13:24
part2disk can leave loop and device-mapper state behind after wrapping a
partition image. In practice the caller may then remove the temporary raw
disk image, leaving loop devices attached to deleted files and loopNp*
mapper entries behind.

Track the loop device returned by kpartx, unmount only paths that are
still mounted, remove mapper entries by loop device, detach the loop
device, and remove the temporary directory during cleanup. This makes
normal exit and failure cleanup use the same guarded path.

Signed-off-by: Sean Mooney <work@seanmooney.info>

Generated-By: pi gpt-5.5
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant