diff --git a/CMakeLists.txt b/CMakeLists.txt
index a47b656d..cb8f133c 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -378,6 +378,7 @@ list(
src/dwarfs/entry.cpp
src/dwarfs/error.cpp
src/dwarfs/features.cpp
+ src/dwarfs/file_access_generic.cpp
src/dwarfs/file_scanner.cpp
src/dwarfs/file_stat.cpp
src/dwarfs/file_type.cpp
diff --git a/include/dwarfs/file_access.h b/include/dwarfs/file_access.h
new file mode 100644
index 00000000..521d73df
--- /dev/null
+++ b/include/dwarfs/file_access.h
@@ -0,0 +1,49 @@
+/* vim:set ts=2 sw=2 sts=2 et: */
+/**
+ * \author Marcus Holland-Moritz (github@mhxnet.de)
+ * \copyright Copyright (c) Marcus Holland-Moritz
+ *
+ * This file is part of dwarfs.
+ *
+ * dwarfs is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * dwarfs is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with dwarfs. If not, see .
+ */
+
+#pragma once
+
+#include
+#include
+#include
+#include
+
+namespace dwarfs {
+
+class output_stream {
+ public:
+ virtual ~output_stream() = default;
+
+ virtual std::ostream& os() = 0;
+ virtual void close(std::error_code& ec) = 0;
+};
+
+class file_access {
+ public:
+ virtual ~file_access() = default;
+
+ virtual bool exists(std::filesystem::path const& path) const = 0;
+ virtual std::unique_ptr
+ open_output_binary(std::filesystem::path const& path,
+ std::error_code& ec) const = 0;
+};
+
+} // namespace dwarfs
diff --git a/include/dwarfs/file_access_generic.h b/include/dwarfs/file_access_generic.h
new file mode 100644
index 00000000..d8cd6f6f
--- /dev/null
+++ b/include/dwarfs/file_access_generic.h
@@ -0,0 +1,32 @@
+/* vim:set ts=2 sw=2 sts=2 et: */
+/**
+ * \author Marcus Holland-Moritz (github@mhxnet.de)
+ * \copyright Copyright (c) Marcus Holland-Moritz
+ *
+ * This file is part of dwarfs.
+ *
+ * dwarfs is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * dwarfs is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with dwarfs. If not, see .
+ */
+
+#pragma once
+
+#include
+
+namespace dwarfs {
+
+class file_access;
+
+std::unique_ptr create_file_access_generic();
+
+} // namespace dwarfs
diff --git a/include/dwarfs/iolayer.h b/include/dwarfs/iolayer.h
index 938b9635..2806ad99 100644
--- a/include/dwarfs/iolayer.h
+++ b/include/dwarfs/iolayer.h
@@ -26,6 +26,7 @@
namespace dwarfs {
+class file_access;
class os_access;
class terminal;
@@ -34,6 +35,7 @@ struct iolayer {
std::shared_ptr os;
std::shared_ptr term;
+ std::shared_ptr file;
std::istream& in;
std::ostream& out;
std::ostream& err;
diff --git a/src/dwarfs/file_access_generic.cpp b/src/dwarfs/file_access_generic.cpp
new file mode 100644
index 00000000..82301c0d
--- /dev/null
+++ b/src/dwarfs/file_access_generic.cpp
@@ -0,0 +1,91 @@
+/* vim:set ts=2 sw=2 sts=2 et: */
+/**
+ * \author Marcus Holland-Moritz (github@mhxnet.de)
+ * \copyright Copyright (c) Marcus Holland-Moritz
+ *
+ * This file is part of dwarfs.
+ *
+ * dwarfs is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * dwarfs is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with dwarfs. If not, see .
+ */
+
+#include
+#include
+#include
+
+#ifdef _WIN32
+#include
+#endif
+
+#include "dwarfs/file_access.h"
+#include "dwarfs/file_access_generic.h"
+#include "dwarfs/util.h"
+
+namespace dwarfs {
+
+namespace {
+
+void assign_error_code(std::error_code& ec) {
+#ifdef _WIN32
+ ec.assign(::GetLastError(), std::system_category());
+#else
+ ec.assign(errno, std::generic_category());
+#endif
+}
+
+class file_output_stream : public output_stream {
+ public:
+ file_output_stream(std::filesystem::path const& path, std::error_code& ec)
+ : os_{path.string().c_str(), std::ios::binary | std::ios::trunc} {
+ if (os_.bad() || os_.fail() || !os_.is_open()) {
+ assign_error_code(ec);
+ }
+ }
+
+ std::ostream& os() override { return os_; }
+
+ void close(std::error_code& ec) override {
+ os_.close();
+ if (os_.bad()) {
+ assign_error_code(ec);
+ }
+ }
+
+ private:
+ std::ofstream os_;
+};
+
+class file_access_generic : public file_access {
+ public:
+ bool exists(std::filesystem::path const& path) const override {
+ return std::filesystem::exists(path);
+ }
+
+ std::unique_ptr
+ open_output_binary(std::filesystem::path const& path,
+ std::error_code& ec) const override {
+ auto rv = std::make_unique(path, ec);
+ if (ec) {
+ rv.reset();
+ }
+ return rv;
+ }
+};
+
+} // namespace
+
+std::unique_ptr create_file_access_generic() {
+ return std::make_unique();
+}
+
+} // namespace dwarfs
diff --git a/src/dwarfs/iolayer.cpp b/src/dwarfs/iolayer.cpp
index 7d55a0ea..f8e5493a 100644
--- a/src/dwarfs/iolayer.cpp
+++ b/src/dwarfs/iolayer.cpp
@@ -21,6 +21,8 @@
#include
+#include "dwarfs/file_access.h"
+#include "dwarfs/file_access_generic.h"
#include "dwarfs/iolayer.h"
#include "dwarfs/os_access_generic.h"
#include "dwarfs/terminal.h"
@@ -31,6 +33,7 @@ iolayer const& iolayer::system_default() {
static iolayer const iol{
.os = std::make_shared(),
.term = terminal::create(),
+ .file = create_file_access_generic(),
.in = std::cin,
.out = std::cout,
.err = std::cerr,
diff --git a/src/mkdwarfs_main.cpp b/src/mkdwarfs_main.cpp
index d304a252..0f551631 100644
--- a/src/mkdwarfs_main.cpp
+++ b/src/mkdwarfs_main.cpp
@@ -37,6 +37,7 @@
#include
#include
#include
+#include
#include
#ifdef _WIN32
@@ -63,6 +64,7 @@
#include "dwarfs/console_writer.h"
#include "dwarfs/entry.h"
#include "dwarfs/error.h"
+#include "dwarfs/file_access.h"
#include "dwarfs/filesystem_block_category_resolver.h"
#include "dwarfs/filesystem_v2.h"
#include "dwarfs/filesystem_writer.h"
@@ -74,6 +76,7 @@
#include "dwarfs/mmap.h"
#include "dwarfs/options.h"
#include "dwarfs/options_interface.h"
+#include "dwarfs/overloaded.h"
#include "dwarfs/program_options_helpers.h"
#include "dwarfs/progress.h"
#include "dwarfs/scanner.h"
@@ -1035,33 +1038,37 @@ int mkdwarfs_main(int argc, sys_char** argv, iolayer const& iol) {
std::filesystem::path output(output_str);
- std::unique_ptr os;
+ std::variant,
+ std::ostringstream>
+ os;
if (!options.debug_filter_function) {
if (output != "-") {
- if (std::filesystem::exists(output) && !force_overwrite) {
+ if (iol.file->exists(output) && !force_overwrite) {
iol.err
<< "error: output file already exists, use --force to overwrite\n";
return 1;
}
- auto ofs = std::make_unique(output, std::ios::binary |
- std::ios::trunc);
+ std::error_code ec;
+ auto stream = iol.file->open_output_binary(output, ec);
- if (ofs->bad() || !ofs->is_open()) {
+ if (ec) {
iol.err << "error: cannot open output file '" << output
- << "': " << ::strerror(errno) << "\n";
+ << "': " << ec.message() << "\n";
return 1;
}
- os = std::move(ofs);
+ assert(stream);
+
+ os.emplace>(std::move(stream));
} else {
#ifdef _WIN32
::_setmode(::_fileno(stdout), _O_BINARY);
#endif
}
} else {
- os = std::make_unique();
+ os.emplace();
}
options.enable_history = !no_history;
@@ -1173,9 +1180,18 @@ int mkdwarfs_main(int argc, sys_char** argv, iolayer const& iol) {
std::unique_ptr fsw;
try {
+ std::ostream& fsw_os = std::visit(
+ overloaded(
+ [&](std::monostate) -> std::ostream& { return iol.out; },
+ [&](std::unique_ptr& os) -> std::ostream& {
+ return os->os();
+ },
+ [&](std::ostringstream& oss) -> std::ostream& { return oss; }),
+ os);
+
fsw = std::make_unique(
- os ? *os : iol.out, lgr, wg_compress, prog, schema_bc, metadata_bc,
- history_bc, fswopts, header_ifs.get());
+ fsw_os, lgr, wg_compress, prog, schema_bc, metadata_bc, history_bc,
+ fswopts, header_ifs.get());
categorized_option compression_opt;
contextual_option_parser cop("--compression", compression_opt, cp,
@@ -1245,21 +1261,34 @@ int mkdwarfs_main(int argc, sys_char** argv, iolayer const& iol) {
if (!options.debug_filter_function) {
LOG_INFO << "compression CPU time: "
<< time_with_unit(wg_compress.get_cpu_time());
+ }
- if (os) {
- if (auto ofs = dynamic_cast(os.get())) {
- ofs->close();
- }
+ {
+ auto ec = std::visit(
+ overloaded([](std::monostate) -> int { return 0; },
+ [&](std::unique_ptr& os) -> int {
+ std::error_code ec;
+ os->close(ec);
+ if (ec) {
+ LOG_ERROR << "failed to close output file '" << output
+ << "': " << ec.message();
+ return 1;
+ }
+ os.reset();
+ return 0;
+ },
+ [](std::ostringstream& oss [[maybe_unused]]) -> int {
+ assert(oss.str().empty());
+ return 0;
+ }),
+ os);
- if (os->bad()) {
- LOG_ERROR << "failed to close output file '" << output
- << "': " << strerror(errno);
- return 1;
- }
-
- os.reset();
+ if (ec != 0) {
+ return ec;
}
+ }
+ if (!options.debug_filter_function) {
std::ostringstream err;
if (prog.errors) {
@@ -1273,11 +1302,6 @@ int mkdwarfs_main(int argc, sys_char** argv, iolayer const& iol) {
ti << "filesystem " << (recompress ? "rewritten " : "created ")
<< err.str();
- } else {
- assert(os);
- auto oss [[maybe_unused]] = dynamic_cast(os.get());
- assert(oss);
- assert(oss->str().empty());
}
return prog.errors > 0;