Добавить в архив Тар-архив

Я написал программу, генерирующую tarball, которая сжимается в zlib
. Через равные промежутки времени одна и та же программа должна добавлять новый файл в архив.

В соответствии с определением, tarball должен empty records (512 байтных блоков) для правильной работы в конце, что уже показывает мою проблему. /р>

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

Прямо сейчас я в своем уме. Аппендинг прекрасно работает с zlib, пока пустые записи не задействованы, но мне нужно, чтобы они «завершили» мой сжатый архив.

Есть идеи?

Ах да, было бы неплохо, если бы я мог избежать распаковки всего этого и /или анализа всего tarball.

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

4 голоса | спросил ATaylor 16 Jpm1000000pmWed, 16 Jan 2013 16:57:37 +040013 2013, 16:57:37

2 ответа


0

На мой взгляд, это невозможно, если TAR строго соответствует стандарту. Я прочитал zlib [1] руководство и GNU tar [2] спецификация файла. Я не нашел никакой информации о том, как добавить TAR. Поэтому я предполагаю, что это должно быть сделано путем перезаписи пустых блоков.

Итак, я полагаю, вы снова можете сделать это, используя gzseek(). Однако вам нужно знать, насколько велик размер несжатого архива (size) и установить offset в size-2*512. Обратите внимание, что это может быть громоздким, поскольку «параметр wherece определен как в lseek (2); значение SEEK_END не поддерживается.» 1 , и вы не можете одновременно открыть файл для чтения и записи, т. е. для внутреннего анализа, где находятся конечные блоки.

Однако , возможно, следует немного злоупотреблять спецификациями TAR. GNU tar [2] в документах упоминается что-то смешное :

» Каждый архивированный файл представлен блоком заголовка, который описывает файл, за которым следует ноль или более блоков, которые дают содержимое файла. В конце файла архива есть два 512-байтовых блока, заполненных двоичными нулями в качестве маркера конца файла. Разумная система должна писать такой маркер конца файла в конце архива, но не должна предполагать, что такой блок существует при чтении архива. В частности, GNU tar всегда выдает предупреждение, если не встречает его. «

Это означает, что вы можете сознательно не писать эти блоки. Это легко, если вы написали tar-архиватор. Затем вы можете использовать zlib в обычном режиме добавления, помня, что декомпрессор TAR должен знать о «сломанном» Файл TAR.

[1] http://www.zlib.net/manual.html#Gzip [2] http://www.gnu.org/software/тар /ручной /html_node /Standard.html # SEC182

ответил luk32 16 Jpm1000000pmWed, 16 Jan 2013 18:39:23 +040013 2013, 18:39:23
0

Это две отдельные проблемы, обе из которых решаемы.

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

Во-вторых, как часто добавлять в файл gzip. Самый простой подход - написать отдельные потоки gzip и объединить их. Запишите последние два 512-байтовых обнуленных блока в отдельном потоке gzip и запомните, где это начинается. Затем замените это новым потоком gzip новой записью tar, а затем другим потоком gzip с двумя конечными блоками. Это можно сделать, выполнив поиск в файле с помощью lseek(), а затем с помощью gzdopen() чтобы начать писать оттуда.

Это будет хорошо работать при хорошем сжатии для добавленных больших файлов (минимум несколько десятков К). Однако, если вы добавляете очень маленькие файлы, простая конкатенация небольших потоков gzip приведет к паршивому сжатию или, что еще хуже, расширению. Вы можете сделать что-то более сложное, чтобы фактически добавить небольшие объемы данных в один поток gzip, чтобы алгоритм сжатия мог использовать предыдущие данные для корреляции и сопоставления строк. Для этого взгляните на подход в gzlog.h и gzlog.c в examples/ в zlib .

Вот пример того, как сделать простой подход:

/* tapp.c -- Example of how to append to a tar.gz file with concatenated gzip
   streams. Placed in the public domain by Mark Adler, 16 Jan 2013. */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <unistd.h>
#include <fcntl.h>
#include "zlib.h"

#define local static

/* Build an allocated string with the prefix string and the NULL-terminated
   sequence of words strings separated by spaces.  The caller should free the
   returned string when done with it. */
