libsnapshot: Split CowReader into CowParserV2.
Remove format-specific logic from CowReader and split it out into a new class called CowParserV2. To make reading the header easier, the version and size bits are now in a separate CowHeaderPrefix struct. Bug: 280529365 Test: apply OTA on CF inspect_cow Change-Id: I29b5617ec094d4fb0c284485883d2e921a5bdbf8
This commit is contained in:
parent
bf11ce74ff
commit
d70a174e95
11 changed files with 342 additions and 266 deletions
|
@ -179,6 +179,7 @@ cc_library_static {
|
|||
"libsnapshot_cow/cow_writer.cpp",
|
||||
"libsnapshot_cow/cow_format.cpp",
|
||||
"libsnapshot_cow/cow_compress.cpp",
|
||||
"libsnapshot_cow/parser_v2.cpp",
|
||||
],
|
||||
host_supported: true,
|
||||
recovery_available: true,
|
||||
|
|
|
@ -52,13 +52,15 @@ static constexpr uint32_t kCowVersionManifest = 2;
|
|||
// between writing the last operation/data pair, or the footer itself. In this
|
||||
// case, the safest way to proceed is to assume the last operation is faulty.
|
||||
|
||||
struct CowHeader {
|
||||
struct CowHeaderPrefix {
|
||||
uint64_t magic;
|
||||
uint16_t major_version;
|
||||
uint16_t minor_version;
|
||||
uint16_t header_size; // size of CowHeader.
|
||||
} __attribute__((packed));
|
||||
|
||||
// Size of this struct.
|
||||
uint16_t header_size;
|
||||
struct CowHeader {
|
||||
CowHeaderPrefix prefix;
|
||||
|
||||
// Size of footer struct
|
||||
uint16_t footer_size;
|
||||
|
@ -88,7 +90,7 @@ struct CowFooterOperation {
|
|||
// the compression type of that data (see constants below).
|
||||
uint8_t compression;
|
||||
|
||||
// Length of Footer Data. Currently 64 for both checksums
|
||||
// Length of Footer Data. Currently 64.
|
||||
uint16_t data_length;
|
||||
|
||||
// The amount of file space used by Cow operations
|
||||
|
@ -98,14 +100,6 @@ struct CowFooterOperation {
|
|||
uint64_t num_ops;
|
||||
} __attribute__((packed));
|
||||
|
||||
struct CowFooterData {
|
||||
// SHA256 checksums of Footer op
|
||||
uint8_t footer_checksum[32];
|
||||
|
||||
// SHA256 of the operation sequence.
|
||||
uint8_t ops_checksum[32];
|
||||
} __attribute__((packed));
|
||||
|
||||
// Cow operations are currently fixed-size entries, but this may change if
|
||||
// needed.
|
||||
struct CowOperation {
|
||||
|
@ -167,7 +161,7 @@ static constexpr uint8_t kCowReadAheadDone = 2;
|
|||
|
||||
struct CowFooter {
|
||||
CowFooterOperation op;
|
||||
CowFooterData data;
|
||||
uint8_t unused[64];
|
||||
} __attribute__((packed));
|
||||
|
||||
struct ScratchMetadata {
|
||||
|
|
|
@ -171,7 +171,7 @@ class CowWriter : public ICowWriter {
|
|||
|
||||
uint64_t GetCowSize() override;
|
||||
|
||||
uint32_t GetCowVersion() { return header_.major_version; }
|
||||
uint32_t GetCowVersion() { return header_.prefix.major_version; }
|
||||
|
||||
protected:
|
||||
virtual bool EmitCopy(uint64_t new_block, uint64_t old_block, uint64_t num_blocks = 1) override;
|
||||
|
|
|
@ -65,9 +65,9 @@ TEST_F(CowTest, CopyContiguous) {
|
|||
ASSERT_TRUE(reader.Parse(cow_->fd));
|
||||
|
||||
const auto& header = reader.GetHeader();
|
||||
ASSERT_EQ(header.magic, kCowMagicNumber);
|
||||
ASSERT_EQ(header.major_version, kCowVersionMajor);
|
||||
ASSERT_EQ(header.minor_version, kCowVersionMinor);
|
||||
ASSERT_EQ(header.prefix.magic, kCowMagicNumber);
|
||||
ASSERT_EQ(header.prefix.major_version, kCowVersionMajor);
|
||||
ASSERT_EQ(header.prefix.minor_version, kCowVersionMinor);
|
||||
ASSERT_EQ(header.block_size, options.block_size);
|
||||
|
||||
CowFooter footer;
|
||||
|
@ -114,9 +114,9 @@ TEST_F(CowTest, ReadWrite) {
|
|||
ASSERT_TRUE(reader.Parse(cow_->fd));
|
||||
|
||||
const auto& header = reader.GetHeader();
|
||||
ASSERT_EQ(header.magic, kCowMagicNumber);
|
||||
ASSERT_EQ(header.major_version, kCowVersionMajor);
|
||||
ASSERT_EQ(header.minor_version, kCowVersionMinor);
|
||||
ASSERT_EQ(header.prefix.magic, kCowMagicNumber);
|
||||
ASSERT_EQ(header.prefix.major_version, kCowVersionMajor);
|
||||
ASSERT_EQ(header.prefix.minor_version, kCowVersionMinor);
|
||||
ASSERT_EQ(header.block_size, options.block_size);
|
||||
|
||||
CowFooter footer;
|
||||
|
@ -193,9 +193,9 @@ TEST_F(CowTest, ReadWriteXor) {
|
|||
ASSERT_TRUE(reader.Parse(cow_->fd));
|
||||
|
||||
const auto& header = reader.GetHeader();
|
||||
ASSERT_EQ(header.magic, kCowMagicNumber);
|
||||
ASSERT_EQ(header.major_version, kCowVersionMajor);
|
||||
ASSERT_EQ(header.minor_version, kCowVersionMinor);
|
||||
ASSERT_EQ(header.prefix.magic, kCowMagicNumber);
|
||||
ASSERT_EQ(header.prefix.major_version, kCowVersionMajor);
|
||||
ASSERT_EQ(header.prefix.minor_version, kCowVersionMinor);
|
||||
ASSERT_EQ(header.block_size, options.block_size);
|
||||
|
||||
CowFooter footer;
|
||||
|
|
|
@ -30,7 +30,7 @@
|
|||
#include <zlib.h>
|
||||
|
||||
#include "cow_decompress.h"
|
||||
#include "libsnapshot/cow_format.h"
|
||||
#include "parser_v2.h"
|
||||
|
||||
namespace android {
|
||||
namespace snapshot {
|
||||
|
@ -43,15 +43,6 @@ CowReader::CowReader(ReaderFlags reader_flag, bool is_merge)
|
|||
reader_flag_(reader_flag),
|
||||
is_merge_(is_merge) {}
|
||||
|
||||
static void SHA256(const void*, size_t, uint8_t[]) {
|
||||
#if 0
|
||||
SHA256_CTX c;
|
||||
SHA256_Init(&c);
|
||||
SHA256_Update(&c, data, length);
|
||||
SHA256_Final(out, &c);
|
||||
#endif
|
||||
}
|
||||
|
||||
std::unique_ptr<CowReader> CowReader::CloneCowReader() {
|
||||
auto cow = std::make_unique<CowReader>();
|
||||
cow->owned_fd_.reset();
|
||||
|
@ -63,7 +54,6 @@ std::unique_ptr<CowReader> CowReader::CloneCowReader() {
|
|||
cow->merge_op_start_ = merge_op_start_;
|
||||
cow->num_total_data_ops_ = num_total_data_ops_;
|
||||
cow->num_ordered_ops_to_merge_ = num_ordered_ops_to_merge_;
|
||||
cow->has_seq_ops_ = has_seq_ops_;
|
||||
cow->data_loc_ = data_loc_;
|
||||
cow->block_pos_index_ = block_pos_index_;
|
||||
cow->is_merge_ = is_merge_;
|
||||
|
@ -101,217 +91,26 @@ bool CowReader::Parse(android::base::unique_fd&& fd, std::optional<uint64_t> lab
|
|||
bool CowReader::Parse(android::base::borrowed_fd fd, std::optional<uint64_t> label) {
|
||||
fd_ = fd;
|
||||
|
||||
auto pos = lseek(fd_.get(), 0, SEEK_END);
|
||||
if (pos < 0) {
|
||||
PLOG(ERROR) << "lseek end failed";
|
||||
return false;
|
||||
}
|
||||
fd_size_ = pos;
|
||||
|
||||
if (lseek(fd_.get(), 0, SEEK_SET) < 0) {
|
||||
PLOG(ERROR) << "lseek header failed";
|
||||
return false;
|
||||
}
|
||||
if (!android::base::ReadFully(fd_, &header_, sizeof(header_))) {
|
||||
PLOG(ERROR) << "read header failed";
|
||||
if (!ReadCowHeader(fd, &header_)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (header_.magic != kCowMagicNumber) {
|
||||
LOG(ERROR) << "Header Magic corrupted. Magic: " << header_.magic
|
||||
<< "Expected: " << kCowMagicNumber;
|
||||
return false;
|
||||
}
|
||||
if (header_.footer_size != sizeof(CowFooter)) {
|
||||
LOG(ERROR) << "Footer size unknown, read " << header_.footer_size << ", expected "
|
||||
<< sizeof(CowFooter);
|
||||
return false;
|
||||
}
|
||||
if (header_.op_size != sizeof(CowOperation)) {
|
||||
LOG(ERROR) << "Operation size unknown, read " << header_.op_size << ", expected "
|
||||
<< sizeof(CowOperation);
|
||||
return false;
|
||||
}
|
||||
if (header_.cluster_ops == 1) {
|
||||
LOG(ERROR) << "Clusters must contain at least two operations to function.";
|
||||
return false;
|
||||
}
|
||||
if (header_.op_size != sizeof(CowOperation)) {
|
||||
LOG(ERROR) << "Operation size unknown, read " << header_.op_size << ", expected "
|
||||
<< sizeof(CowOperation);
|
||||
return false;
|
||||
}
|
||||
if (header_.cluster_ops == 1) {
|
||||
LOG(ERROR) << "Clusters must contain at least two operations to function.";
|
||||
CowParserV2 parser;
|
||||
if (!parser.Parse(fd, header_, label)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((header_.major_version > kCowVersionMajor) || (header_.minor_version != kCowVersionMinor)) {
|
||||
LOG(ERROR) << "Header version mismatch";
|
||||
LOG(ERROR) << "Major version: " << header_.major_version
|
||||
<< "Expected: " << kCowVersionMajor;
|
||||
LOG(ERROR) << "Minor version: " << header_.minor_version
|
||||
<< "Expected: " << kCowVersionMinor;
|
||||
return false;
|
||||
}
|
||||
footer_ = parser.footer();
|
||||
fd_size_ = parser.fd_size();
|
||||
last_label_ = parser.last_label();
|
||||
ops_ = std::move(parser.ops());
|
||||
data_loc_ = parser.data_loc();
|
||||
|
||||
if (!ParseOps(label)) {
|
||||
return false;
|
||||
}
|
||||
// If we're resuming a write, we're not ready to merge
|
||||
if (label.has_value()) return true;
|
||||
return PrepMergeOps();
|
||||
}
|
||||
|
||||
bool CowReader::ParseOps(std::optional<uint64_t> label) {
|
||||
uint64_t pos;
|
||||
auto data_loc = std::make_shared<std::unordered_map<uint64_t, uint64_t>>();
|
||||
|
||||
// Skip the scratch space
|
||||
if (header_.major_version >= 2 && (header_.buffer_size > 0)) {
|
||||
LOG(DEBUG) << " Scratch space found of size: " << header_.buffer_size;
|
||||
size_t init_offset = header_.header_size + header_.buffer_size;
|
||||
pos = lseek(fd_.get(), init_offset, SEEK_SET);
|
||||
if (pos != init_offset) {
|
||||
PLOG(ERROR) << "lseek ops failed";
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
pos = lseek(fd_.get(), header_.header_size, SEEK_SET);
|
||||
if (pos != header_.header_size) {
|
||||
PLOG(ERROR) << "lseek ops failed";
|
||||
return false;
|
||||
}
|
||||
// Reading a v1 version of COW which doesn't have buffer_size.
|
||||
header_.buffer_size = 0;
|
||||
}
|
||||
uint64_t data_pos = 0;
|
||||
|
||||
if (header_.cluster_ops) {
|
||||
data_pos = pos + header_.cluster_ops * sizeof(CowOperation);
|
||||
} else {
|
||||
data_pos = pos + sizeof(CowOperation);
|
||||
}
|
||||
|
||||
auto ops_buffer = std::make_shared<std::vector<CowOperation>>();
|
||||
uint64_t current_op_num = 0;
|
||||
uint64_t cluster_ops = header_.cluster_ops ?: 1;
|
||||
bool done = false;
|
||||
|
||||
// Alternating op clusters and data
|
||||
while (!done) {
|
||||
uint64_t to_add = std::min(cluster_ops, (fd_size_ - pos) / sizeof(CowOperation));
|
||||
if (to_add == 0) break;
|
||||
ops_buffer->resize(current_op_num + to_add);
|
||||
if (!android::base::ReadFully(fd_, &ops_buffer->data()[current_op_num],
|
||||
to_add * sizeof(CowOperation))) {
|
||||
PLOG(ERROR) << "read op failed";
|
||||
return false;
|
||||
}
|
||||
// Parse current cluster to find start of next cluster
|
||||
while (current_op_num < ops_buffer->size()) {
|
||||
auto& current_op = ops_buffer->data()[current_op_num];
|
||||
current_op_num++;
|
||||
if (current_op.type == kCowXorOp) {
|
||||
data_loc->insert({current_op.new_block, data_pos});
|
||||
}
|
||||
pos += sizeof(CowOperation) + GetNextOpOffset(current_op, header_.cluster_ops);
|
||||
data_pos += current_op.data_length + GetNextDataOffset(current_op, header_.cluster_ops);
|
||||
|
||||
if (current_op.type == kCowClusterOp) {
|
||||
break;
|
||||
} else if (current_op.type == kCowLabelOp) {
|
||||
last_label_ = {current_op.source};
|
||||
|
||||
// If we reach the requested label, stop reading.
|
||||
if (label && label.value() == current_op.source) {
|
||||
done = true;
|
||||
break;
|
||||
}
|
||||
} else if (current_op.type == kCowFooterOp) {
|
||||
footer_.emplace();
|
||||
CowFooter* footer = &footer_.value();
|
||||
memcpy(&footer_->op, ¤t_op, sizeof(footer->op));
|
||||
off_t offs = lseek(fd_.get(), pos, SEEK_SET);
|
||||
if (offs < 0 || pos != static_cast<uint64_t>(offs)) {
|
||||
PLOG(ERROR) << "lseek next op failed " << offs;
|
||||
return false;
|
||||
}
|
||||
if (!android::base::ReadFully(fd_, &footer->data, sizeof(footer->data))) {
|
||||
LOG(ERROR) << "Could not read COW footer";
|
||||
return false;
|
||||
}
|
||||
|
||||
// Drop the footer from the op stream.
|
||||
current_op_num--;
|
||||
done = true;
|
||||
break;
|
||||
} else if (current_op.type == kCowSequenceOp) {
|
||||
has_seq_ops_ = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Position for next cluster read
|
||||
off_t offs = lseek(fd_.get(), pos, SEEK_SET);
|
||||
if (offs < 0 || pos != static_cast<uint64_t>(offs)) {
|
||||
PLOG(ERROR) << "lseek next op failed " << offs;
|
||||
return false;
|
||||
}
|
||||
ops_buffer->resize(current_op_num);
|
||||
}
|
||||
|
||||
LOG(DEBUG) << "COW file read complete. Total ops: " << ops_buffer->size();
|
||||
// To successfully parse a COW file, we need either:
|
||||
// (1) a label to read up to, and for that label to be found, or
|
||||
// (2) a valid footer.
|
||||
if (label) {
|
||||
if (!last_label_) {
|
||||
LOG(ERROR) << "Did not find label " << label.value()
|
||||
<< " while reading COW (no labels found)";
|
||||
return false;
|
||||
}
|
||||
if (last_label_.value() != label.value()) {
|
||||
LOG(ERROR) << "Did not find label " << label.value()
|
||||
<< ", last label=" << last_label_.value();
|
||||
return false;
|
||||
}
|
||||
} else if (!footer_) {
|
||||
LOG(ERROR) << "No COW footer found";
|
||||
return false;
|
||||
}
|
||||
|
||||
uint8_t csum[32];
|
||||
memset(csum, 0, sizeof(uint8_t) * 32);
|
||||
|
||||
if (footer_) {
|
||||
if (ops_buffer->size() != footer_->op.num_ops) {
|
||||
LOG(ERROR) << "num ops does not match, expected " << footer_->op.num_ops << ", found "
|
||||
<< ops_buffer->size();
|
||||
return false;
|
||||
}
|
||||
if (ops_buffer->size() * sizeof(CowOperation) != footer_->op.ops_size) {
|
||||
LOG(ERROR) << "ops size does not match ";
|
||||
return false;
|
||||
}
|
||||
SHA256(&footer_->op, sizeof(footer_->op), footer_->data.footer_checksum);
|
||||
if (memcmp(csum, footer_->data.ops_checksum, sizeof(csum)) != 0) {
|
||||
LOG(ERROR) << "ops checksum does not match";
|
||||
return false;
|
||||
}
|
||||
SHA256(ops_buffer->data(), footer_->op.ops_size, csum);
|
||||
if (memcmp(csum, footer_->data.ops_checksum, sizeof(csum)) != 0) {
|
||||
LOG(ERROR) << "ops checksum does not match";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
ops_ = ops_buffer;
|
||||
ops_->shrink_to_fit();
|
||||
data_loc_ = data_loc;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//
|
||||
// This sets up the data needed for MergeOpIter. MergeOpIter presents
|
||||
// data in the order we intend to merge in.
|
||||
|
@ -446,7 +245,8 @@ bool CowReader::PrepMergeOps() {
|
|||
continue;
|
||||
}
|
||||
|
||||
if (!has_seq_ops_ && IsOrderedOp(current_op)) {
|
||||
// Sequence ops must be the first ops in the stream.
|
||||
if (seq_ops_set.empty() && IsOrderedOp(current_op)) {
|
||||
merge_op_blocks->emplace_back(current_op.new_block);
|
||||
} else if (seq_ops_set.count(current_op.new_block) == 0) {
|
||||
other_ops.push_back(current_op.new_block);
|
||||
|
@ -718,8 +518,8 @@ std::unique_ptr<ICowOpIter> CowReader::GetMergeOpIter(bool ignore_progress) {
|
|||
|
||||
bool CowReader::GetRawBytes(uint64_t offset, void* buffer, size_t len, size_t* read) {
|
||||
// Validate the offset, taking care to acknowledge possible overflow of offset+len.
|
||||
if (offset < header_.header_size || offset >= fd_size_ - sizeof(CowFooter) || len >= fd_size_ ||
|
||||
offset + len > fd_size_ - sizeof(CowFooter)) {
|
||||
if (offset < header_.prefix.header_size || offset >= fd_size_ - sizeof(CowFooter) ||
|
||||
len >= fd_size_ || offset + len > fd_size_ - sizeof(CowFooter)) {
|
||||
LOG(ERROR) << "invalid data offset: " << offset << ", " << len << " bytes";
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -186,10 +186,10 @@ void CowWriter::SetupWriteOptions() {
|
|||
|
||||
void CowWriter::SetupHeaders() {
|
||||
header_ = {};
|
||||
header_.magic = kCowMagicNumber;
|
||||
header_.major_version = kCowVersionMajor;
|
||||
header_.minor_version = kCowVersionMinor;
|
||||
header_.header_size = sizeof(CowHeader);
|
||||
header_.prefix.magic = kCowMagicNumber;
|
||||
header_.prefix.major_version = kCowVersionMajor;
|
||||
header_.prefix.minor_version = kCowVersionMinor;
|
||||
header_.prefix.header_size = sizeof(CowHeader);
|
||||
header_.footer_size = sizeof(CowFooter);
|
||||
header_.op_size = sizeof(CowOperation);
|
||||
header_.block_size = options_.block_size;
|
||||
|
@ -614,17 +614,6 @@ bool CowWriter::EmitClusterIfNeeded() {
|
|||
return true;
|
||||
}
|
||||
|
||||
// TODO: Fix compilation issues when linking libcrypto library
|
||||
// when snapuserd is compiled as part of ramdisk.
|
||||
static void SHA256(const void*, size_t, uint8_t[]) {
|
||||
#if 0
|
||||
SHA256_CTX c;
|
||||
SHA256_Init(&c);
|
||||
SHA256_Update(&c, data, length);
|
||||
SHA256_Final(out, &c);
|
||||
#endif
|
||||
}
|
||||
|
||||
bool CowWriter::Finalize() {
|
||||
if (!FlushCluster()) {
|
||||
LOG(ERROR) << "Finalize: FlushCluster() failed";
|
||||
|
@ -665,10 +654,8 @@ bool CowWriter::Finalize() {
|
|||
PLOG(ERROR) << "Failed to seek to footer position.";
|
||||
return false;
|
||||
}
|
||||
memset(&footer_.data.ops_checksum, 0, sizeof(uint8_t) * 32);
|
||||
memset(&footer_.data.footer_checksum, 0, sizeof(uint8_t) * 32);
|
||||
memset(&footer_.unused, 0, sizeof(footer_.unused));
|
||||
|
||||
SHA256(&footer_.op, sizeof(footer_.op), footer_.data.footer_checksum);
|
||||
// Write out footer at end of file
|
||||
if (!android::base::WriteFully(fd_, reinterpret_cast<const uint8_t*>(&footer_),
|
||||
sizeof(footer_))) {
|
||||
|
|
|
@ -104,8 +104,9 @@ static bool Inspect(const std::string& path, Options opt) {
|
|||
if (reader.GetFooter(&footer)) has_footer = true;
|
||||
|
||||
if (!opt.silent) {
|
||||
std::cout << "Version: " << header.major_version << "." << header.minor_version << "\n";
|
||||
std::cout << "Header size: " << header.header_size << "\n";
|
||||
std::cout << "Version: " << header.prefix.major_version << "."
|
||||
<< header.prefix.minor_version << "\n";
|
||||
std::cout << "Header size: " << header.prefix.header_size << "\n";
|
||||
std::cout << "Footer size: " << header.footer_size << "\n";
|
||||
std::cout << "Block size: " << header.block_size << "\n";
|
||||
std::cout << "Merge ops: " << header.num_merge_ops << "\n";
|
||||
|
|
238
fs_mgr/libsnapshot/libsnapshot_cow/parser_v2.cpp
Normal file
238
fs_mgr/libsnapshot/libsnapshot_cow/parser_v2.cpp
Normal file
|
@ -0,0 +1,238 @@
|
|||
// Copyright (C) 2023 The Android Open Source Project
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
#include "parser_v2.h"
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
#include <android-base/file.h>
|
||||
#include <android-base/logging.h>
|
||||
|
||||
namespace android {
|
||||
namespace snapshot {
|
||||
|
||||
using android::base::borrowed_fd;
|
||||
|
||||
bool ReadCowHeader(android::base::borrowed_fd fd, CowHeader* header) {
|
||||
if (lseek(fd.get(), 0, SEEK_SET) < 0) {
|
||||
PLOG(ERROR) << "lseek header failed";
|
||||
return false;
|
||||
}
|
||||
|
||||
memset(header, 0, sizeof(*header));
|
||||
|
||||
if (!android::base::ReadFully(fd, &header->prefix, sizeof(header->prefix))) {
|
||||
return false;
|
||||
}
|
||||
if (header->prefix.magic != kCowMagicNumber) {
|
||||
LOG(ERROR) << "Header Magic corrupted. Magic: " << header->prefix.magic
|
||||
<< "Expected: " << kCowMagicNumber;
|
||||
return false;
|
||||
}
|
||||
if (header->prefix.header_size > sizeof(CowHeader)) {
|
||||
LOG(ERROR) << "Unknown CowHeader size (got " << header->prefix.header_size
|
||||
<< " bytes, expected at most " << sizeof(CowHeader) << " bytes)";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (lseek(fd.get(), 0, SEEK_SET) < 0) {
|
||||
PLOG(ERROR) << "lseek header failed";
|
||||
return false;
|
||||
}
|
||||
return android::base::ReadFully(fd, header, header->prefix.header_size);
|
||||
}
|
||||
|
||||
bool CowParserV2::Parse(borrowed_fd fd, const CowHeader& header, std::optional<uint64_t> label) {
|
||||
auto pos = lseek(fd.get(), 0, SEEK_END);
|
||||
if (pos < 0) {
|
||||
PLOG(ERROR) << "lseek end failed";
|
||||
return false;
|
||||
}
|
||||
fd_size_ = pos;
|
||||
header_ = header;
|
||||
|
||||
if (header_.footer_size != sizeof(CowFooter)) {
|
||||
LOG(ERROR) << "Footer size unknown, read " << header_.footer_size << ", expected "
|
||||
<< sizeof(CowFooter);
|
||||
return false;
|
||||
}
|
||||
if (header_.op_size != sizeof(CowOperation)) {
|
||||
LOG(ERROR) << "Operation size unknown, read " << header_.op_size << ", expected "
|
||||
<< sizeof(CowOperation);
|
||||
return false;
|
||||
}
|
||||
if (header_.cluster_ops == 1) {
|
||||
LOG(ERROR) << "Clusters must contain at least two operations to function.";
|
||||
return false;
|
||||
}
|
||||
if (header_.op_size != sizeof(CowOperation)) {
|
||||
LOG(ERROR) << "Operation size unknown, read " << header_.op_size << ", expected "
|
||||
<< sizeof(CowOperation);
|
||||
return false;
|
||||
}
|
||||
if (header_.cluster_ops == 1) {
|
||||
LOG(ERROR) << "Clusters must contain at least two operations to function.";
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((header_.prefix.major_version > kCowVersionMajor) ||
|
||||
(header_.prefix.minor_version != kCowVersionMinor)) {
|
||||
LOG(ERROR) << "Header version mismatch, "
|
||||
<< "major version: " << header_.prefix.major_version
|
||||
<< ", expected: " << kCowVersionMajor
|
||||
<< ", minor version: " << header_.prefix.minor_version
|
||||
<< ", expected: " << kCowVersionMinor;
|
||||
return false;
|
||||
}
|
||||
|
||||
return ParseOps(fd, label);
|
||||
}
|
||||
|
||||
bool CowParserV2::ParseOps(borrowed_fd fd, std::optional<uint64_t> label) {
|
||||
uint64_t pos;
|
||||
auto data_loc = std::make_shared<std::unordered_map<uint64_t, uint64_t>>();
|
||||
|
||||
// Skip the scratch space
|
||||
if (header_.prefix.major_version >= 2 && (header_.buffer_size > 0)) {
|
||||
LOG(DEBUG) << " Scratch space found of size: " << header_.buffer_size;
|
||||
size_t init_offset = header_.prefix.header_size + header_.buffer_size;
|
||||
pos = lseek(fd.get(), init_offset, SEEK_SET);
|
||||
if (pos != init_offset) {
|
||||
PLOG(ERROR) << "lseek ops failed";
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
pos = lseek(fd.get(), header_.prefix.header_size, SEEK_SET);
|
||||
if (pos != header_.prefix.header_size) {
|
||||
PLOG(ERROR) << "lseek ops failed";
|
||||
return false;
|
||||
}
|
||||
// Reading a v1 version of COW which doesn't have buffer_size.
|
||||
header_.buffer_size = 0;
|
||||
}
|
||||
uint64_t data_pos = 0;
|
||||
|
||||
if (header_.cluster_ops) {
|
||||
data_pos = pos + header_.cluster_ops * sizeof(CowOperation);
|
||||
} else {
|
||||
data_pos = pos + sizeof(CowOperation);
|
||||
}
|
||||
|
||||
auto ops_buffer = std::make_shared<std::vector<CowOperation>>();
|
||||
uint64_t current_op_num = 0;
|
||||
uint64_t cluster_ops = header_.cluster_ops ?: 1;
|
||||
bool done = false;
|
||||
|
||||
// Alternating op clusters and data
|
||||
while (!done) {
|
||||
uint64_t to_add = std::min(cluster_ops, (fd_size_ - pos) / sizeof(CowOperation));
|
||||
if (to_add == 0) break;
|
||||
ops_buffer->resize(current_op_num + to_add);
|
||||
if (!android::base::ReadFully(fd, &ops_buffer->data()[current_op_num],
|
||||
to_add * sizeof(CowOperation))) {
|
||||
PLOG(ERROR) << "read op failed";
|
||||
return false;
|
||||
}
|
||||
// Parse current cluster to find start of next cluster
|
||||
while (current_op_num < ops_buffer->size()) {
|
||||
auto& current_op = ops_buffer->data()[current_op_num];
|
||||
current_op_num++;
|
||||
if (current_op.type == kCowXorOp) {
|
||||
data_loc->insert({current_op.new_block, data_pos});
|
||||
}
|
||||
pos += sizeof(CowOperation) + GetNextOpOffset(current_op, header_.cluster_ops);
|
||||
data_pos += current_op.data_length + GetNextDataOffset(current_op, header_.cluster_ops);
|
||||
|
||||
if (current_op.type == kCowClusterOp) {
|
||||
break;
|
||||
} else if (current_op.type == kCowLabelOp) {
|
||||
last_label_ = {current_op.source};
|
||||
|
||||
// If we reach the requested label, stop reading.
|
||||
if (label && label.value() == current_op.source) {
|
||||
done = true;
|
||||
break;
|
||||
}
|
||||
} else if (current_op.type == kCowFooterOp) {
|
||||
footer_.emplace();
|
||||
CowFooter* footer = &footer_.value();
|
||||
memcpy(&footer_->op, ¤t_op, sizeof(footer->op));
|
||||
off_t offs = lseek(fd.get(), pos, SEEK_SET);
|
||||
if (offs < 0 || pos != static_cast<uint64_t>(offs)) {
|
||||
PLOG(ERROR) << "lseek next op failed " << offs;
|
||||
return false;
|
||||
}
|
||||
if (!android::base::ReadFully(fd, &footer->unused, sizeof(footer->unused))) {
|
||||
LOG(ERROR) << "Could not read COW footer";
|
||||
return false;
|
||||
}
|
||||
|
||||
// Drop the footer from the op stream.
|
||||
current_op_num--;
|
||||
done = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Position for next cluster read
|
||||
off_t offs = lseek(fd.get(), pos, SEEK_SET);
|
||||
if (offs < 0 || pos != static_cast<uint64_t>(offs)) {
|
||||
PLOG(ERROR) << "lseek next op failed " << offs;
|
||||
return false;
|
||||
}
|
||||
ops_buffer->resize(current_op_num);
|
||||
}
|
||||
|
||||
LOG(DEBUG) << "COW file read complete. Total ops: " << ops_buffer->size();
|
||||
// To successfully parse a COW file, we need either:
|
||||
// (1) a label to read up to, and for that label to be found, or
|
||||
// (2) a valid footer.
|
||||
if (label) {
|
||||
if (!last_label_) {
|
||||
LOG(ERROR) << "Did not find label " << label.value()
|
||||
<< " while reading COW (no labels found)";
|
||||
return false;
|
||||
}
|
||||
if (last_label_.value() != label.value()) {
|
||||
LOG(ERROR) << "Did not find label " << label.value()
|
||||
<< ", last label=" << last_label_.value();
|
||||
return false;
|
||||
}
|
||||
} else if (!footer_) {
|
||||
LOG(ERROR) << "No COW footer found";
|
||||
return false;
|
||||
}
|
||||
|
||||
uint8_t csum[32];
|
||||
memset(csum, 0, sizeof(uint8_t) * 32);
|
||||
|
||||
if (footer_) {
|
||||
if (ops_buffer->size() != footer_->op.num_ops) {
|
||||
LOG(ERROR) << "num ops does not match, expected " << footer_->op.num_ops << ", found "
|
||||
<< ops_buffer->size();
|
||||
return false;
|
||||
}
|
||||
if (ops_buffer->size() * sizeof(CowOperation) != footer_->op.ops_size) {
|
||||
LOG(ERROR) << "ops size does not match ";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
ops_ = ops_buffer;
|
||||
ops_->shrink_to_fit();
|
||||
data_loc_ = data_loc;
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace snapshot
|
||||
} // namespace android
|
55
fs_mgr/libsnapshot/libsnapshot_cow/parser_v2.h
Normal file
55
fs_mgr/libsnapshot/libsnapshot_cow/parser_v2.h
Normal file
|
@ -0,0 +1,55 @@
|
|||
// Copyright (C) 2023 The Android Open Source Project
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
#include <android-base/unique_fd.h>
|
||||
#include <libsnapshot/cow_format.h>
|
||||
|
||||
namespace android {
|
||||
namespace snapshot {
|
||||
|
||||
class CowParserV2 {
|
||||
public:
|
||||
bool Parse(android::base::borrowed_fd fd, const CowHeader& header,
|
||||
std::optional<uint64_t> label = {});
|
||||
|
||||
const CowHeader& header() const { return header_; }
|
||||
const std::optional<CowFooter>& footer() const { return footer_; }
|
||||
std::shared_ptr<std::vector<CowOperation>> ops() { return ops_; }
|
||||
std::shared_ptr<std::unordered_map<uint64_t, uint64_t>> data_loc() const { return data_loc_; }
|
||||
uint64_t fd_size() const { return fd_size_; }
|
||||
const std::optional<uint64_t>& last_label() const { return last_label_; }
|
||||
|
||||
private:
|
||||
bool ParseOps(android::base::borrowed_fd fd, std::optional<uint64_t> label);
|
||||
|
||||
CowHeader header_ = {};
|
||||
std::optional<CowFooter> footer_;
|
||||
std::shared_ptr<std::vector<CowOperation>> ops_;
|
||||
std::shared_ptr<std::unordered_map<uint64_t, uint64_t>> data_loc_;
|
||||
uint64_t fd_size_;
|
||||
std::optional<uint64_t> last_label_;
|
||||
};
|
||||
|
||||
bool ReadCowHeader(android::base::borrowed_fd fd, CowHeader* header);
|
||||
|
||||
} // namespace snapshot
|
||||
} // namespace android
|
|
@ -628,8 +628,8 @@ bool Snapuserd::ReadMetadata() {
|
|||
bool Snapuserd::MmapMetadata() {
|
||||
const auto& header = reader_->GetHeader();
|
||||
|
||||
if (header.major_version >= 2 && header.buffer_size > 0) {
|
||||
total_mapped_addr_length_ = header.header_size + BUFFER_REGION_DEFAULT_SIZE;
|
||||
if (header.prefix.major_version >= 2 && header.buffer_size > 0) {
|
||||
total_mapped_addr_length_ = header.prefix.header_size + BUFFER_REGION_DEFAULT_SIZE;
|
||||
read_ahead_feature_ = true;
|
||||
} else {
|
||||
// mmap the first 4k page - older COW format
|
||||
|
@ -823,7 +823,7 @@ bool Snapuserd::Start() {
|
|||
uint64_t Snapuserd::GetBufferMetadataOffset() {
|
||||
const auto& header = reader_->GetHeader();
|
||||
|
||||
size_t size = header.header_size + sizeof(BufferState);
|
||||
size_t size = header.prefix.header_size + sizeof(BufferState);
|
||||
return size;
|
||||
}
|
||||
|
||||
|
@ -845,7 +845,7 @@ size_t Snapuserd::GetBufferMetadataSize() {
|
|||
size_t Snapuserd::GetBufferDataOffset() {
|
||||
const auto& header = reader_->GetHeader();
|
||||
|
||||
return (header.header_size + GetBufferMetadataSize());
|
||||
return (header.prefix.header_size + GetBufferMetadataSize());
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -862,7 +862,7 @@ struct BufferState* Snapuserd::GetBufferState() {
|
|||
const auto& header = reader_->GetHeader();
|
||||
|
||||
struct BufferState* ra_state =
|
||||
reinterpret_cast<struct BufferState*>((char*)mapped_addr_ + header.header_size);
|
||||
reinterpret_cast<struct BufferState*>((char*)mapped_addr_ + header.prefix.header_size);
|
||||
return ra_state;
|
||||
}
|
||||
|
||||
|
|
|
@ -240,9 +240,9 @@ bool SnapshotHandler::ReadMetadata() {
|
|||
bool SnapshotHandler::MmapMetadata() {
|
||||
const auto& header = reader_->GetHeader();
|
||||
|
||||
total_mapped_addr_length_ = header.header_size + BUFFER_REGION_DEFAULT_SIZE;
|
||||
total_mapped_addr_length_ = header.prefix.header_size + BUFFER_REGION_DEFAULT_SIZE;
|
||||
|
||||
if (header.major_version >= 2 && header.buffer_size > 0) {
|
||||
if (header.prefix.major_version >= 2 && header.buffer_size > 0) {
|
||||
scratch_space_ = true;
|
||||
}
|
||||
|
||||
|
@ -362,7 +362,7 @@ bool SnapshotHandler::Start() {
|
|||
uint64_t SnapshotHandler::GetBufferMetadataOffset() {
|
||||
const auto& header = reader_->GetHeader();
|
||||
|
||||
return (header.header_size + sizeof(BufferState));
|
||||
return (header.prefix.header_size + sizeof(BufferState));
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -390,7 +390,7 @@ size_t SnapshotHandler::GetBufferMetadataSize() {
|
|||
size_t SnapshotHandler::GetBufferDataOffset() {
|
||||
const auto& header = reader_->GetHeader();
|
||||
|
||||
return (header.header_size + GetBufferMetadataSize());
|
||||
return (header.prefix.header_size + GetBufferMetadataSize());
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -413,7 +413,7 @@ struct BufferState* SnapshotHandler::GetBufferState() {
|
|||
const auto& header = reader_->GetHeader();
|
||||
|
||||
struct BufferState* ra_state =
|
||||
reinterpret_cast<struct BufferState*>((char*)mapped_addr_ + header.header_size);
|
||||
reinterpret_cast<struct BufferState*>((char*)mapped_addr_ + header.prefix.header_size);
|
||||
return ra_state;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue