/* 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 namespace dwarfs::reader::internal { using namespace dwarfs::internal; file_off_t filesystem_parser::find_image_offset(mmif& mm, file_off_t image_offset) { if (image_offset != filesystem_options::IMAGE_OFFSET_AUTO) { return image_offset; } static constexpr std::array magic{ {'D', 'W', 'A', 'R', 'F', 'S', MAJOR_VERSION}}; file_off_t start = 0; for (;;) { if (start + magic.size() >= mm.size()) { break; } auto ss = mm.span(start); #if __cpp_lib_boyer_moore_searcher >= 201603 auto searcher = std::boyer_moore_searcher(magic.begin(), magic.end()); #else auto searcher = std::default_searcher(magic.begin(), magic.end()); #endif auto it = std::search(ss.begin(), ss.end(), searcher); if (it == ss.end()) { break; } file_off_t pos = start + std::distance(ss.begin(), it); if (pos + sizeof(file_header) >= mm.size()) { break; } auto fh = mm.as(pos); if (fh->minor < 2) { // best we can do for older file systems return pos; } // do a little more validation before we return if (pos + sizeof(section_header_v2) >= mm.size()) { break; } auto sh = mm.as(pos); if (sh->number == 0) { auto endpos = pos + sh->length + 2 * sizeof(section_header_v2); if (endpos < sh->length) { // overflow break; } if (endpos >= mm.size()) { break; } auto ps = mm.as(pos + sh->length + sizeof(section_header_v2)); if (::memcmp(ps, magic.data(), magic.size()) == 0 and reinterpret_cast(ps)->number == 1) { return pos; } } start = pos + magic.size(); } DWARFS_THROW(runtime_error, "no filesystem found"); } filesystem_parser::filesystem_parser(std::shared_ptr mm, file_off_t image_offset) : mm_{std::move(mm)} , image_offset_{find_image_offset(*mm_, image_offset)} { if (mm_->size() < image_offset_ + sizeof(file_header)) { DWARFS_THROW(runtime_error, "file too small"); } auto fh = mm_->as(image_offset_); if (::memcmp(&fh->magic[0], "DWARFS", 6) != 0) { DWARFS_THROW(runtime_error, "magic not found"); } if (fh->major != MAJOR_VERSION) { DWARFS_THROW(runtime_error, "different major version"); } if (fh->minor > MINOR_VERSION) { DWARFS_THROW(runtime_error, "newer minor version"); } version_ = fh->minor >= 2 ? 2 : 1; major_ = fh->major; minor_ = fh->minor; if (minor_ >= 4) { find_index(); } rewind(); } std::optional filesystem_parser::next_section() { if (index_.empty()) { if (offset_ < static_cast(mm_->size())) { auto section = fs_section(*mm_, offset_, version_); offset_ = section.end(); return section; } } else { if (offset_ < static_cast(index_.size())) { uint64_t id = index_[offset_++]; uint64_t offset = id & section_offset_mask; uint64_t next_offset = offset_ < static_cast(index_.size()) ? index_[offset_] & section_offset_mask : mm_->size() - image_offset_; return fs_section(mm_, static_cast(id >> 48), image_offset_ + offset, next_offset - offset, version_); } } return std::nullopt; } std::optional> filesystem_parser::header() const { if (image_offset_ == 0) { return std::nullopt; } return mm_->span(0, image_offset_); } void filesystem_parser::rewind() { if (index_.empty()) { offset_ = image_offset_; if (version_ == 1) { offset_ += sizeof(file_header); } } else { offset_ = 0; } } std::string filesystem_parser::version() const { return fmt::format("{0}.{1} [{2}]", major_, minor_, version_); } bool filesystem_parser::has_checksums() const { return version_ >= 2; } bool filesystem_parser::has_index() const { return !index_.empty(); } size_t filesystem_parser::filesystem_size() const { return mm_->size(); } std::span filesystem_parser::section_data(fs_section const& s) const { return s.data(*mm_); } void filesystem_parser::find_index() { uint64_t index_pos; ::memcpy(&index_pos, mm_->as(mm_->size() - sizeof(uint64_t)), sizeof(uint64_t)); if ((index_pos >> 48) == static_cast(section_type::SECTION_INDEX)) { index_pos &= section_offset_mask; index_pos += image_offset_; if (index_pos < mm_->size()) { auto section = fs_section(*mm_, index_pos, version_); if (section.check_fast(*mm_)) { index_.resize(section.length() / sizeof(uint64_t)); ::memcpy(index_.data(), section.data(*mm_).data(), section.length()); } } } } } // namespace dwarfs::reader::internal