#!/bin/bash ########################################################################## # Script....: /tools/edit-initrd (OS InitRD and Slackware Installer) # Purpose...: Unpack an initrd, drop into an interactive shell in the # extracted tree, then repack it when you exit the shell. # This is a development tool for testing changes to the Initial # RAM disk. # Designer..: Stuart Winter # Programmer: Vibe coded with ChatGPT, tweaked by MoZes. # # Supports: uncompressed, gzip, xz. # Repack: gzip (max) or xz (max) # Typical use: # edit-initrd -i /boot/initrd.gz # defaults to repack using gzip (regardless of input compression) # edit-initrd -i /boot/initrd.gz -c xz # repack as xz # edit-initrd -i initrd.xz -s /bin/bash # use a specific shell # # Notes: # - Repacking overwrites the original initrd unless -o is provided. ########################################################################## set -euo pipefail usage() { cat >&2 <<'EOF' Usage: edit-initrd -i [-o ] [-c gzip|xz|auto] [-s ] [-k] Options: -i Path to initrd image (required) -o Output path (default: overwrite input initrd) -c Repack compression: gzip, xz, or auto (default: gzip) auto = reuse detected compression; if none, defaults to gzip -s Shell to run (default: $SHELL or /bin/bash) -k Keep temporary extraction directory (for debugging) Examples: edit-initrd -i /boot/initrd-armv8 edit-initrd -i /boot/initrd-armv8 -c gzip -o /tmp/initrd-armv8 EOF exit 2 } INITRD="" OUT="" REPACK_MODE="gzip" # Auto is nice, but the installers are delivered xz compressed. It's faster to repack # with gzip, which is the best for use during development. #REPACK_MODE="auto" SHELL_BIN="${SHELL:-/bin/bash}" KEEP_TMP=0 export XZ_OPTS="--threads 0 -ez9f -C crc32" while getopts ":i:o:c:s:kh" opt; do case "$opt" in i) INITRD="$OPTARG" ;; o) OUT="$OPTARG" ;; c) REPACK_MODE="$OPTARG" ;; s) SHELL_BIN="$OPTARG" ;; k) KEEP_TMP=1 ;; h) usage ;; \?) echo "Unknown option: -$OPTARG" >&2; usage ;; :) echo "Option -$OPTARG requires an argument." >&2; usage ;; esac done [[ -n "$INITRD" ]] || { echo "ERROR: -i is required." >&2; usage; } [[ -r "$INITRD" ]] || { echo "ERROR: Cannot read initrd: $INITRD" >&2; exit 1; } if [[ -z "$OUT" ]]; then OUT="$INITRD" fi if [[ ! -x "$SHELL_BIN" ]]; then echo "ERROR: Shell not executable: $SHELL_BIN" >&2 exit 1 fi need_cmd() { command -v "$1" >/dev/null 2>&1 || { echo "ERROR: Missing required command: $1" >&2; exit 1; }; } need_cmd cpio need_cmd gzip need_cmd xz need_cmd mktemp tmpdir="$(mktemp -d -t edit-initrd.XXXXXX)" cleanup() { if (( KEEP_TMP )); then echo "Keeping temp directory: $tmpdir" >&2 else rm -rf "$tmpdir" fi } trap cleanup EXIT # Compression detection is done by magic bytes only (no 'file' utility required): # - gzip: 1f 8b # - xz: fd 37 7a 58 5a 00 detect_compression() { # Returns: gzip | xz | none local f="$1" if head -c 2 "$f" | LC_ALL=C od -An -tx1 | tr -d ' \n' | grep -qi '^1f8b'; then echo "gzip" elif head -c 6 "$f" | LC_ALL=C od -An -tx1 | tr -d ' \n' | grep -qi '^fd377a585a00'; then echo "xz" else echo "none" fi } unpack_initrd() { local in="$1" comp="$2" outdir="$3" echo "Unpacking: $in" mkdir -p "$outdir" ( cd "$outdir" case "$comp" in gzip) gzip -dc "$in" | cpio -idm ;; xz) xz -dc "$in" | cpio -idm ;; none) cpio -idm < "$in" ;; *) echo "ERROR: Unknown compression: $comp" >&2; exit 1 ;; esac ) } choose_repack_mode() { local detected="$1" requested="$2" case "$requested" in auto) if [[ "$detected" == "gzip" || "$detected" == "xz" ]]; then echo "$detected" else echo "gzip" fi ;; gzip|xz) echo "$requested" ;; *) echo "ERROR: Invalid -c mode: $requested (use gzip, xz, or auto)" >&2 exit 1 ;; esac } repack_initrd() { local indir="$1" mode="$2" out="$3" local outtmp="${out}.tmp.$$" echo "Repacking to: $out (mode: $mode)" # Create cpio archive in a stable way (sorted paths), then compress. # Verbose compression requested; cpio verbosity kept quiet to avoid huge spew. ( cd "$indir" # Important: do not include '.' itself; feed relative paths. find . -mindepth 1 -print0 \ | LC_ALL=C sort -z \ | cpio --null -o -H newc 2>/dev/null \ | case "$mode" in gzip) gzip -9 -v > "$outtmp" ;; xz) xz $XZ_OPTS > "$outtmp" ;; esac ) # Preserve permissions on overwrite where possible; atomic replace. chmod --reference="$out" "$outtmp" 2>/dev/null || true mv -f "$outtmp" "$out" } COMP_DETECTED="$(detect_compression "$INITRD")" REPACK_CHOSEN="$(choose_repack_mode "$COMP_DETECTED" "$REPACK_MODE")" echo "Initrd: $INITRD" echo "Detected: $COMP_DETECTED" echo "Repack mode: $REPACK_CHOSEN" echo "Output: $OUT" echo "Work dir: $tmpdir" echo unpack_initrd "$INITRD" "$COMP_DETECTED" "$tmpdir" echo echo "Entering shell in: $tmpdir" echo "Tip: edit files, add/remove modules/firmware, etc. Exit the shell to repack." echo ( cd "$tmpdir" export PS1="initrd-root# " exec "$SHELL_BIN" -i ) echo repack_initrd "$tmpdir" "$REPACK_CHOSEN" "$OUT" echo "Done." #read -p "Press ENTER to reboot, or CTRL-C to abort" #reboot