Skip to content
This repository was archived by the owner on Aug 22, 2026. It is now read-only.

Fix btrfs-subvolume install aborting at 99% after root GPT retag - #9

Open
joshmccall221 wants to merge 1 commit into
frostyard:devfrom
joshmccall221:fix/btrfs-subvol-remount-after-retag
Open

Fix btrfs-subvolume install aborting at 99% after root GPT retag#9
joshmccall221 wants to merge 1 commit into
frostyard:devfrom
joshmccall221:fix/btrfs-subvol-remount-after-retag

Conversation

@joshmccall221

Copy link
Copy Markdown

Problem

On btrfs installs with subvolumes, a fresh install aborts at step 7/8 (99%) — right after bootc install completes — with:

fisherman: fatal: writing hostname: finding composefs deploy etc:
reading composefs deploy base /mnt/fisherman-target/state/deploy:
no such file or directory

The OS is fully deployed on disk (composefs deploy, systemd-boot, UKI, loader entry all present), but the system is left unconfigured (no hostname written) and the installer reports failure.

Root cause

SetupBtrfsSubvolumes mounts the target with subvol=@, and the composefs deployment (state/deploy/<hash>) lives inside the @ subvolume.

After bootc install, for systemd-boot + composefs installs the root partition is retagged to the Linux-root GPT type for GPT auto-discovery. This unmounts and remounts the root — but the remount passed empty mount options:

disk.Mount(rootPart, activeTargetMount, "")   // drops subvol=@

So the root is remounted at the btrfs top-level instead of the @ subvolume. Post-install steps that write through state/deploy (WriteHostname, system Flatpak copy) then look at the top-level state/, which has no deploy/, and fail. The inline comment on that block ("…the empty /boot/efi directory in the XFS root") shows the path was written/validated for XFS and regressed on btrfs-subvolume layouts.

Fix

Remount the root with the same options SetupBtrfsSubvolumes used (subvol=@,compress=zstd:1) when the install uses btrfs subvolumes. To keep the two mount sites from drifting:

  • Hoist the options into a shared disk.BtrfsRootMountOpts constant (used by both SetupBtrfsSubvolumes and the retag remount).
  • Extract the retag remount into a testable disk.RemountRoot(diskDev, partNum, target, btrfsSubvols) helper.

Non-btrfs-subvolume installs (xfs/ext4) remount bare exactly as before.

Test

Adds TestRemountRoot, asserting the btrfs-subvolume remount carries subvol=@ (and compress=zstd:1) while non-subvolume installs remount with no options.

go test ./internal/disk/ -run TestRemountRoot   # PASS
GOOS=linux GOARCH=amd64 go build ./...          # OK
go vet ./internal/disk/ ./cmd/fisherman/         # clean

@joshmccall221

Copy link
Copy Markdown
Author

Validated live end-to-end on a real machine (Snow Linux live ISO → install to internal NVMe).

Before the fix: install died at step 7/8 (99%) with:

fatal: writing hostname: finding composefs deploy etc: reading composefs deploy base /mnt/fisherman-target/state/deploy: no such file or directory

Root cause confirmed on-disk: after the GPT retag, root was remounted with empty options, exposing the btrfs top-level, so state/deploy/<hash> (which only exists inside the @ subvolume) was unreachable.

With this patch: a full install (btrfs + subvolumes, systemd-boot, composefs backend, no encryption) completed all 8 steps:

Writing hostname: snow
+ mkdir -p .../state/deploy/75afcb…/etc
wrote hostname "snow" to .../state/deploy/75afcb…/etc/hostname
...
Installation complete!

Post-install verification of the NVMe: composefs deploy present under @/state/deploy/<hash>, /etc/hostname written, user account created, and systemd-boot loader entry + systemd-bootx64.efi in place — i.e. a bootable system.

The remount now preserving subvol=@ is exactly what fixes the failure.

@bketelsen
bketelsen requested a review from Copilot August 2, 2026 04:33
@bketelsen

Copy link
Copy Markdown

bootcrew vm tests expected to fail

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Fixes a btrfs subvolume install regression where the root filesystem was remounted without subvol=@ after retagging the root GPT type for systemd-boot auto-discovery, causing post-install steps (e.g., hostname/flatpak writes through state/deploy) to fail and abort the install at 99%.

