Skip to content

GC UKI and UKI Addons from Composefs Repository - #2380

Merged
cgwalters merged 5 commits into
bootc-dev:mainfrom
Johan-Liebert1:save-boot-structure
Sep 9, 2026
Merged

cgwalters merged 5 commits into
bootc-dev:mainfrom
Johan-Liebert1:save-boot-structure

Conversation

@Johan-Liebert1

@Johan-Liebert1 Johan-Liebert1 commented Aug 13, 2026

Copy link
Copy Markdown
Member

cfs/gc: GC UKI and UKI Addons

Make use of the non-bootable EROFS to GC everything in the boot
directory of the deployment being GC'd. This is only done for UKI boots
as for normal Type1 booted systems, there should be nothing in /boot


tmt: Add tests for .boot images and UKI asset cleanup

Test whether UKI assets are being cleaned up from the objects directory
during GC


cfs/filesystem: Pass manifest verity to create_filesystem
There are two reasons to do this

  1. We skip the expensive operation of reading every layer and computing
    its hash to preserve integrity.

  2. We end up also reading whiteouts in /boot which end up erroring out
    since those objects don't exist anymore

@cgwalters

Copy link
Copy Markdown
Collaborator

Why can't we just read the non-bootable tree? That's already supported in composefs-rs directly - the same filesystem tree one would get to run as a container image. Yes, we won't have e.g. SELinux labeling, but that's not a problem.

@cgwalters cgwalters left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

See above

@Johan-Liebert1

Copy link
Copy Markdown
Member Author

Why can't we just read the non-bootable tree?

I didn't even realise we saved this :|
We can totally just use this image

@Johan-Liebert1

Copy link
Copy Markdown
Member Author

Okay, going through the code a bit more, we're doing things extremely inefficiently. We're recreating the filesystem multiple times, one for non-bootable fs and once for bootable. I had some optimizations here edb2ef0, but the current stuff requires some more refactoring

@cgwalters

Copy link
Copy Markdown
Collaborator

Okay, going through the code a bit more, we're doing things extremely inefficiently. We're recreating the filesystem multiple times, one for non-bootable fs and once for bootable.

What's inefficient about that? It's just O(metadata) so I'd be surprised if it was taking a noticeable amount of CPU etc.

@Johan-Liebert1

Johan-Liebert1 commented Aug 18, 2026

Copy link
Copy Markdown
Member Author

image::create_filesystem does untar and parse for all layers. This is mostly on the bootc side since we're not returning the created fs from composefs APIs the same filesystem is being created twice for one install/upgrade, one for the bootable erofs and one for the non-bootable. Also, currently we're not even passing the verity digest of the image to create_filesystem which hashes all of the layers for verification

@cgwalters

Copy link
Copy Markdown
Collaborator

image::create_filesystem does untar and parse for all layers.

Hmm, but we only need to process the tar metadata not the data, that seems like a plain bug to fix in composefs-rs - like we want a tar reading path that only gives headers from split stream.

Also, currently we're not even passing the verity digest of the image to create_filesystem which hashes all of the layers for verification

I don't understand this one.

But at this point can you spawn an agent on this and let's move this to composefs-rs?

All that needs to be done on the bootc side I think is to read the non-bootable image to access /boot, and we fix any perf issues in composefs-rs.

@Johan-Liebert1

Copy link
Copy Markdown
Member Author

I don't understand this one.

Here https://github.com/bootc-dev/bootc/blob/main/crates/lib/src/bootc_composefs/boot.rs#L1486, we pass config verity as None, which in turn invokes https://github.com/composefs/composefs-rs/blob/main/crates/composefs-oci/src/image.rs#L124

But at this point can you spawn an agent on this and let's move this to composefs-rs?

This would be a combined effort really. I think the easiest solution is to simply return the created fs from composefs APIs and have other functions take in an Option<Filesystem<..>> as a param. But yeah, that can be a followup

@Johan-Liebert1

Johan-Liebert1 commented Aug 18, 2026

Copy link
Copy Markdown
Member Author

Also, regarding just this PR, I don't believe there's any way we link the bootable and non-bootable EROFS-es? I see we have them as GC links in the config splitstream, but that requires reading the splitstream metadata. Should we create a ref in bootc for easier accessibility, similar to a ref I'm creating here 6660f94?

@cgwalters

Copy link
Copy Markdown
Collaborator

Hmm...yes we may be missing APIs for this, will look

Signed-off-by: Pragyan Poudyal <[email protected]>
Make use of the non-bootable EROFS to GC everything in the boot
directory of the deployment being GC'd. This is only done for UKI boots
as for normal Type1 booted systems, there should be nothing in /boot

Signed-off-by: Pragyan Poudyal <[email protected]>
Test whether UKI assets are being cleaned up from the objects directory
during GC

Signed-off-by: Pragyan Poudyal <[email protected]>
There are two reasons to do this

1. We skip the expensive operation of reading every layer and computing
   its hash to preserve integrity.

2. We end up also reading whiteouts in `/boot` which end up erroring out
   since those objects don't exist anymore

Signed-off-by: Pragyan Poudyal <[email protected]>
If we find an orphan state directory that has invalid fs-verity,
which could've resulted from the user snooping around or one of our
operations messing things up, we do not want to block on GC and allow
user to switch/upgrade to a new deployment.

Signed-off-by: Pragyan Poudyal <[email protected]>
@Johan-Liebert1

Copy link
Copy Markdown
Member Author

@cgwalters I think this should be good now

@Johan-Liebert1 Johan-Liebert1 changed the title Save /boot structure as an image GC UKI and UKI Addons from Composefs Repository Sep 9, 2026

@cgwalters cgwalters left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

See above


let (mut objects_bytes, mut objects_removed) = (0, 0);

// Now GC the UKI/UKI Addons from `.boot` EROFS if we have them

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

We don't have any integration tests for this though...a bit of a risk

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Comment on lines +543 to +550
tracing::debug!(
"{}: objects/{path:?}",
if gc_opts.dry_run {
"would remove"
} else {
"removing"
},
);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

This construction is a bit awkward I think it'd be simpler to use structured logging and pass dry_run=gc_opts.dry_run or so


objects_removed += 1;

unlinkat(&objects_dir, path.as_ref(), AtFlags::empty())

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Here and elsewhere we need to be able to recover from corrupted state - in this particular case should gracefully handle ENOENT

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

This is something that I'm unsure about. I think the GC op should be aware of what exists and what does not and we should definitely error out if something we absolutely expected does not exist. I think it's worth discussing in the weekly meet

Related #2442

@cgwalters
cgwalters merged commit ef59c30 into bootc-dev:main Sep 9, 2026
97 of 101 checks passed
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.

2 participants