Merge "libsnapshot: Add Parser base class" into main

This commit is contained in:
Daniel Zheng 2023-11-02 20:07:50 +00:00 committed by Gerrit Code Review
commit cb618c18c9
8 changed files with 152 additions and 73 deletions

View file

@ -185,7 +185,6 @@ class CowReader final : public ICowReader {
std::shared_ptr<std::unordered_map<uint64_t, uint64_t>> data_loc_;
ReaderFlags reader_flag_;
bool is_merge_{};
uint8_t compression_type_ = kCowCompressNone;
};
// Though this function takes in a CowHeaderV3, the struct could be populated as a v1/v2 CowHeader.

View file

@ -28,7 +28,6 @@
#include <zlib.h>
#include "cow_decompress.h"
#include "libsnapshot/cow_format.h"
#include "parser_v2.h"
namespace android {
@ -85,7 +84,6 @@ std::unique_ptr<CowReader> CowReader::CloneCowReader() {
cow->data_loc_ = data_loc_;
cow->block_pos_index_ = block_pos_index_;
cow->is_merge_ = is_merge_;
cow->compression_type_ = compression_type_;
return cow;
}
@ -104,11 +102,14 @@ bool CowReader::InitForMerge(android::base::unique_fd&& fd) {
PLOG(ERROR) << "lseek header failed";
return false;
}
if (!android::base::ReadFully(fd_, &header_, sizeof(header_))) {
CHECK_GE(header_.prefix.header_size, sizeof(CowHeader));
CHECK_LE(header_.prefix.header_size, sizeof(header_));
if (!android::base::ReadFully(fd_, &header_, header_.prefix.header_size)) {
PLOG(ERROR) << "read header failed";
return false;
}
return true;
}
@ -124,52 +125,35 @@ bool CowReader::Parse(android::base::borrowed_fd fd, std::optional<uint64_t> lab
return false;
}
CowParserV2 parser;
if (!parser.Parse(fd, header_, label)) {
std::unique_ptr<CowParserBase> parser;
switch (header_.prefix.major_version) {
case 1:
case 2:
parser = std::make_unique<CowParserV2>();
break;
case 3:
break;
default:
LOG(ERROR) << "Unknown version: " << header_.prefix.major_version;
return false;
}
if (!parser->Parse(fd, header_, label)) {
return false;
}
footer_ = parser.footer();
fd_size_ = parser.fd_size();
last_label_ = parser.last_label();
data_loc_ = parser.data_loc();
ops_ = std::make_shared<std::vector<CowOperation>>(parser.ops()->size());
// Translate the operation buffer from on disk to in memory
for (size_t i = 0; i < parser.ops()->size(); i++) {
const auto& v2_op = parser.ops()->at(i);
auto& new_op = ops_->at(i);
new_op.type = v2_op.type;
new_op.data_length = v2_op.data_length;
if (v2_op.new_block > std::numeric_limits<uint32_t>::max()) {
LOG(ERROR) << "Out-of-range new block in COW op: " << v2_op;
return false;
}
new_op.new_block = v2_op.new_block;
uint64_t source_info = v2_op.source;
if (new_op.type != kCowLabelOp) {
source_info &= kCowOpSourceInfoDataMask;
if (source_info != v2_op.source) {
LOG(ERROR) << "Out-of-range source value in COW op: " << v2_op;
return false;
}
}
if (v2_op.compression != kCowCompressNone) {
if (compression_type_ == kCowCompressNone) {
compression_type_ = v2_op.compression;
} else if (compression_type_ != v2_op.compression) {
LOG(ERROR) << "COW has mixed compression types which is not supported;"
<< " previously saw " << compression_type_ << ", got "
<< v2_op.compression << ", op: " << v2_op;
return false;
}
}
new_op.source_info = source_info;
TranslatedCowOps ops_info;
if (!parser->Translate(&ops_info)) {
return false;
}
header_ = ops_info.header;
ops_ = std::move(ops_info.ops);
footer_ = parser->footer();
fd_size_ = parser->fd_size();
last_label_ = parser->last_label();
data_loc_ = parser->data_loc();
// If we're resuming a write, we're not ready to merge
if (label.has_value()) return true;
return PrepMergeOps();
@ -664,7 +648,7 @@ class CowDataStream final : public IByteStream {
};
uint8_t CowReader::GetCompressionType() {
return compression_type_;
return header_.compression_algorithm;
}
ssize_t CowReader::ReadData(const CowOperation* op, void* buffer, size_t buffer_size,

View file

@ -74,13 +74,13 @@ static void ShowBad(CowReader& reader, const CowOperation* op) {
}
}
static bool ShowRawOpStreamV2(borrowed_fd fd, const CowHeader& header) {
static bool ShowRawOpStreamV2(borrowed_fd fd, const CowHeaderV3& header) {
CowParserV2 parser;
if (!parser.Parse(fd, header)) {
LOG(ERROR) << "v2 parser failed";
return false;
}
for (const auto& op : *parser.ops()) {
for (const auto& op : *parser.get_v2ops()) {
std::cout << op << "\n";
if (auto iter = parser.data_loc()->find(op.new_block); iter != parser.data_loc()->end()) {
std::cout << " data loc: " << iter->second << "\n";

View file

@ -0,0 +1,53 @@
//
// 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 <optional>
#include <unordered_map>
#include <android-base/unique_fd.h>
#include <libsnapshot/cow_format.h>
namespace android {
namespace snapshot {
struct TranslatedCowOps {
CowHeaderV3 header;
std::shared_ptr<std::vector<CowOperationV3>> ops;
};
class CowParserBase {
public:
virtual ~CowParserBase() = default;
virtual bool Parse(android::base::borrowed_fd fd, const CowHeaderV3& header,
std::optional<uint64_t> label = {}) = 0;
virtual bool Translate(TranslatedCowOps* out) = 0;
virtual std::optional<CowFooter> footer() const { return std::nullopt; }
virtual std::shared_ptr<std::unordered_map<uint64_t, uint64_t>> data_loc() const = 0;
uint64_t fd_size() const { return fd_size_; }
const std::optional<uint64_t>& last_label() const { return last_label_; }
protected:
CowHeaderV3 header_ = {};
uint64_t fd_size_;
std::optional<uint64_t> last_label_;
};
} // namespace snapshot
} // namespace android

View file

@ -18,12 +18,14 @@
#include <android-base/file.h>
#include <android-base/logging.h>
#include <libsnapshot/cow_format.h>
namespace android {
namespace snapshot {
using android::base::borrowed_fd;
bool CowParserV2::Parse(borrowed_fd fd, const CowHeader& header, std::optional<uint64_t> label) {
bool CowParserV2::Parse(borrowed_fd fd, const CowHeaderV3& header, std::optional<uint64_t> label) {
auto pos = lseek(fd.get(), 0, SEEK_END);
if (pos < 0) {
PLOG(ERROR) << "lseek end failed";
@ -47,8 +49,7 @@ bool CowParserV2::Parse(borrowed_fd fd, const CowHeader& header, std::optional<u
return false;
}
if ((header_.prefix.major_version > kCowVersionMajor) ||
(header_.prefix.minor_version != kCowVersionMinor)) {
if (header_.prefix.major_version > 2 || header_.prefix.minor_version != 0) {
LOG(ERROR) << "Header version mismatch, "
<< "major version: " << header_.prefix.major_version
<< ", expected: " << kCowVersionMajor
@ -190,11 +191,57 @@ bool CowParserV2::ParseOps(borrowed_fd fd, std::optional<uint64_t> label) {
}
}
ops_ = ops_buffer;
ops_->shrink_to_fit();
v2_ops_ = ops_buffer;
v2_ops_->shrink_to_fit();
data_loc_ = data_loc;
return true;
}
bool CowParserV2::Translate(TranslatedCowOps* out) {
out->ops = std::make_shared<std::vector<CowOperationV3>>(v2_ops_->size());
// Translate the operation buffer from on disk to in memory
for (size_t i = 0; i < out->ops->size(); i++) {
const auto& v2_op = v2_ops_->at(i);
auto& new_op = out->ops->at(i);
new_op.type = v2_op.type;
new_op.data_length = v2_op.data_length;
if (v2_op.new_block > std::numeric_limits<uint32_t>::max()) {
LOG(ERROR) << "Out-of-range new block in COW op: " << v2_op;
return false;
}
new_op.new_block = v2_op.new_block;
uint64_t source_info = v2_op.source;
if (new_op.type != kCowLabelOp) {
source_info &= kCowOpSourceInfoDataMask;
if (source_info != v2_op.source) {
LOG(ERROR) << "Out-of-range source value in COW op: " << v2_op;
return false;
}
}
if (v2_op.compression != kCowCompressNone) {
if (header_.compression_algorithm == kCowCompressNone) {
header_.compression_algorithm = v2_op.compression;
} else if (header_.compression_algorithm != v2_op.compression) {
LOG(ERROR) << "COW has mixed compression types which is not supported;"
<< " previously saw " << header_.compression_algorithm << ", got "
<< v2_op.compression << ", op: " << v2_op;
return false;
}
}
new_op.source_info = source_info;
}
out->header = header_;
return true;
}
std::shared_ptr<std::unordered_map<uint64_t, uint64_t>> CowParserV2::data_loc() const {
return data_loc_;
}
} // namespace snapshot
} // namespace android

View file

@ -17,36 +17,31 @@
#include <memory>
#include <optional>
#include <unordered_map>
#include <vector>
#include <android-base/unique_fd.h>
#include <libsnapshot/cow_format.h>
#include "parser_base.h"
namespace android {
namespace snapshot {
class CowParserV2 {
class CowParserV2 final : public CowParserBase {
public:
bool Parse(android::base::borrowed_fd fd, const CowHeader& header,
std::optional<uint64_t> label = {});
bool Parse(android::base::borrowed_fd fd, const CowHeaderV3& header,
std::optional<uint64_t> label = {}) override;
bool Translate(TranslatedCowOps* out) override;
std::optional<CowFooter> footer() const override { return footer_; }
const CowHeader& header() const { return header_; }
const std::optional<CowFooter>& footer() const { return footer_; }
std::shared_ptr<std::vector<CowOperationV2>> 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_; }
std::shared_ptr<std::vector<CowOperationV2>> get_v2ops() { return v2_ops_; }
std::shared_ptr<std::unordered_map<uint64_t, uint64_t>> data_loc() const override;
private:
bool ParseOps(android::base::borrowed_fd fd, std::optional<uint64_t> label);
CowHeader header_ = {};
std::optional<CowFooter> footer_;
std::shared_ptr<std::vector<CowOperationV2>> ops_;
std::shared_ptr<std::unordered_map<uint64_t, uint64_t>> data_loc_;
uint64_t fd_size_;
std::optional<uint64_t> last_label_;
bool ParseOps(android::base::borrowed_fd fd, std::optional<uint64_t> label);
std::shared_ptr<std::vector<CowOperationV2>> v2_ops_;
std::optional<CowFooter> footer_;
};
} // namespace snapshot

View file

@ -273,10 +273,11 @@ bool CowWriterV2::OpenForAppend(uint64_t label) {
if (!ReadCowHeader(fd_, &header_v3)) {
return false;
}
header_ = header_v3;
CowParserV2 parser;
if (!parser.Parse(fd_, header_, {label})) {
if (!parser.Parse(fd_, header_v3, {label})) {
return false;
}
if (header_.prefix.major_version > 2) {
@ -292,7 +293,7 @@ bool CowWriterV2::OpenForAppend(uint64_t label) {
footer_.op.num_ops = 0;
InitPos();
for (const auto& op : *parser.ops()) {
for (const auto& op : *parser.get_v2ops()) {
AddOperation(op);
}

View file

@ -112,7 +112,7 @@ bool SnapshotHandler::CommitMerge(int num_merge_ops) {
return false;
}
if (!android::base::WriteFully(cow_fd_, &header, sizeof(CowHeader))) {
if (!android::base::WriteFully(cow_fd_, &header, header.prefix.header_size)) {
SNAP_PLOG(ERROR) << "Write to header failed";
return false;
}