Как в C/C++ работать с сжатыми через CPIO данными?

Как в C или C++ распаковать данные, запакованные через cpio [к слову, оригинальная утилита cpio работает с данными через stdin]. Ну грубо говоря, вот:

const unsigned char archive_cpio[archive_size]
unpack_cpio(archive_cpio, archive_size);

ОС - Linux, язык - C++ (можно ответ и для C, с extern "C" я разберусь)

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


Ответы (2 шт):

Автор решения: eri

cpio поток без сжатия. нужно прочитать заголовок и поместить указатель на начало файла:

Для этого читаешь заголовок в структуру

Bytes Field names
2     magic
2     dev
2     ino
2     mode
2     uid
2     gid
2     nlink
2     rdev
2     mtime
2     namesize
2     filesize

Перемещай указатель на длину заголовка - там найдешь имя файла, дальше сам файл, перемещая на размер файла найдешь новый заголовок.

https://www.ibm.com/docs/en/zos/2.2.0?topic=formats-cpio-format-cpio-archives

В man 5 cpio есть структуры для разных форматов

https://www.systutorials.com/docs/linux/man/5-cpio/

struct cpio_newc_header {
        char    c_magic[6];
        char    c_ino[8];
        char    c_mode[8];
        char    c_uid[8];
        char    c_gid[8];
        char    c_nlink[8];
        char    c_mtime[8];
        char    c_filesize[8];
        char    c_devmajor[8];
        char    c_devminor[8];
        char    c_rdevmajor[8];
        char    c_rdevminor[8];
        char    c_namesize[8];
        char    c_check[8];
};

Для сдк с посикс это всё должно найтись в #include <cpio.h>

→ Ссылка
Автор решения: RandomDice 779

Можно было использовать libarchive, но я пошёл другим путём.

Читаю test.cpio:

 ~ # find ./test* | cpio -H newc -o > test.cpio
# мне критически надо именно newc архив
 ~ # cat test.cpio
070701003C0E8C000081800000000000000000000000016293A86500000007000001030000002600000000000000000000000A00000000test2.txtHello!
070701003C0E84000081800000000000000000000000016293A4F00000001B000001030000002600000000000000000000000900000000test.txtIt works!
It really works!
07070100000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000B00000000TRAILER!!!

На что можно обратить внимание?

Первое - 070701 - сигнатура newc архива. Можно сразу проверить тип файла. Во-вторых, ряд параметров просто обложены нулями, что затрудняет работу с ними. Но, строго говоря, они мне особо не нужны для работы. Т е можно просто проигнорировать 111 байт в "магической" строке. В этой же "магической" строчке расположены имя файла и первая строка из него. Отделить их просто, имя файла отделено от первой строки двумя нулевыми байтами. А ещё последняя строка файла отделена от "магической" строки двумя нулевыми байтами:

 ~ # echo "070701003C0E8C000081800000000000000000000000016293A86500000007000001030000002600000000000000000000000A00000000test2.txtHello!" | xxd
00000000: 3037 3037 3031 3030 3343 3045 3843 3030  070701003C0E8C00
00000010: 3030 3831 3830 3030 3030 3030 3030 3030  0081800000000000
00000020: 3030 3030 3030 3030 3030 3030 3031 3632  0000000000000162
00000030: 3933 4138 3635 3030 3030 3030 3037 3030  93A8650000000700
00000040: 3030 3031 3033 3030 3030 3030 3236 3030  0001030000002600
00000050: 3030 3030 3030 3030 3030 3030 3030 3030  0000000000000000
00000060: 3030 3030 3041 3030 3030 3030 3030 7465  00000A00000000te
00000070: 7374 322e 7478 7448 656c 6c6f 210a       st2.txtHello.
 ~ # xxd test.cpio | head -n 8
00000000: 3037 3037 3031 3030 3343 3045 3843 3030  070701003C0E8C00
00000010: 3030 3831 3830 3030 3030 3030 3030 3030  0081800000000000
00000020: 3030 3030 3030 3030 3030 3030 3031 3632  0000000000000162
00000030: 3933 4138 3635 3030 3030 3030 3037 3030  93A8650000000700
00000040: 3030 3031 3033 3030 3030 3030 3236 3030  0001030000002600
00000050: 3030 3030 3030 3030 3030 3030 3030 3030  0000000000000000
00000060: 3030 3030 3041 3030 3030 3030 3030 7465  00000A00000000te
00000070: 7374 322e 7478 7400 4865 6c6c 6f21 0a00  st2.txt.Hello!..

Т е можно построчно почитать файл cpio. Находим заголовок - проверяем длину строки. Если она больше 113, то пытаемся вычленить имя файла и его содержимое. Вычленили? - все, перед нами та самая "магическая" строка с новым файлом в архиве.

Однако, что если перед нами будет устройство или пустой файл? xxd даёт ответ:

 ~ # mknod c 1 3 null
 ~ # echo -ne "\0" > test.txt0
 ~ # find ./null ./test.txt0 | cpio -H newc -o | xxd
