Как разобрать tar-файл на C ++

Я хочу загрузить файл .tar с несколькими каталогами по 2 файла в каждом. Проблема в том, что я не могу найти способ прочитать tar-файл без фактического извлечения файлов (используя tar).

Идеальным решением будет что-то вроде:

#include <easytar>

Tarfile tar("somefile.tar");
std::string currentFile, currentFileName;
for(int i=0; i<tar.size(); i++){
  file = tar.getFileText(i);
  currentFileName = tar.getFileName(i);
  // do stuff with it
}

Мне, наверное, придется написать это самому, но любые идеи будут признательны ..


person Brendan Long    schedule 24.03.2010    source источник
comment
man tar говорит мне -t List archive contents to stdout. Ты этого хочешь?   -  person Potatoswatter    schedule 24.03.2010
comment
На самом деле я хочу обратного: читать tar-файл из stdin.   -  person Brendan Long    schedule 25.03.2010


Ответы (3)


Я понял это сам после небольшой работы. В спецификации tar-файла фактически указано все, что вам нужно знать.

Во-первых, каждый файл начинается с 512-байтового заголовка, поэтому вы можете представить его с помощью char [512] или char *, указывающих где-нибудь в вашем большом массиве char (например, если у вас есть весь файл, загруженный в один массив).

Заголовок выглядит так:

location  size  field
0         100   File name
100       8     File mode
108       8     Owner's numeric user ID
116       8     Group's numeric user ID
124       12    File size in bytes
136       12    Last modification time in numeric Unix time format
148       8     Checksum for header block
156       1     Link indicator (file type)
157       100   Name of linked file

Так что, если вам нужно имя файла, вы можете взять его прямо здесь с помощью string filename(buffer[0], 100);. Имя файла дополняется нулем, поэтому вы можете проверить, есть ли хотя бы один нуль, а затем оставить размер, если вы хотите сэкономить место.

Теперь мы хотим знать, файл это или папка. Эта информация содержится в поле «индикатор ссылки», поэтому:

// Note that we're comparing to ascii numbers, not ints
switch(buffer[156]){
    case '0': // intentionally dropping through
    case '\0':
        // normal file
        break;
    case '1':
        // hard link
        break;
    case '2':
        // symbolic link
        break;
    case '3':
        // device file/special file
        break;
    case '4':
        // block device
        break;
    case '5':
        // directory
        break;
    case '6':
        // named pipe
        break;
}

На данный момент у нас уже есть вся необходимая информация о каталогах, но нам нужно еще кое-что из обычных файлов: фактическое содержимое файла.

Длина файла может быть сохранена двумя разными способами: либо в виде восьмеричной строки с нулевым символом в конце, дополненной нулями или пробелами, либо в виде «кодировки base-256, которая указывается установкой старшего бита крайнего левого байта. числового поля ».

Числовые значения кодируются восьмеричными числами с использованием цифр ASCII с ведущими нулями. По историческим причинам следует использовать последний символ NUL или пробел. Таким образом, хотя для хранения размера файла зарезервировано 12 байтов, можно сохранить только 11 восьмеричных цифр. Это дает максимальный размер заархивированных файлов 8 гигабайт. Чтобы преодолеть это ограничение, в 2001 году компания Star ввела кодировку base-256, которая указывается установкой старшего бита крайнего левого байта числового поля. GNU-tar и BSD-tar последовали этой идее. Кроме того, в версиях tar, предшествующих первому стандарту POSIX 1988 г., значения добавляются пробелами вместо нулей.

Вот как вы читали бы восьмеричный формат, но я не писал код для версии base-256:

// in one function
int size_of_file = octal_string_to_int(&buffer[124], 11);

// elsewhere
int octal_string_to_int(char *current_char, unsigned int size){
    unsigned int output = 0;
    while(size > 0){
        output = output * 8 + *current_char - '0';
        current_char++;
        size--;
    }
    return output;
}

Итак, теперь у нас есть все, кроме фактического содержимого файла. Все, что нам нужно сделать, это взять следующие size байтов данных из tar-файла, и у нас будет содержимое нашего файла:

// Get to the next block after the header ends
location += 512;
file_contents = new char[size];
memcpy(file_contents, &buffer[location], size);
// Go to the next block by rounding up to 512
// This isn't necessarily the most efficient way to do this,
// but it's the most obvious.
location += (int)ceil(size / 512.0)
person Brendan Long    schedule 24.03.2010
comment
В настоящее время я использую ваш код, а для файлов tar, созданных с помощью Gnome File Roller, sizeOfFile = octalStringToInt (..., 11) в некоторых редких случаях кажется неправильным. Не могли бы вы указать, какая магия была упущена в 12-м байте? - person rodrigob; 17.06.2013
comment
@rodrigob Я правда не знаю. Если узнаешь, дай мне знать. - person Brendan Long; 29.06.2013
comment
Обратите внимание: если размер файла ровно 512 байт, тогда location = location + ((size / 512) + 1) * 512 пропустит следующий заголовок. - person Matvey Aksenov; 04.12.2013
comment
@MatveyAksenov Спасибо, исправлено. - person Brendan Long; 04.12.2013
comment
если размер равен 0 (например, для каталога), тогда я думаю, что эта строка продвинет дополнительные 512, когда этого не должно быть. Может быть условие: if (size > 0) { location = location + (((size - 1) / 512) + 1) * 512; } - person andy; 18.12.2013
comment
@andy Я сделал это проще, чтобы перестать думать о крайних случаях. Я предполагаю, что люди, которые используют это, могут найти более эффективный способ округления, если захотят. - person Brendan Long; 18.12.2013
comment
@rodrigob Это может быть немного поздно для вас, но, очевидно, есть какой-то формат base-256, который отличается первым битом поля размера. Я планирую изучить этот вопрос позже и написать код для его анализа. - person Brendan Long; 30.04.2014

Вы смотрели libtar?

Из информации о пакете fink:

libtar-1.2-1: API для работы с tar-файлами libtar - это библиотека C для управления tar-файлами POSIX. Он обрабатывает добавление и извлечение файлов в / из tar-архива. libtar предлагает следующие возможности:
* Гибкий API - вы можете манипулировать отдельными файлами или просто извлекать весь архив сразу.
* Позволяет заданные пользователем функции read () и write (), такие как zlib's gzread () и gzwrite ().
* Поддерживает форматы файлов tar POSIX 1003.1-1990 и GNU.

Не c ++ как таковой, но вы можете довольно легко создать ссылку на c ...

person dmckee --- ex-moderator kitten    schedule 24.03.2010
comment
@BrendanLong Король отстой - это преувеличение. - person Alexander Huszagh; 03.07.2016

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

person JackChen255    schedule 13.01.2016