local char *build_cmd(char *prefix, char **words)
{
    size_t len;
    char **scan;
    char *str, *next;

    len = strlen(prefix) + 1;
    for (scan = words; *scan != NULL; scan++)
        len += strlen(*scan) + 1;
    str = malloc(len);                                  assert(str != NULL);
    next = stpcpy(str, prefix);
    for (scan = words; *scan != NULL; scan++) {
        *next++ = ' ';
        next = stpcpy(next, *scan);
    }
    return str;
}

/* Usage:

      tapp archive.tar.gz addthis.file andthisfile.too

   tapp will create a new archive.tar.gz file if it doesn't exist, or it will
   append the files to the existing archive.tar.gz.  tapp must have been used
   to create the archive in the first place.  If it did not, then tapp will
   exit with an error and leave the file unchanged.  Each use of tapp appends a
   new gzip stream whose compression cannot benefit from the files already in
   the archive.  As a result, tapp should not be used to append a small amount
   of data at a time, else the compression will be particularly poor.  Since
   this is just an instructive example, the error checking is done mostly with
   asserts.
 */
int main(int argc, char **argv)
{
    int tgz;
    off_t offset;
    char *cmd;
    FILE *pipe;
    gzFile gz;
    int page;
    size_t got;
    int ret;
    ssize_t raw;
    unsigned char buf[3][512];
    const unsigned char z1k[] =     /* gzip stream of 1024 zeros */
        {0x1f, 0x8b, 8, 0, 0, 0, 0, 0, 2, 3, 0x63, 0x60, 0x18, 5, 0xa3, 0x60,
         0x14, 0x8c, 0x54, 0, 0, 0x2e, 0xaf, 0xb5, 0xef, 0, 4, 0, 0};

    if (argc < 2)
        return 0;
    tgz = open(argv[1], O_RDWR | O_CREAT, 0644);        assert(tgz != -1);
    offset = lseek(tgz, 0, SEEK_END);                   assert(offset == 0 || offset >= (off_t)sizeof(z1k));
    if (offset) {
        if (argc == 2) {
            close(tgz);
            return 0;
        }
        offset = lseek(tgz, -sizeof(z1k), SEEK_END);    assert(offset != -1);
        raw = read(tgz, buf, sizeof(z1k));              assert(raw == sizeof(z1k));
        if (memcmp(buf, z1k, sizeof(z1k)) != 0) {
            close(tgz);
            fprintf(stderr, "tapp abort: %s was not created by tapp\n", argv[1]);
            return 1;
        }
        offset = lseek(tgz, -sizeof(z1k), SEEK_END);    assert(offset != -1);
    }
    if (argc > 2) {
        gz = gzdopen(tgz, "wb");                        assert(gz != NULL);
        cmd = build_cmd("tar cf - -b 1", argv + 2);
        pipe = popen(cmd, "r");                         assert(pipe != NULL);
        free(cmd);
        got = fread(buf, 1, 1024, pipe);                assert(got == 1024);
        page = 2;
        while ((got = fread(buf[page], 1, 512, pipe)) == 512) {
            if (++page == 3)
                page = 0;
            ret = gzwrite(gz, buf[page], 512);          assert(ret == 512);
        }                                               assert(got == 0);
        ret = pclose(pipe);                             assert(ret != -1);
        ret = gzclose(gz);                              assert(ret == Z_OK);
        tgz = open(argv[1], O_WRONLY | O_APPEND);       assert(tgz != -1);
    }
    raw = write(tgz, z1k, sizeof(z1k));                 assert(raw == sizeof(z1k));
    close(tgz);
    return 0;
}
ответил Mark Adler 16 Jpm1000000pmWed, 16 Jan 2013 19:38:04 +040013 2013, 19:38:04

Похожие вопросы

Популярные теги

security × 330linux × 316macos × 2827 × 268performance × 244command-line × 241sql-server × 235joomla-3.x × 222java × 189c++ × 186windows × 180cisco × 168bash × 158c# × 142gmail × 139arduino-uno × 139javascript × 134ssh × 133seo × 132mysql × 132