Skip to content

Commit 90b88f9

Browse files
composefs-backend: Add garbage collection
Update the deletion of deployment to only simply delete the bootloader entries related to the deployment and then call a `gc` function, which will just get the difference between the states represented by the bootloader entries and the repository then try to reconcile everything by performing GC operation on the repository. Signed-off-by: Pragyan Poudyal <[email protected]>
1 parent 1101839 commit 90b88f9

File tree

7 files changed

+359
-87
lines changed

7 files changed

+359
-87
lines changed

crates/lib/src/bootc_composefs/delete.rs

Lines changed: 80 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -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;
99
use composefs_boot::bootloader::{EFI_ADDON_DIR_EXT, EFI_EXT};
1010

1111
use 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}")]
158155
fn 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)]
212211
fn 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

Comments
 (0)