/* 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 #include #include #include #include #include #include namespace dwarfs::utility { void rewrite_filesystem(logger& lgr, dwarfs::reader::filesystem_v2 const& fs, dwarfs::writer::filesystem_writer& fs_writer, dwarfs::writer::category_resolver const& cat_resolver, rewrite_options const& opts) { using dwarfs::writer::fragment_category; using dwarfs::writer::internal::block_data; LOG_PROXY(debug_logger_policy, lgr); auto parser = fs.get_parser(); auto& writer = fs_writer.get_internal(); if (opts.recompress_block) { size_t block_no{0}; parser->rewind(); while (auto s = parser->next_section()) { if (s->type() == section_type::BLOCK) { if (auto catstr = fs.get_block_category(block_no)) { if (auto cat = cat_resolver.category_value(catstr.value())) { writer.check_block_compression(s->compression(), parser->section_data(*s), cat); } } ++block_no; } } } writer.configure_rewrite(parser->filesystem_size(), fs.num_blocks()); if (auto header = parser->header()) { writer.copy_header(*header); } size_t block_no{0}; auto log_rewrite = [&](bool compressing, const auto& s, std::optional const& cat) { auto prefix = compressing ? "recompressing" : "copying"; std::string catinfo; std::string compinfo; if (cat) { catinfo = fmt::format(", {}", cat_resolver.category_name(*cat)); } if (compressing) { compinfo = fmt::format( " using '{}'", writer.get_compressor(s->type(), cat).describe()); } LOG_VERBOSE << prefix << " " << size_with_unit(s->length()) << " " << get_section_name(s->type()) << " (" << get_compression_name(s->compression()) << catinfo << ")" << compinfo; }; auto log_recompress = [&](const auto& s, std::optional const& cat = std::nullopt) { log_rewrite(true, s, cat); }; auto copy_compressed = [&](const auto& s, std::optional const& cat = std::nullopt) { log_rewrite(false, s, cat); writer.write_compressed_section(*s, parser->section_data(*s)); }; auto from_none_to_none = [&](auto const& s, std::optional const& cat = std::nullopt) { if (s->compression() == compression_type::NONE) { auto& bc = writer.get_compressor(s->type(), cat); if (bc.type() == compression_type::NONE) { return true; } } return false; }; parser->rewind(); while (auto s = parser->next_section()) { switch (s->type()) { case section_type::BLOCK: { std::optional cat; bool recompress_block{true}; if (opts.recompress_block) { auto catstr = fs.get_block_category(block_no); if (catstr) { cat = cat_resolver.category_value(catstr.value()); if (!cat) { LOG_ERROR << "unknown category '" << catstr.value() << "' for block " << block_no; } if (!opts.recompress_categories.empty()) { bool is_in_set{opts.recompress_categories.count(catstr.value()) > 0}; recompress_block = opts.recompress_categories_exclude ? !is_in_set : is_in_set; } } } if (recompress_block && from_none_to_none(s, cat)) { recompress_block = false; } if (recompress_block) { log_recompress(s, cat); writer.write_section(section_type::BLOCK, s->compression(), parser->section_data(*s), cat); } else { copy_compressed(s, cat); } ++block_no; } break; case section_type::METADATA_V2_SCHEMA: case section_type::METADATA_V2: if (opts.recompress_metadata && !from_none_to_none(s)) { log_recompress(s); writer.write_section(s->type(), s->compression(), parser->section_data(*s)); } else { copy_compressed(s); } break; case section_type::HISTORY: if (opts.enable_history) { history hist{opts.history}; hist.parse(fs.get_history().serialize()); hist.append(opts.command_line_arguments); LOG_VERBOSE << "updating " << get_section_name(s->type()) << " (" << get_compression_name(s->compression()) << "), compressing using '" << writer.get_compressor(s->type()).describe() << "'"; writer.write_history(std::make_shared(hist.serialize())); } else { LOG_VERBOSE << "removing " << get_section_name(s->type()); } break; case section_type::SECTION_INDEX: // this will be automatically added by the filesystem_writer break; default: // verbatim copy everything else copy_compressed(s); break; } } writer.flush(); } } // namespace dwarfs::utility