@@ -17,6 +17,11 @@ use ostree_ext::composefs_oci::linked_erofs_images;
1717use rustix:: fs:: AtFlags ;
1818use rustix:: fs:: { statat, unlinkat} ;
1919
20+ use crate :: bootc_composefs:: boot:: GLOBAL_UKI_ADDONS_DIR ;
21+ use crate :: bootc_composefs:: boot:: get_global_uki_addon_name;
22+ use crate :: bootc_composefs:: uki_addon:: UkiAddonType ;
23+ use crate :: bootc_composefs:: uki_addon:: list_installed_uki_addons;
24+ use crate :: bootc_composefs:: uki_addon:: list_referenced_uki_addons;
2025use crate :: {
2126 bootc_composefs:: {
2227 boot:: { BOOTC_UKI_DIR , BootType , get_type1_dir_name, get_uki_addon_dir_name, get_uki_name} ,
@@ -142,9 +147,6 @@ fn delete_kernel_initrd(storage: &Storage, dir_to_delete: &str, dry_run: bool) -
142147fn delete_uki ( storage : & Storage , uki_id : & str , dry_run : bool ) -> Result < ( ) > {
143148 let esp_mnt = storage. require_esp ( ) ?;
144149
145- // NOTE: We don't delete global addons here (see `GLOBAL_UKI_ADDONS_DIR`)
146- // Which is fine as global addons don't belong to any single deployment, but it also
147- // means they're never cleaned up at all: see the TODO on `GLOBAL_UKI_ADDONS_DIR`.
148150 let uki_dir = esp_mnt. fd . open_dir ( BOOTC_UKI_DIR ) ?;
149151
150152 for entry in uki_dir. entries_utf8 ( ) ? {
@@ -291,7 +293,7 @@ pub(crate) async fn composefs_gc(
291293 )
292294 }
293295
294- for ( ty, verity) in unreferenced_boot_binaries {
296+ for ( ty, verity) in & unreferenced_boot_binaries {
295297 match ty {
296298 BootType :: Bls => {
297299 delete_kernel_initrd ( storage, & get_type1_dir_name ( verity) , gc_opts. dry_run ) ?
@@ -300,6 +302,55 @@ pub(crate) async fn composefs_gc(
300302 }
301303 }
302304
305+ // Remove unreferenced global UKI Addons
306+ let currently_referenced_addons = list_referenced_uki_addons ( booted_cfs, & bootloader_entries) ?;
307+ let currently_installed_addons = list_installed_uki_addons ( storage) ?;
308+ let mut unreferenced_global_addons = vec ! [ ] ;
309+
310+ tracing:: debug!( "currently_referenced_addons: {currently_referenced_addons:#?}" ) ;
311+ tracing:: debug!( "currently_installed_addons: {currently_installed_addons:#?}" ) ;
312+
313+ for installed_addon in & currently_installed_addons {
314+ // We handle scoped UKI Addons along with the UKI itself
315+ if installed_addon. addon_type != UkiAddonType :: Global {
316+ continue ;
317+ }
318+
319+ let is_referenced = currently_referenced_addons. iter ( ) . any ( |( _, refs) | {
320+ refs. iter ( ) . any ( |r| {
321+ r. addon_type == installed_addon. addon_type && r. name == installed_addon. name
322+ } )
323+ } ) ;
324+
325+ if !is_referenced {
326+ unreferenced_global_addons. push ( installed_addon. name . clone ( ) ) ;
327+ }
328+ }
329+
330+ tracing:: debug!( "Unreferenced Global Addons: {unreferenced_global_addons:?}" ) ;
331+
332+ if !unreferenced_global_addons. is_empty ( ) {
333+ let global_uki_dir = storage
334+ . require_esp ( ) ?
335+ . fd
336+ . open_dir ( GLOBAL_UKI_ADDONS_DIR )
337+ . context ( "Opening global UKI Addons dir" ) ?;
338+
339+ for addon in & unreferenced_global_addons {
340+ let global_addon_name = get_global_uki_addon_name ( & addon) ;
341+
342+ tracing:: debug!( "Deleting Global UKI Addon: {}" , addon) ;
343+
344+ if gc_opts. dry_run {
345+ continue ;
346+ }
347+
348+ global_uki_dir
349+ . remove_file ( & global_addon_name)
350+ . with_context ( || format ! ( "Removing global addon {global_addon_name}" ) ) ?;
351+ }
352+ }
353+
303354 if !gc_opts. prune_repo {
304355 return Ok ( GcResult :: default ( ) ) ;
305356 }
0 commit comments