Added sandbox policy for extract function

This commit is contained in:
Andrei Medar 2020-09-16 15:57:31 +00:00
parent 9ec90b741c
commit 7e24ee2232
6 changed files with 237 additions and 169 deletions

View File

@ -23,6 +23,7 @@ target_link_libraries(helpers PUBLIC
sandbox2::fileops sandbox2::fileops
sandbox2::util sandbox2::util
sandbox2::file_base sandbox2::file_base
sandbox2::executor
glog::glog glog::glog
libarchive_sapi libarchive_sapi
) )

View File

@ -2,9 +2,10 @@
#include "helpers.h" #include "helpers.h"
std::string MakeAbsolutePathAtCWD(std::string path) { std::string MakeAbsolutePathAtCWD(std::string path) {
std::string result = sandbox2::file_util::fileops::MakeAbsolute(path, sandbox2::file_util::fileops::GetCWD()); std::string result = sandbox2::file_util::fileops::MakeAbsolute(
CHECK(result != "") << "Could not create absolute path for: " << path; path, sandbox2::file_util::fileops::GetCWD());
return sandbox2::file::CleanPath(result); CHECK(result != "") << "Could not create absolute path for: " << path;
return sandbox2::file::CleanPath(result);
} }
std::vector<std::string> MakeAbsolutePathsVec(char *argv[]) { std::vector<std::string> MakeAbsolutePathsVec(char *argv[]) {
@ -14,30 +15,33 @@ std::vector<std::string> MakeAbsolutePathsVec(char *argv[]) {
return arr; return arr;
} }
// std::string GetErrorString(sapi::v::Ptr *archive, LibarchiveSandbox &sandbox, LibarchiveApi &api) { // std::string GetErrorString(sapi::v::Ptr *archive, LibarchiveSandbox &sandbox,
// LibarchiveApi &api) {
// sapi::StatusOr<char *> ret = api.archive_error_string(archive); // sapi::StatusOr<char *> ret = api.archive_error_string(archive);
// CHECK(ret.ok() && ret) << "Could not get error message"; // CHECK(ret.ok() && ret) << "Could not get error message";
// sapi::StatusOr<std::string> ret2 = sandbox.GetCString(sapi::v::RemotePtr(ret.value())); // sapi::StatusOr<std::string> ret2 =
// CHECK(ret.ok()) << "Could not transfer error message"; // sandbox.GetCString(sapi::v::RemotePtr(ret.value())); CHECK(ret.ok()) <<
// return ret2.value(); // "Could not transfer error message"; return ret2.value();
// } // }
std::string CheckStatusAndGetString(const sapi::StatusOr<char *> &status,
LibarchiveSandbox &sandbox) {
CHECK(status.ok() && status.value() != NULL) << "Could not get error message";
std::string CheckStatusAndGetString(const sapi::StatusOr<char *> &status, LibarchiveSandbox &sandbox) { sapi::StatusOr<std::string> ret =
CHECK(status.ok() && status.value() != NULL) << "Could not get error message"; sandbox.GetCString(sapi::v::RemotePtr(status.value()));
CHECK(ret.ok()) << "Could not transfer error message";
sapi::StatusOr<std::string> ret = sandbox.GetCString(sapi::v::RemotePtr(status.value())); return ret.value();
CHECK(ret.ok()) << "Could not transfer error message";
return ret.value();
} }
// std::string CallFunctionAndGetString(sapi::v::Ptr *archive, LibarchiveSandbox &sandbox, // std::string CallFunctionAndGetString(sapi::v::Ptr *archive, LibarchiveSandbox
// LibarchiveApi *api, sapi::StatusOr<char *> (LibarchiveApi::*func)(sapi::v::Ptr *)) { // &sandbox, LibarchiveApi *api, sapi::StatusOr<char *>
// (LibarchiveApi::*func)(sapi::v::Ptr *)) {
// sapi::StatusOr<char *> ret = (api->*func)(archive); // sapi::StatusOr<char *> ret = (api->*func)(archive);
// CHECK(ret.ok() && ret) << "Could not get error message"; // CHECK(ret.ok() && ret) << "Could not get error message";
// sapi::StatusOr<std::string> ret2 = sandbox.GetCString(sapi::v::RemotePtr(ret.value())); // sapi::StatusOr<std::string> ret2 =
// CHECK(ret.ok()) << "Could not transfer error message"; // sandbox.GetCString(sapi::v::RemotePtr(ret.value())); CHECK(ret.ok()) <<
// return ret2.value(); // "Could not transfer error message"; return ret2.value();
// } // }

View File

@ -2,30 +2,30 @@
#define SAPI_LIBARCHIVE_HELPERS_H #define SAPI_LIBARCHIVE_HELPERS_H
#include <glog/logging.h> #include <glog/logging.h>
#include "libarchive_sapi.sapi.h"
#include "sandboxed_api/sandbox2/util.h" #include "sandboxed_api/sandbox2/util.h"
#include "sandboxed_api/sandbox2/util/fileops.h" #include "sandboxed_api/sandbox2/util/fileops.h"
#include "sandboxed_api/sandbox2/util/path.h" #include "sandboxed_api/sandbox2/util/path.h"
#include "libarchive_sapi.sapi.h"
// Used to convert the paths provided as arguments for the program // Used to convert the paths provided as arguments for the program
// (the paths used) to an array of absolute paths. This allows the user // (the paths used) to an array of absolute paths. This allows the user
// to use either relative or absolute paths // to use either relative or absolute paths
std::vector<std::string> MakeAbsolutePathsVec(char *argv[]); std::vector<std::string> MakeAbsolutePathsVec(char *argv[]);
// Converts only one string to an absolute path by prepending the current
// Converts only one string to an absolute path by prepending the current working // working directory to the relative path
// directory to the relative path
std::string MakeAbsolutePathAtCWD(std::string path); std::string MakeAbsolutePathAtCWD(std::string path);
// Calls the archive_error_string and returns the mesage after it was transferred // Calls the archive_error_string and returns the mesage after it was
// to the client process. // transferred to the client process. std::string GetErrorString(sapi::v::Ptr
// std::string GetErrorString(sapi::v::Ptr *archive, LibarchiveSandbox &sandbox, LibarchiveApi &api); // *archive, LibarchiveSandbox &sandbox, LibarchiveApi &api);
std::string CheckStatusAndGetString(const sapi::StatusOr<char *> &status,
LibarchiveSandbox &sandbox);
std::string CheckStatusAndGetString(const sapi::StatusOr<char *> &status, LibarchiveSandbox &sandbox); // std::string CallFunctionAndGetString(sapi::v::Ptr *archive, LibarchiveSandbox
// &sandbox, LibarchiveApi *api, sapi::StatusOr<char *>
// std::string CallFunctionAndGetString(sapi::v::Ptr *archive, LibarchiveSandbox &sandbox, // (LibarchiveApi::*func)(sapi::v::Ptr *));
// LibarchiveApi *api, sapi::StatusOr<char *> (LibarchiveApi::*func)(sapi::v::Ptr *));
#endif // SAPI_LIBARCHIVE_HELPERS_H #endif // SAPI_LIBARCHIVE_HELPERS_H

View File

@ -20,44 +20,51 @@ class SapiLibarchiveSandboxCreate : public LibarchiveSandbox {
class SapiLibarchiveSandboxExtract : public LibarchiveSandbox { class SapiLibarchiveSandboxExtract : public LibarchiveSandbox {
public: public:
// TODO // TODO
explicit SapiLibarchiveSandboxExtract(const std::string &archive_path, const int do_extract) explicit SapiLibarchiveSandboxExtract(const std::string& archive_path,
: archive_path_(archive_path), do_extract_(do_extract) {} const int do_extract)
: archive_path_(archive_path), do_extract_(do_extract) {}
private: private:
virtual void ModifyExecutor(sandbox2::Executor* executor) override { virtual void ModifyExecutor(sandbox2::Executor* executor) override {
// TODO create /output/ + chdir here if do_execute // TODO create /output/ + chdir here if do_execute
if (do_extract_) { if (do_extract_) {
// TODO change the directory // TODO change the directory
std::cout << "inside executor do extract" << std::endl; std::cout << "changing dir" << std::endl;
} else { if (!sandbox2::file_util::fileops::Exists("/output", false)) {
// Do nothing since we do not need to create any files CHECK(sandbox2::util::CreateDirRecursive("/output", 644))
<< "Could not create /output directory";
}
executor = &executor->set_cwd("/output");
} }
} }
std::unique_ptr<sandbox2::Policy> ModifyPolicy( std::unique_ptr<sandbox2::Policy> ModifyPolicy(
sandbox2::PolicyBuilder*) override { sandbox2::PolicyBuilder*) override {
auto policy = sandbox2::PolicyBuilder() // TODO no auto
.AllowRead() auto policy = sandbox2::PolicyBuilder()
.AllowWrite() .AllowRead()
.AllowOpen() .AllowWrite()
.AllowSystemMalloc() .AllowOpen()
.AllowGetIDs() .AllowSystemMalloc()
.AllowSafeFcntl() .AllowGetIDs()
.AllowStat() .AllowSafeFcntl()
.AllowExit() .AllowStat()
.AllowSyscalls({ .AllowExit()
__NR_futex, .AllowSyscalls({
__NR_lseek, __NR_futex,
__NR_close, __NR_lseek,
__NR_gettid, __NR_close,
__NR_umask, __NR_gettid,
}) __NR_umask,
.AddFile(archive_path_); })
.AddFile(archive_path_);
if (do_extract_) { if (do_extract_) {
// map "/output/" to cwd // map "/output/" to cwd
std::cout << "do extract inside policy" << std::endl; policy = policy.AddDirectoryAt(sandbox2::file_util::fileops::GetCWD(),
} "/output", false);
}
return policy.BuildOrDie(); return policy.BuildOrDie();
} }

View File

@ -5,14 +5,13 @@
int main() { int main() {
std::cout << "WORKS2" << std::endl; std::cout << "WORKS2" << std::endl;
LibarchiveSandbox sandbox; LibarchiveSandbox sandbox;
sandbox.Init().IgnoreError(); sandbox.Init().IgnoreError();
LibarchiveApi api(&sandbox); LibarchiveApi api(&sandbox);
if (api.archive_write_disk_new().ok()) {
std::cout << "OK" << std::endl;
}
if (api.archive_write_disk_new().ok()) {
std::cout << "OK" << std::endl;
}
return 0; return 0;
} }

View File

@ -125,14 +125,15 @@ static void create(const char *filename, int compress, const char **argv);
#endif #endif
static void errmsg(const char *); static void errmsg(const char *);
static void extract(const char *filename, int do_extract, int flags); static void extract(const char *filename, int do_extract, int flags);
static int copy_data(struct archive *, struct archive *); static int copy_data(sapi::v::RemotePtr *ar, sapi::v::RemotePtr *aw,
LibarchiveApi &api, SapiLibarchiveSandboxExtract &sandbox);
static void msg(const char *); static void msg(const char *);
static void usage(void); static void usage(void);
static int verbose = 0; static int verbose = 0;
int main(int argc, const char **argv) { int main(int argc, const char **argv) {
google::InitGoogleLogging(argv[0]); google::InitGoogleLogging(argv[0]);
std::cout << "BEGIN\n"; std::cout << "BEGIN\n";
const char *filename = NULL; const char *filename = NULL;
int compress, flags, mode, opt; int compress, flags, mode, opt;
@ -228,146 +229,202 @@ static void create(const char *filename, int compress, const char **argv) {
static void extract(const char *filename, int do_extract, int flags) { static void extract(const char *filename, int do_extract, int flags) {
std::cout << "extract" << std::endl; std::cout << "extract" << std::endl;
std::string filename_absolute = MakeAbsolutePathAtCWD(filename); std::string filename_absolute = MakeAbsolutePathAtCWD(filename);
std::cout << "filename = " << filename_absolute << std::endl; std::cout << "filename = " << filename_absolute << std::endl;
SapiLibarchiveSandboxExtract sandbox(filename_absolute, do_extract); SapiLibarchiveSandboxExtract sandbox(filename_absolute, do_extract);
CHECK(sandbox.Init().ok()) << "Error during sandbox initialization"; CHECK(sandbox.Init().ok()) << "Error during sandbox initialization";
LibarchiveApi api(&sandbox); LibarchiveApi api(&sandbox);
struct archive *a; struct archive *a;
struct archive *ext; struct archive *ext;
struct archive_entry *entry; struct archive_entry *entry;
int r; int r;
api.archive_read_new().IgnoreError(); api.archive_read_new().IgnoreError();
sapi::StatusOr<archive *> ret = api.archive_read_new(); sapi::StatusOr<archive *> ret = api.archive_read_new();
CHECK(ret.ok()) << "archive_read_new call failed"; CHECK(ret.ok()) << "archive_read_new call failed";
// std::cout << "RET VALUE = " << ret.value() << std::endl; // std::cout << "RET VALUE = " << ret.value() << std::endl;
CHECK(ret.value() != NULL) << "Failed to create read archive"; CHECK(ret.value() != NULL) << "Failed to create read archive";
a = ret.value(); a = ret.value();
ret = api.archive_write_disk_new(); ret = api.archive_write_disk_new();
CHECK(ret.ok()) << "write_disk_new call failed"; CHECK(ret.ok()) << "write_disk_new call failed";
CHECK(ret.value() != NULL) << "Failed to create write disk archive"; CHECK(ret.value() != NULL) << "Failed to create write disk archive";
ext = ret.value(); ext = ret.value();
sapi::v::RemotePtr a_ptr(a); sapi::v::RemotePtr a_ptr(a);
sapi::v::RemotePtr ext_ptr(ext); sapi::v::RemotePtr ext_ptr(ext);
sapi::StatusOr<int> ret2; sapi::StatusOr<int> ret2;
ret2 = api.archive_write_disk_set_options(&ext_ptr, flags); ret2 = api.archive_write_disk_set_options(&ext_ptr, flags);
CHECK(ret2.ok()) << "write_disk_set_options call failed"; CHECK(ret2.ok()) << "write_disk_set_options call failed";
CHECK(ret2.value() != ARCHIVE_FATAL) << "Unexpected result from write_disk_set_options call"; CHECK(ret2.value() != ARCHIVE_FATAL)
<< "Unexpected result from write_disk_set_options call";
#ifndef NO_BZIP2_EXTRACT #ifndef NO_BZIP2_EXTRACT
ret2 = api.archive_read_support_filter_bzip2(&a_ptr); ret2 = api.archive_read_support_filter_bzip2(&a_ptr);
CHECK(ret2.ok()) << "read_support_filter_bzip2 call failed"; CHECK(ret2.ok()) << "read_support_filter_bzip2 call failed";
CHECK(ret2.value() != ARCHIVE_FATAL) << "Unexpected result from read_support_filter_bzip2 call"; CHECK(ret2.value() != ARCHIVE_FATAL)
<< "Unexpected result from read_support_filter_bzip2 call";
#endif #endif
#ifndef NO_GZIP_EXTRACT #ifndef NO_GZIP_EXTRACT
ret2 = api.archive_read_support_filter_gzip(&a_ptr); ret2 = api.archive_read_support_filter_gzip(&a_ptr);
CHECK(ret2.ok()) << "read_suppport_filter_gzip call failed"; CHECK(ret2.ok()) << "read_suppport_filter_gzip call failed";
CHECK(ret2.value() != ARCHIVE_FATAL) << "Unexpected result from read_suppport_filter_gzip call"; CHECK(ret2.value() != ARCHIVE_FATAL)
<< "Unexpected result from read_suppport_filter_gzip call";
#endif #endif
#ifndef NO_COMPRESS_EXTRACT #ifndef NO_COMPRESS_EXTRACT
ret2 = api.archive_read_support_filter_compress(&a_ptr); ret2 = api.archive_read_support_filter_compress(&a_ptr);
CHECK(ret2.ok()) << "read_support_filter_compress call failed"; CHECK(ret2.ok()) << "read_support_filter_compress call failed";
CHECK(ret2.value() != ARCHIVE_FATAL) << "Unexpected result from read_support_filter_compress call"; CHECK(ret2.value() != ARCHIVE_FATAL)
<< "Unexpected result from read_support_filter_compress call";
#endif #endif
#ifndef NO_TAR_EXTRACT #ifndef NO_TAR_EXTRACT
ret2 = api.archive_read_support_format_tar(&a_ptr); ret2 = api.archive_read_support_format_tar(&a_ptr);
CHECK(ret2.ok()) << "read_support_format_tar call failed"; CHECK(ret2.ok()) << "read_support_format_tar call failed";
CHECK(ret2.value() != ARCHIVE_FATAL) << "Unexpected result fromread_support_format_tar call"; CHECK(ret2.value() != ARCHIVE_FATAL)
<< "Unexpected result fromread_support_format_tar call";
#endif #endif
#ifndef NO_CPIO_EXTRACT #ifndef NO_CPIO_EXTRACT
ret2 = api.archive_read_support_format_cpio(&a_ptr); ret2 = api.archive_read_support_format_cpio(&a_ptr);
CHECK(ret2.ok()) << "read_support_format_cpio call failed"; CHECK(ret2.ok()) << "read_support_format_cpio call failed";
CHECK(ret2.value() != ARCHIVE_FATAL) << "Unexpected result from read_support_format_tar call"; CHECK(ret2.value() != ARCHIVE_FATAL)
<< "Unexpected result from read_support_format_tar call";
#endif #endif
#ifndef NO_LOOKUP #ifndef NO_LOOKUP
ret2 = api.archive_write_disk_set_standard_lookup(&ext_ptr); ret2 = api.archive_write_disk_set_standard_lookup(&ext_ptr);
CHECK(ret2.ok()) << "write_disk_set_standard_lookup call failed"; CHECK(ret2.ok()) << "write_disk_set_standard_lookup call failed";
CHECK(ret2.value() != ARCHIVE_FATAL) << "Unexpected result from write_disk_set_standard_lookup call"; CHECK(ret2.value() != ARCHIVE_FATAL)
<< "Unexpected result from write_disk_set_standard_lookup call";
#endif #endif
if (filename != NULL && strcmp(filename, "-") == 0) filename = NULL;
if (filename != NULL && strcmp(filename, "-") == 0) sapi::v::ConstCStr sapi_filename(filename_absolute.c_str());
filename = NULL;
sapi::v::ConstCStr sapi_filename(filename_absolute.c_str()); std::cout << "opening filename" << std::endl;
std::cout << "opening filename" << std::endl; ret2 =
api.archive_read_open_filename(&a_ptr, sapi_filename.PtrBefore(), 10240);
CHECK(ret2.ok()) << "read_open_filename call failed";
// CHECK(!ret2.value()) << GetErrorString(&a_ptr, sandbox, api);
CHECK(!ret2.value()) << CheckStatusAndGetString(
api.archive_error_string(&a_ptr), sandbox);
// CHECK(!ret2.value()) << CallFunctionAndGetString(&a_ptr, sandbox, &api,
// &api.archive_error_string);
ret2 = api.archive_read_open_filename(&a_ptr, sapi_filename.PtrBefore(), 10240); for (;;) {
CHECK(ret2.ok()) << "read_open_filename call failed"; int needcr = 0;
// CHECK(!ret2.value()) << GetErrorString(&a_ptr, sandbox, api); std::cout << "================reading headers==============" << std::endl;
CHECK(!ret2.value()) << CheckStatusAndGetString(api.archive_error_string(&a_ptr), sandbox); sapi::v::IntBase<struct archive_entry *> entry_ptr_tmp(0);
// CHECK(!ret2.value()) << CallFunctionAndGetString(&a_ptr, sandbox, &api, &api.archive_error_string);
sapi::v::IntBase<struct archive_entry *> entry_ptr_tmp(0); ret2 = api.archive_read_next_header(&a_ptr, entry_ptr_tmp.PtrBoth());
// std::cout << "val = " << ret2.value() << std::endl;
CHECK(ret2.ok()) << "read_next_header call failed";
for (;;) { // CHECK(ret2.value() != ARCHIVE_OK) << GetErrorString(&a_ptr, sandbox,
// api);
std::cout << "================reading headers==============" << std::endl;
ret2 = api.archive_read_next_header(&a_ptr, entry_ptr_tmp.PtrBoth());
//std::cout << "val = " << ret2.value() << std::endl;
CHECK(ret2.ok()) << "read_next_header call failed";
// CHECK(ret2.value() != ARCHIVE_OK) << GetErrorString(&a_ptr, sandbox, api);
if(ret2.value() == ARCHIVE_EOF) {
break;
}
CHECK(ret2.value() == ARCHIVE_OK) << CheckStatusAndGetString(api.archive_error_string(&a_ptr), sandbox);
sapi::v::RemotePtr entry_ptr(entry_ptr_tmp.GetValue());
if(verbose && do_extract) {
std::cout << "x " ;
}
if (verbose || !do_extract) {
std::cout << CheckStatusAndGetString(api.archive_entry_pathname(&entry_ptr), sandbox) << " ";
}
if (do_extract) {
std::cout << "EXTRACT HERE";
}
// use the needcr stuff here TODO
std::cout << std::endl;
if (ret2.value() == ARCHIVE_EOF) {
break;
} }
std::cout << "out of loop" << std::endl; CHECK(ret2.value() == ARCHIVE_OK)
<< CheckStatusAndGetString(api.archive_error_string(&a_ptr), sandbox);
ret2 = api.archive_read_close(&a_ptr); sapi::v::RemotePtr entry_ptr(entry_ptr_tmp.GetValue());
CHECK(ret2.ok()) << "read_close call failed";
CHECK(!ret2.value()) << "Unexpected value from read_close call";
ret2 = api.archive_read_free(&a_ptr); if (verbose && do_extract) {
CHECK(ret2.ok()) << "read_free call failed"; std::cout << "x ";
CHECK(!ret2.value()) << "Unexpected result from read_free call"; }
if (verbose || !do_extract) {
std::cout << CheckStatusAndGetString(
api.archive_entry_pathname(&entry_ptr), sandbox)
<< " ";
needcr = 1;
}
ret2 = api.archive_write_close(&ext_ptr); std::cout << "qqqqq" << std::endl;
CHECK(ret2.ok()) << "write_close call failed";
CHECK(!ret2.value()) << "Unexpected result from write_close call";
if (do_extract) {
std::cout << "EXTRACT HERE" << std::endl;
ret2 = api.archive_write_header(&ext_ptr, &entry_ptr);
CHECK(ret2.ok()) << "write_header call faield";
ret2 = api.archive_write_free(&ext_ptr); std::cout << "val = " << ret2.value() << std::endl;
CHECK(ret2.ok()) << "write_free call failed";
CHECK(!ret2.value()) << "Unexpected result from write_free call";
if (ret2.value() != ARCHIVE_OK) {
std::cout << CheckStatusAndGetString(api.archive_error_string(&a_ptr),
sandbox);
needcr = 1;
} else if (copy_data(&a_ptr, &ext_ptr, api, sandbox) != ARCHIVE_OK) {
needcr = 1;
}
}
// use the needcr stuff here TODO
if (needcr) {
std::cout << std::endl;
}
}
std::cout << "out of loop" << std::endl;
ret2 = api.archive_read_close(&a_ptr);
CHECK(ret2.ok()) << "read_close call failed";
CHECK(!ret2.value()) << "Unexpected value from read_close call";
ret2 = api.archive_read_free(&a_ptr);
CHECK(ret2.ok()) << "read_free call failed";
CHECK(!ret2.value()) << "Unexpected result from read_free call";
ret2 = api.archive_write_close(&ext_ptr);
CHECK(ret2.ok()) << "write_close call failed";
CHECK(!ret2.value()) << "Unexpected result from write_close call";
ret2 = api.archive_write_free(&ext_ptr);
CHECK(ret2.ok()) << "write_free call failed";
CHECK(!ret2.value()) << "Unexpected result from write_free call";
} }
static int copy_data(struct archive *ar, struct archive *aw) { static int copy_data(sapi::v::RemotePtr *ar, sapi::v::RemotePtr *aw,
return 0; LibarchiveApi &api,
SapiLibarchiveSandboxExtract &sandbox) {
std::cout << "CALL COPY_DATA XXXXXXXXXXXX\n";
sapi::StatusOr<int> ret;
sapi::v::IntBase<struct archive_entry *> buff_ptr_tmp(0);
sapi::v::ULLong size;
sapi::v::SLLong offset;
for (;;) {
ret = api.archive_read_data_block(ar, buff_ptr_tmp.PtrBoth(),
size.PtrBoth(), offset.PtrBoth());
CHECK(ret.ok()) << "read_data_block call failed";
if (ret.value() == ARCHIVE_EOF) {
return ARCHIVE_OK;
}
if (ret.value() != ARCHIVE_OK) {
std::cout << CheckStatusAndGetString(api.archive_error_string(ar),
sandbox);
return ret.value();
}
sapi::v::RemotePtr buff_ptr(buff_ptr_tmp.GetValue());
ret = api.archive_write_data_block(aw, &buff_ptr, size.GetValue(),
offset.GetValue());
CHECK(ret.ok()) << "write_data_block call failed";
if (ret.value() != ARCHIVE_OK) {
std::cout << CheckStatusAndGetString(api.archive_error_string(ar),
sandbox);
return ret.value();
}
}
} }
static void msg(const char *m) { write(1, m, strlen(m)); } static void msg(const char *m) { write(1, m, strlen(m)); }