Как в 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 шт):
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>
Можно было использовать 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 ................
Имя директории отделено двумя нулевыми байтами, устройство - одним нулевым байтам. Да уж, можно ориентироваться на нулевые байты, но это явно не очень надёжно. Плюс, надо бы определять тип файла - директория или устройство. Обычные файлы определить можно запросто. А ещё это все идёт в одну строку, но это уже не такая большая проблема...