00000000: 3037 3037 3031 3030 3343 3045 3844 3030  070701003C0E8D00
00000010: 3030 3231 3830 3030 3030 3030 3030 3030  0021800000000000
00000020: 3030 3030 3030 3030 3030 3030 3031 3632  0000000000000162
00000030: 3934 4135 3530 3030 3030 3030 3030 3030  94A5500000000000
00000040: 3030 3031 3033 3030 3030 3030 3236 3030  0001030000002600
00000050: 3030 3030 3031 3030 3030 3030 3033 3030  0000010000000300
00000060: 3030 3030 3035 3030 3030 3030 3030 6e75  00000500000000nu
00000070: 6c6c 0000 3037 3037 3031 3030 3343 3038  ll..070701003C08
00000080: 3935 3030 3030 3831 3830 3030 3030 3030  9500008180000000
00000090: 3030 3030 3030 3030 3030 3030 3030 3030  0000000000000000
000000a0: 3031 3632 3934 4135 4236 3030 3030 3030  016294A5B6000000
000000b0: 3031 3030 3030 3031 3033 3030 3030 3030  0100000103000000
000000c0: 3236 3030 3030 3030 3030 3030 3030 3030  2600000000000000
000000d0: 3030 3030 3030 3030 3041 3030 3030 3030  000000000A000000
000000e0: 3030 7465 7374 2e74 7874 3000 0000 0000  00test.txt0.....
000000f0: 3037 3037 3031 3030 3030 3030 3030 3030  0707010000000000
00000100: 3030 3030 3030 3030 3030 3030 3030 3030  0000000000000000
00000110: 3030 3030 3030 3030 3030 3030 3031 3030  0000000000000100
00000120: 3030 3030 3030 3030 3030 3030 3030 3030  0000000000000000
00000130: 3030 3030 3030 3030 3030 3030 3030 3030  0000000000000000
00000140: 3030 3030 3030 3030 3030 3030 3030 3030  0000000000000000
00000150: 3030 3030 3042 3030 3030 3030 3030 5452  00000B00000000TR
00000160: 4149 4c45 5221 2121 0000 0000 0000 0000  AILER!!!........
00000170: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000180: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000190: 0000 0000 0000 0000 0000 0000 0000 0000  ................
000001a0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
000001b0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
000001c0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
000001d0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
000001e0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
000001f0: 0000 0000 0000 0000 0000 0000 0000 0000  ................

Правда что забыли нулевые байты на конце, я так и не понял...

У устройства нет содержимого, поэтому его имя отделяется от новой магической строки двумя нулевыми байтами. А если мы занесем даже пустой файл, тут будет хотя бы 3-4 нулевых байта - для отделения имени файла и его содержимого.

 ~ # mkdir test
 ~ # mv null test
 ~ # mv test.txt0 test
 ~ # find ./test | cpio -H newc -o | xxd
00000000: 3037 3037 3031 3030 3343 4331 4544 3030  070701003CC1ED00
00000010: 3030 3431 4339 3030 3030 3030 3030 3030  0041C90000000000
00000020: 3030 3030 3030 3030 3030 3030 3032 3632  0000000000000262
00000030: 3934 4137 3541 3030 3030 3030 3030 3030  94A75A0000000000
00000040: 3030 3031 3033 3030 3030 3030 3236 3030  0001030000002600
00000050: 3030 3030 3030 3030 3030 3030 3030 3030  0000000000000000
00000060: 3030 3030 3035 3030 3030 3030 3030 7465  00000500000000te
00000070: 7374 0000 3037 3037 3031 3030 3343 3038  st..070701003C08
00000080: 3935 3030 3030 3831 3830 3030 3030 3030  9500008180000000
00000090: 3030 3030 3030 3030 3030 3030 3030 3030  0000000000000000
000000a0: 3031 3632 3934 4135 4236 3030 3030 3030  016294A5B6000000
000000b0: 3031 3030 3030 3031 3033 3030 3030 3030  0100000103000000
000000c0: 3236 3030 3030 3030 3030 3030 3030 3030  2600000000000000
000000d0: 3030 3030 3030 3030 3046 3030 3030 3030  000000000F000000
000000e0: 3030 7465 7374 2f74 6573 742e 7478 7430  00test/test.txt0
000000f0: 0000 0000 0000 0000 3037 3037 3031 3030  ........07070100
00000100: 3343 3045 3844 3030 3030 3231 3830 3030  3C0E8D0000218000
00000110: 3030 3030 3030 3030 3030 3030 3030 3030  0000000000000000
00000120: 3030 3030 3031 3632 3934 4135 3530 3030  0000016294A55000
00000130: 3030 3030 3030 3030 3030 3031 3033 3030  0000000000010300
00000140: 3030 3030 3236 3030 3030 3030 3031 3030  0000260000000100
00000150: 3030 3030 3033 3030 3030 3030 3041 3030  0000030000000A00
00000160: 3030 3030 3030 7465 7374 2f6e 756c 6c00  000000test/null.
00000170: 3037 3037 3031 3030 3030 3030 3030 3030  0707010000000000
00000180: 3030 3030 3030 3030 3030 3030 3030 3030  0000000000000000
00000190: 3030 3030 3030 3030 3030 3030 3031 3030  0000000000000100
000001a0: 3030 3030 3030 3030 3030 3030 3030 3030  0000000000000000
000001b0: 3030 3030 3030 3030 3030 3030 3030 3030  0000000000000000
000001c0: 3030 3030 3030 3030 3030 3030 3030 3030  0000000000000000
000001d0: 3030 3030 3042 3030 3030 3030 3030 5452  00000B00000000TR
000001e0: 4149 4c45 5221 2121 0000 0000 0000 0000  AILER!!!........
000001f0: 0000 0000 0000 0000 0000 0000 0000 0000  ................

Имя директории отделено двумя нулевыми байтами, устройство - одним нулевым байтам. Да уж, можно ориентироваться на нулевые байты, но это явно не очень надёжно. Плюс, надо бы определять тип файла - директория или устройство. Обычные файлы определить можно запросто. А ещё это все идёт в одну строку, но это уже не такая большая проблема...

→ Ссылка