Merge branch 'unmk-regression' into 'debian/latest'

unmkinitramfs: Restore split to "early" and "main" subdirectories

See merge request kernel-team/initramfs-tools!162
This commit is contained in:
Ben Hutchings 2025-03-24 23:50:00 +00:00
commit 8722bb830a
2 changed files with 112 additions and 55 deletions

View File

@ -25,24 +25,28 @@ usage_error()
# Extract a compressed cpio archive
xcpio()
{
archive="$1"
dir="$2"
shift 2
archive_uncomp="$1"
archive="$2"
dir="$3"
shift 3
if gzip -t "$archive" >/dev/null 2>&1 ; then
gzip -c -d "$archive"
elif zstd -q -c -t "$archive" >/dev/null 2>&1 ; then
zstd -q -c -d "$archive"
elif xzcat -t "$archive" >/dev/null 2>&1 ; then
xzcat "$archive"
elif lz4cat -t < "$archive" >/dev/null 2>&1 ; then
lz4cat "$archive"
elif bzip2 -t "$archive" >/dev/null 2>&1 ; then
bzip2 -c -d "$archive"
elif lzop -t "$archive" >/dev/null 2>&1 ; then
lzop -c -d "$archive"
# Ignoring other data, which may be garbage at the end of the file
fi | (
{
cat "$archive_uncomp"
if gzip -t "$archive" >/dev/null 2>&1 ; then
gzip -c -d "$archive"
elif zstd -q -c -t "$archive" >/dev/null 2>&1 ; then
zstd -q -c -d "$archive"
elif xzcat -t "$archive" >/dev/null 2>&1 ; then
xzcat "$archive"
elif lz4cat -t < "$archive" >/dev/null 2>&1 ; then
lz4cat "$archive"
elif bzip2 -t "$archive" >/dev/null 2>&1 ; then
bzip2 -c -d "$archive"
elif lzop -t "$archive" >/dev/null 2>&1 ; then
lzop -c -d "$archive"
# Ignoring other data, which may be garbage at the end of the file
fi
} | (
if [ -n "$dir" ]; then
mkdir -p -- "$dir"
cd -- "$dir"
@ -65,13 +69,16 @@ checkzero()
LANG=C grep -q -z '^$'
}
# Split an initramfs into archives and call xcpio on each
# Split an initramfs into archives and run cpio/xcpio to extract them
splitinitramfs()
{
initramfs="$1"
dir="$2"
shift 2
# Ensure this exists so we can use it unconditionally later
touch "$tempdir/main-uncomp.cpio"
count=0
start=0
while true; do
@ -81,55 +88,92 @@ splitinitramfs()
# very roughly based on linux/lib/earlycpio.c
end=$start
while true; do
if checkzero "$initramfs" $end; then
# This is the EOF marker. There might
# be more zero padding before the next
# archive, so read through all of it.
end=$((end + 4))
headoff=$end
magic="$(readhex "$initramfs" $end 6)" || break
test "$magic" = 070701 || test "$magic" = 070702 || break
namesize=$((0x$(readhex "$initramfs" $((end + 94)) 8)))
filesize=$((0x$(readhex "$initramfs" $((end + 54)) 8)))
nameoff=$((end + 110))
end=$(((nameoff + namesize + 3) & ~3))
end=$(((end + filesize + 3) & ~3))
# Check for EOF marker. Note that namesize
# includes a null terminator.
if [ $namesize = 11 ] \
&& name="$(dd if="$initramfs" bs=1 skip=$nameoff count=$((namesize - 1)) 2> /dev/null)" \
&& [ "$name" = 'TRAILER!!!' ]; then
# There might be more zero padding
# before the next archive, so read
# through all of it.
while checkzero "$initramfs" $end; do
end=$((end + 4))
done
break
fi
magic="$(readhex "$initramfs" $end 6)" || break
test "$magic" = 070701 || test "$magic" = 070702 || break
namesize=0x$(readhex "$initramfs" $((end + 94)) 8)
filesize=0x$(readhex "$initramfs" $((end + 54)) 8)
end=$((end + 110))
end=$(((end + namesize + 3) & ~3))
end=$(((end + filesize + 3) & ~3))
done
if [ $end -eq $start ]; then
break
fi
# Extract to cpio1, cpio2, ... subdirectories
count=$((count + 1))
subdir=cpio$count
dd < "$initramfs" skip=$start count=$((end - start)) iflag=skip_bytes,count_bytes 2> /dev/null |
(
if [ -n "$dir" ]; then
mkdir -p -- "$dir/$subdir"
cd -- "$dir/$subdir"
# Check whether this should be treated as an "early"
# or "main" initramfs. Currently all filenames the
# kernel looks for in an early initramfs begin with
# kernel/ subdirectory, but we should never create
# this in the main initramfs.
if dd < "$initramfs" skip=$start count=$((end - start)) \
iflag=skip_bytes,count_bytes 2> /dev/null |
cpio -i --list 2> /dev/null |
grep -q ^kernel/; then
# Extract to early, early2, ... subdirectories
count=$((count + 1))
if [ $count -eq 1 ]; then
subdir=early
else
subdir=early$count
fi
cpio -i "$@"
)
dd < "$initramfs" skip=$start count=$((end - start)) \
iflag=skip_bytes,count_bytes 2> /dev/null |
(
if [ -n "$dir" ]; then
mkdir -p -- "$dir/$subdir"
cd -- "$dir/$subdir"
fi
cpio -i "$@"
)
else
# Append to main-uncomp.cpio, excluding the
# trailer so cpio won't stop before the
# (de)compressed part.
dd < "$initramfs" skip=$start \
count=$((headoff - start)) \
iflag=skip_bytes,count_bytes \
>> "$tempdir/main-uncomp.cpio" 2> /dev/null
fi
start=$end
done
# Split out final archive if necessary
if [ "$end" -gt 0 ]; then
# Extract to main subdirectory
subarchive=$(mktemp "${TMPDIR:-/var/tmp}/unmkinitramfs_XXXXXX")
trap 'rm -f "$subarchive"' EXIT
subarchive="$tempdir/main-comp.cpio"
dd < "$initramfs" skip="$end" iflag=skip_bytes 2> /dev/null \
> "$subarchive"
count=$((count + 1))
subdir=cpio$count
xcpio "$subarchive" "${dir:+$dir/$subdir}" -i "$@"
else
# Don't use subdirectories (for backward compatibility)
xcpio "$initramfs" "$dir" -i "$@"
subarchive="$initramfs"
fi
# If we found an early initramfs, extract main initramfs to
# main subdirectory. Otherwise don't use a subdirectory (for
# backward compatibility).
if [ "$count" -gt 0 ]; then
subdir=main
else
subdir=.
fi
xcpio "$tempdir/main-uncomp.cpio" "$subarchive" \
"${dir:+$dir/$subdir}" -i "$@"
}
OPTIONS=$(getopt -o hv --long help,list,verbose -n "$0" -- "$@") || usage_error
@ -168,5 +212,8 @@ if [ $# -ne $expected_args ]; then
usage_error
fi
tempdir="$(mktemp -d "${TMPDIR:-/var/tmp}/unmkinitramfs_XXXXXX")"
trap 'rm -rf "$tempdir"' EXIT
# shellcheck disable=SC2086
splitinitramfs "$1" "${2:-}" $cpio_opts

View File

@ -1,4 +1,4 @@
.TH UNMKINITRAMFS 8 "2016/12/15" "initramfs\-tools" "System Administration"
.TH UNMKINITRAMFS 8 "2025/03/20" "initramfs\-tools" "System Administration"
.SH NAME
unmkinitramfs \- extract content from an initramfs image
@ -14,10 +14,21 @@ unmkinitramfs \- extract content from an initramfs image
The
.B unmkinitramfs
command extracts the content of a given initramfs image using
.BR cpio .
If the image contains multiple segments, each are passed to
.B cpio
in order.
and the appropriate decompressor command.
The initramfs image may be a single compressed cpio archive, or the
concatenation of any number of uncompressed cpio archives followed by
a compressed cpio archive.
When the initramfs image includes one or more "early initramfs"
archives, that is uncompressed archives with microcode or other files
that the kernel uses directly, these are extracted to sub\-directories
named "\fBearly\fR", "\fBearly2\fR", etc., and the other archives are
extracted to a sub\-directory named "\fBmain\fR".
In a future version of initramfs\-tools this behaviour may change so
that all archives are extracted directly into the given directory.
.SH OPTIONS
@ -40,9 +51,8 @@ Extract initramfs content of current running kernel:
.SH BUGS
.BR unmkinitramfs
cannot deal with multiple-segmented initramfs images, except where an
early (uncompressed) initramfs with system firmware is prepended to
the regular compressed initramfs.
does not support initramfs images with more or less than one
compressed cpio archive.
.SH AUTHOR
The initramfs-tools are written by Maximilian Attems <maks@debian.org>