From e5a343b1af1abaf27c56ec69c71b79b0e80d3102 Mon Sep 17 00:00:00 2001 From: David Anderson Date: Mon, 23 Oct 2023 20:10:37 -0700 Subject: [PATCH] libsnapshot: Add Parser base class Add in Parser base class and refactor code to work with it. Base class will have ops() method which returns a vector of v3 operations. v2_parser translates it's v2 operations to v3 operations with this method. Bug: 307452468 Test: Test with critical OTA paths? Change-Id: I52d0d0554973714189a6e1013d026f96503238b6 --- .../include/libsnapshot/cow_reader.h | 1 - .../libsnapshot_cow/cow_reader.cpp | 76 ++++++++----------- .../libsnapshot_cow/inspect_cow.cpp | 4 +- .../libsnapshot/libsnapshot_cow/parser_base.h | 53 +++++++++++++ .../libsnapshot/libsnapshot_cow/parser_v2.cpp | 57 ++++++++++++-- .../libsnapshot/libsnapshot_cow/parser_v2.h | 27 +++---- .../libsnapshot/libsnapshot_cow/writer_v2.cpp | 5 +- .../user-space-merge/snapuserd_core.cpp | 2 +- 8 files changed, 152 insertions(+), 73 deletions(-) create mode 100644 fs_mgr/libsnapshot/libsnapshot_cow/parser_base.h diff --git a/fs_mgr/libsnapshot/include/libsnapshot/cow_reader.h b/fs_mgr/libsnapshot/include/libsnapshot/cow_reader.h index debaf3683..4cbcab137 100644 --- a/fs_mgr/libsnapshot/include/libsnapshot/cow_reader.h +++ b/fs_mgr/libsnapshot/include/libsnapshot/cow_reader.h @@ -185,7 +185,6 @@ class CowReader final : public ICowReader { std::shared_ptr> 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. diff --git a/fs_mgr/libsnapshot/libsnapshot_cow/cow_reader.cpp b/fs_mgr/libsnapshot/libsnapshot_cow/cow_reader.cpp index 3b84c95cc..6f1854d0a 100644 --- a/fs_mgr/libsnapshot/libsnapshot_cow/cow_reader.cpp +++ b/fs_mgr/libsnapshot/libsnapshot_cow/cow_reader.cpp @@ -28,7 +28,6 @@ #include #include "cow_decompress.h" -#include "libsnapshot/cow_format.h" #include "parser_v2.h" namespace android { @@ -85,7 +84,6 @@ std::unique_ptr 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 lab return false; } - CowParserV2 parser; - if (!parser.Parse(fd, header_, label)) { + std::unique_ptr parser; + switch (header_.prefix.major_version) { + case 1: + case 2: + parser = std::make_unique(); + 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>(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::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, diff --git a/fs_mgr/libsnapshot/libsnapshot_cow/inspect_cow.cpp b/fs_mgr/libsnapshot/libsnapshot_cow/inspect_cow.cpp index a29146985..76509de78 100644 --- a/fs_mgr/libsnapshot/libsnapshot_cow/inspect_cow.cpp +++ b/fs_mgr/libsnapshot/libsnapshot_cow/inspect_cow.cpp @@ -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"; diff --git a/fs_mgr/libsnapshot/libsnapshot_cow/parser_base.h b/fs_mgr/libsnapshot/libsnapshot_cow/parser_base.h new file mode 100644 index 000000000..278340740 --- /dev/null +++ b/fs_mgr/libsnapshot/libsnapshot_cow/parser_base.h @@ -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 +#include + +#include +#include + +namespace android { +namespace snapshot { + +struct TranslatedCowOps { + CowHeaderV3 header; + std::shared_ptr> ops; +}; + +class CowParserBase { + public: + virtual ~CowParserBase() = default; + + virtual bool Parse(android::base::borrowed_fd fd, const CowHeaderV3& header, + std::optional label = {}) = 0; + virtual bool Translate(TranslatedCowOps* out) = 0; + virtual std::optional footer() const { return std::nullopt; } + virtual std::shared_ptr> data_loc() const = 0; + + uint64_t fd_size() const { return fd_size_; } + const std::optional& last_label() const { return last_label_; } + + protected: + CowHeaderV3 header_ = {}; + uint64_t fd_size_; + std::optional last_label_; +}; + +} // namespace snapshot +} // namespace android diff --git a/fs_mgr/libsnapshot/libsnapshot_cow/parser_v2.cpp b/fs_mgr/libsnapshot/libsnapshot_cow/parser_v2.cpp index 8f2031783..51489c83a 100644 --- a/fs_mgr/libsnapshot/libsnapshot_cow/parser_v2.cpp +++ b/fs_mgr/libsnapshot/libsnapshot_cow/parser_v2.cpp @@ -18,12 +18,14 @@ #include #include +#include + namespace android { namespace snapshot { using android::base::borrowed_fd; -bool CowParserV2::Parse(borrowed_fd fd, const CowHeader& header, std::optional label) { +bool CowParserV2::Parse(borrowed_fd fd, const CowHeaderV3& header, std::optional 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 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 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>(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::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> CowParserV2::data_loc() const { + return data_loc_; +} + } // namespace snapshot } // namespace android diff --git a/fs_mgr/libsnapshot/libsnapshot_cow/parser_v2.h b/fs_mgr/libsnapshot/libsnapshot_cow/parser_v2.h index f51ff88cb..a70c780e4 100644 --- a/fs_mgr/libsnapshot/libsnapshot_cow/parser_v2.h +++ b/fs_mgr/libsnapshot/libsnapshot_cow/parser_v2.h @@ -17,36 +17,31 @@ #include #include -#include #include #include #include +#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 label = {}); + bool Parse(android::base::borrowed_fd fd, const CowHeaderV3& header, + std::optional label = {}) override; + bool Translate(TranslatedCowOps* out) override; + std::optional footer() const override { return footer_; } const CowHeader& header() const { return header_; } - const std::optional& footer() const { return footer_; } - std::shared_ptr> ops() { return ops_; } - std::shared_ptr> data_loc() const { return data_loc_; } - uint64_t fd_size() const { return fd_size_; } - const std::optional& last_label() const { return last_label_; } + std::shared_ptr> get_v2ops() { return v2_ops_; } + std::shared_ptr> data_loc() const override; private: - bool ParseOps(android::base::borrowed_fd fd, std::optional label); - - CowHeader header_ = {}; - std::optional footer_; - std::shared_ptr> ops_; std::shared_ptr> data_loc_; - uint64_t fd_size_; - std::optional last_label_; + bool ParseOps(android::base::borrowed_fd fd, std::optional label); + std::shared_ptr> v2_ops_; + std::optional footer_; }; } // namespace snapshot diff --git a/fs_mgr/libsnapshot/libsnapshot_cow/writer_v2.cpp b/fs_mgr/libsnapshot/libsnapshot_cow/writer_v2.cpp index 83a9b1b22..37324c767 100644 --- a/fs_mgr/libsnapshot/libsnapshot_cow/writer_v2.cpp +++ b/fs_mgr/libsnapshot/libsnapshot_cow/writer_v2.cpp @@ -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); } diff --git a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_core.cpp b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_core.cpp index 8208d67c8..950d7719d 100644 --- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_core.cpp +++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_core.cpp @@ -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; }