Skip to content

Commit

Permalink
Detect diagsession files in "Compound File Binary" format
Browse files Browse the repository at this point in the history
The diagsession files created by the Visual Studio GUI are in CFB format and not just simple ZIP files (in contrast to what is created by vsdiagnostics.exe).

CFB is not supported yet, but we will at least give a meaningful error message.
  • Loading branch information
albertziegenhagel committed Apr 11, 2024
1 parent 3992939 commit 5b950b2
Showing 1 changed file with 65 additions and 0 deletions.
65 changes: 65 additions & 0 deletions snail/analysis/diagsession_data_provider.cpp
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@

#include <snail/analysis/diagsession_data_provider.hpp>

#include <cassert>

#include <array>
#include <bit>
#include <filesystem>
#include <format>
#include <fstream>
#include <span>
#include <stdexcept>

#include <libzippp.h>
Expand All @@ -17,6 +22,52 @@ using namespace snail::analysis;

namespace {

template<std::size_t MaxSize>
struct file_magic
{
char* data()
{
return reinterpret_cast<char*>(buffer_.data());
}

void set_size(std::size_t size)
{
assert(size <= MaxSize);
size_ = size;
}

bool equals(std::span<const std::byte> expected) const
{
if(size_ < expected.size()) return false;

return std::ranges::equal(std::span(buffer_).subspan(0, expected.size()), expected);
}

private:
std::size_t size_;
std::array<std::byte, MaxSize> buffer_;
};

template<std::size_t MaxSize>
file_magic<MaxSize> try_read_magic(const std::filesystem::path& file_path)
{
std::ifstream file_stream(file_path, std::ios::in | std::ios::binary);
if(!file_stream.is_open())
{
throw std::runtime_error(std::format("Could not open file '{}'", file_path.string()));
}

Check warning on line 58 in snail/analysis/diagsession_data_provider.cpp

View check run for this annotation

Codecov / codecov/patch

snail/analysis/diagsession_data_provider.cpp#L56-L58

Added lines #L56 - L58 were not covered by tests
assert(file_stream.tellg() == 0);

file_magic<MaxSize> magic;
file_stream.read(magic.data(), MaxSize);
if(!file_stream.good() && !file_stream.eof())
{
throw std::runtime_error(std::format("Failed to read magic from '{}'", file_path.string()));
}

Check warning on line 66 in snail/analysis/diagsession_data_provider.cpp

View check run for this annotation

Codecov / codecov/patch

snail/analysis/diagsession_data_provider.cpp#L64-L66

Added lines #L64 - L66 were not covered by tests
magic.set_size(static_cast<std::size_t>(file_stream.tellg()));
return magic;
}

std::optional<std::string_view> extract_xml_attribute(std::string_view xml_node, std::string_view attribute_name)
{
const auto attr_name_offset = xml_node.find(attribute_name);
Expand Down Expand Up @@ -47,6 +98,20 @@ diagsession_data_provider::~diagsession_data_provider()

void diagsession_data_provider::process(const std::filesystem::path& file_path)
{
static constexpr std::array<std::uint8_t, 4> zip_magic = {0x50, 0x4b, 0x03, 0x04};
static constexpr std::array<std::uint8_t, 8> compound_file_magic = {0xd0, 0xcf, 0x11, 0xe0, 0xa1, 0xb1, 0x1a, 0xe1};

const auto file_magic = try_read_magic<8>(file_path);
if(file_magic.equals(std::as_bytes(std::span(compound_file_magic))))
{
throw std::runtime_error(std::format("Could not open diagsession file '{}': Compound File Binary Format not yet supported", file_path.string()));
}

Check warning on line 108 in snail/analysis/diagsession_data_provider.cpp

View check run for this annotation

Codecov / codecov/patch

snail/analysis/diagsession_data_provider.cpp#L106-L108

Added lines #L106 - L108 were not covered by tests

if(!file_magic.equals(std::as_bytes(std::span(zip_magic))))
{
throw std::runtime_error(std::format("Could not open diagsession file '{}': Invalid file format. It is neither a ZIP file, nor in Compound File Binary Format.", file_path.string()));
}

Check warning on line 113 in snail/analysis/diagsession_data_provider.cpp

View check run for this annotation

Codecov / codecov/patch

snail/analysis/diagsession_data_provider.cpp#L111-L113

Added lines #L111 - L113 were not covered by tests

libzippp::ZipArchive archive(file_path.string());
if(!archive.open(libzippp::ZipArchive::ReadOnly))
{
Expand Down

0 comments on commit 5b950b2

Please sign in to comment.