cockpit-kata/qcrows-initrd-regen

1337 lines
49 KiB
Bash
Executable File

#!/usr/bin/env bash
# ============================================================================
# qcrows-initrd-regen — Environment-aware initrd/ramfs/cramfs regeneration
#
# Rebuilds the initrd component of a QCrows bundle with precise awareness
# of the Kata Containers runtime environment. Unlike dracut or mkinitramfs,
# this script produces an initrd where kata-agent IS the final process
# (no pivot_root, no switch_root) and includes ONLY the kernel modules
# required by the detected VMM transport layer.
#
# Usage:
# qcrows-initrd-regen image.qcrows [options]
# qcrows-initrd-regen image.qcrows --vmm qemu --compress lz4 -o new-initrd.img
# qcrows-initrd-regen image.qcrows --in-place
#
# Environment detection (automatic when --vmm is not specified):
# 1. Reads /etc/kata-containers/configuration.toml or the path in
# KATA_CFG to determine the active hypervisor profile.
# 2. Inspects the QCrows bundle's kernel/.config to determine which
# compression algorithms the guest kernel supports.
# 3. Examines the rootfs to detect init system type (systemd, OpenRC,
# busybox /init) and locate the kata-agent binary.
# 4. Detects host architecture for correct module paths.
#
# Output formats:
# cpio-gzip — gzip-compressed cpio (default, universal)
# cpio-lz4 — lz4-compressed cpio (fastest boot, kernel must support)
# cpio-xz — xz-compressed cpio (smallest size, slowest boot)
# cpio-zstd — zstd-compressed cpio (balanced, kernel 5.15+)
# cramfs — cramfs filesystem (read-only, extremely minimal)
# ============================================================================
set -euo pipefail
# ─── Version ────────────────────────────────────────────────────────────────
VERSION="0.1.0"
# ─── Colors ─────────────────────────────────────────────────────────────────
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
CYAN='\033[0;36m'
BLUE='\033[0;34m'
NC='\033[0m'
log() { echo -e "${GREEN}[initrd-regen]${NC} $*"; }
warn() { echo -e "${YELLOW}[initrd-regen]${NC} WARNING: $*"; }
error() { echo -e "${RED}[initrd-regen]${NC} ERROR: $*" >&2; }
die() { error "$@"; exit 1; }
# ─── Defaults ───────────────────────────────────────────────────────────────
INPUT=""
OUTPUT=""
VMM="" # auto-detected from kata config if empty
COMPRESS="" # auto-detected from kernel config if empty
FORMAT="" # cpio-gzip, cpio-lz4, cpio-xz, cpio-zstd, cramfs
AGENT_PATH="" # auto-discovered from rootfs if empty
INIT_STYLE="" # auto-detected: systemd, openrc, busybox
IN_PLACE=false
KEEP_TEMP=false
STRIP_LOCALES=true
STRIP_DOCS=true
STRIP_MAN=true
EXTRA_MODULES="" # comma-separated additional kernel modules
EXCLUDE_MODULES="" # comma-separated modules to exclude
KATA_CFG="" # path to configuration.toml (auto-detected if empty)
VERBOSE=false
KERNEL_MODULES_DIR="" # auto-discovered from bundle kernel version
# ─── Temp directory ────────────────────────────────────────────────────────
TMPDIR=""
cleanup() {
if [[ -n "$TMPDIR" && -d "$TMPDIR" && "$KEEP_TEMP" == "false" ]]; then
rm -rf "$TMPDIR"
elif [[ -n "$TMPDIR" && -d "$TMPDIR" && "$KEEP_TEMP" == "true" ]]; then
log "Temp directory preserved: ${TMPDIR}"
fi
}
trap cleanup EXIT
# ============================================================================
# VMM Kernel Module Matrix
# ============================================================================
# Each VMM requires a specific set of virtio transports and guest drivers.
# This table drives module selection — no nested ifs, just array lookup.
#
# Format: VMM:required_modules:optional_modules
# required — MUST be included for the VMM to function
# optional — included only if present in the kernel and not excluded
declare -A VMM_REQUIRED_MODULES
declare -A VMM_OPTIONAL_MODULES
VMM_REQUIRED_MODULES[qemu]="virtio_pci virtio_blk virtio_net virtio_rng virtio_balloon virtio_console virtio_gpu"
VMM_OPTIONAL_MODULES[qemu]="virtio_scsi virtio_input virtio_crypto virtio_mem virtio_pmem 9pnet_virtio"
VMM_REQUIRED_MODULES[cloud-hypervisor]="virtio_mmio virtio_blk virtio_net virtio_rng virtio_console virtio_balloon"
VMM_OPTIONAL_MODULES[cloud-hypervisor]="virtio_gpu virtio_scsi 9pnet_virtio"
VMM_REQUIRED_MODULES[firecracker]="virtio_mmio virtio_blk virtio_net virtio_rng"
VMM_OPTIONAL_MODULES[firecracker]="virtio_console"
VMM_REQUIRED_MODULES[dragonball]="virtio_blk virtio_net virtio_rng virtio_console"
VMM_OPTIONAL_MODULES[dragonball]="virtio_balloon virtio_gpu 9pnet_virtio"
# Core modules needed regardless of VMM — filesystem and device infrastructure
CORE_MODULES="ext4 vfat nls_cp437 nls_iso8859_1 dm_mod dm_bufio loop squashfs overlay"
# ============================================================================
# Compression detection from kernel .config
# ============================================================================
# The guest kernel must be compiled with support for the chosen initrd
# compression algorithm. These config symbols determine what works.
declare -A COMPRESS_CONFIG_MAP
COMPRESS_CONFIG_MAP[cpio-gzip]="CONFIG_RD_GZIP"
COMPRESS_CONFIG_MAP[cpio-lz4]="CONFIG_RD_LZ4"
COMPRESS_CONFIG_MAP[cpio-xz]="CONFIG_RD_XZ"
COMPRESS_CONFIG_MAP[cpio-zstd]="CONFIG_RD_ZSTD"
# Compression commands: how to compress the cpio stream
declare -A COMPRESS_CMD
COMPRESS_CMD[cpio-gzip]="gzip -9"
COMPRESS_CMD[cpio-lz4]="lz4 -l"
COMPRESS_CMD[cpio-xz]="xz -9 --check=crc32"
COMPRESS_CMD[cpio-zstd]="zstd -19"
# Compression file extensions
declare -A COMPRESS_EXT
COMPRESS_EXT[cpio-gzip]=".img"
COMPRESS_EXT[cpio-lz4]=".cpio.lz4"
COMPRESS_EXT[cpio-xz]=".cpio.xz"
COMPRESS_EXT[cpio-zstd]=".cpio.zst"
# Priority order for auto-detection (fastest boot first)
COMPRESS_PRIORITY=("cpio-lz4" "cpio-zstd" "cpio-gzip" "cpio-xz")
# ============================================================================
# Kata configuration.toml VMM detection
# ============================================================================
# Maps hypervisor entries in configuration.toml to our VMM identifiers.
declare -A CFG_HYPERVISOR_MAP
CFG_HYPERVISOR_MAP[qemu]="qemu"
CFG_HYPERVISOR_MAP[cloud-hypervisor]="cloud-hypervisor"
CFG_HYPERVISOR_MAP[clh]="cloud-hypervisor"
CFG_HYPERVISOR_MAP[firecracker]="firecracker"
CFG_HYPERVISOR_MAP[fc]="firecracker"
CFG_HYPERVISOR_MAP[dragonball]="dragonball"
# ============================================================================
# Usage
# ============================================================================
usage() {
cat <<EOF
qcrows-initrd-regen v${VERSION} — Environment-aware initrd regeneration for Kata Containers
USAGE:
qcrows-initrd-regen QCROWS_FILE [OPTIONS]
REQUIRED:
QCROWS_FILE Path to .qcrows archive
VMM SELECTION (auto-detected from kata config if not specified):
--vmm VMM Target VMM: qemu|cloud-hypervisor|firecracker|dragonball
--kata-cfg FILE Path to configuration.toml (default: auto-detect)
OUTPUT:
-o, --output FILE Output initrd file path (default: kata-initrd.img in cwd)
--in-place Replace the initrd inside the QCrows bundle
--format FORMAT Force output format: cpio-gzip|cpio-lz4|cpio-xz|cpio-zstd|cramfs
--compress ALGO Shorthand: gzip|lz4|xz|zstd (sets format to cpio-\$ALGO)
INITRD CONTENT:
--agent PATH Path to kata-agent binary (default: extract from rootfs)
--init-style STYLE Init system: systemd|openrc|busybox (default: auto-detect)
--extra-modules LIST Comma-separated additional kernel modules to include
--exclude-modules LIST Comma-separated kernel modules to exclude
OPTIMIZATION:
--no-strip-locales Keep locale files in initrd
--no-strip-docs Keep documentation files
--no-strip-man Keep man pages
--keep-temp Preserve temp directory for inspection
GENERAL:
-v, --verbose Verbose output
-h, --help Show this help
--version Show version
ENVIRONMENT:
KATA_CFG Default path to configuration.toml
AUTO-DETECTION:
When --vmm is not specified, the script reads configuration.toml
to determine the active hypervisor. When --compress is not specified,
it reads the bundled kernel/.config to find the fastest algorithm
the guest kernel supports.
When --init-style is not specified, the script examines the rootfs
to detect the init system:
- /sbin/init → systemd → systemd init style
- /sbin/openrc-init → OpenRC → openrc init style
- Neither → busybox /init style (kata-agent as PID 1)
EXAMPLES:
# Auto-detect everything, output to cwd
qcrows-initrd-regen alpine-3.20-kata.qcrows
# Target QEMU with lz4 compression
qcrows-initrd-regen alpine.qcrows --vmm qemu --compress lz4
# Update the bundle in-place
qcrows-initrd-regen alpine.qcrows --in-place --vmm cloud-hypervisor
# Custom agent binary and extra modules
qcrows-initrd-regen gentoo.qcrows --agent /usr/bin/kata-agent \\
--extra-modules "tun,veth,bridge" -o custom-initrd.img
# Inspect what would be included without building
qcrows-initrd-regen alpine.qcrows --keep-temp --verbose
EOF
}
# ============================================================================
# Parse Arguments
# ============================================================================
parse_args() {
local positional_count=0
while [[ $# -gt 0 ]]; do
case "$1" in
--vmm) VMM="$2"; shift 2 ;;
--kata-cfg) KATA_CFG="$2"; shift 2 ;;
-o|--output) OUTPUT="$2"; shift 2 ;;
--in-place) IN_PLACE=true; shift ;;
--format) FORMAT="$2"; shift 2 ;;
--compress) FORMAT="cpio-${2}"; shift 2 ;;
--agent) AGENT_PATH="$2"; shift 2 ;;
--init-style) INIT_STYLE="$2"; shift 2 ;;
--extra-modules) EXTRA_MODULES="$2"; shift 2 ;;
--exclude-modules) EXCLUDE_MODULES="$2"; shift 2 ;;
--no-strip-locales) STRIP_LOCALES=false; shift ;;
--no-strip-docs) STRIP_DOCS=false; shift ;;
--no-strip-man) STRIP_MAN=false; shift ;;
--keep-temp) KEEP_TEMP=true; shift ;;
-v|--verbose) VERBOSE=true; shift ;;
-h|--help) usage; exit 0 ;;
--version) echo "qcrows-initrd-regen v${VERSION}"; exit 0 ;;
-*) die "Unknown option: $1 (use --help)" ;;
*)
positional_count=$((positional_count + 1))
case "$positional_count" in
1) INPUT="$1" ;;
*) die "Unexpected positional argument: $1" ;;
esac
shift
;;
esac
done
}
# ============================================================================
# Validation (array-driven)
# ============================================================================
validate_inputs() {
[[ -z "$INPUT" ]] && die "QCrows file is required. Usage: qcrows-initrd-regen <file.qcrows> [options]"
[[ ! -f "$INPUT" ]] && die "File not found: ${INPUT}"
# Validate --vmm value against known VMMs
local -a known_vmms=("qemu" "cloud-hypervisor" "firecracker" "dragonball")
if [[ -n "$VMM" ]]; then
local vmm_valid=false
local known
for known in "${known_vmms[@]}"; do
[[ "$VMM" == "$known" ]] && { vmm_valid=true; break; }
done
[[ "$vmm_valid" == "false" ]] && die "Unknown VMM: ${VMM}. Supported: ${known_vmms[*]}"
fi
# Validate --init-style
local -a known_init_styles=("systemd" "openrc" "busybox")
if [[ -n "$INIT_STYLE" ]]; then
local style_valid=false
local known
for known in "${known_init_styles[@]}"; do
[[ "$INIT_STYLE" == "$known" ]] && { style_valid=true; break; }
done
[[ "$style_valid" == "false" ]] && die "Unknown init style: ${INIT_STYLE}. Supported: ${known_init_styles[*]}"
fi
# Validate --format
local -a known_formats=("cpio-gzip" "cpio-lz4" "cpio-xz" "cpio-zstd" "cramfs")
if [[ -n "$FORMAT" ]]; then
local fmt_valid=false
local known
for known in "${known_formats[@]}"; do
[[ "$FORMAT" == "$known" ]] && { fmt_valid=true; break; }
done
[[ "$fmt_valid" == "false" ]] && die "Unknown format: ${FORMAT}. Supported: ${known_formats[*]}"
fi
# Validate --agent path if specified
if [[ -n "$AGENT_PATH" ]]; then
[[ ! -f "$AGENT_PATH" ]] && die "Agent binary not found: ${AGENT_PATH}"
[[ ! -x "$AGENT_PATH" ]] && die "Agent binary not executable: ${AGENT_PATH}"
fi
# In-place requires output to be empty
[[ "$IN_PLACE" == "true" && -n "$OUTPUT" ]] && die "Cannot use --in-place and --output together"
# cramfs format cannot be used in-place (different structure)
[[ "$IN_PLACE" == "true" && "$FORMAT" == "cramfs" ]] && die "cramfs format is not supported with --in-place (use cpio format instead)"
}
# ============================================================================
# Extract QCrows bundle
# ============================================================================
extract_qcrows() {
local archive="$1"
local dest="$2"
log "Extracting QCrows bundle: $(basename "$archive")"
mkdir -p "$dest"
# Detect compression and extract
case "$archive" in
*.gz|*.qcrows.gz|*.tar.gz)
tar -xzf "$archive" -C "$dest"
;;
*)
# Try gzip first, then uncompressed
if tar -tzf "$archive" &>/dev/null; then
tar -xzf "$archive" -C "$dest"
else
tar -xf "$archive" -C "$dest"
fi
;;
esac
# Verify required components exist
local -a required_files=("metadata.toml" "rootfs.tar.gz" "kernel/")
local req
for req in "${required_files[@]}"; do
[[ -e "${dest}/${req}" ]] || die "Missing required component in bundle: ${req}"
done
# Verify kernel binary exists
local kernel_found=false
[[ -f "${dest}/kernel/vmlinuz" ]] && kernel_found=true
[[ -f "${dest}/kernel/vmlinux" ]] && kernel_found=true
[[ "$kernel_found" == "false" ]] && die "No kernel binary found in bundle (expected kernel/vmlinuz or kernel/vmlinux)"
# Verify kernel config exists (QCrows v0.2 requirement)
[[ -f "${dest}/kernel/config" ]] || die "No kernel config found in bundle — required for env-aware module selection"
log "Bundle extracted to: ${dest}"
}
# ============================================================================
# Auto-detect VMM from kata configuration.toml
# ============================================================================
detect_vmm() {
[[ -n "$VMM" ]] && return
log "Auto-detecting VMM from Kata configuration..."
# Candidate configuration.toml paths
local -a cfg_candidates=(
"${KATA_CFG:-}"
"/etc/kata-containers/configuration.toml"
"/usr/share/defaults/kata-containers/configuration.toml"
"/usr/share/kata-containers/configuration.toml"
"/opt/kata/share/defaults/kata-containers/configuration.toml"
)
local cfg_path=""
local candidate
for candidate in "${cfg_candidates[@]}"; do
[[ -n "$candidate" && -f "$candidate" ]] && { cfg_path="$candidate"; break; }
done
if [[ -z "$cfg_path" ]]; then
warn "No configuration.toml found — defaulting to qemu"
VMM="qemu"
return
fi
log "Found configuration: ${cfg_path}"
# Extract the hypervisor type from configuration.toml.
# The active hypervisor section is identified by the [hypervisor.qemu] /
# [hypervisor.cloud-hypervisor] / etc. section headers.
# The default runtime determines which section is active.
local runtime_link=""
if [[ -L "/usr/bin/kata-runtime" ]]; then
runtime_link="$(readlink -f /usr/bin/kata-runtime 2>/dev/null || true)"
fi
# Check kata-runtime symlink naming convention
local -a runtime_vmm_map=(
"kata-qemu:qemu"
"kata-clh:cloud-hypervisor"
"kata-cloud-hypervisor:cloud-hypervisor"
"kata-fc:firecracker"
"kata-firecracker:firecracker"
"kata-dragonball:dragonball"
)
local entry pattern vmm_id
for entry in "${runtime_vmm_map[@]}"; do
pattern="${entry%%:*}"
vmm_id="${entry##*:}"
if [[ "$runtime_link" == *"$pattern"* ]]; then
VMM="$vmm_id"
log "Detected VMM from runtime symlink: ${VMM}"
return
fi
done
# Fallback: parse configuration.toml for the first hypervisor section
local -a section_patterns=("qemu" "cloud-hypervisor" "clh" "firecracker" "fc" "dragonball")
local sect
for sect in "${section_patterns[@]}"; do
if grep -q "\\[hypervisor\\.${sect}\\]" "$cfg_path" 2>/dev/null; then
VMM="${CFG_HYPERVISOR_MAP[$sect]:-$sect}"
log "Detected VMM from config section: ${VMM}"
return
fi
done
# Check which runtime is actually active via kata-runtime
local kata_runtime
kata_runtime="$(which kata-runtime 2>/dev/null || true)"
if [[ -n "$kata_runtime" ]]; then
local runtime_info
runtime_info="$("$kata_runtime" --version 2>/dev/null | head -1 || true)"
[[ "$VERBOSE" == "true" ]] && log "kata-runtime version: ${runtime_info}"
fi
warn "Could not determine active VMM — defaulting to qemu"
VMM="qemu"
}
# ============================================================================
# Auto-detect compression from kernel .config
# ============================================================================
detect_compression() {
[[ -n "$FORMAT" && "$FORMAT" != "cramfs" ]] && return
log "Auto-detecting compression from kernel config..."
local kernel_config="${EXTRACT_DIR}/kernel/config"
[[ ! -f "$kernel_config" ]] && { warn "No kernel config available — defaulting to gzip"; FORMAT="${FORMAT:-cpio-gzip}"; return; }
# Check each compression algorithm in priority order
local algo config_key
for algo in "${COMPRESS_PRIORITY[@]}"; do
config_key="${COMPRESS_CONFIG_MAP[$algo]:-}"
[[ -z "$config_key" ]] && continue
if grep -q "^${config_key}=y" "$kernel_config" 2>/dev/null; then
FORMAT="$algo"
log "Kernel supports ${algo##*-} — selected as optimal compression"
return
fi
done
# No supported compression found in kernel config — this should not happen
warn "No initrd compression support detected in kernel config — defaulting to gzip"
FORMAT="${FORMAT:-cpio-gzip}"
}
# ============================================================================
# Auto-detect init style from rootfs
# ============================================================================
detect_init_style() {
[[ -n "$INIT_STYLE" ]] && return
log "Auto-detecting init system from rootfs..."
local rootfs_dir="${EXTRACT_DIR}/rootfs-extracted"
mkdir -p "$rootfs_dir"
# Extract just enough of the rootfs to detect init system.
# We only need the directory listing, not the full content.
tar -tzf "${EXTRACT_DIR}/rootfs.tar.gz" 2>/dev/null | head -500 > "${TMPDIR}/rootfs-listing.txt"
if grep -q "sbin/init$\|sbin/systemd$" "${TMPDIR}/rootfs-listing.txt"; then
INIT_STYLE="systemd"
log "Detected init system: systemd"
elif grep -q "sbin/openrc-init$" "${TMPDIR}/rootfs-listing.txt"; then
INIT_STYLE="openrc"
log "Detected init system: OpenRC"
else
INIT_STYLE="busybox"
log "Detected init system: busybox (kata-agent as PID 1)"
fi
}
# ============================================================================
# Discover kata-agent in rootfs
# ============================================================================
discover_agent() {
[[ -n "$AGENT_PATH" && -f "$AGENT_PATH" ]] && return
log "Discovering kata-agent in rootfs..."
local listing="${TMPDIR}/rootfs-listing.txt"
# Agent candidate paths in the rootfs
local -a agent_candidates=(
"usr/bin/kata-agent"
"usr/local/bin/kata-agent"
"usr/sbin/kata-agent"
"sbin/kata-agent"
)
local candidate
for candidate in "${agent_candidates[@]}"; do
if grep -q "^\\./${candidate}$\\|^${candidate}$" "$listing" 2>/dev/null; then
AGENT_PATH="${EXTRACT_DIR}/rootfs-extracted/${candidate}"
log "Found kata-agent at: /${candidate}"
return
fi
done
die "kata-agent not found in rootfs. Specify path with --agent"
}
# ============================================================================
# Extract rootfs for initrd building
# ============================================================================
extract_rootfs() {
local rootfs_dir="${EXTRACT_DIR}/rootfs-extracted"
[[ -d "$rootfs_dir" && -f "${rootfs_dir}/init" ]] && return
log "Extracting rootfs for initrd assembly..."
mkdir -p "$rootfs_dir"
tar -xzf "${EXTRACT_DIR}/rootfs.tar.gz" -C "$rootfs_dir" 2>/dev/null || \
tar -xf "${EXTRACT_DIR}/rootfs.tar.gz" -C "$rootfs_dir" 2>/dev/null || \
die "Failed to extract rootfs"
log "Rootfs extracted: $(du -sh "$rootfs_dir" | cut -f1)"
}
# ============================================================================
# Resolve kernel module paths
# ============================================================================
resolve_kernel_modules() {
log "Resolving kernel modules for VMM: ${VMM}"
local kernel_config="${EXTRACT_DIR}/kernel/config"
# Determine kernel version from the bundled kernel
local kernel_version
kernel_version="$(strings "${EXTRACT_DIR}/kernel/vmlinuz" "${EXTRACT_DIR}/kernel/vmlinux" 2>/dev/null \
| grep -oP '^\d+\.\d+\.\d+' | head -1 || true)"
if [[ -z "$kernel_version" ]]; then
# Fallback: extract from kernel config
kernel_version="$(grep "^CONFIG_VERSION_SIGNATURE=" "$kernel_config" 2>/dev/null \
| grep -oP '\d+\.\d+\.\d+' | head -1 || true)"
fi
if [[ -z "$kernel_version" ]]; then
warn "Could not determine kernel version — module inclusion will be name-only"
else
log "Kernel version: ${kernel_version}"
fi
# Build the complete module list from the matrix
local -a required_modules=()
local -a optional_modules=()
# Core modules (always needed)
local core
for core in $CORE_MODULES; do
required_modules+=("$core")
done
# VMM-specific required modules
local vmm_req="${VMM_REQUIRED_MODULES[$VMM]:-}"
local mod
for mod in $vmm_req; do
required_modules+=("$mod")
done
# VMM-specific optional modules (only if compiled in kernel)
local vmm_opt="${VMM_OPTIONAL_MODULES[$VMM]:-}"
for mod in $vmm_opt; do
# Check if the module is compiled (=y or =m) in the kernel config
local config_name="CONFIG_${mod^^}"
if grep -q "^${config_name}=y\\|^${config_name}=m" "$kernel_config" 2>/dev/null; then
optional_modules+=("$mod")
fi
done
# Extra modules from --extra-modules
if [[ -n "$EXTRA_MODULES" ]]; then
IFS=',' read -ra extra_arr <<< "$EXTRA_MODULES"
for mod in "${extra_arr[@]}"; do
[[ -n "$mod" ]] && required_modules+=("$mod")
done
fi
# Apply exclusion list from --exclude-modules
local -a exclude_arr=()
if [[ -n "$EXCLUDE_MODULES" ]]; then
IFS=',' read -ra exclude_arr <<< "$EXCLUDE_MODULES"
fi
# Final module list (deduplicated, exclusions applied)
local -A module_set=()
local m
for m in "${required_modules[@]}" "${optional_modules[@]}"; do
local excluded=false
local ex
for ex in "${exclude_arr[@]}"; do
[[ "$m" == "$ex" ]] && { excluded=true; break; }
done
[[ "$excluded" == "false" ]] && module_set[$m]=1
done
# Store final module list
FINAL_MODULES=()
for m in "${!module_set[@]}"; do
FINAL_MODULES+=("$m")
done
log "Module list (${#FINAL_MODULES[@]} modules): ${FINAL_MODULES[*]}"
KERNEL_VERSION="$kernel_version"
}
# ============================================================================
# Copy kernel modules from rootfs
# ============================================================================
copy_kernel_modules() {
local initrd_root="$1"
local rootfs_dir="${EXTRACT_DIR}/rootfs-extracted"
[[ -z "$KERNEL_VERSION" ]] && { warn "No kernel version — skipping module copy"; return; }
local modules_src="${rootfs_dir}/lib/modules/${KERNEL_VERSION}"
local modules_dest="${initrd_root}/lib/modules/${KERNEL_VERSION}"
# Check if modules exist in the rootfs
if [[ ! -d "$modules_src" ]]; then
warn "Kernel modules directory not found in rootfs: ${modules_src}"
warn "Initrd will not include loadable kernel modules (built-in only)"
return
fi
log "Copying kernel modules for ${KERNEL_VERSION}..."
mkdir -p "$modules_dest"
# Copy module files for selected modules
local mod_count=0
local mod
for mod in "${FINAL_MODULES[@]}"; do
# Find the module .ko file (multiple possible paths and extensions)
local -a ko_candidates
mapfile -t ko_candidates < <(
find "$modules_src" -name "${mod}.ko*" -type f 2>/dev/null || true
)
local ko
for ko in "${ko_candidates[@]}"; do
local rel_path="${ko#${modules_src}/}"
mkdir -p "${modules_dest}/$(dirname "$rel_path")"
cp "$ko" "${modules_dest}/${rel_path}"
mod_count=$((mod_count + 1))
[[ "$VERBOSE" == "true" ]] && log " Module: ${mod}${rel_path}"
done
done
# Copy module metadata files
local dep_file
for dep_file in modules.dep modules.dep.bin modules.alias modules.alias.bin modules.symbols modules.symbols.bin; do
[[ -f "${modules_src}/${dep_file}" ]] && cp "${modules_src}/${dep_file}" "${modules_dest}/"
done
# Generate a minimal modules.dep if not present
if [[ ! -f "${modules_dest}/modules.dep" ]]; then
(cd "$modules_dest" && find . -name '*.ko*' -print0 | xargs -0 -I{} echo "{}: " | sort > modules.dep)
fi
log "Copied ${mod_count} kernel modules"
}
# ============================================================================
# Generate /init for the initrd
# ============================================================================
generate_init() {
local initrd_root="$1"
local init_file="${initrd_root}/init"
log "Generating /init for ${INIT_STYLE} style..."
case "$INIT_STYLE" in
systemd)
# systemd init: mount virtual filesystems, then exec systemd which
# will start kata-agent.service via the kata-containers.target
cat > "$init_file" << 'INITEOF'
#!/bin/sh
# Kata Containers initrd — systemd init style
# Mounts virtual filesystems, then hands off to systemd which starts kata-agent.
mount -t proc proc /proc
mount -t sysfs sysfs /sys
mount -t devtmpfs devtmpfs /dev
mkdir -p /dev/pts /dev/shm /run
mount -t devpts devpts /dev/pts
mount -t tmpfs tmpfs /dev/shm
mount -t tmpfs tmpfs /run
# Load kernel modules from initrd
[ -d /lib/modules ] && {
KVER=$(ls /lib/modules/ 2>/dev/null | head -1)
[ -n "$KVER" ] && depmod -a "$KVER" 2>/dev/null
}
exec /sbin/init
INITEOF
;;
openrc)
# OpenRC init: mount virtual filesystems, then exec openrc-init
cat > "$init_file" << 'INITEOF'
#!/bin/sh
# Kata Containers initrd — OpenRC init style
# Mounts virtual filesystems, then hands off to openrc-init which starts kata-agent.
mount -t proc proc /proc
mount -t sysfs sysfs /sys
mount -t devtmpfs devtmpfs /dev
mkdir -p /dev/pts /dev/shm /run
mount -t devpts devpts /dev/pts
mount -t tmpfs tmpfs /dev/shm
mount -t tmpfs tmpfs /run
# Load kernel modules from initrd
[ -d /lib/modules ] && {
KVER=$(ls /lib/modules/ 2>/dev/null | head -1)
[ -n "$KVER" ] && depmod -a "$KVER" 2>/dev/null
}
exec /sbin/openrc-init
INITEOF
;;
busybox|*)
# Busybox /init: mount virtual filesystems and exec kata-agent as PID 1.
# This is the minimal, fastest boot path — no service manager overhead.
cat > "$init_file" << 'INITEOF'
#!/bin/sh
# Kata Containers initrd — busybox init style
# Mounts virtual filesystems and execs kata-agent as PID 1.
# This is the minimal boot path for fastest VM startup.
mount -t proc proc /proc
mount -t sysfs sysfs /sys
mount -t devtmpfs devtmpfs /dev
mkdir -p /dev/pts /dev/shm /run
mount -t devpts devpts /dev/pts
mount -t tmpfs tmpfs /dev/shm
mount -t tmpfs tmpfs /run
# Load kernel modules from initrd
[ -d /lib/modules ] && {
KVER=$(ls /lib/modules/ 2>/dev/null | head -1)
[ -n "$KVER" ] && depmod -a "$KVER" 2>/dev/null
}
# Execute kata-agent as the final process (PID 1)
exec /usr/bin/kata-agent
INITEOF
;;
esac
chmod 755 "$init_file"
log "Generated /init (${INIT_STYLE} style, $(wc -l < "$init_file") lines)"
}
# ============================================================================
# Helper: Find first existing file in rootfs
# ============================================================================
first_in_rootfs() {
local name="$1"
local rootfs="$2"
local -a paths=("bin/${name}" "sbin/${name}" "usr/bin/${name}" "usr/sbin/${name}" "usr/local/bin/${name}")
local p
for p in "${paths[@]}"; do
if [[ -f "${rootfs}/${p}" ]]; then
echo "${rootfs}/${p}"
return 0
fi
done
return 1
}
# ============================================================================
# Helper: Copy if source exists
# ============================================================================
copy_if_exists() {
local src="$1"
local dest="$2"
if [[ -e "$src" ]]; then
if [[ -d "$src" ]]; then
mkdir -p "$dest"
cp -r "${src}/"* "$dest/" 2>/dev/null || true
else
mkdir -p "$(dirname "$dest")"
cp "$src" "$dest"
fi
fi
}
# ============================================================================
# Copy required shared libraries
# ============================================================================
copy_required_libs() {
local initrd_root="$1"
local rootfs_dir="$2"
log "Resolving shared library dependencies..."
# Collect all ELF binaries in the initrd
local -a elf_files=()
local elf
while IFS= read -r -d '' elf; do
elf_files+=("$elf")
done < <(find "${initrd_root}" -type f -executable -print0 2>/dev/null | head -50)
# Use ldd to resolve dependencies (if available)
if command -v ldd &>/dev/null; then
local -a lib_set=()
local file
for file in "${elf_files[@]}"; do
local libs
libs="$(ldd "$file" 2>/dev/null | grep -oP '/[^ ]+\.so[^ ]*' || true)"
local lib
for lib in $libs; do
lib_set+=("$lib")
done
done
# Deduplicate and copy
local -a unique_libs=()
local -A seen=()
local lib
for lib in "${lib_set[@]}"; do
[[ -z "${seen[$lib]:-}" ]] && { seen[$lib]=1; unique_libs+=("$lib"); }
done
local copied=0
for lib in "${unique_libs[@]}"; do
if [[ -f "$lib" ]]; then
local dest_dir="${initrd_root}/$(dirname "$lib")"
mkdir -p "$dest_dir"
cp "$lib" "${initrd_root}/${lib}" 2>/dev/null && copied=$((copied + 1)) || true
fi
done
log "Copied ${copied} shared libraries"
# Copy the dynamic linker
local -a linker_candidates=(
"${rootfs_dir}/lib64/ld-linux-x86-64.so.2"
"${rootfs_dir}/lib/ld-linux-x86-64.so.2"
"${rootfs_dir}/lib64/ld-musl-x86_64.so.1"
"${rootfs_dir}/lib/ld-musl-x86_64.so.1"
)
local linker
for linker in "${linker_candidates[@]}"; do
if [[ -f "$linker" ]]; then
mkdir -p "${initrd_root}/$(dirname "${linker#${rootfs_dir}}")"
cp "$linker" "${initrd_root}/${linker#${rootfs_dir}}"
log "Copied dynamic linker: ${linker#${rootfs_dir}}"
break
fi
done
else
# No ldd — copy common library paths from rootfs
warn "ldd not available — copying common library paths from rootfs"
local -a lib_dirs=("lib" "lib64" "usr/lib" "usr/lib64")
local ldir
for ldir in "${lib_dirs[@]}"; do
if [[ -d "${rootfs_dir}/${ldir}" ]]; then
mkdir -p "${initrd_root}/${ldir}"
cp "${rootfs_dir}/${ldir}/"*.so* "${initrd_root}/${ldir}/" 2>/dev/null || true
fi
done
fi
}
# ============================================================================
# Strip unnecessary files from initrd
# ============================================================================
strip_initrd() {
local initrd_root="$1"
log "Stripping unnecessary files from initrd..."
# Stripping targets driven by flag-state table
local -a strip_targets=()
[[ "$STRIP_LOCALES" == "true" ]] && strip_targets+=("usr/share/locale")
[[ "$STRIP_DOCS" == "true" ]] && strip_targets+=("usr/share/doc")
[[ "$STRIP_MAN" == "true" ]] && strip_targets+=("usr/share/man")
# Always strip these
strip_targets+=("usr/share/info" "usr/share/groff" "usr/share/emacs")
strip_targets+=("var/cache" "var/lib/apt" "var/lib/dpkg" "var/lib/pacman")
local path_pattern
for path_pattern in "${strip_targets[@]}"; do
rm -rf "${initrd_root}/${path_pattern}" 2>/dev/null || true
done
# Remove Python cache files
find "$initrd_root" -name '*.pyc' -delete 2>/dev/null || true
find "$initrd_root" -name '__pycache__' -type d -exec rm -rf {} + 2>/dev/null || true
# Remove .a and .la files (static libraries not needed at runtime)
find "$initrd_root" -name '*.a' -delete 2>/dev/null || true
find "$initrd_root" -name '*.la' -delete 2>/dev/null || true
# Strip debug symbols from ELF binaries (reduces size significantly)
if command -v strip &>/dev/null; then
find "$initrd_root" -type f -executable -print0 2>/dev/null \
| xargs -0 strip --strip-unneeded 2>/dev/null || true
fi
log "Stripping complete: $(du -sh "$initrd_root" | cut -f1)"
}
# ============================================================================
# Build the initrd root filesystem
# ============================================================================
build_initrd_root() {
local initrd_root="${TMPDIR}/initrd-root"
local rootfs_dir="${EXTRACT_DIR}/rootfs-extracted"
log "Building initrd root filesystem..."
mkdir -p "$initrd_root"
# Essential directory structure
local -a essential_dirs=(
"bin" "sbin" "usr/bin" "usr/sbin" "usr/lib" "usr/lib64"
"lib" "lib64" "etc" "proc" "sys" "dev" "run"
"dev/pts" "dev/shm" "tmp" "root" "var/run"
"var/log" "usr/share" "usr/libexec"
)
local dir
for dir in "${essential_dirs[@]}"; do
mkdir -p "${initrd_root}/${dir}"
done
# ── Copy kata-agent ───────────────────────────────────────────────────
if [[ -n "$AGENT_PATH" && -f "$AGENT_PATH" ]]; then
cp "$AGENT_PATH" "${initrd_root}/usr/bin/kata-agent"
chmod 755 "${initrd_root}/usr/bin/kata-agent"
log "Installed kata-agent from: ${AGENT_PATH}"
else
# Copy from rootfs
local -a agent_src_candidates=(
"${rootfs_dir}/usr/bin/kata-agent"
"${rootfs_dir}/usr/local/bin/kata-agent"
"${rootfs_dir}/usr/sbin/kata-agent"
"${rootfs_dir}/sbin/kata-agent"
)
local src
for src in "${agent_src_candidates[@]}"; do
if [[ -f "$src" ]]; then
cp "$src" "${initrd_root}/usr/bin/kata-agent"
chmod 755 "${initrd_root}/usr/bin/kata-agent"
log "Installed kata-agent from rootfs: ${src#${rootfs_dir}}"
break
fi
done
fi
[[ ! -f "${initrd_root}/usr/bin/kata-agent" ]] && die "kata-agent not found — cannot build initrd"
# ── Copy essential binaries and libraries ──────────────────────────────
# Minimal set needed for init and kata-agent operation
local -a essential_bins=(
"sh" "mount" "umount" "mkdir" "mknod" "ln" "cp" "mv"
"rm" "ls" "cat" "echo" "sleep" "depmod" "modprobe"
)
local bin
for bin in "${essential_bins[@]}"; do
local bin_src
bin_src="$(first_in_rootfs "$bin" "$rootfs_dir")" || true
if [[ -n "$bin_src" ]]; then
cp "$bin_src" "${initrd_root}/bin/${bin}" 2>/dev/null || true
fi
done
# Copy busybox if available (provides most essential binaries via symlinks)
if [[ -f "${rootfs_dir}/bin/busybox" ]]; then
cp "${rootfs_dir}/bin/busybox" "${initrd_root}/bin/busybox"
chmod 755 "${initrd_root}/bin/busybox"
# Create busybox symlinks for our essential bins that aren't already present
local sym_bin
for sym_bin in "${essential_bins[@]}"; do
[[ ! -f "${initrd_root}/bin/${sym_bin}" ]] && \
ln -sf busybox "${initrd_root}/bin/${sym_bin}" 2>/dev/null || true
done
log "Installed busybox with symlinks"
fi
# ── Copy shared libraries ─────────────────────────────────────────────
copy_required_libs "$initrd_root" "$rootfs_dir"
# ── Copy kernel modules ───────────────────────────────────────────────
copy_kernel_modules "$initrd_root"
# ── Copy init system files ────────────────────────────────────────────
case "$INIT_STYLE" in
systemd)
copy_if_exists "${rootfs_dir}/sbin/init" "${initrd_root}/sbin/init"
copy_if_exists "${rootfs_dir}/lib/systemd" "${initrd_root}/lib/systemd"
copy_if_exists "${rootfs_dir}/usr/lib/systemd" "${initrd_root}/usr/lib/systemd"
copy_if_exists "${rootfs_dir}/etc/systemd" "${initrd_root}/etc/systemd"
# Ensure kata-agent service unit exists
mkdir -p "${initrd_root}/etc/systemd/system"
mkdir -p "${initrd_root}/etc/systemd/target"
if [[ -f "${rootfs_dir}/etc/systemd/system/kata-agent.service" ]]; then
cp "${rootfs_dir}/etc/systemd/system/kata-agent.service" "${initrd_root}/etc/systemd/system/"
else
# Generate minimal service unit
cat > "${initrd_root}/etc/systemd/system/kata-agent.service" << 'EOF'
[Unit]
Description=kata-agent
Requires=kata-containers.target
After=kata-containers.target
[Service]
ExecStart=/usr/bin/kata-agent
Restart=always
RestartSec=1
[Install]
WantedBy=kata-containers.target
EOF
fi
# Generate kata-containers.target
cat > "${initrd_root}/etc/systemd/system/kata-containers.target" << 'EOF'
[Unit]
Description=kata containers target
Requires=multi-user.target
After=multi-user.target
EOF
;;
openrc)
copy_if_exists "${rootfs_dir}/sbin/openrc-init" "${initrd_root}/sbin/openrc-init"
copy_if_exists "${rootfs_dir}/etc/init.d" "${initrd_root}/etc/init.d"
# Generate OpenRC init script for kata-agent
mkdir -p "${initrd_root}/etc/init.d"
cat > "${initrd_root}/etc/init.d/kata-agent" << 'EOF'
#!/sbin/openrc-run
description="kata-agent"
command="/usr/bin/kata-agent"
command_background=true
pidfile="/run/kata-agent.pid"
depend() {
after localmount
}
EOF
chmod 755 "${initrd_root}/etc/init.d/kata-agent"
;;
esac
# ── Generate /init ────────────────────────────────────────────────────
generate_init "$initrd_root"
# ── Copy DNS config ───────────────────────────────────────────────────
if [[ -f "${rootfs_dir}/etc/resolv.conf" ]]; then
cp "${rootfs_dir}/etc/resolv.conf" "${initrd_root}/etc/resolv.conf"
else
echo "nameserver 8.8.8.8" > "${initrd_root}/etc/resolv.conf"
fi
# ── Copy /etc/passwd and /etc/group (minimal) ────────────────────────
copy_if_exists "${rootfs_dir}/etc/passwd" "${initrd_root}/etc/passwd"
copy_if_exists "${rootfs_dir}/etc/group" "${initrd_root}/etc/group"
# Generate minimal passwd/group if not present
[[ ! -f "${initrd_root}/etc/passwd" ]] && echo "root:x:0:0:root:/root:/bin/sh" > "${initrd_root}/etc/passwd"
[[ ! -f "${initrd_root}/etc/group" ]] && echo "root:x:0:" > "${initrd_root}/etc/group"
# ── Strip unnecessary files ───────────────────────────────────────────
strip_initrd "$initrd_root"
log "Initrd root built: $(du -sh "$initrd_root" | cut -f1)"
}
# ============================================================================
# Create cpio archive
# ============================================================================
create_cpio_archive() {
local initrd_root="${TMPDIR}/initrd-root"
local output_file="${OUTPUT:-kata-initrd${COMPRESS_EXT[$FORMAT]:-.img}}"
log "Creating ${FORMAT} initrd image..."
# Verify /init exists
[[ -f "${initrd_root}/init" ]] || die "Missing /init in initrd root — cannot create archive"
local compress_cmd="${COMPRESS_CMD[$FORMAT]:-cat}"
# Create cpio archive with compression
(cd "$initrd_root" && find . | cpio -o -H newc 2>/dev/null) | $compress_cmd > "$output_file"
local size
size="$(du -sh "$output_file" | cut -f1)"
log "Initrd created: ${output_file} (${size})"
# Validate the output
validate_initrd "$output_file"
OUTPUT_FILE="$output_file"
}
# ============================================================================
# Create cramfs image
# ============================================================================
create_cramfs_image() {
local initrd_root="${TMPDIR}/initrd-root"
local output_file="${OUTPUT:-kata-initrd.cramfs}"
log "Creating cramfs initrd image..."
command -v mkcramfs &>/dev/null || die "mkcramfs not found — install cramfsprogs or util-linux"
mkcramfs "$initrd_root" "$output_file"
local size
size="$(du -sh "$output_file" | cut -f1)"
log "Cramfs initrd created: ${output_file} (${size})"
OUTPUT_FILE="$output_file"
}
# ============================================================================
# Validate the generated initrd
# ============================================================================
validate_initrd() {
local initrd_file="$1"
log "Validating generated initrd..."
local -a checks=()
# Check 1: File exists and is non-empty
[[ -f "$initrd_file" && -s "$initrd_file" ]] && checks+=("PASS:file_exists") || checks+=("FAIL:file_exists")
# Check 2: File format matches expected
local file_type
file_type="$(file -b "$initrd_file" 2>/dev/null || true)"
case "$FORMAT" in
cpio-gzip) echo "$file_type" | grep -qi "gzip" && checks+=("PASS:format_match") || checks+=("FAIL:format_match:${file_type}") ;;
cpio-lz4) echo "$file_type" | grep -qi "lz4" && checks+=("PASS:format_match") || checks+=("WARN:format_match:file(1) may not identify LZ4") ;;
cpio-xz) echo "$file_type" | grep -qi "xz" && checks+=("PASS:format_match") || checks+=("FAIL:format_match:${file_type}") ;;
cpio-zstd) echo "$file_type" | grep -qi "zstd" && checks+=("PASS:format_match") || checks+=("WARN:format_match:file(1) may not identify ZSTD") ;;
cramfs) echo "$file_type" | grep -qi "cramfs" && checks+=("PASS:format_match") || checks+=("FAIL:format_match:${file_type}") ;;
esac
# Check 3: Can list archive contents (cpio formats only)
if [[ "$FORMAT" != "cramfs" ]]; then
local decompress_cmd
case "$FORMAT" in
cpio-gzip) decompress_cmd="zcat" ;;
cpio-lz4) decompress_cmd="lz4 -d -c" ;;
cpio-xz) decompress_cmd="xzcat" ;;
cpio-zstd) decompress_cmd="zstdcat" ;;
esac
local listing
listing="$($decompress_cmd "$initrd_file" 2>/dev/null | cpio -t 2>/dev/null | head -50 || true)"
if echo "$listing" | grep -q "init"; then
checks+=("PASS:init_exists")
else
checks+=("FAIL:init_exists")
fi
if echo "$listing" | grep -q "kata-agent"; then
checks+=("PASS:agent_exists")
else
checks+=("FAIL:agent_exists")
fi
fi
# Check 4: Size is reasonable (< 512MB for initrd)
local size_bytes
size_bytes="$(stat --format="%s" "$initrd_file" 2>/dev/null || stat -f "%z" "$initrd_file" 2>/dev/null || echo 0)"
local size_mb=$(( size_bytes / (1024 * 1024) ))
if [[ $size_mb -lt 512 ]]; then
checks+=("PASS:size_reasonable:${size_mb}MB")
else
checks+=("WARN:size_large:${size_mb}MB — consider stripping more content")
fi
# Report results
local pass=0 fail=0 warn=0
local check
for check in "${checks[@]}"; do
local status="${check%%:*}"
case "$status" in
PASS) pass=$((pass + 1)) ;;
FAIL) fail=$((fail + 1)); error " ${check}" ;;
WARN) warn=$((warn + 1)); warn " ${check}" ;;
esac
done
log "Validation: ${pass} passed, ${fail} failed, ${warn} warnings"
[[ $fail -gt 0 ]] && die "Initrd validation failed — see errors above"
}
# ============================================================================
# Update QCrows bundle in-place
# ============================================================================
update_qcrows_inplace() {
[[ "$IN_PLACE" != "true" ]] && return
local qcrows_file="$INPUT"
local new_initrd="$OUTPUT_FILE"
log "Updating QCrows bundle in-place with new initrd..."
# Create a new bundle with the updated initrd
local staging="${TMPDIR}/inplace-staging"
mkdir -p "$staging"
# Copy all existing bundle contents
cp -r "${EXTRACT_DIR}/"* "$staging/" 2>/dev/null || true
# Replace the initrd
local initrd_name
initrd_name="$(ls "${staging}"/initrd* 2>/dev/null | head -1 || true)"
if [[ -n "$initrd_name" ]]; then
rm -f "${staging}"/initrd.*
cp "$new_initrd" "${staging}/initrd.img"
else
cp "$new_initrd" "${staging}/initrd.img"
fi
# Recompute hashes
(cd "$staging" && find . -type f ! -name "hashes.sha256" -print0 | sort -z | xargs -0 sha256sum) \
> "${staging}/hashes.sha256"
# Rebuild the archive
local backup="${qcrows_file}.bak"
cp "$qcrows_file" "$backup"
log "Backup created: ${backup}"
(cd "$staging" && tar czf - ./*) > "$qcrows_file"
log "QCrows bundle updated: ${qcrows_file}"
}
# ============================================================================
# Print summary
# ============================================================================
print_summary() {
local initrd_file="${OUTPUT_FILE:-}"
local size=""
[[ -f "$initrd_file" ]] && size="$(du -sh "$initrd_file" | cut -f1)"
echo ""
echo -e "${GREEN}╔══════════════════════════════════════════════════════╗${NC}"
echo -e "${GREEN}║ Initrd regenerated successfully ║${NC}"
echo -e "${GREEN}╚══════════════════════════════════════════════════════╝${NC}"
echo ""
echo -e " Output: ${CYAN}${initrd_file}${NC}"
echo -e " Size: ${CYAN}${size}${NC}"
echo -e " Format: ${CYAN}${FORMAT}${NC}"
echo -e " VMM: ${CYAN}${VMM}${NC}"
echo -e " Init style: ${CYAN}${INIT_STYLE}${NC}"
echo -e " Kernel: ${CYAN}${KERNEL_VERSION:-unknown}${NC}"
echo -e " Modules: ${CYAN}${#FINAL_MODULES[@]} included${NC}"
echo -e " In-place: ${CYAN}${IN_PLACE}${NC}"
echo ""
echo -e " Next steps:"
echo -e " ${CYAN}qcrows-verify ${INPUT}${NC}"
echo -e " ${CYAN}qcrows-export --format qcow2 ${INPUT} -o disk.qcow2${NC}"
echo -e " ${CYAN}qcrows-export --format pxe ${INPUT} --server-ip 192.168.1.1${NC}"
echo ""
}
# ============================================================================
# Entry Point
# ============================================================================
EXTRACT_DIR=""
KERNEL_VERSION=""
OUTPUT_FILE=""
declare -a FINAL_MODULES=()
parse_args "$@"
validate_inputs
# ── 1. Setup temp directory ─────────────────────────────────────────────
TMPDIR="$(mktemp -d /tmp/qcrows-initrd-regen-XXXXXX)"
EXTRACT_DIR="${TMPDIR}/bundle"
log "Working directory: ${TMPDIR}"
# ── 2. Extract QCrows bundle ────────────────────────────────────────────
extract_qcrows "$INPUT" "$EXTRACT_DIR"
# ── 3. Detect environment ───────────────────────────────────────────────
detect_vmm
detect_init_style
detect_compression
# ── 4. Extract rootfs for content access ────────────────────────────────
extract_rootfs
# ── 5. Discover kata-agent ──────────────────────────────────────────────
discover_agent
# ── 6. Resolve kernel modules ───────────────────────────────────────────
resolve_kernel_modules
# ── 7. Build initrd root filesystem ─────────────────────────────────────
build_initrd_root
# ── 8. Create the initrd image ──────────────────────────────────────────
if [[ "$FORMAT" == "cramfs" ]]; then
create_cramfs_image
else
create_cpio_archive
fi
# ── 9. Update bundle in-place if requested ──────────────────────────────
update_qcrows_inplace
# ── 10. Print summary ───────────────────────────────────────────────────
print_summary