summaryrefslogtreecommitdiff
path: root/src/CacheEntryReader.cpp
blob: 82dae950c4a8c89a6914cf57344732c0fe6e8273 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
// Copyright (C) 2019 Joel Rosdahl and other contributors
//
// See doc/AUTHORS.adoc for a complete list of contributors.
//
// This program 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.
//
// This program 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
// this program; if not, write to the Free Software Foundation, Inc., 51
// Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA

#include "CacheEntryReader.hpp"

#include "Checksum.hpp"
#include "Compressor.hpp"
#include "Error.hpp"
#include "ccache.hpp"

#include <fmt/core.h>

CacheEntryReader::CacheEntryReader(FILE* stream,
                                   const uint8_t expected_magic[4],
                                   uint8_t expected_version,
                                   Checksum* checksum)
  : m_checksum(checksum)
{
  uint8_t header_bytes[15];
  if (fread(header_bytes, sizeof(header_bytes), 1, stream) != 1) {
    throw Error("Error reading header");
  }

  memcpy(m_magic, header_bytes, sizeof(m_magic));
  m_version = header_bytes[4];
  m_compression_type = Compression::type_from_int(header_bytes[5]);
  m_compression_level = header_bytes[6];
  Util::big_endian_to_int(header_bytes + 7, m_content_size);

  if (memcmp(m_magic, expected_magic, sizeof(m_magic)) != 0) {
    throw Error(fmt::format("Bad magic value 0x{:02x}{:02x}{:02x}{:02x}",
                            m_magic[0],
                            m_magic[1],
                            m_magic[2],
                            m_magic[3]));
  }
  if (m_version != expected_version) {
    throw Error(fmt::format(
      "Unknown version (actual {}, expected {})", m_version, expected_version));
  }
  if (m_compression_type == Compression::Type::none) {
    // Since we have the size available, let's use it as a super primitive
    // consistency check for the non-compressed case. (A real checksum is used
    // for compressed data.)
    struct stat st;
    if (x_fstat(fileno(stream), &st) != 0) {
      throw Error(fmt::format("Failed to fstat: {}", strerror(errno)));
    }
    if (static_cast<uint64_t>(st.st_size) != m_content_size) {
      throw Error(fmt::format(
        "Bad uncompressed file size (actual {} bytes, expected {} bytes)",
        st.st_size,
        m_content_size));
    }
  }

  if (m_checksum) {
    m_checksum->update(header_bytes, sizeof(header_bytes));
  }

  m_decompressor = Decompressor::create_from_type(m_compression_type, stream);
}

void
CacheEntryReader::dump_header(FILE* dump_stream)
{
  fmt::print(dump_stream, "Magic: {:.4}\n", m_magic);
  fmt::print(dump_stream, "Version: {}\n", m_version);
  fmt::print(dump_stream,
             "Compression type: {}\n",
             Compression::type_to_string(m_compression_type));
  fmt::print(dump_stream, "Compression level: {}\n", m_compression_level);
  fmt::print(dump_stream, "Content size: {}\n", m_content_size);
}

void
CacheEntryReader::read(void* data, size_t count)
{
  m_decompressor->read(data, count);
  m_checksum->update(data, count);
}

void
CacheEntryReader::finalize()
{
  m_decompressor->finalize();
}