C++で libtarを使って tarファイルを生成する

オンメモリのデータを tarファイルに格納したものを生成するのに一時ファイルやサブプロセスを使わずに直接生成したい

2021年10月15日 嶋田大貴

libtar というライブラリを使うと、プログラムから直接 tarファイルを生成することができる。2013年までで開発が止まっているライブラリだが、一応主要な Linuxディストリビューションでは現在もパッケージが提供されているようだ。

下記サンプルプログラムでは、hello.txt というファイルが格納された hello.tar ファイルを生成する。

という処理を回避して直接 hello.tar ファイルだけを出力している。

#include <fcntl.h>
#include <string.h>

#include <stdexcept>
#include <memory>

#include <libtar.h>

// write whole file content to tar file
// last block's padding is taken care of
int tar_content_write(TAR* tar, const void* data, size_t len) {
    uint8_t buf[T_BLOCKSIZE];
    uint8_t* current_pos = buf;

    const uint8_t* data_pos = (const uint8_t*)data;
    int cnt = 0;
    while (data_pos < (const uint8_t*)data + len) {
        size_t l = std::min((const uint8_t*)data + len - data_pos, buf + T_BLOCKSIZE - current_pos);
        memcpy(current_pos, data_pos, l);
        current_pos += l;
        data_pos += l;
        if (current_pos == buf + T_BLOCKSIZE) {
            auto r = tar_block_write(tar, buf);
            current_pos = buf;
            if (r > 0) {
                cnt += r;
            } else {
                return r;
            }
        }
    }

    if (current_pos == buf) return cnt;
    //else
    memset(current_pos, 0, buf + T_BLOCKSIZE - current_pos);
    auto r = tar_block_write(tar, buf);
    return (r >= 0)? cnt + r : r;
}

int main()
{
    // create new tar file
    TAR* _tar;
    if (tar_open(&_tar, "hello.tar", NULL, O_WRONLY|O_CREAT|O_TRUNC, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH, 0) < 0) {
        throw std::runtime_error("tar_open() failed");
    }

    // make it shared_ptr to be deleted automatically
    std::shared_ptr<TAR> tar(_tar, tar_close);

    // generate sample text content
    std::string content;
    for (int i = 0; i < 100; i++) {
        content += "hello, wolrd!\n";
    }

    // append the in-memory content to tar archive as a regular file
    // file metadata
    memset(&(tar->th_buf), 0, sizeof(tar->th_buf));
    th_set_path(tar.get(), "hello.txt");
    th_set_size(tar.get(), content.length());
    th_set_type(tar.get(), S_IFREG);
    th_set_mode(tar.get(), S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
    th_set_user(tar.get(), getuid());
    th_set_group(tar.get(), getgid());
    th_set_mtime(tar.get(), time(NULL));
    if (th_write(tar.get()) < 0) throw std::runtime_error("th_write() failed");

    // file content following metadata
    if (tar_content_write(tar.get(), content.c_str(), content.length()) < 0) {
        throw std::runtime_error("tar_content_write() failed");
    }
    // a regular file entry ends here

    // Mark the end of tar file
    if (tar_append_eof(tar.get()) < 0) throw std::runtime_error("tar_append_eof() failed");

    return 0;
}

// g++ -std=c++20 -o tartest tartest.cpp -ltar

コンパイルと実行の様子

2021年10月15日 嶋田大貴

記事一覧へ戻る