Как разобрать tar-файл в C ++ - PullRequest
14 голосов
/ 24 марта 2010

Что я хочу сделать, это загрузить файл .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
}

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

Ответы [ 3 ]

31 голосов
/ 25 марта 2010

Я понял это сам после небольшой работы. Спецификация 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 году ввела кодировку 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)
12 голосов
/ 24 марта 2010

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

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

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

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

4 голосов
/ 13 января 2016

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

...