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

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

[libtar](https://repo.or.cz/w/libtar.git) というライブラリを使うと、プログラムから直接 tarファイルを生成することができる。2013年までで開発が止まっているライブラリだが、一応主要な Linuxディストリビューションでは現在もパッケージが提供されているようだ。 下記サンプルプログラムでは、hello.txt というファイルが格納された hello.tar ファイルを生成する。 - hello.txtをメモリから一旦ファイルシステム上に書き出す - サブプロセスとしてtarコマンドを呼び出して hello.tarを生成させる という処理を**回避して**直接 hello.tar ファイルだけを出力している。 ```c++ #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 ``` ![コンパイルと実行の様子](libtar.png?classes=img-fluid)