Skip to content

Commit 4a78a04

Browse files
committed
fix(ubuntu): add zram module support for Ubuntu cloud images
- Add install_zram_module_if_needed function to install linux-modules-extra for Ubuntu - Implement proper package version detection and installation - Add space expansion using growpart before package installation to prevent 'No space left on device' errors - Align DEB installer logic with RPM installer for consistency - Improve error handling and logging for module installation Fixes space constraint issues when installing kernel modules on Ubuntu cloud images, which caused 'pigz: abort: write error on <stdout> (No space left on device)' errors. Signed-off-by: Kun Lai <klai@amazon.com>
1 parent f02e28f commit 4a78a04

1 file changed

Lines changed: 149 additions & 53 deletions

File tree

cryptpilot-convert.sh

Lines changed: 149 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -435,6 +435,39 @@ step::extract_boot_part_from_rootfs() {
435435
disk::umount_wait_busy "${rootfs_mount_point}"
436436
}
437437

438+
# Install zram kernel modules if needed (for Ubuntu systems)
439+
install_zram_module_if_needed() {
440+
local rootfs_mount_point="$1"
441+
442+
# Check if we're in an Ubuntu-like system by looking for the presence of apt
443+
if [ -x "${rootfs_mount_point}/usr/bin/apt-get" ] && [ -x "${rootfs_mount_point}/usr/bin/dpkg" ]; then
444+
log::info "Detecting Ubuntu-like system, attempting to install zram kernel modules"
445+
446+
# Find the kernel version from the currently installed kernel image
447+
local kernel_version
448+
kernel_version=$(chroot "${rootfs_mount_point}" bash -c "dpkg -l | grep -oP 'linux-image-\K[0-9.-]+-generic' | head -n1")
449+
450+
if [ -z "$kernel_version" ]; then
451+
log::error "Could not determine kernel version, zram module installation failed"
452+
return 1
453+
fi
454+
455+
log::info "Detected kernel version: $kernel_version"
456+
457+
# Install the modules package with minimal dependencies
458+
if ! chroot "${rootfs_mount_point}" /usr/bin/env ${http_proxy:+http_proxy=$http_proxy} \
459+
${https_proxy:+https_proxy=$https_proxy} \
460+
${ftp_proxy:+ftp_proxy=$ftp_proxy} \
461+
${rsync_proxy:+rsync_proxy=$rsync_proxy} \
462+
${all_proxy:+all_proxy=$all_proxy} \
463+
${no_proxy:+no_proxy=$no_proxy} \
464+
bash -c "apt-get update && apt-get install -y --no-install-recommends --no-install-suggests linux-modules-extra-$kernel_version"; then
465+
log::error "Could not install zram modules, possibly due to disk space constraints or missing package"
466+
return 1
467+
fi
468+
fi
469+
}
470+
438471
disk::install_rpm_on_rootfs() {
439472
local rootfs_mount_point="$1"
440473
shift
@@ -471,17 +504,17 @@ disk::install_rpm_on_rootfs() {
471504

472505
# Step 2: Build essential packages list
473506
local cryptpilot_fde_version=""
474-
507+
475508
# Try to query the version of cryptpilot-fde from the current system
476509
if command -v rpm >/dev/null 2>&1; then
477510
cryptpilot_fde_version=$(rpm -q cryptpilot-fde --qf '%{VERSION}-%{RELEASE}' 2>/dev/null || true)
478511
elif command -v dpkg-query >/dev/null 2>&1; then
479512
cryptpilot_fde_version=$(dpkg-query -W -f='${Version}' cryptpilot-fde 2>/dev/null || true)
480513
fi
481-
514+
482515
local essential_packages_with_version=()
483516
local essential_package_names=()
484-
517+
485518
if [ -n "${cryptpilot_fde_version}" ]; then
486519
log::info "Detected cryptpilot-fde version: ${cryptpilot_fde_version}"
487520
essential_packages_with_version+=("cryptpilot-fde-${cryptpilot_fde_version}")
@@ -490,7 +523,7 @@ disk::install_rpm_on_rootfs() {
490523
essential_packages_with_version+=("cryptpilot-fde")
491524
fi
492525
essential_package_names+=("cryptpilot-fde")
493-
526+
494527
essential_packages_with_version+=("yum-plugin-versionlock")
495528
essential_package_names+=("yum-plugin-versionlock")
496529

@@ -499,7 +532,7 @@ disk::install_rpm_on_rootfs() {
499532
for i in "${!essential_packages_with_version[@]}"; do
500533
local pkg_with_version="${essential_packages_with_version[$i]}"
501534
local pkg_name="${essential_package_names[$i]}"
502-
535+
503536
# Check if package is already installed in chroot
504537
if chroot "${rootfs_mount_point}" rpm -q "$pkg_name" >/dev/null 2>&1; then
505538
log::info "Package $pkg_name is already installed, skipping"
@@ -536,47 +569,29 @@ disk::install_deb_on_rootfs() {
536569
shift
537570
local packages=("$@")
538571

539-
# Essential packages for Debian/Ubuntu
540-
local essential_packages=(
541-
"cryptpilot-fde"
542-
)
543-
544-
local copied_debs=()
545-
local deb_args=()
546-
local packages_to_install=()
572+
local copied_debs=() # Will store the local paths inside chroot to the copied .deb files
573+
local user_packages=() # User provided packages to install
547574

575+
# Step 1: Install user-provided packages first
548576
for package in "${packages[@]}"; do
549577
if [[ -f "$package" && "$package" == *.deb ]]; then
578+
# This is a valid .deb file on the host
550579
base_name=$(basename "$package")
551-
cp "$package" "${rootfs_mount_point}/tmp/"
552-
copied_debs+=("/tmp/$base_name")
553-
deb_args+=("/tmp/$base_name")
580+
cp "$package" "${rootfs_mount_point}/tmp/" # Copy into rootfs /tmp/
581+
copied_debs+=("/tmp/$base_name") # Record path inside rootfs
582+
user_packages+=("/tmp/$base_name") # Add to installation list
554583
else
555-
# For package names, we will ask apt to install after dpkg -i
556-
deb_args+=("$package")
557-
fi
558-
done
559-
560-
# Check which essential packages are NOT available as local .deb files
561-
for essential_pkg in "${essential_packages[@]}"; do
562-
local found=false
563-
for deb in "${copied_debs[@]}"; do
564-
if [[ "$deb" == *"${essential_pkg}"* ]]; then
565-
found=true
566-
break
567-
fi
568-
done
569-
if [ "$found" = false ]; then
570-
packages_to_install+=("$essential_pkg")
584+
# Assume this is a regular package name (to be installed via apt)
585+
user_packages+=("$package")
571586
fi
572587
done
573588

574-
# Try to install .deb files first, then fix dependencies via apt
575-
if [ ${#deb_args[@]} -gt 0 ]; then
589+
# Install user-provided packages
590+
if [ ${#user_packages[@]} -gt 0 ]; then
576591
chroot "${rootfs_mount_point}" bash -c "dpkg --configure -a || true"
577-
chroot "${rootfs_mount_point}" bash -c "dpkg -i $(printf '%s ' "${deb_args[@]}"| sed 's/ $//')" || true
592+
chroot "${rootfs_mount_point}" bash -c "dpkg -i $(printf '%s ' "${user_packages[@]}"| sed 's/ $//')" || true
578593

579-
# Fix dependencies
594+
# Fix dependencies
580595
chroot "${rootfs_mount_point}" /usr/bin/env ${http_proxy:+http_proxy=$http_proxy} \
581596
${https_proxy:+https_proxy=$https_proxy} \
582597
${ftp_proxy:+ftp_proxy=$ftp_proxy} \
@@ -593,28 +608,74 @@ disk::install_deb_on_rootfs() {
593608
${all_proxy:+all_proxy=$all_proxy} \
594609
${no_proxy:+no_proxy=$no_proxy} \
595610
apt-get -y -f install || true
611+
fi
612+
613+
# Step 2: Build essential packages list
614+
local cryptpilot_fde_version=""
596615

597-
# Install only packages not provided as local .deb files
598-
if [ ${#packages_to_install[@]} -gt 0 ]; then
599-
chroot "${rootfs_mount_point}" /usr/bin/env ${http_proxy:+http_proxy=$http_proxy} \
600-
${https_proxy:+https_proxy=$https_proxy} \
601-
${ftp_proxy:+ftp_proxy=$ftp_proxy} \
602-
${rsync_proxy:+rsync_proxy=$rsync_proxy} \
603-
${all_proxy:+all_proxy=$all_proxy} \
604-
${no_proxy:+no_proxy=$no_proxy} \
605-
apt-get -y install "${packages_to_install[@]}" || true
616+
# Try to query the version of cryptpilot-fde from the current system
617+
if command -v rpm >/dev/null 2>&1; then
618+
cryptpilot_fde_version=$(rpm -q cryptpilot-fde --qf '%{VERSION}-%{RELEASE}' 2>/dev/null || true)
619+
elif command -v dpkg-query >/dev/null 2>&1; then
620+
# Extract version from dpkg-query output, removing epoch if present
621+
cryptpilot_fde_version=$(dpkg-query -W -f='${Version}' cryptpilot-fde 2>/dev/null || true)
622+
fi
623+
624+
local essential_packages_with_version=()
625+
local essential_package_names=()
626+
627+
if [ -n "${cryptpilot_fde_version}" ]; then
628+
log::info "Detected cryptpilot-fde version: ${cryptpilot_fde_version}"
629+
essential_packages_with_version+=("cryptpilot-fde=${cryptpilot_fde_version}")
630+
else
631+
log::warn "Failed to detect cryptpilot-fde version, installing latest version"
632+
essential_packages_with_version+=("cryptpilot-fde")
633+
fi
634+
essential_package_names+=("cryptpilot-fde")
635+
636+
# Also include apt-utils for better apt handling
637+
if ! chroot "${rootfs_mount_point}" dpkg -l apt-utils >/dev/null 2>&1; then
638+
essential_packages_with_version+=("apt-utils")
639+
essential_package_names+=("apt-utils")
640+
fi
641+
642+
# Step 3: Check and install missing essential packages
643+
local packages_to_install=()
644+
for i in "${!essential_packages_with_version[@]}"; do
645+
local pkg_with_version="${essential_packages_with_version[$i]}"
646+
local pkg_name="${essential_package_names[$i]}"
647+
648+
# Check if package is already installed in chroot
649+
if chroot "${rootfs_mount_point}" dpkg -l "$pkg_name" 2>/dev/null | grep -q "^ii"; then
650+
log::info "Package $pkg_name is already installed, skipping"
651+
else
652+
log::info "Package $pkg_name is not installed, will install: $pkg_with_version"
653+
packages_to_install+=("$pkg_with_version")
606654
fi
655+
done
607656

608-
# Hold package versions for essential packages
609-
for p in "${essential_packages[@]}"; do
610-
chroot "${rootfs_mount_point}" apt-mark hold "$p" || true
611-
done
612-
chroot "${rootfs_mount_point}" apt-get clean || true
657+
# Install missing essential packages
658+
if [ ${#packages_to_install[@]} -gt 0 ]; then
659+
chroot "${rootfs_mount_point}" /usr/bin/env ${http_proxy:+http_proxy=$http_proxy} \
660+
${https_proxy:+https_proxy=$https_proxy} \
661+
${ftp_proxy:+ftp_proxy=$ftp_proxy} \
662+
${rsync_proxy:+rsync_proxy=$rsync_proxy} \
663+
${all_proxy:+all_proxy=$all_proxy} \
664+
${no_proxy:+no_proxy=$no_proxy} \
665+
apt-get -y install "${packages_to_install[@]}"
613666
fi
614667

615-
# cleanup copied debs
616-
for d in "${copied_debs[@]}"; do
617-
rm -f "${rootfs_mount_point}${d}"
668+
# Step 4: Install zram kernel module for Ubuntu
669+
install_zram_module_if_needed "${rootfs_mount_point}"
670+
671+
# Step 5: Lock version for all essential packages (using base package name)
672+
chroot "${rootfs_mount_point}" apt-mark hold "${essential_package_names[@]}"
673+
674+
chroot "${rootfs_mount_point}" apt-get clean
675+
676+
# Remove the copied .deb files from the chroot after installation
677+
for deb in "${copied_debs[@]}"; do
678+
rm -f "${rootfs_mount_point}${deb}"
618679
done
619680
}
620681

@@ -779,6 +840,41 @@ step:update_rootfs() {
779840
local boot_file_path=$2
780841
local uki=$3
781842

843+
# Expand the rootfs partition to utilize available disk space before update
844+
# This ensures sufficient space for package installations that may trigger scripts
845+
log::info "Expanding rootfs partition to utilize available disk space before update"
846+
847+
# Get current rootfs partition size information
848+
local original_size_in_bytes
849+
original_size_in_bytes=$(blockdev --getsize64 "${rootfs_orig_part}")
850+
local original_size_mb=$((original_size_in_bytes / 1024 / 1024))
851+
852+
log::info "Original rootfs partition size: ${original_size_mb}MB (${original_size_in_bytes} bytes)"
853+
854+
# Use growpart to expand the partition to maximum available space
855+
# This is more reliable than manual calculations
856+
if command -v growpart >/dev/null 2>&1; then
857+
log::info "Using growpart to expand partition ${rootfs_orig_part_num} on device $device"
858+
if growpart "$device" "$rootfs_orig_part_num"; then
859+
# Resize the filesystem to fill the new partition size
860+
log::info "Resizing filesystem to fill new partition size..."
861+
e2fsck -f "${rootfs_orig_part}" -p || true # Run e2fsck first to ensure filesystem integrity
862+
resize2fs "${rootfs_orig_part}"
863+
864+
# Verify the new size
865+
local new_size_in_bytes
866+
new_size_in_bytes=$(blockdev --getsize64 "${rootfs_orig_part}")
867+
local new_size_mb=$((new_size_in_bytes / 1024 / 1024))
868+
869+
log::info "New rootfs partition size: ${new_size_mb}MB (${new_size_in_bytes} bytes)"
870+
log::info "Rootfs partition and filesystem resized successfully"
871+
else
872+
log::warn "growpart failed, proceeding with original partition size"
873+
fi
874+
else
875+
log::warn "growpart command not found, proceeding with original partition size"
876+
fi
877+
782878
update_rootfs_inner() {
783879
local rootfs_mount_point=$1
784880
local uki=$2

0 commit comments

Comments
 (0)