Deprecated
GC code this script uses has been merged upstream.
Since bootc 1.14.0, GC is automatically performed at the end of an update. The use of this script is no longer neccessary.
Below is the original script kept for reference reasons.
#!/bin/bash
# binary dependencies outside bootc, coreutils and util-linux: cfsctl jq
# use together with cfsctl from https://github.com/chaserhkj/composefs-rs (PR underway)
#
# This script cleans up bootc composefs repository, leaving only booted, stage and rollback deployments,
# pinning their images and streams running garbage collection on everything else, and finally removes
# unused BLS entires
set -e
shopt -s extglob nullglob
program=$(basename $0)
progress() {
echo "($program) $@"
}
log_and_run() {
echo "(running) $@"
"$@"
}
dry_run() {
echo "(dry run) $@"
}
[ "$(readlink /proc/self/ns/mnt)" = "$(readlink /proc/1/ns/mnt)" ] && { progress "Entering private mntns" ; exec unshare -m "$0" "$@"; }
run_prefix=dry_run
while [ $# -gt 0 ]; do
if [ $1 = -f ] || [ $1 = --force ]; then
run_prefix=log_and_run
fi
shift
done
bootc_status="$(bootc status --format json)"
for type in booted rollback staged; do
printf -v "${type}_digest" "%s" $(echo "$bootc_status" | jq -r ".status.$type.image.imageDigest // \"\" " | cut -d: -f2)
printf -v "${type}_verity" "%s" $(echo "$bootc_status" | jq -r ".status.$type.composefs.verity // \"\" ")
printf -v "${type}_boot_digest" "%s" $(echo "$bootc_status" | jq -r ".status.$type.composefs.bootDigest // \"\" ")
printf -v "${type}_boot_type" "%s" $(echo "$bootc_status" | jq -r ".status.$type.composefs.bootType // \"\" ")
printf -v "${type}_bootloader" "%s" $(echo "$bootc_status" | jq -r ".status.$type.composefs.bootloader // \"\" ")
done
progress "Resolved GC roots:"
for type in booted rollback staged; do
for item in digest verity boot_digest boot_type bootloader; do
var=${type}_${item}
echo " $var: ${!var}"
done
echo
done
efi_device=$(lsblk -o PATH -nr -Q 'PARTTYPE == "c12a7328-f81f-11d2-ba4b-00a0c93ec93b"')
gc_root_flags=
for type in booted rollback staged; do
digest_var=${type}_digest
[ -n "${!digest_var}" ] && gc_root_flags="$gc_root_flags -r oci-config-sha256:${!digest_var}"
verity_var=${type}_verity
[ -n "${!verity_var}" ] && gc_root_flags="$gc_root_flags -r ${!verity_var}"
done
progress "Remounting /sysroot RW"
$run_prefix mount --make-private /sysroot
$run_prefix mount -o remount,rw /sysroot
progress "Pruning /sysroot/state/deploy"
stale_deployments=( /sysroot/state/deploy/!($booted_verity|$rollback_verity|$staged_verity) )
$run_prefix rm -rf "${stale_deployments[@]}"
dry_run_flag=
[ "$run_prefix" = "dry_run" ] && dry_run_flag=--dry-run
progress "Performing GC $dry_run_flag on /sysroot/composefs"
cfsctl_cmdline="cfsctl --repo /sysroot/composefs gc $gc_root_flags $dry_run_flag"
echo "(cfsctl) $cfsctl_cmdline"
$cfsctl_cmdline
clean_esp=yes
for type in booted rollback staged; do
var=${type}_boot_type
# when boot_type is empty string, assume the current entry is empty and skip to next entry
[ -z "${!var}" ] && continue
[ "${!var}" != "Bls" ] && clean_esp=no
var=${type}_bootloader
[ "${!var}" != "systemd" ] && clean_esp=no
done
if [ "$clean_esp" != "yes" ]; then
progress "Unsupported bootloader/boot type is used, skip cleaning ESP"
exit 0
fi
progress "Mounting ESP"
log_and_run mount --make-private $efi_device /sysroot/boot
boot_contents=( /sysroot/boot/EFI/Linux/* )
progress "Cleaning unused kernel files"
# /sysroot/boot/EFI/Linux entires may still refer to stale deployment ids
# Try to rename them to current deployment ids
# This is necessary to make "bootc upgrade" and "bootc switch" function correctly
stale_deployment_ids=()
for path in "${stale_deployments[@]}"; do
stale_deployment_ids+=( "$(basename $path)" )
done
declare -A rename_map
for boot_content_dir in "${boot_contents[@]}"; do
boot_content_id=$(basename $boot_content_dir)
used=no
boot_digest=$(cat $boot_content_dir/vmlinuz $boot_content_dir/initrd | sha256sum | cut -d" " -f1)
echo "(ESP GC) Boot binary entry: $boot_content_id"
echo "(ESP GC) Boot binary digest: $boot_digest"
# This order decides which digest to use for rename when multiple entries share same boot binaries
# Always prioritize staged > booted > rollback
for entry_type in rollback booted staged; do
digest_var_name=${entry_type}_boot_digest
match_digest=${!digest_var_name}
if [[ "$boot_digest" == "$match_digest" ]]; then
used=yes
else
continue
fi
verity_var_name=${entry_type}_verity
match_verity=${!verity_var_name}
# Check if used boot binary is referring to stale deployment id
for stale_id in "${stale_deployment_ids[@]}"; do
[[ "$stale_id" != "$boot_content_id" ]] && continue
rename_map[$stale_id]=$match_verity
echo " mark rename, from $stale_id"
echo " to $match_verity"
done
done
[ "$used" != "yes" ] && $run_prefix rm -rf $boot_content_dir
done
progress "Renaming ESP references to stale entries"
for from in "${!rename_map[@]}"; do
to=${rename_map[$from]}
$run_prefix mv /sysroot/boot/EFI/Linux/$from /sysroot/boot/EFI/Linux/$to
$run_prefix sed -i "s/\\/$from\\//\\/$to\\//g" /sysroot/boot/loader/entries/*.conf
done