Извлечение соединенных архивов cpio

Вопрос или проблема

Архивы 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. Это представляет собой определённые трудности при извлечении, особенно если необходимо извлечь все вложенные архивы, а не только первый. В данном руководстве мы рассмотрим несколько методик для выполнения этой задачи.

Задача

Основная задача заключается в том, чтобы быстро и эффективно извлечь все вложенные архивы из одного конкатенированного архива без дополнительных манипуляций, которые могут привести к ошибкам или потере данных.

Основные подходы

  1. Использование циклов и утилиты gunzip

    Простейший подход, который можно использовать для извлечения всех вложенных архивов, является использование циклов с командой gunzip для разжатия содержимого:

    gunzip -c input.cgz | while cpio -i; do :; done

    Этот способ позволяет запускать cpio для каждого из архивов по отдельности. Однако стоит отметить, что при этом gunzip будет использоваться только один раз, передавая всю входную информацию в цикл.

  2. Инструмент skipcpio

    Если вы работаете с дистрибутивами, использующими dracut, полезно будет задействовать инструмент skipcpio. Он позволяет пропускать уже извлечённые архивы, благодаря чему можно избежать повторного извлечения:

    /usr/lib/dracut/skipcpio ваше-изображение-initrd | zcat | cpio -id --no-absolute-file-names

    Для работы с skipcpio требуется наличие этого инструмента, который можно собрать самостоятельно или установить через пакетный менеджер.

  3. Использование утилиты dd для ручного извлечения

    Если предыдущие методы не подходят, можно воспользоваться dd. Утилита позволяет выполнять «пропуски» в архивах:

    dd if=ваше-изображение-initrd.img skip=0 | cpio -it

    Данный метод требует немного больше усилий, так как пользователю необходимо вручную отслеживать смещения для каждого отдельного cpio архива.

  4. Утилита binwalk

    В ситуациях, когда структура архивов сильно усложнена, можно воспользоваться binwalk для анализа и извлечения частей архива:

    binwalk -e /path/to/initrd

    binwalk распознает и извлекает все отдельные элементы из архива, делая задачу значительно проще.

  5. Интеграция 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 может быть сложной задачей, однако, с помощью вышеперечисленных методов, эта задача может быть успешно выполнена. Каждый из приведённых инструментов и методов имеет свои преимущества и лучше всего использовать комбинацию из них для достижения наилучших результатов. Если возможно, протестируйте каждый метод на локальной копии архива, чтобы убедиться в его эффективности и корректной работе. Выбор инструмента зависит от предпочтений и специфических потребностей вашей системы, поэтому рекомендуется применять тот подход, который вам наиболее удобен.

Оцените материал
Добавить комментарий

Капча загружается...