Changes:

  • Introduces a shared disk.RemountRoot(...) helper to remount the root partition with the correct btrfs subvolume options when applicable.
  • Hoists the btrfs root mount options into a shared constant to avoid drift between mount sites.
  • Adds a regression unit test to assert the remount uses subvol=@,compress=zstd:1 for btrfs-subvolume installs and no opts otherwise.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 2 comments.

File Description
fisherman/internal/disk/format.go Adds shared btrfs root mount options constant and RemountRoot helper; reuses the constant in SetupBtrfsSubvolumes.
fisherman/internal/disk/format_test.go Adds TestRemountRoot regression coverage for btrfs-subvolume vs non-subvolume remount behavior.
fisherman/cmd/fisherman/main.go Switches retag post-step to use disk.RemountRoot and preserve btrfs subvolume mount options.
CHANGELOG.md Documents the bug fix and its user-visible impact in the Unreleased changelog.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +12 to +16
// BtrfsRootMountOpts are the mount options used for the root btrfs subvolume
// layout (@, @home, @snapshots). The installed system's state/deploy tree lives
// inside the @ subvolume, so any (re)mount of the root partition that post-install
// steps write through must use these options; a bare mount would expose the
// btrfs top-level instead, where state/deploy does not exist.
Comment thread CHANGELOG.md
Comment on lines +18 to +20
no such file or directory", aborting the install at 99%. The remount now
reuses the `subvol=@,compress=zstd:1` options via the new
`disk.BtrfsRootMountOpts` constant.
@bketelsen

Copy link
Copy Markdown

GPT-5.6-sol review:

No blocking defects found. The fix correctly preserves `subvol=@,compress=zstd:1` after GPT retagging, while non-subvolume installs retain the previous bare mount behavior. I would approve the code change.
**Residual Risks**
- `fisherman/internal/disk/format_test.go:215` tests `RemountRoot` directly but not the production wiring at `fisherman/cmd/fisherman/main.go:714`. A future call-site regression could evade this test. Non-blocking given the monolithic `main()`.
- Pre-existing adjacent issue: a separately mounted `/var` is not remounted after the root retag sequence (`main.go:589-607`, `main.go:694-721`). Subsequent writes may land in the root filesystem instead. This is not introduced by PR #9, but should be tracked separately.
- GitHub CI is red because whole-tree lint failures already exist on base commit `44ae0de`; the PR’s changed lines are clean.
**Verification**
- `go test -race ./...`: passed
- `go vet ./...`: passed
- `go build ./...`: passed
- `git diff --check 44ae0de..b50eb02`: passed
- The author also documented a successful physical-machine end-to-end install.```
- 

@bketelsen

Copy link
Copy Markdown

Looks great - fix the conflicts please.

On btrfs installs with subvolumes, `SetupBtrfsSubvolumes` mounts the target
with `subvol=@`, and the composefs deployment (`state/deploy`) lives inside the
`@` subvolume. After `bootc install`, the root partition is retagged to the
Linux root GPT type for systemd-boot GPT auto-discovery, which requires
unmounting and remounting the root. That remount passed empty mount options, so
it exposed the btrfs top-level instead of the `@` subvolume.

Post-install steps that write through `state/deploy` (WriteHostname, system
Flatpak copy) then failed with:

    finding composefs deploy etc: reading composefs deploy base
    /mnt/fisherman-target/state/deploy: no such file or directory

aborting the install at step 7/8 (99%) with a fully-written but unconfigured
system on disk.

Remount the root with `subvol=@,compress=zstd:1` (the same options
`SetupBtrfsSubvolumes` uses) when the install uses btrfs subvolumes. The options
are hoisted into a shared `disk.BtrfsRootMountOpts` constant so the initial
mount and the retag remount cannot drift apart, and the remount is extracted
into a testable `disk.RemountRoot` helper.

Regression test `TestRemountRoot` asserts the btrfs-subvolume remount carries
`subvol=@` while non-subvolume installs remount bare.
@bketelsen
bketelsen force-pushed the fix/btrfs-subvol-remount-after-retag branch from b50eb02 to 704ffb3 Compare August 6, 2026 14:47
@bketelsen

Copy link
Copy Markdown

For the record this fix is "valid" but not currently useful because the subvol support isn't wired in fisherman. Will create tracking issue and update here.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants