Вопрос или проблема
Архивы initramfs в Linux могут состоять из последовательности конкатенированных, сжатых с помощью gzip файлов cpio.
Как извлечь все встроенные архивы из такого архива, а не только первый?
Следующий пример показывает шаблон, который, хотя и кажется потенциально рабочим, извлекает только первый архив:
while gunzip -c | cpio -i; do :; done <input.cgz
Я также пробовал помощник skipcpio из dracut, чтобы переместить указатель файла после первого изображения cpio, но следующий код приводит к поврежденному потоку (не в правильной точке входных данных), отправляемому в cpio:
# это не идеально -- предположительно, нужно будет перезапустить с дополнительным skipcpio в конвейере
# ...пока не будут достигнуты все файлы в архиве.
gunzip -c <input.cgz | skipcpio /dev/stdin | cpio -i
gunzip нужно запустить только один раз (потребляя все входные данные), в то время как cpio следует запускать один раз на каждый встроенный архив, как так:
gunzip -c <input.cgz | while cpio -i; do :; done
/usr/lib/dracut/skipcpio $your-initrd-img | zcat | cpio -id --no-absolute-file-names
или
/usr/lib/dracut/skipcpio $your-img | gunzip -c | cpio -id
(в FreeBSD нет опции –no-absolute-file-names для cpio)
Программа skipcpio
является частью пакета dracut. Но вы можете скачать код (skipcpio.c) и скомпилировать его даже под FreeBSD.
Это необходимо при извлечении образов initrd, созданных dracut, по крайней мере, в дистрибутивах на базе RedHat, таких как Fedora. Она помещает файл под названием “early_cpio” в образ, поэтому извлечение вашего initramfs обычным способом, известным ранее, не сработает.
Вы можете сделать это вручную с помощью dd skip=
. На моем Ubuntu 20.04, я могу просмотреть первую часть (смещение 0 блоков) с помощью
# dd if=/boot/initrd.img-5.4.0-45-generic skip=0 | file -
/dev/stdin: ASCII cpio archive (SVR4 with no CRC)
а затем увидеть содержимое
# dd if=/boot/initrd.img-5.4.0-45-generic skip=0 | cpio -it
.
kernel
kernel/x86
kernel/x86/microcode
kernel/x86/microcode/AuthenticAMD.bin
62 blocks
Вторая часть находится на 62 блока дальше
# dd if=/boot/initrd.img-5.4.0-45-generic skip=62 | file -
/dev/stdin: ASCII cpio archive (SVR4 with no CRC)
и снова просто простой архив cpio, но в этот раз больше
# dd if=/boot/initrd.img-5.4.0-45-generic skip=62 | cpio -it
kernel
kernel/x86
kernel/x86/microcode
kernel/x86/microcode/.enuineIntel.align.0123456789abc
kernel/x86/microcode/GenuineIntel.bin
5868 blocks
Теперь пропустим 5868 + 62
блоков в initramfs
# dd if=/boot/initrd.img-5.4.0-45-generic skip=5930 | file -
/dev/stdin: LZ4 compressed data (v0.1-v0.9)
На этот раз это сжатый поток, так что
# dd if=/boot/initrd.img-5.4.0-45-generic skip=5930 | lz4cat | file -
/dev/stdin: ASCII cpio archive (SVR4 with no CRC)
И снова мы нашли следующий (и последний) архив cpio
# dd if=/boot/initrd.img-5.4.0-45-generic skip=5930 | lz4cat | cpio -it
... много вывода
usr/share/plymouth/themes/spinner/watermark.png
usr/share/plymouth/ubuntu-logo.png
var
var/cache
var/cache/fontconfig
var/cache/fontconfig/383ee5b3-5437-4bdc-87f6-cf314658a7c0-le64.cache-7
var/cache/fontconfig/575cffd4-ae01-4067-914f-7545fe566c1b-le64.cache-7
var/cache/fontconfig/CACHEDIR.TAG
var/cache/fontconfig/c467a813-186f-476e-880a-3770402989a9-le64.cache-7
var/cache/fontconfig/d912fc4e-f5b6-456d-a86d-e4c3ccbbefe9-le64.cache-7
var/lib
var/lib/dhcp
450460 blocks
Хотя это работает только в том случае, если первые потоки не сжаты. В противном случае cpio не сообщит размер в initramfs, а о несжатой части.
Debian с установленными пакетами amd64-microcode
/ intel-microcode
кажется, использует некий беспорядок из несжатого cpio
архива, содержащего микрокод процессора, за которым следует сжатый с помощью gzip
архив cpio
с фактическим содержимым initrd. Единственный способ, которым я когда-либо смог извлечь это, – это использовать binwalk
(apt install binwalk
), который может правильно перечислить структуру:
binwalk /path/to/initrd
пример вывода:
host ~ # binwalk /boot/initrd.img-5.10.0-15-amd64
DECIMAL HEXADECIMAL DESCRIPTION
--------------------------------------------------------------------------------
0 0x0 ASCII cpio archive (SVR4 with no CRC), file name: "kernel", file name length: "0x00000007", file size: "0x00000000"
120 0x78 ASCII cpio archive (SVR4 with no CRC), file name: "kernel/x86", file name length: "0x0000000B", file size: "0x00000000"
244 0xF4 ASCII cpio archive (SVR4 with no CRC), file name: "kernel/x86/microcode", file name length: "0x00000015", file size: "0x00000000"
376 0x178 ASCII cpio archive (SVR4 with no CRC), file name: "kernel/x86/microcode/.enuineIntel.align.0123456789abc", file name length: "0x00000036", file size: "0x00000000"
540 0x21C ASCII cpio archive (SVR4 with no CRC), file name: "kernel/x86/microcode/GenuineIntel.bin", file name length: "0x00000026", file size: "0x00455C00"
4546224 0x455EB0 ASCII cpio archive (SVR4 with no CRC), file name: "TRAILER!!!", file name length: "0x0000000B", file size: "0x00000000"
4546560 0x456000 gzip compressed data, has original file name: "mkinitramfs-MAIN_dTZaRk", from Unix, last modified: 2022-06-14 14:02:57
37332712 0x239A6E8 MySQL ISAM compressed data file Version 9
и извлечь отдельные части:
binwalk -e /path/to/initrd
пример вывода:
host ~ # binwalk -e /boot/initrd.img-5.10.0-15-amd64
DECIMAL HEXADECIMAL DESCRIPTION
--------------------------------------------------------------------------------
0 0x0 ASCII cpio archive (SVR4 with no CRC), file name: "kernel", file name length: "0x00000007", file size: "0x00000000"
120 0x78 ASCII cpio archive (SVR4 with no CRC), file name: "kernel/x86", file name length: "0x0000000B", file size: "0x00000000"
244 0xF4 ASCII cpio archive (SVR4 with no CRC), file name: "kernel/x86/microcode", file name length: "0x00000015", file size: "0x00000000"
376 0x178 ASCII cpio archive (SVR4 with no CRC), file name: "kernel/x86/microcode/.enuineIntel.align.0123456789abc", file name length: "0x00000036", file size: "0x00000000"
540 0x21C ASCII cpio archive (SVR4 with no CRC), file name: "kernel/x86/microcode/GenuineIntel.bin", file name length: "0x00000026", file size: "0x00455C00"
4546224 0x455EB0 ASCII cpio archive (SVR4 with no CRC), file name: "TRAILER!!!", file name length: "0x0000000B", file size: "0x00000000"
4546560 0x456000 gzip compressed data, has original file name: "mkinitramfs-MAIN_dTZaRk", from Unix, last modified: 2022-06-14 14:02:57
37332712 0x239A6E8 MySQL ISAM compressed data file Version 9
Это даст вам отдельные части в отдельных файлах, и теперь вы сможете наконец извлечь правильный архив cpio
:
host ~ # ls -l _initrd.img-5.10.0-15-amd64.extracted
insgesamt 187M
drwxr-xr-x 3 root root 4,0K 14. Jun 17:53 cpio-root/
-rw-r--r-- 1 root root 114M 14. Jun 17:53 mkinitramfs-MAIN_dTZaRk
-rw-r--r-- 1 root root 39M 14. Jun 17:53 0.cpio
-rw-r--r-- 1 root root 35M 14. Jun 17:53 mkinitramfs-MAIN_dTZaRk.gz
host ~/_initrd.img-5.10.0-15-amd64.extracted # mkdir extracted
host ~/_initrd.img-5.10.0-15-amd64.extracted # cd extracted
host ~/_initrd.img-5.10.0-15-amd64.extracted/extracted # cat ../mkinitramfs-MAIN_dTZaRk | cpio -idmv --no-absolute-filenames
[...]
host ~/_initrd.img-5.10.0-15-amd64.extracted/extracted # ll
insgesamt 28K
lrwxrwxrwx 1 root root 7 14. Jun 17:55 bin -> usr/bin/
drwxr-xr-x 3 root root 4,0K 14. Jun 17:55 conf/
drwxr-xr-x 7 root root 4,0K 14. Jun 17:55 etc/
lrwxrwxrwx 1 root root 7 14. Jun 17:55 lib -> usr/lib/
lrwxrwxrwx 1 root root 9 14. Jun 17:55 lib32 -> usr/lib32/
lrwxrwxrwx 1 root root 9 14. Jun 17:55 lib64 -> usr/lib64/
lrwxrwxrwx 1 root root 10 14. Jun 17:55 libx32 -> usr/libx32/
drwxr-xr-x 2 root root 4,0K 14. Jun 16:02 run/
lrwxrwxrwx 1 root root 8 14. Jun 17:55 sbin -> usr/sbin/
drwxr-xr-x 8 root root 4,0K 14. Jun 17:55 scripts/
drwxr-xr-x 8 root root 4,0K 14. Jun 17:55 usr/
-rwxr-xr-x 1 root root 6,2K 14. Янв 2021 init*
Если у вас есть zstd и cpio, вот еще одна альтернатива программы для извлечения всего:
cpio_all() (IFS="+ /-";o=${2:-0};dd "if=$1" skip=$o |zstd -dc --pass-through |cpio -idvm 2>.t;set -- "$1" $o $(tail -n1 .t |cut "-d+ /-" -f1);rm .t;[ -n "$3" ] &&cpio_all "$1" $(( $2 + $3 )); )
Запуск cpio_all /boot/initrd.img-5.4.0-45-generic
извлечет все архивы cpio в любом формате сжатия.
Ответ или решение
Извлечение конкатенированных архивов cpio
В мире Linux отдельные архивы initramfs могут представлять собой серию конкатенированных, сжатых с помощью gzip архивов cpio. Это представляет собой определённые трудности при извлечении, особенно если необходимо извлечь все вложенные архивы, а не только первый. В данном руководстве мы рассмотрим несколько методик для выполнения этой задачи.
Задача
Основная задача заключается в том, чтобы быстро и эффективно извлечь все вложенные архивы из одного конкатенированного архива без дополнительных манипуляций, которые могут привести к ошибкам или потере данных.
Основные подходы
-
Использование циклов и утилиты gunzip
Простейший подход, который можно использовать для извлечения всех вложенных архивов, является использование циклов с командой
gunzip
для разжатия содержимого:gunzip -c input.cgz | while cpio -i; do :; done
Этот способ позволяет запускать
cpio
для каждого из архивов по отдельности. Однако стоит отметить, что при этомgunzip
будет использоваться только один раз, передавая всю входную информацию в цикл. -
Инструмент skipcpio
Если вы работаете с дистрибутивами, использующими dracut, полезно будет задействовать инструмент
skipcpio
. Он позволяет пропускать уже извлечённые архивы, благодаря чему можно избежать повторного извлечения:/usr/lib/dracut/skipcpio ваше-изображение-initrd | zcat | cpio -id --no-absolute-file-names
Для работы с
skipcpio
требуется наличие этого инструмента, который можно собрать самостоятельно или установить через пакетный менеджер. -
Использование утилиты dd для ручного извлечения
Если предыдущие методы не подходят, можно воспользоваться
dd
. Утилита позволяет выполнять «пропуски» в архивах:dd if=ваше-изображение-initrd.img skip=0 | cpio -it
Данный метод требует немного больше усилий, так как пользователю необходимо вручную отслеживать смещения для каждого отдельного cpio архива.
-
Утилита binwalk
В ситуациях, когда структура архивов сильно усложнена, можно воспользоваться
binwalk
для анализа и извлечения частей архива:binwalk -e /path/to/initrd
binwalk
распознает и извлекает все отдельные элементы из архива, делая задачу значительно проще. -
Интеграция zstd и cpio
В более современных дистрибутивах, где используется формат сжатия zstd, можно использовать следующую функцию для извлечения всех архивов:
cpio_all() (IFS="+ /-";o=${2:-0};dd "if=$1" skip=$o |zstd -dc --pass-through |cpio -idvm 2>.t;set -- "$1" $o $(tail -n1 .t |cut "-d+ /-" -f1);rm .t;[ -n "$3" ] && cpio_all "$1" $(( $2 + $3 )); )
Запустив данную функцию, вы сможете извлечь все архивы в любой комбинации, используя zstd.
Заключение
Извлечение конкатенированных архивов cpio может быть сложной задачей, однако, с помощью вышеперечисленных методов, эта задача может быть успешно выполнена. Каждый из приведённых инструментов и методов имеет свои преимущества и лучше всего использовать комбинацию из них для достижения наилучших результатов. Если возможно, протестируйте каждый метод на локальной копии архива, чтобы убедиться в его эффективности и корректной работе. Выбор инструмента зависит от предпочтений и специфических потребностей вашей системы, поэтому рекомендуется применять тот подход, который вам наиболее удобен.