feat(filesystem): initial support for file system history

This commit is contained in:
Marcus Holland-Moritz 2023-11-29 13:11:57 +01:00
parent b508f4a7b9
commit c2840cbe79
16 changed files with 388 additions and 16 deletions

View File

@ -378,6 +378,7 @@ list(
src/dwarfs/fstypes.cpp
src/dwarfs/fs_section.cpp
src/dwarfs/global_entry_data.cpp
src/dwarfs/history.cpp
src/dwarfs/inode_element_view.cpp
src/dwarfs/inode_fragments.cpp
src/dwarfs/inode_manager.cpp
@ -466,6 +467,12 @@ target_compile_definitions(
dwarfs_tool PRIVATE PRJ_BUILD_ID="${CMAKE_SYSTEM_PROCESSOR}, ${CMAKE_SYSTEM}, ${CMAKE_CXX_COMPILER_ID} ${CMAKE_CXX_COMPILER_VERSION}"
)
target_compile_definitions(
dwarfs PRIVATE
PRJ_SYSTEM_ID="${CMAKE_SYSTEM} [${CMAKE_SYSTEM_PROCESSOR}]"
PRJ_COMPILER_ID="${CMAKE_CXX_COMPILER_ID} ${CMAKE_CXX_COMPILER_VERSION}"
)
target_link_libraries(dwarfs_categorizer folly)
target_link_libraries(dwarfs_compression folly compression_thrift)
target_link_libraries(dwarfs_tool dwarfs)
@ -720,6 +727,27 @@ list(
${CMAKE_CURRENT_BINARY_DIR}/thrift/dwarfs/gen-cpp2/compression_visit_union.h
${CMAKE_CURRENT_BINARY_DIR}/thrift/dwarfs/gen-cpp2/compression_visitation.h)
list(
APPEND
HISTORY_THRIFT_SRC
${CMAKE_CURRENT_BINARY_DIR}/thrift/dwarfs/gen-cpp2/history_clients.h
${CMAKE_CURRENT_BINARY_DIR}/thrift/dwarfs/gen-cpp2/history_constants.cpp
${CMAKE_CURRENT_BINARY_DIR}/thrift/dwarfs/gen-cpp2/history_constants.h
${CMAKE_CURRENT_BINARY_DIR}/thrift/dwarfs/gen-cpp2/history_data.cpp
${CMAKE_CURRENT_BINARY_DIR}/thrift/dwarfs/gen-cpp2/history_data.h
${CMAKE_CURRENT_BINARY_DIR}/thrift/dwarfs/gen-cpp2/history_for_each_field.h
${CMAKE_CURRENT_BINARY_DIR}/thrift/dwarfs/gen-cpp2/history_handlers.h
${CMAKE_CURRENT_BINARY_DIR}/thrift/dwarfs/gen-cpp2/history_metadata.cpp
${CMAKE_CURRENT_BINARY_DIR}/thrift/dwarfs/gen-cpp2/history_metadata.h
${CMAKE_CURRENT_BINARY_DIR}/thrift/dwarfs/gen-cpp2/history_types.cpp
${CMAKE_CURRENT_BINARY_DIR}/thrift/dwarfs/gen-cpp2/history_types.h
${CMAKE_CURRENT_BINARY_DIR}/thrift/dwarfs/gen-cpp2/history_types.tcc
${CMAKE_CURRENT_BINARY_DIR}/thrift/dwarfs/gen-cpp2/history_types_custom_protocol.h
${CMAKE_CURRENT_BINARY_DIR}/thrift/dwarfs/gen-cpp2/history_types_fwd.h
${CMAKE_CURRENT_BINARY_DIR}/thrift/dwarfs/gen-cpp2/history_visit_by_thrift_field_metadata.h
${CMAKE_CURRENT_BINARY_DIR}/thrift/dwarfs/gen-cpp2/history_visit_union.h
${CMAKE_CURRENT_BINARY_DIR}/thrift/dwarfs/gen-cpp2/history_visitation.h)
add_custom_command(
OUTPUT thrift/lib/thrift/_keep
COMMAND ${CMAKE_COMMAND} -E make_directory thrift/lib/thrift
@ -780,6 +808,22 @@ add_custom_command(
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/thrift/dwarfs
)
add_custom_command(
OUTPUT ${HISTORY_THRIFT_SRC}
COMMAND ${CMAKE_COMMAND} -E copy
${CMAKE_CURRENT_SOURCE_DIR}/thrift/history.thrift
${CMAKE_CURRENT_BINARY_DIR}/thrift/dwarfs/history.thrift
COMMAND ${CMAKE_CURRENT_BINARY_DIR}/bin/thrift1
-I ${CMAKE_CURRENT_SOURCE_DIR}/fbthrift
-o ${CMAKE_CURRENT_BINARY_DIR}/thrift/dwarfs
--gen mstch_cpp2
history.thrift
DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/bin/thrift1
${CMAKE_CURRENT_BINARY_DIR}/thrift/dwarfs/_keep
${CMAKE_CURRENT_SOURCE_DIR}/thrift/history.thrift
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/thrift/dwarfs
)
list(
APPEND
INCLUDE_DIRS
@ -841,14 +885,22 @@ add_library(
${CMAKE_CURRENT_BINARY_DIR}/thrift/dwarfs/gen-cpp2/compression_types.cpp
${CMAKE_CURRENT_BINARY_DIR}/thrift/dwarfs/gen-cpp2/compression_data.cpp)
add_library(
history_thrift
${CMAKE_CURRENT_BINARY_DIR}/thrift/dwarfs/gen-cpp2/history_types.cpp
${CMAKE_CURRENT_BINARY_DIR}/thrift/dwarfs/gen-cpp2/history_data.cpp)
set_property(TARGET metadata_thrift PROPERTY CXX_STANDARD 20)
set_property(TARGET compression_thrift PROPERTY CXX_STANDARD 20)
set_property(TARGET history_thrift PROPERTY CXX_STANDARD 20)
target_include_directories(metadata_thrift PRIVATE ${INCLUDE_DIRS})
target_include_directories(compression_thrift PRIVATE ${INCLUDE_DIRS})
target_include_directories(history_thrift PRIVATE ${INCLUDE_DIRS})
target_link_libraries(metadata_thrift thrift_light)
target_link_libraries(compression_thrift thrift_light)
target_link_libraries(history_thrift thrift_light)
foreach(tgt dwarfs dwarfs_compression dwarfs_categorizer
dwarfs_compression_metadata dwarfs_tool
@ -889,7 +941,7 @@ foreach(tgt dwarfs dwarfs_compression dwarfs_categorizer
set_property(TARGET ${tgt} PROPERTY CXX_STANDARD_REQUIRED ON)
set_property(TARGET ${tgt} PROPERTY CXX_EXTENSIONS OFF)
add_dependencies(${tgt} metadata_thrift)
add_dependencies(${tgt} metadata_thrift history_thrift)
if(ENABLE_ASAN)
target_compile_options(${tgt} PRIVATE -fsanitize=address
@ -939,6 +991,7 @@ target_include_directories(dwarfs PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/fsst)
target_link_libraries(
dwarfs
metadata_thrift
history_thrift
thrift_light
folly
fsst

View File

@ -114,6 +114,11 @@ There are currently 4 different section types.
file, you should find a valid section header for the section
index.
- `HISTORY` (10):
File system history information as defined `thrift/history.thrift`.
This is stored in "compact" thrift encoding. Zero or more history
sections are supported.
## METADATA FORMAT
Here is a high-level overview of how all the bits and pieces relate

View File

@ -22,6 +22,7 @@
#pragma once
#include <cstdint>
#include <string_view>
#include <vector>
namespace dwarfs {
@ -31,6 +32,8 @@ class block_data {
block_data() = default;
explicit block_data(std::vector<uint8_t>&& vec)
: vec_{std::move(vec)} {}
explicit block_data(std::string_view str)
: vec_{str.begin(), str.end()} {}
std::vector<uint8_t> const& vec() const { return vec_; }
std::vector<uint8_t>& vec() { return vec_; }

View File

@ -50,6 +50,7 @@ struct file_stat;
struct vfs_stat;
class filesystem_writer;
class history;
class logger;
class mmif;
class performance_monitor;
@ -171,6 +172,8 @@ class filesystem_v2 {
bool has_symlinks() const { return impl_->has_symlinks(); }
history const& get_history() const { return impl_->get_history(); }
class impl {
public:
virtual ~impl() = default;
@ -210,6 +213,7 @@ class filesystem_v2 {
virtual void set_cache_tidy_config(cache_tidy_config const& cfg) = 0;
virtual size_t num_blocks() const = 0;
virtual bool has_symlinks() const = 0;
virtual history const& get_history() const = 0;
};
private:

View File

@ -102,6 +102,10 @@ class filesystem_writer {
impl_->write_metadata_v2(std::move(data));
}
void write_history(std::shared_ptr<block_data>&& data) {
impl_->write_history(std::move(data));
}
void write_compressed_section(section_type type, compression_type compression,
std::span<uint8_t const> data) {
impl_->write_compressed_section(type, compression, data);
@ -136,6 +140,7 @@ class filesystem_writer {
virtual void
write_metadata_v2_schema(std::shared_ptr<block_data>&& data) = 0;
virtual void write_metadata_v2(std::shared_ptr<block_data>&& data) = 0;
virtual void write_history(std::shared_ptr<block_data>&& data) = 0;
virtual void
write_compressed_section(section_type type, compression_type compression,
std::span<uint8_t const> data) = 0;

View File

@ -46,6 +46,9 @@ enum class section_type : uint16_t {
SECTION_INDEX = 9,
// Section index.
HISTORY = 10,
// History of file system changes.
};
struct file_header {

52
include/dwarfs/history.h Normal file
View File

@ -0,0 +1,52 @@
/* 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 <https://www.gnu.org/licenses/>.
*/
#pragma once
#include <iosfwd>
#include <optional>
#include <span>
#include <string>
#include <vector>
#include "dwarfs/options.h"
#include "dwarfs/gen-cpp2/history_types.h"
namespace dwarfs {
class history {
public:
explicit history(history_config const& cfg = {});
void parse(std::span<uint8_t const> data);
void parse_append(std::span<uint8_t const> data);
thrift::history::history const& get() const { return history_; }
void append(std::optional<std::vector<std::string>> args);
std::vector<uint8_t> serialize() const;
void dump(std::ostream& os) const;
private:
thrift::history::history history_;
history_config const cfg_;
};
} // namespace dwarfs

View File

@ -27,6 +27,7 @@
#include <iosfwd>
#include <memory>
#include <optional>
#include <vector>
#include "dwarfs/categorized_option.h"
#include "dwarfs/file_stat.h"
@ -50,6 +51,10 @@ struct block_cache_options {
bool disable_block_integrity_check{false};
};
struct history_config {
bool with_timestamps{false};
};
struct cache_tidy_config {
cache_tidy_strategy strategy{cache_tidy_strategy::NONE};
std::chrono::milliseconds interval;
@ -123,6 +128,9 @@ struct scanner_options {
bool no_create_timestamp{true};
std::optional<std::function<void(bool, entry const*)>> debug_filter_function;
size_t num_segmenter_workers{1};
bool enable_history{true};
std::optional<std::vector<std::string>> command_line_arguments;
history_config history;
};
struct rewrite_options {

View File

@ -28,6 +28,7 @@ sectypes = {
7: "schema",
8: "metadata",
9: "index",
10: "history",
}
compalgs = {

View File

@ -39,6 +39,7 @@
#include "dwarfs/filesystem_writer.h"
#include "dwarfs/fs_section.h"
#include "dwarfs/fstypes.h"
#include "dwarfs/history.h"
#include "dwarfs/inode_reader_v2.h"
#include "dwarfs/logger.h"
#include "dwarfs/metadata_v2.h"
@ -260,7 +261,7 @@ class filesystem_parser {
std::vector<uint64_t> index_;
};
using section_map = std::unordered_map<section_type, fs_section>;
using section_map = std::unordered_map<section_type, std::vector<fs_section>>;
size_t
get_uncompressed_section_size(std::shared_ptr<mmif> mm, fs_section const& sec) {
@ -303,11 +304,19 @@ make_metadata(logger& lgr, std::shared_ptr<mmif> mm,
DWARFS_THROW(runtime_error, "no metadata schema found");
}
if (schema_it->second.size() > 1) {
DWARFS_THROW(runtime_error, "multiple metadata schemas found");
}
if (meta_it == sections.end()) {
DWARFS_THROW(runtime_error, "no metadata found");
}
auto& meta_section = meta_it->second;
if (meta_it->second.size() > 1) {
DWARFS_THROW(runtime_error, "multiple metadata found");
}
auto& meta_section = meta_it->second.front();
auto meta_section_range =
get_section_data(mm, meta_section, meta_buffer, force_buffers);
@ -329,10 +338,11 @@ make_metadata(logger& lgr, std::shared_ptr<mmif> mm,
}
}
return metadata_v2(
lgr,
get_section_data(mm, schema_it->second, schema_buffer, force_buffers),
meta_section_range, options, inode_offset, force_consistency_check);
return metadata_v2(lgr,
get_section_data(mm, schema_it->second.front(),
schema_buffer, force_buffers),
meta_section_range, options, inode_offset,
force_consistency_check);
}
template <typename LoggerPolicy>
@ -376,6 +386,7 @@ class filesystem_ final : public filesystem_v2::impl {
}
size_t num_blocks() const override { return ir_.num_blocks(); }
bool has_symlinks() const override { return meta_.has_symlinks(); }
history const& get_history() const override { return history_; }
private:
filesystem_info const& get_info() const;
@ -390,6 +401,7 @@ class filesystem_ final : public filesystem_v2::impl {
std::vector<uint8_t> meta_buffer_;
std::optional<std::span<uint8_t const>> header_;
mutable std::unique_ptr<filesystem_info const> fsinfo_;
history history_;
PERFMON_CLS_PROXY_DECL
PERFMON_CLS_TIMER_DECL(find_path)
PERFMON_CLS_TIMER_DECL(find_inode)
@ -461,6 +473,7 @@ filesystem_<LoggerPolicy>::filesystem_(
: LOG_PROXY_INIT(lgr)
, mm_(std::move(mm))
, parser_(mm_, options.image_offset)
, history_({.with_timestamps = true})
// clang-format off
PERFMON_CLS_PROXY_INIT(perfmon, "filesystem_v2")
PERFMON_CLS_TIMER_INIT(find_path)
@ -498,9 +511,7 @@ filesystem_<LoggerPolicy>::filesystem_(
DWARFS_THROW(runtime_error, "checksum error in section: " + s->name());
}
if (!sections.emplace(s->type(), *s).second) {
DWARFS_THROW(runtime_error, "duplicate section: " + s->name());
}
sections[s->type()].push_back(*s);
}
}
@ -516,10 +527,21 @@ filesystem_<LoggerPolicy>::filesystem_(
cache.set_block_size(meta_.block_size());
ir_ = inode_reader_v2(lgr, std::move(cache), perfmon);
if (auto it = sections.find(section_type::HISTORY); it != sections.end()) {
for (auto& section : it->second) {
std::vector<uint8_t> buffer;
history_.parse_append(get_section_data(mm_, section, buffer, false));
}
}
}
template <typename LoggerPolicy>
void filesystem_<LoggerPolicy>::dump(std::ostream& os, int detail_level) const {
if (detail_level > 1) {
history_.dump(os);
}
meta_.dump(os, detail_level, get_info(),
[&](const std::string& indent, uint32_t inode) {
if (auto chunks = meta_.get_chunks(inode)) {
@ -716,10 +738,11 @@ void filesystem_v2::rewrite(logger& lgr, progress& prog,
if (s->type() == section_type::BLOCK) {
++prog.block_count;
} else if (s->type() != section_type::SECTION_INDEX) {
if (!sections.emplace(s->type(), *s).second) {
DWARFS_THROW(runtime_error, "duplicate section: " + s->name());
auto& secvec = sections[s->type()];
if (secvec.empty()) {
section_types.push_back(s->type());
}
section_types.push_back(s->type());
secvec.push_back(*s);
}
}
@ -755,8 +778,10 @@ void filesystem_v2::rewrite(logger& lgr, progress& prog,
writer.write_metadata_v2(std::make_shared<block_data>(std::move(meta_raw)));
} else {
for (auto type : section_types) {
auto& sec = DWARFS_NOTHROW(sections.at(type));
writer.write_compressed_section(type, sec.compression(), sec.data(*mm));
auto& secvec = DWARFS_NOTHROW(sections.at(type));
for (auto& sec : secvec) {
writer.write_compressed_section(type, sec.compression(), sec.data(*mm));
}
}
}

View File

@ -359,6 +359,7 @@ class filesystem_writer_ final : public filesystem_writer::impl {
std::optional<std::string> meta) override;
void write_metadata_v2_schema(std::shared_ptr<block_data>&& data) override;
void write_metadata_v2(std::shared_ptr<block_data>&& data) override;
void write_history(std::shared_ptr<block_data>&& data) override;
void write_compressed_section(section_type type, compression_type compression,
std::span<uint8_t const> data) override;
void flush() override;
@ -737,6 +738,13 @@ void filesystem_writer_<LoggerPolicy>::write_metadata_v2(
write_section(section_type::METADATA_V2, std::move(data), metadata_bc_);
}
template <typename LoggerPolicy>
void filesystem_writer_<LoggerPolicy>::write_history(
std::shared_ptr<block_data>&& data) {
write_section(section_type::HISTORY, std::move(data),
metadata_bc_); // TODO: history_bc_?
}
template <typename LoggerPolicy>
void filesystem_writer_<LoggerPolicy>::flush() {
{

View File

@ -35,14 +35,17 @@ namespace dwarfs {
namespace {
// clang-format off
const std::map<section_type, std::string_view> sections{
#define SECTION_TYPE_(x) {section_type::x, #x}
SECTION_TYPE_(BLOCK),
SECTION_TYPE_(METADATA_V2_SCHEMA),
SECTION_TYPE_(METADATA_V2),
SECTION_TYPE_(SECTION_INDEX),
SECTION_TYPE_(HISTORY),
#undef SECTION_TYPE_
};
// clang-format on
const std::map<compression_type, std::string_view> compressions{
#define DWARFS_COMPRESSION_TYPE_(name, value) {compression_type::name, #name}

114
src/dwarfs/history.cpp Normal file
View File

@ -0,0 +1,114 @@
/* 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 <https://www.gnu.org/licenses/>.
*/
#include <ostream>
#include <fmt/chrono.h>
#include <fmt/format.h>
#include <thrift/lib/cpp2/protocol/Serializer.h>
#include "dwarfs/history.h"
#include "dwarfs/version.h"
namespace dwarfs {
history::history(history_config const& cfg)
: cfg_{cfg} {}
void history::parse(std::span<uint8_t const> data) {
history_.entries()->clear();
parse_append(data);
}
void history::parse_append(std::span<uint8_t const> data) {
folly::Range<const uint8_t*> range{data.data(), data.size()};
thrift::history::history tmp;
apache::thrift::CompactSerializer::deserialize(range, tmp);
history_.entries()->insert(history_.entries()->end(),
std::make_move_iterator(tmp.entries()->begin()),
std::make_move_iterator(tmp.entries()->end()));
}
void history::append(std::optional<std::vector<std::string>> args) {
auto& histent = history_.entries()->emplace_back();
auto& version = histent.version().value();
version.major() = PRJ_VERSION_MAJOR;
version.minor() = PRJ_VERSION_MINOR;
version.patch() = PRJ_VERSION_PATCH;
version.is_release() = std::string_view(PRJ_GIT_DESC) == PRJ_GIT_ID;
version.git_rev() = PRJ_GIT_REV;
version.git_branch() = PRJ_GIT_BRANCH;
version.git_desc() = PRJ_GIT_DESC;
histent.system_id() = PRJ_SYSTEM_ID;
histent.compiler_id() = PRJ_COMPILER_ID;
if (args) {
histent.arguments() = std::move(*args);
}
if (cfg_.with_timestamps) {
histent.timestamp() = std::time(nullptr);
}
}
std::vector<uint8_t> history::serialize() const {
std::string buf;
::apache::thrift::CompactSerializer::serialize(history_, &buf);
return std::vector<uint8_t>(buf.begin(), buf.end());
}
void history::dump(std::ostream& os) const {
if (!history_.entries()->empty()) {
size_t const iwidth{std::to_string(history_.entries()->size()).size()};
size_t i{1};
os << "History:\n";
for (auto const& histent : *history_.entries()) {
os << " " << fmt::format("{:>{}}:", i++, iwidth);
if (histent.timestamp().has_value()) {
os << " "
<< fmt::format("[{:%Y-%m-%d %H:%M:%S}]",
fmt::localtime(histent.timestamp().value()));
}
auto const& version = histent.version().value();
os << " libdwarfs " << version.git_desc().value();
if (!version.is_release().value()) {
os << " (" << version.git_branch().value() << ")";
}
os << " on " << histent.system_id().value() << ", "
<< histent.compiler_id().value() << "\n";
if (histent.arguments().has_value() && !histent.arguments()->empty()) {
os << fmt::format(" {:>{}} args:", "", iwidth);
for (auto const& arg : histent.arguments().value()) {
os << ' ' << arg;
}
os << "\n";
}
}
}
}
} // namespace dwarfs

View File

@ -46,6 +46,7 @@
#include "dwarfs/filesystem_writer.h"
#include "dwarfs/fragment_chunkable.h"
#include "dwarfs/global_entry_data.h"
#include "dwarfs/history.h"
#include "dwarfs/inode.h"
#include "dwarfs/inode_manager.h"
#include "dwarfs/inode_ordering.h"
@ -904,6 +905,12 @@ void scanner_<LoggerPolicy>::scan(
fsw.write_metadata_v2_schema(std::make_shared<block_data>(std::move(schema)));
fsw.write_metadata_v2(std::make_shared<block_data>(std::move(data)));
if (options_.enable_history) {
history hist(options_.history);
hist.append(options_.command_line_arguments);
fsw.write_history(std::make_shared<block_data>(hist.serialize()));
}
LOG_INFO << "waiting for compression to finish...";
fsw.flush();

View File

@ -286,7 +286,8 @@ int mkdwarfs_main(int argc, sys_char** argv) {
bloom_filter_size, compression;
size_t num_workers, num_scanner_workers, num_segmenter_workers;
bool no_progress = false, remove_header = false, no_section_index = false,
force_overwrite = false;
force_overwrite = false, no_history = false,
no_history_timestamps = false, no_history_command_line = false;
unsigned level;
int compress_niceness;
uint16_t uid, gid;
@ -414,6 +415,15 @@ int mkdwarfs_main(int argc, sys_char** argv) {
("no-section-index",
po::value<bool>(&no_section_index)->zero_tokens(),
"don't add section index to file system")
("no-history",
po::value<bool>(&no_history)->zero_tokens(),
"don't add history to file system")
("no-history-timestamps",
po::value<bool>(&no_history_timestamps)->zero_tokens(),
"don't add timestamps to file system history")
("no-history-command-line",
po::value<bool>(&no_history_command_line)->zero_tokens(),
"don't add command line to file system history")
;
po::options_description segmenter_opts("Segmenter options");
@ -510,6 +520,13 @@ int mkdwarfs_main(int argc, sys_char** argv) {
auto& sys_err_out = SYS_CERR;
std::vector<std::string> command_line;
command_line.reserve(argc);
for (int i = 0; i < argc; ++i) {
command_line.emplace_back(sys_string_to_string(argv[i]));
}
try {
auto parsed = po::parse_command_line(argc, argv, opts);
@ -954,6 +971,15 @@ int mkdwarfs_main(int argc, sys_char** argv) {
os = std::make_unique<std::ostringstream>();
}
options.enable_history = !no_history;
if (options.enable_history) {
options.history.with_timestamps = !no_history_timestamps;
if (!no_history_command_line) {
options.command_line_arguments = command_line;
}
}
// TODO: the whole re-writing thing will be a bit weird in combination
// with categories; we'd likely require a "category"-section to be
// present (which we'll also require for bit-identical mode)

55
thrift/history.thrift Normal file
View File

@ -0,0 +1,55 @@
/* 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 <https://www.gnu.org/licenses/>.
*/
include "thrift/annotation/cpp.thrift"
namespace cpp2 dwarfs.thrift.history
@cpp.Type{name = "uint8_t"}
typedef byte UInt8
@cpp.Type{name = "uint16_t"}
typedef i16 UInt16
@cpp.Type{name = "uint32_t"}
typedef i32 UInt32
@cpp.Type{name = "uint64_t"}
typedef i64 UInt64
struct dwarfs_version {
1: UInt16 major
2: UInt16 minor
3: UInt16 patch
4: bool is_release
5: optional string git_rev
6: optional string git_branch
7: optional string git_desc
}
struct history_entry {
1: dwarfs_version version
2: string system_id
3: string compiler_id
4: optional list<string> arguments
5: optional UInt64 timestamp
}
struct history {
1: list<history_entry> entries
}