PartitionCowCreator accounts for extra extents

dm-verity and error correction use part of the partitions using these
features to store their data.  Their data may be modified during an
update, so the COW device for the dm-snapshot must reserve some extra
space for them.
This patch extends the PartitionCowCreator data structure with the
extra_extents field that will store the (optional) additional extents,
e.g., the hash table extent for dm-verity or the error correction
extent.

Test: incremental OTA apply
Test: libsnapshot_test
Bug: 145180464
Change-Id: I387a6cc8438507ad41a85cc3400241ecaf627b8f
Signed-off-by: Alessio Balsini <balsini@google.com>
This commit is contained in:
Alessio Balsini 2019-12-02 11:46:52 +00:00 committed by Yifan Hong
parent 4ac630b562
commit 33836a6061
3 changed files with 50 additions and 19 deletions

View file

@ -62,27 +62,34 @@ bool PartitionCowCreator::HasExtent(Partition* p, Extent* e) {
return false;
}
void WriteExtent(DmSnapCowSizeCalculator* sc, const chromeos_update_engine::Extent& de,
unsigned int sectors_per_block) {
const auto block_boundary = de.start_block() + de.num_blocks();
for (auto b = de.start_block(); b < block_boundary; ++b) {
for (unsigned int s = 0; s < sectors_per_block; ++s) {
const auto sector_id = b * sectors_per_block + s;
sc->WriteSector(sector_id);
}
}
}
uint64_t PartitionCowCreator::GetCowSize() {
// WARNING: The origin partition should be READ-ONLY
const uint64_t logical_block_size = current_metadata->logical_block_size();
const unsigned int sectors_per_block = logical_block_size / kSectorSize;
DmSnapCowSizeCalculator sc(kSectorSize, kSnapshotChunkSize);
// Allocate space for extra extents (if any). These extents are those that can be
// used for error corrections or to store verity hash trees.
for (const auto& de : extra_extents) {
WriteExtent(&sc, de, sectors_per_block);
}
if (operations == nullptr) return sc.cow_size_bytes();
for (const auto& iop : *operations) {
for (const auto& de : iop.dst_extents()) {
// Skip if no blocks are written
if (de.num_blocks() == 0) continue;
// Flag all the blocks that were written
const auto block_boundary = de.start_block() + de.num_blocks();
for (auto b = de.start_block(); b < block_boundary; ++b) {
for (unsigned int s = 0; s < sectors_per_block; ++s) {
const auto sector_id = b * sectors_per_block + s;
sc.WriteSector(sector_id);
}
}
WriteExtent(&sc, de, sectors_per_block);
}
}

View file

@ -18,6 +18,7 @@
#include <optional>
#include <string>
#include <vector>
#include <liblp/builder.h>
#include <update_engine/update_metadata.pb.h>
@ -30,6 +31,7 @@ namespace snapshot {
// Helper class that creates COW for a partition.
struct PartitionCowCreator {
using Extent = android::fs_mgr::Extent;
using ChromeOSExtent = chromeos_update_engine::Extent;
using Interval = android::fs_mgr::Interval;
using MetadataBuilder = android::fs_mgr::MetadataBuilder;
using Partition = android::fs_mgr::Partition;
@ -50,6 +52,9 @@ struct PartitionCowCreator {
std::string current_suffix;
// List of operations to be applied on the partition.
const RepeatedPtrField<InstallOperation>* operations = nullptr;
// Extra extents that are going to be invalidated during the update
// process.
std::vector<ChromeOSExtent> extra_extents = {};
struct Return {
SnapshotStatus snapshot_status;

View file

@ -65,6 +65,7 @@ using android::fs_mgr::MetadataBuilder;
using android::fs_mgr::SlotNumberForSlotSuffix;
using android::hardware::boot::V1_1::MergeStatus;
using chromeos_update_engine::DeltaArchiveManifest;
using chromeos_update_engine::Extent;
using chromeos_update_engine::InstallOperation;
template <typename T>
using RepeatedPtrField = google::protobuf::RepeatedPtrField<T>;
@ -1886,12 +1887,15 @@ bool SnapshotManager::CreateUpdateSnapshots(const DeltaArchiveManifest& manifest
// these devices.
AutoDeviceList created_devices;
PartitionCowCreator cow_creator{.target_metadata = target_metadata.get(),
.target_suffix = target_suffix,
.target_partition = nullptr,
.current_metadata = current_metadata.get(),
.current_suffix = current_suffix,
.operations = nullptr};
PartitionCowCreator cow_creator{
.target_metadata = target_metadata.get(),
.target_suffix = target_suffix,
.target_partition = nullptr,
.current_metadata = current_metadata.get(),
.current_suffix = current_suffix,
.operations = nullptr,
.extra_extents = {},
};
if (!CreateUpdateSnapshotsInternal(lock.get(), manifest, &cow_creator, &created_devices,
&all_snapshot_status)) {
@ -1937,15 +1941,24 @@ bool SnapshotManager::CreateUpdateSnapshotsInternal(
}
std::map<std::string, const RepeatedPtrField<InstallOperation>*> install_operation_map;
std::map<std::string, std::vector<Extent>> extra_extents_map;
for (const auto& partition_update : manifest.partitions()) {
auto suffixed_name = partition_update.partition_name() + target_suffix;
auto&& [it, inserted] = install_operation_map.emplace(std::move(suffixed_name),
&partition_update.operations());
auto&& [it, inserted] =
install_operation_map.emplace(suffixed_name, &partition_update.operations());
if (!inserted) {
LOG(ERROR) << "Duplicated partition " << partition_update.partition_name()
<< " in update manifest.";
return false;
}
auto& extra_extents = extra_extents_map[suffixed_name];
if (partition_update.has_hash_tree_extent()) {
extra_extents.push_back(partition_update.hash_tree_extent());
}
if (partition_update.has_fec_extent()) {
extra_extents.push_back(partition_update.fec_extent());
}
}
for (auto* target_partition : ListPartitionsWithSuffix(target_metadata, target_suffix)) {
@ -1956,6 +1969,12 @@ bool SnapshotManager::CreateUpdateSnapshotsInternal(
cow_creator->operations = operations_it->second;
}
cow_creator->extra_extents.clear();
auto extra_extents_it = extra_extents_map.find(target_partition->name());
if (extra_extents_it != extra_extents_map.end()) {
cow_creator->extra_extents = std::move(extra_extents_it->second);
}
// Compute the device sizes for the partition.
auto cow_creator_ret = cow_creator->Run();
if (!cow_creator_ret.has_value()) {