[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

Bug#807457: initramfs-tools: Add command to unpack initramfs



On Wed, 2015-12-09 at 03:23 +0000, Ben Hutchings wrote:
> Rather than duplicating code, please combine this with lsinitramfs
> (checking $0 to determine which command was invoked) or move the common
> code into a shell library.

I've taken a slightly different tact and implemented uninitramfs as a
thin wrapper for cpio.  It is designed as a drop-in replacement for
cpio wherever an initramfs may be present, and passes through all
options unmodified.  With this, I changed lsinitramfs to call
uninitramfs instead of cpio.

See what you think.

Thanks,
Kevin

P.S.  I named it "uninitramfs" instead of "unmkinitramfs" since it
does not depend on mkinitramfs specifically, and it seemed to fit with
"lsinitramfs" and the "initramfs" format.  But if you would prefer
"unmkinitramfs", I can change it.

-- 
Cheers,      |  kevin@kevinlocke.name    | XMPP: kevin@kevinlocke.name
Kevin        |  https://kevinlocke.name  | IRC:   kevinoid on freenode
>From a45f16697106d36993762c39ffa82e28e201b72a Mon Sep 17 00:00:00 2001
From: Kevin Locke <kevin@kevinlocke.name>
Date: Tue, 9 Feb 2016 20:01:05 -0800
Subject: [PATCH] Create uninitramfs command

This command supports extracting files from an initramfs image.  It is
designed to be a drop-in for cpio wherever an initramfs may be present.

It is implemented using the code from lsinitramfs, which now delegates
to uninitramfs instead of cpio.

Signed-off-by: Kevin Locke <kevin@kevinlocke.name>
---
 lsinitramfs   | 71 ++----------------------------------------
 lsinitramfs.8 |  1 +
 mkinitramfs.8 |  3 +-
 uninitramfs   | 99 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 uninitramfs.8 | 59 +++++++++++++++++++++++++++++++++++
 5 files changed, 163 insertions(+), 70 deletions(-)
 create mode 100755 uninitramfs
 create mode 100644 uninitramfs.8

diff --git a/lsinitramfs b/lsinitramfs
index 34d84e4..5a5a049 100755
--- a/lsinitramfs
+++ b/lsinitramfs
@@ -12,7 +12,7 @@ if [ "$#" -eq 0 ] ; then
 	exit 1
 fi
 
-cpio_args="--extract --quiet --list"
+cpio_args="--quiet --list"
 
 OPTIONS=`getopt -o hl --long help,long -n "$0" -- "$@"`
 # Check for non-GNU getopt
@@ -40,73 +40,6 @@ while true; do
 	esac
 done
 
-listarchive()
-{
-	archive="$1"
-	if zcat -t "${archive}" >/dev/null 2>&1 ; then
-		zcat "${archive}" | cpio ${cpio_args}
-	elif xzcat -t "${archive}" >/dev/null 2>&1 ; then
-		xzcat "${archive}" | cpio ${cpio_args}
-	elif bzip2 -t "${archive}" >/dev/null 2>&1 ; then
-		bzip2 -c -d "${archive}" | cpio ${cpio_args}
-	elif lzop -t "${archive}" >/dev/null 2>&1 ; then
-		lzop -c -d "${archive}" | cpio ${cpio_args}
-	fi
-}
-
-# Read bytes out of a file, checking that they are valid hex digits
-readhex()
-{
-	dd < "$1" bs=1 skip="$2" count="$3" 2> /dev/null | \
-		LANG=C grep -E "^[0-9A-Fa-f]{$3}\$"
-}
-
-# Check for a zero byte in a file
-checkzero()
-{
-	dd < "$1" bs=1 skip="$2" count=1 2> /dev/null | \
-		LANG=C grep -q -z '^$'
-}
-
 for initramfs in "$@" ; do
-	if ! [ -r "${initramfs}" ] ; then
-		echo "Specified file could not be read." >&2
-		exit 1
-	else
-		echo "${initramfs}"
-
-		# There may be a prepended uncompressed archive.  cpio
-		# won't tell us the true size of this so we have to
-		# parse the headers and padding ourselves.  This is
-		# very roughly based on linux/lib/earlycpio.c
-		offset=0
-		while true; do
-			if checkzero "$initramfs" $offset; then
-				offset=$((offset + 4))
-				continue
-			fi
-			magic="$(readhex "$initramfs" $offset 6)" || break
-			test $magic = 070701 || test $magic = 070702 || break
-			namesize=0x$(readhex "$initramfs" $((offset + 94)) 8)
-			filesize=0x$(readhex "$initramfs" $((offset + 54)) 8)
-			offset=$(((offset + 110)))
-			offset=$(((offset + $namesize + 3) & ~3))
-			offset=$(((offset + $filesize + 3) & ~3))
-		done
-
-		if [ $offset -ne 0 ]; then
-			# List uncompressed archive
-			cpio ${cpio_args} < "$initramfs"
-
-			# List main archive
-			subarchive=$(mktemp ${TMPDIR:-/var/tmp}/lsinitramfs_XXXXXX)
-			trap "rm -f $subarchive" EXIT
-			dd < "$initramfs" bs="$offset" skip=1 2> /dev/null \
-				> $subarchive
-			listarchive $subarchive
-		else
-			listarchive "${initramfs}"
-		fi
-
-	fi
+	uninitramfs $cpio_args < "$initramfs"
 done
diff --git a/lsinitramfs.8 b/lsinitramfs.8
index a717113..4082e3b 100644
--- a/lsinitramfs.8
+++ b/lsinitramfs.8
@@ -50,4 +50,5 @@ and numerous others.
 .BR
 .IR initramfs-tools (8),
 .IR mkinitramfs (8),
+.IR uninitramfs (8),
 .IR update-initramfs (8).
diff --git a/mkinitramfs.8 b/mkinitramfs.8
index 0a109f6..74a68dc 100644
--- a/mkinitramfs.8
+++ b/mkinitramfs.8
@@ -155,4 +155,5 @@ Jeff Bailey <jbailey@raspberryginger.com> and numerous others.
 .IR initramfs.conf (5),
 .IR initramfs-tools (8),
 .IR update-initramfs (8),
-.IR lsinitramfs (8).
+.IR lsinitramfs (8),
+.IR uninitramfs (8).
diff --git a/uninitramfs b/uninitramfs
new file mode 100755
index 0000000..bca2f1d
--- /dev/null
+++ b/uninitramfs
@@ -0,0 +1,99 @@
+#!/bin/sh
+
+set -eu
+
+usage()
+{
+	echo "Usage: $(basename "$0") [cpio options ...] < <initramfs file>"
+}
+
+# Abort if stdin is a terminal.
+#
+# Although cpio supports this, the chance of someone actually keying in a cpio
+# archive on the terminal is negligible compared to the risk of someone who
+# forgot cpio's arcane argument convention.
+if [ -t 0 ] ; then
+	echo "Error: stdin must be an initramfs" >&2
+	usage >&2
+	exit 1
+fi
+
+# Extract a compressed cpio archive
+xcpio()
+{
+	archive="$1" ; shift
+
+	if zcat -t "$archive" >/dev/null 2>&1 ; then
+		zcat "$archive" | cpio "$@"
+	elif xzcat -t "$archive" >/dev/null 2>&1 ; then
+		xzcat "$archive" | cpio "$@"
+	elif bzip2 -t "$archive" >/dev/null 2>&1 ; then
+		bzip2 -c -d "$archive" | cpio "$@"
+	elif lzop -t "$archive" >/dev/null 2>&1 ; then
+		lzop -c -d "$archive" | cpio "$@"
+	# Ignoring other data, which may be garbage at the end of the file
+	fi
+}
+
+# Read bytes out of a file, checking that they are valid hex digits
+readhex()
+{
+	dd < "$1" bs=1 skip="$2" count="$3" 2> /dev/null | \
+		LANG=C grep -E "^[0-9A-Fa-f]{$3}\$"
+}
+
+# Check for a zero byte in a file
+checkzero()
+{
+	dd < "$1" bs=1 skip="$2" count=1 2> /dev/null | \
+		LANG=C grep -q -z '^$'
+}
+
+# Split an initramfs into archives and call xcpio on each
+splitinitramfs()
+{
+	initramfs="$1" ; shift
+
+	# There may be a prepended uncompressed archive.  cpio
+	# won't tell us the true size of this so we have to
+	# parse the headers and padding ourselves.  This is
+	# very roughly based on linux/lib/earlycpio.c
+	offset=0
+	while true; do
+		if checkzero "$initramfs" $offset; then
+			offset=$((offset + 4))
+			continue
+		fi
+		magic="$(readhex "$initramfs" $offset 6)" || break
+		test $magic = 070701 || test $magic = 070702 || break
+		namesize=0x$(readhex "$initramfs" $((offset + 94)) 8)
+		filesize=0x$(readhex "$initramfs" $((offset + 54)) 8)
+		offset=$(((offset + 110)))
+		offset=$(((offset + $namesize + 3) & ~3))
+		offset=$(((offset + $filesize + 3) & ~3))
+	done
+
+	if [ $offset -ne 0 ]; then
+		# uncompressed archive
+		cpio -i "$@" < "$initramfs"
+
+		# main archive
+		subarchive=$(mktemp ${TMPDIR:-/var/tmp}/uninitramfs_XXXXXX)
+		trap "rm -f '$subarchive'" EXIT
+		dd < "$initramfs" bs="$offset" skip=1 2> /dev/null \
+			> $subarchive
+		xcpio "$subarchive" -i "$@"
+	else
+		xcpio "$initramfs" -i "$@"
+	fi
+}
+
+# If we can get the name of the file passed in, use it.  Otherwise, create one.
+stdin_filename=$(readlink /proc/$$/fd/0 2>/dev/null || true)
+if ! [ -r "$stdin_filename" ] ; then
+	stdin_filename=$(mktemp ${TMPDIR:-/var/tmp}/uninitramfs_stdin_XXXXXX)
+	trap "rm -f '$stdin_filename'" EXIT
+	cat > "$stdin_filename"
+fi
+
+splitinitramfs "$stdin_filename" "$@"
diff --git a/uninitramfs.8 b/uninitramfs.8
new file mode 100644
index 0000000..d3649fe
--- /dev/null
+++ b/uninitramfs.8
@@ -0,0 +1,59 @@
+.TH UNINITRAMFS 8  "2016/02/09" "Linux" "uninitramfs manual"
+
+.SH NAME
+uninitramfs \- extract content from an initramfs image
+
+.SH SYNOPSIS
+.B uninitramfs
+.RI "" "[cpio options ...]" " < " <initramfsfile>
+.br
+
+.SH DESCRIPTION
+The
+.B uninitramfs
+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.
+
+.SH OPTIONS
+
+All options are passed through to
+.B cpio
+, after an initial
+.B \-i
+option which puts
+.B cpio
+into \*(lqextract\*(rq or \*(lqCopy-in\*(rq mode.
+
+.SH USAGE EXAMPLES
+
+Extract initramfs content of current running kernel:
+
+.PP
+.B uninitramfs < /boot/initrd.img-$(uname -r)
+
+Extract
+.I init
+from the initramfs verbosely and keep its mtime:
+
+.PP
+.B uninitramfs -m -v init < /boot/initrd.img-$(uname -r)
+
+.SH BUGS
+.BR uninitramfs
+cannot deal with multiple-segmented initramfs images, except where an
+early (uncompressed) initramfs with system firmware is prepended to
+the regular compressed initramfs.
+
+.SH AUTHOR
+The initramfs-tools are written by Maximilian Attems <maks@debian.org>
+and numerous others.
+
+.SH SEE ALSO
+.BR
+.IR initramfs-tools (8),
+.IR lsinitramfs (8),
+.IR mkinitramfs (8),
+.IR update-initramfs (8).
-- 
2.7.0


Reply to: