diff --git a/include/dwarfs/utility/filesystem_extractor.h b/include/dwarfs/utility/filesystem_extractor.h index e4a03fed..971a9dee 100644 --- a/include/dwarfs/utility/filesystem_extractor.h +++ b/include/dwarfs/utility/filesystem_extractor.h @@ -38,6 +38,7 @@ namespace dwarfs { +class file_access; class glob_matcher; class library_dependencies; class logger; @@ -67,7 +68,8 @@ struct filesystem_extractor_archive_format { class filesystem_extractor { public: - filesystem_extractor(logger& lgr, os_access const& os); + filesystem_extractor(logger& lgr, os_access const& os, + std::shared_ptr fa = nullptr); static void add_library_dependencies(library_dependencies& deps); diff --git a/src/utility/filesystem_extractor.cpp b/src/utility/filesystem_extractor.cpp index cda75c22..b4ce5056 100644 --- a/src/utility/filesystem_extractor.cpp +++ b/src/utility/filesystem_extractor.cpp @@ -53,6 +53,7 @@ #include #include +#include #include #include #include @@ -112,9 +113,11 @@ class archive_error : public std::runtime_error { template class filesystem_extractor_ final : public filesystem_extractor::impl { public: - explicit filesystem_extractor_(logger& lgr, os_access const& os) + explicit filesystem_extractor_(logger& lgr, os_access const& os, + std::shared_ptr fa) : LOG_PROXY_INIT(lgr) - , os_{os} {} + , os_{os} + , fa_{std::move(fa)} {} ~filesystem_extractor_() override { try { @@ -139,13 +142,13 @@ class filesystem_extractor_ final : public filesystem_extractor::impl { configure_format(format, &output); -#ifdef _WIN32 - check_result(::archive_write_open_filename_w( - a_, output.empty() ? nullptr : output.wstring().c_str())); -#else - check_result(::archive_write_open_filename( - a_, output.empty() ? nullptr : output.string().c_str())); -#endif + if (output.empty()) { + check_result(::archive_write_open_filename(a_, nullptr)); + } else { + out_ = fa_->open_output_binary(output); + check_result(::archive_write_open2(a_, this, nullptr, on_stream_write, + on_stream_close, on_stream_free)); + } #endif } @@ -219,6 +222,26 @@ class filesystem_extractor_ final : public filesystem_extractor::impl { filesystem_extractor_options const& opts) override; private: + static la_ssize_t on_stream_write(struct archive* /*a*/, void* client_data, + void const* buffer, size_t length) { + auto self = static_cast(client_data); + auto& os = self->out_->os(); + os.write(static_cast(buffer), length); + return os.good() ? static_cast(length) : -1; + } + + static int on_stream_close(struct archive* /*a*/, void* client_data) { + auto self = static_cast(client_data); + self->out_->close(); + return ARCHIVE_OK; + } + + static int on_stream_free(struct archive* /*a*/, void* client_data) { + auto self = static_cast(client_data); + self->out_.reset(); + return ARCHIVE_OK; + } + void configure_format(filesystem_extractor_archive_format const& format [[maybe_unused]], std::filesystem::path const* output @@ -301,7 +324,9 @@ class filesystem_extractor_ final : public filesystem_extractor::impl { LOG_PROXY_DECL(debug_logger_policy); os_access const& os_; + std::shared_ptr fa_; struct ::archive* a_{nullptr}; + std::unique_ptr out_; std::array pipefd_{-1, -1}; std::unique_ptr iot_; }; @@ -560,10 +585,12 @@ std::string filesystem_extractor_archive_format::description() const { return desc; } -filesystem_extractor::filesystem_extractor(logger& lgr, os_access const& os) +filesystem_extractor::filesystem_extractor( + logger& lgr, os_access const& os, std::shared_ptr fa) : impl_(make_unique_logging_object(lgr, os)) {} + logger_policies>(lgr, os, + std::move(fa))) {} void filesystem_extractor::add_library_dependencies( library_dependencies& deps) { diff --git a/test/tool_main_test.cpp b/test/tool_main_test.cpp index 755d7f81..4e7305ba 100644 --- a/test/tool_main_test.cpp +++ b/test/tool_main_test.cpp @@ -2289,6 +2289,30 @@ TEST(dwarfsextract_test, filters) { EXPECT_EQ(ARCHIVE_OK, ::archive_read_free(ar)) << ::archive_error_string(ar); } +TEST(dwarfsextract_test, auto_format) { + auto t = dwarfsextract_tester::create_with_image(); + ASSERT_EQ(0, t.run({"-i", "image.dwarfs", "-f", "auto", "-o", "image.tar"})) + << t.err(); + + auto out = t.fa->get_file("image.tar").value(); + + auto ar = ::archive_read_new(); + ASSERT_EQ(ARCHIVE_OK, ::archive_read_support_format_all(ar)) + << ::archive_error_string(ar); + ASSERT_EQ(ARCHIVE_OK, ::archive_read_open_memory(ar, out.data(), out.size())) + << ::archive_error_string(ar); + + struct archive_entry* entry; + int ret = ::archive_read_next_header(ar, &entry); + + EXPECT_EQ(ARCHIVE_OK, ret) << ::archive_error_string(ar); + auto fmt = ::archive_format(ar); + EXPECT_EQ(ARCHIVE_FORMAT_TAR, fmt & ARCHIVE_FORMAT_BASE_MASK) << fmt::format( + "expected TAR ({:08x}), got {:08x}", ARCHIVE_FORMAT_TAR, fmt); + + EXPECT_EQ(ARCHIVE_OK, ::archive_read_free(ar)) << ::archive_error_string(ar); +} + TEST(dwarfsextract_test, patterns) { auto mkdt = mkdwarfs_tester::create_empty(); mkdt.add_test_file_tree(); diff --git a/tools/src/dwarfsextract_main.cpp b/tools/src/dwarfsextract_main.cpp index b0eef3c7..2c0942b8 100644 --- a/tools/src/dwarfsextract_main.cpp +++ b/tools/src/dwarfsextract_main.cpp @@ -211,7 +211,7 @@ int dwarfsextract_main(int argc, sys_char** argv, iolayer const& iol) { #endif reader::filesystem_v2_lite fs(lgr, *iol.os, fs_image, fsopts, perfmon); - utility::filesystem_extractor fsx(lgr, *iol.os); + utility::filesystem_extractor fsx(lgr, *iol.os, iol.file); #ifndef DWARFS_FILESYSTEM_EXTRACTOR_NO_OPEN_FORMAT if (format.name.empty()) {