unmkinitramfs 3.41 KB
Newer Older
1 2 3 4 5 6
#!/bin/sh

set -eu

usage()
{
7
	echo "Usage: $(basename "$0") [-v] <initramfs file> <directory>"
8 9 10 11 12
}

# Extract a compressed cpio archive
xcpio()
{
13 14 15
	archive="$1"
	dir="$2"
	shift 2
16 17

	if zcat -t "$archive" >/dev/null 2>&1 ; then
18
		zcat "$archive"
19
	elif xzcat -t "$archive" >/dev/null 2>&1 ; then
20
		xzcat "$archive"
21 22
	elif lz4cat -t "$archive" >/dev/null 2>&1 ; then
		lz4cat "$archive"
23
	elif bzip2 -t "$archive" >/dev/null 2>&1 ; then
24
		bzip2 -c -d "$archive"
25
	elif lzop -t "$archive" >/dev/null 2>&1 ; then
26
		lzop -c -d "$archive"
27
	# Ignoring other data, which may be garbage at the end of the file
28
	fi | (
29 30 31 32
		if [ -n "$dir" ]; then
			mkdir -p -- "$dir"
			cd -- "$dir"
		fi
33 34
		cpio "$@"
	)
35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53
}

# 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()
{
54 55 56
	initramfs="$1"
	dir="$2"
	shift 2
57

58 59
	count=0
	start=0
60
	while true; do
61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86
		# There may be prepended uncompressed archives.  cpio
		# won't tell us the true size of these so we have to
		# parse the headers and padding ourselves.  This is
		# 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))
				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
87 88
		fi

89 90 91 92 93 94 95 96
		# Extract to early, early2, ... subdirectories
		count=$((count + 1))
		if [ $count -eq 1 ]; then
			subdir=early
		else
			subdir=early$count
		fi
		dd < "$initramfs" skip=$start count=$((end - start)) iflag=skip_bytes 2> /dev/null |
97
		(
98
			if [ -n "$dir" ]; then
99 100
				mkdir -p -- "$dir/$subdir"
				cd -- "$dir/$subdir"
101
			fi
102
			cpio -i "$@"
103 104 105
		)
		start=$end
	done
106

107 108
	if [ $end -gt 0 ]; then
		# Extract to main subdirectory
109 110
		subarchive=$(mktemp ${TMPDIR:-/var/tmp}/unmkinitramfs_XXXXXX)
		trap "rm -f '$subarchive'" EXIT
111
		dd < "$initramfs" skip=$end iflag=skip_bytes 2> /dev/null \
112
			> $subarchive
113
		xcpio "$subarchive" "${dir:+$dir/main}" -i "$@"
114
	else
115
		# Don't use subdirectories (for backward compatibility)
116
		xcpio "$initramfs" "$dir" -i "$@"
117 118 119
	fi
}

120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156
OPTIONS=`getopt -o hv --long help,list,verbose -n "$0" -- "$@"`
# Check for non-GNU getopt
if [ $? != 0 ] ; then echo "W: non-GNU getopt" >&2 ; exit 1 ; fi

cpio_opts="--preserve-modification-time --no-absolute-filenames --quiet"
expected_args=2
eval set -- "$OPTIONS"

while true; do
	case "$1" in
        -h|--help)
		usage
		exit 0
	;;
	--list)
		# For lsinitramfs
		cpio_opts="${cpio_opts:+${cpio_opts} --list}"
		expected_args=1
		shift
	;;
	-v|--verbose)
		cpio_opts="${cpio_opts:+${cpio_opts} --verbose}"
		shift
	;;
	--)
		shift
		break
	;;
	*)
		echo "Internal error!" >&2
		exit 1
	esac
done

if [ $# -ne $expected_args ]; then
	usage
	exit 2
157 158
fi

159
splitinitramfs "$1" "${2:-}" $cpio_opts