blob: d1226c323dcb8a57cbfcb3bc1e7eb8f6da43926b (
plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
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
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
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
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
|
#!/bin/sh
set -eu
usage()
{
cat << EOF
Usage: unmkinitramfs [-v] initramfs-file directory
Options:
-v Display verbose messages about extraction
See unmkinitramfs(8) for further details.
EOF
}
usage_error()
{
usage >&2
exit 2
}
# Extract a compressed cpio archive
xcpio()
{
archive="$1"
dir="$2"
shift 2
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"
fi
cpio "$@"
)
}
# 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"
dir="$2"
shift 2
count=0
start=0
while true; do
# 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
fi
# 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 |
(
if [ -n "$dir" ]; then
mkdir -p -- "$dir/$subdir"
cd -- "$dir/$subdir"
fi
cpio -i "$@"
)
start=$end
done
if [ $end -gt 0 ]; then
# Extract to main subdirectory
subarchive=$(mktemp "${TMPDIR:-/var/tmp}/unmkinitramfs_XXXXXX")
trap 'rm -f "$subarchive"' EXIT
dd < "$initramfs" skip=$end iflag=skip_bytes 2> /dev/null \
> "$subarchive"
xcpio "$subarchive" "${dir:+$dir/main}" -i "$@"
else
# Don't use subdirectories (for backward compatibility)
xcpio "$initramfs" "$dir" -i "$@"
fi
}
OPTIONS=$(getopt -o hv --long help,list,verbose -n "$0" -- "$@") || usage_error
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_error
fi
# shellcheck disable=SC2086
splitinitramfs "$1" "${2:-}" $cpio_opts
|