C++で libtarを使って tarファイルを生成する
オンメモリのデータを tarファイルに格納したものを生成するのに一時ファイルやサブプロセスを使わずに直接生成したい
2021年10月15日 嶋田大貴
libtar というライブラリを使うと、プログラムから直接 tarファイルを生成することができる。2013年までで開発が止まっているライブラリだが、一応主要な Linuxディストリビューションでは現在もパッケージが提供されているようだ。
下記サンプルプログラムでは、hello.txt というファイルが格納された hello.tar ファイルを生成する。
- hello.txtをメモリから一旦ファイルシステム上に書き出す
- サブプロセスとしてtarコマンドを呼び出して 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
記事へのコメントは Twitterにお願いします。
2021年10月15日 嶋田大貴