@@ -5,7 +5,7 @@ use cap_std_ext::{
55    cap_std:: { ambient_authority,  fs:: Dir } , 
66    dirext:: CapStdExtDirExt , 
77} ; 
8- use  composefs:: fsverity:: { FsVerityHashValue ,   Sha512HashValue } ; 
8+ use  composefs:: fsverity:: Sha512HashValue ; 
99use  composefs_boot:: bootloader:: { EFI_ADDON_DIR_EXT ,  EFI_EXT } ; 
1010
1111use  crate :: { 
@@ -14,6 +14,7 @@ use crate::{
1414            find_vmlinuz_initrd_duplicates,  get_efi_uuid_source,  get_esp_partition, 
1515            get_sysroot_parent_dev,  mount_esp,  BootType ,  SYSTEMD_UKI_DIR , 
1616        } , 
17+         gc:: composefs_gc, 
1718        repo:: open_composefs_repo, 
1819        rollback:: { composefs_rollback,  rename_exchange_user_cfg} , 
1920        status:: { composefs_deployment_status,  get_sorted_grub_uki_boot_entries} , 
@@ -23,21 +24,17 @@ use crate::{
2324        TYPE1_ENT_PATH ,  TYPE1_ENT_PATH_STAGED ,  USER_CFG_STAGED , 
2425    } , 
2526    parsers:: bls_config:: { parse_bls_config,  BLSConfigType } , 
26-     spec:: { Bootloader ,  DeploymentEntry } , 
27+     spec:: { BootEntry ,   Bootloader ,  DeploymentEntry } , 
2728    status:: Slot , 
2829} ; 
2930
30- struct  ObjectRefs  { 
31-     other_depl :  HashSet < Sha512HashValue > , 
32-     depl_to_del :  HashSet < Sha512HashValue > , 
31+ pub ( crate )   struct  ObjectRefs  { 
32+     pub ( crate )   other_depl :  HashSet < Sha512HashValue > , 
33+     pub ( crate )   depl_to_del :  HashSet < Sha512HashValue > , 
3334} 
3435
3536#[ fn_error_context:: context( "Deleting Type1 Entry {}" ,  depl. deployment. verity) ]  
36- fn  delete_type1_entries ( 
37-     depl :  & DeploymentEntry , 
38-     boot_dir :  & Dir , 
39-     deleting_staged :  bool , 
40- )  -> Result < ( ) >  { 
37+ fn  delete_type1_entry ( depl :  & DeploymentEntry ,  boot_dir :  & Dir ,  deleting_staged :  bool )  -> Result < ( ) >  { 
4138    let  entries_dir_path = if  deleting_staged { 
4239        TYPE1_ENT_PATH_STAGED 
4340    }  else  { 
@@ -81,8 +78,8 @@ fn delete_type1_entries(
8178                } 
8279
8380                // Boot dir in case of EFI will be the ESP 
84-                 delete_uki ( & depl. deployment . verity ,  boot_dir) ?; 
8581                entry. remove_file ( ) . context ( "Removing .conf file" ) ?; 
82+                 delete_uki ( & depl. deployment . verity ,  boot_dir) ?; 
8683
8784                break ; 
8885            } 
@@ -96,12 +93,12 @@ fn delete_type1_entries(
9693                    continue ; 
9794                } 
9895
96+                 entry. remove_file ( ) . context ( "Removing .conf file" ) ?; 
97+ 
9998                if  should_del_kernel { 
10099                    delete_kernel_initrd ( & bls_config. cfg_type ,  boot_dir) ?; 
101100                } 
102101
103-                 entry. remove_file ( ) . context ( "Removing .conf file" ) ?; 
104- 
105102                break ; 
106103            } 
107104
@@ -156,6 +153,7 @@ fn delete_kernel_initrd(bls_config: &BLSConfigType, boot_dir: &Dir) -> Result<()
156153/// Deletes the UKI `uki_id` and any addons specific to it 
157154#[ fn_error_context:: context( "Deleting UKI and UKI addons {uki_id}" ) ]  
158155fn  delete_uki ( uki_id :  & str ,  esp_mnt :  & Dir )  -> Result < ( ) >  { 
156+     // TODO: We don't delete global addons here 
159157    let  ukis = esp_mnt. open_dir ( SYSTEMD_UKI_DIR ) ?; 
160158
161159    for  entry in  ukis. entries_utf8 ( ) ? { 
@@ -209,27 +207,28 @@ fn remove_grub_menucfg_entry(id: &str, boot_dir: &Dir, deleting_staged: bool) ->
209207    rename_exchange_user_cfg ( & grub_dir) 
210208} 
211209
210+ #[ fn_error_context:: context( "Deleting boot entries for deployment {}" ,  deployment. deployment. verity) ]  
212211fn  delete_depl_boot_entries ( deployment :  & DeploymentEntry ,  deleting_staged :  bool )  -> Result < ( ) >  { 
213212    match  deployment. deployment . bootloader  { 
214213        Bootloader :: Grub  => { 
215214            let  boot_dir = Dir :: open_ambient_dir ( "/sysroot/boot" ,  ambient_authority ( ) ) 
216215                . context ( "Opening boot dir" ) ?; 
217216
218217            match  deployment. deployment . boot_type  { 
219-                 BootType :: Bls  => delete_type1_entries ( deployment,  & boot_dir,  deleting_staged) , 
218+                 BootType :: Bls  => delete_type1_entry ( deployment,  & boot_dir,  deleting_staged) , 
220219
221220                BootType :: Uki  => { 
222221                    let  device = get_sysroot_parent_dev ( ) ?; 
223222                    let  ( esp_part,  ..)  = get_esp_partition ( & device) ?; 
224223                    let  esp_mount = mount_esp ( & esp_part) ?; 
225224
226-                     delete_uki ( & deployment. deployment . verity ,  & esp_mount. fd ) ?; 
227- 
228225                    remove_grub_menucfg_entry ( 
229226                        & deployment. deployment . verity , 
230227                        & boot_dir, 
231228                        deleting_staged, 
232-                     ) 
229+                     ) ?; 
230+ 
231+                     delete_uki ( & deployment. deployment . verity ,  & esp_mount. fd ) 
233232                } 
234233            } 
235234        } 
@@ -241,44 +240,12 @@ fn delete_depl_boot_entries(deployment: &DeploymentEntry, deleting_staged: bool)
241240            let  esp_mount = mount_esp ( & esp_part) ?; 
242241
243242            // For Systemd UKI as well, we use .conf files 
244-             delete_type1_entries ( deployment,  & esp_mount. fd ,  deleting_staged) 
243+             delete_type1_entry ( deployment,  & esp_mount. fd ,  deleting_staged) 
245244        } 
246245    } 
247246} 
248247
249- pub ( crate )  async  fn  delete_composefs_deployment ( deployment_id :  & str ,  delete :  bool )  -> Result < ( ) >  { 
250-     let  host = composefs_deployment_status ( ) . await ?; 
251- 
252-     let  booted = host. require_composefs_booted ( ) ?; 
253- 
254-     let  all_depls = host. all_composefs_deployments ( ) ?; 
255- 
256-     let  depl_to_del = all_depls
257-         . iter ( ) 
258-         . find ( |d| d. deployment . verity  == deployment_id) ; 
259- 
260-     let  Some ( depl_to_del)  = depl_to_del else  { 
261-         anyhow:: bail!( "Deployment {deployment_id} not found" ) ; 
262-     } ; 
263- 
264-     let  deleting_staged = host
265-         . status 
266-         . staged 
267-         . as_ref ( ) 
268-         . and_then ( |s| s. composefs . as_ref ( ) ) 
269-         . map_or ( false ,  |cfs| cfs. verity  == deployment_id) ; 
270- 
271-     // Get all objects referenced by all images 
272-     // Delete objects that are only referenced by the deployment to be deleted 
273- 
274-     // Unqueue rollback. This makes it easier to delete boot entries later on 
275-     if  matches ! ( depl_to_del. ty,  Some ( Slot :: Rollback ) )  && host. status . rollback_queued  { 
276-         composefs_rollback ( ) . await ?; 
277-     } 
278- 
279-     let  sysroot =
280-         Dir :: open_ambient_dir ( "/sysroot" ,  ambient_authority ( ) ) . context ( "Opening sysroot" ) ?; 
281- 
248+ pub ( crate )  fn  get_image_objects ( sysroot :  & Dir ,  deployment_id :  Option < & str > )  -> Result < ObjectRefs >  { 
282249    let  repo = open_composefs_repo ( & sysroot) ?; 
283250
284251    let  images_dir = sysroot
@@ -303,19 +270,73 @@ pub(crate) async fn delete_composefs_deployment(deployment_id: &str, delete: boo
303270            . objects_for_image ( & img_name) 
304271            . with_context ( || format ! ( "Getting objects for image {img_name}" ) ) ?; 
305272
306-         if  img_name == deployment_id { 
307-             object_refs. depl_to_del . extend ( objects) ; 
273+         if  let  Some ( deployment_id)  = deployment_id { 
274+             if  deployment_id == img_name { 
275+                 object_refs. depl_to_del . extend ( objects) ; 
276+             } 
308277        }  else  { 
309278            object_refs. other_depl . extend ( objects) ; 
310279        } 
311280    } 
312281
313-     let  diff:  Vec < & Sha512HashValue >  = object_refs
314-         . depl_to_del 
315-         . difference ( & object_refs. other_depl ) 
316-         . collect ( ) ; 
282+     Ok ( object_refs) 
283+ } 
284+ 
285+ pub ( crate )  fn  delete_image ( sysroot :  & Dir ,  deployment_id :  & str )  -> Result < ( ) >  { 
286+     let  img_path = Path :: new ( "composefs" ) . join ( "images" ) . join ( deployment_id) ; 
287+ 
288+     sysroot
289+         . remove_file ( & img_path) 
290+         . context ( "Deleting EROFS image" ) 
291+ } 
317292
318-     tracing:: debug!( "diff: {:#?}" ,  diff) ; 
293+ pub ( crate )  fn  delete_state_dir ( sysroot :  & Dir ,  deployment_id :  & str )  -> Result < ( ) >  { 
294+     let  state_dir = Path :: new ( STATE_DIR_RELATIVE ) . join ( deployment_id) ; 
295+ 
296+     sysroot
297+         . remove_dir_all ( & state_dir) 
298+         . with_context ( || format ! ( "Removing dir {state_dir:?}" ) ) 
299+ } 
300+ 
301+ pub ( crate )  fn  delete_staged ( staged :  & Option < BootEntry > )  -> Result < ( ) >  { 
302+     if  staged. is_none ( )  { 
303+         tracing:: debug!( "No staged deployment" ) ; 
304+         return  Ok ( ( ) ) ; 
305+     } ; 
306+ 
307+     let  file = Path :: new ( COMPOSEFS_TRANSIENT_STATE_DIR ) . join ( COMPOSEFS_STAGED_DEPLOYMENT_FNAME ) ; 
308+     tracing:: debug!( "Deleting staged file {file:?}" ) ; 
309+     std:: fs:: remove_file ( file) . context ( "Removing staged file" ) ?; 
310+ 
311+     Ok ( ( ) ) 
312+ } 
313+ 
314+ pub ( crate )  async  fn  delete_composefs_deployment ( deployment_id :  & str ,  delete :  bool )  -> Result < ( ) >  { 
315+     let  host = composefs_deployment_status ( ) . await ?; 
316+ 
317+     let  booted = host. require_composefs_booted ( ) ?; 
318+ 
319+     let  all_depls = host. all_composefs_deployments ( ) ?; 
320+ 
321+     let  depl_to_del = all_depls
322+         . iter ( ) 
323+         . find ( |d| d. deployment . verity  == deployment_id) ; 
324+ 
325+     let  Some ( depl_to_del)  = depl_to_del else  { 
326+         anyhow:: bail!( "Deployment {deployment_id} not found" ) ; 
327+     } ; 
328+ 
329+     let  deleting_staged = host
330+         . status 
331+         . staged 
332+         . as_ref ( ) 
333+         . and_then ( |s| s. composefs . as_ref ( ) ) 
334+         . map_or ( false ,  |cfs| cfs. verity  == deployment_id) ; 
335+ 
336+     // Unqueue rollback. This makes it easier to delete boot entries later on 
337+     if  matches ! ( depl_to_del. ty,  Some ( Slot :: Rollback ) )  && host. status . rollback_queued  { 
338+         composefs_rollback ( ) . await ?; 
339+     } 
319340
320341    // For debugging, but maybe useful elsewhere? 
321342    if  !delete { 
@@ -338,32 +359,7 @@ pub(crate) async fn delete_composefs_deployment(deployment_id: &str, delete: boo
338359
339360    delete_depl_boot_entries ( & depl_to_del,  deleting_staged) ?; 
340361
341-     // Delete the image 
342-     let  img_path = Path :: new ( "composefs" ) . join ( "images" ) . join ( deployment_id) ; 
343-     sysroot
344-         . remove_file ( & img_path) 
345-         . context ( "Deleting EROFS image" ) ?; 
346- 
347-     if  deleting_staged { 
348-         let  file = Path :: new ( COMPOSEFS_TRANSIENT_STATE_DIR ) . join ( COMPOSEFS_STAGED_DEPLOYMENT_FNAME ) ; 
349-         tracing:: debug!( "Deleting staged file {file:?}" ) ; 
350-         std:: fs:: remove_file ( file) . context ( "Removing staged file" ) ?; 
351-     } 
352- 
353-     let  state_dir = Path :: new ( STATE_DIR_RELATIVE ) . join ( deployment_id) ; 
354-     sysroot
355-         . remove_dir_all ( & state_dir) 
356-         . with_context ( || format ! ( "Removing dir {state_dir:?}" ) ) ?; 
357- 
358-     for  sha in  diff { 
359-         let  object_path = Path :: new ( "composefs" ) 
360-             . join ( "objects" ) 
361-             . join ( sha. to_object_pathname ( ) ) ; 
362- 
363-         sysroot
364-             . remove_file ( & object_path) 
365-             . with_context ( || format ! ( "Removing {object_path:?}" ) ) ?; 
366-     } 
362+     composefs_gc ( ) . await ?; 
367363
368364    Ok ( ( ) ) 
369365} 
0 commit comments