libsnapshot: APIs for all partitions
Add CreateCowForUpdate / MapSnapshotDevicesForUpdate that update_engine and init can call them directly. Bug: 134536978 Test: libsnapshot_test Change-Id: If53c48855931db27454fd2893745915c77fd37f8
This commit is contained in:
parent
c02509b500
commit
c69029f2a8
10 changed files with 562 additions and 14 deletions
|
@ -40,6 +40,10 @@ bool LinearExtent::AddTo(LpMetadata* out) const {
|
|||
return true;
|
||||
}
|
||||
|
||||
Interval LinearExtent::AsInterval() const {
|
||||
return Interval(device_index(), physical_sector(), end_sector());
|
||||
}
|
||||
|
||||
bool ZeroExtent::AddTo(LpMetadata* out) const {
|
||||
out->extents.emplace_back(LpMetadataExtent{num_sectors_, LP_TARGET_TYPE_ZERO, 0, 0});
|
||||
return true;
|
||||
|
@ -96,6 +100,20 @@ void Partition::ShrinkTo(uint64_t aligned_size) {
|
|||
DCHECK(size_ == aligned_size);
|
||||
}
|
||||
|
||||
Partition Partition::GetBeginningExtents(uint64_t aligned_size) const {
|
||||
Partition p(name_, group_name_, attributes_);
|
||||
for (const auto& extent : extents_) {
|
||||
auto le = extent->AsLinearExtent();
|
||||
if (le) {
|
||||
p.AddExtent(std::make_unique<LinearExtent>(*le));
|
||||
} else {
|
||||
p.AddExtent(std::make_unique<ZeroExtent>(extent->num_sectors()));
|
||||
}
|
||||
}
|
||||
p.ShrinkTo(aligned_size);
|
||||
return p;
|
||||
}
|
||||
|
||||
uint64_t Partition::BytesOnDisk() const {
|
||||
uint64_t sectors = 0;
|
||||
for (const auto& extent : extents_) {
|
||||
|
@ -602,6 +620,10 @@ std::vector<Interval> Interval::Intersect(const std::vector<Interval>& a,
|
|||
return ret;
|
||||
}
|
||||
|
||||
std::unique_ptr<Extent> Interval::AsExtent() const {
|
||||
return std::make_unique<LinearExtent>(length(), device_index, start);
|
||||
}
|
||||
|
||||
bool MetadataBuilder::GrowPartition(Partition* partition, uint64_t aligned_size,
|
||||
const std::vector<Interval>& free_region_hint) {
|
||||
uint64_t space_needed = aligned_size - partition->size();
|
||||
|
@ -1168,5 +1190,9 @@ std::string MetadataBuilder::GetBlockDevicePartitionName(uint64_t index) const {
|
|||
: "";
|
||||
}
|
||||
|
||||
uint64_t MetadataBuilder::logical_block_size() const {
|
||||
return geometry_.logical_block_size;
|
||||
}
|
||||
|
||||
} // namespace fs_mgr
|
||||
} // namespace android
|
||||
|
|
|
@ -33,10 +33,11 @@ namespace android {
|
|||
namespace fs_mgr {
|
||||
|
||||
class LinearExtent;
|
||||
struct Interval;
|
||||
|
||||
// By default, partitions are aligned on a 1MiB boundary.
|
||||
static const uint32_t kDefaultPartitionAlignment = 1024 * 1024;
|
||||
static const uint32_t kDefaultBlockSize = 4096;
|
||||
static constexpr uint32_t kDefaultPartitionAlignment = 1024 * 1024;
|
||||
static constexpr uint32_t kDefaultBlockSize = 4096;
|
||||
|
||||
// Name of the default group in a metadata.
|
||||
static constexpr std::string_view kDefaultGroup = "default";
|
||||
|
@ -74,6 +75,8 @@ class LinearExtent final : public Extent {
|
|||
return sector >= physical_sector_ && sector < end_sector();
|
||||
}
|
||||
|
||||
Interval AsInterval() const;
|
||||
|
||||
private:
|
||||
uint32_t device_index_;
|
||||
uint64_t physical_sector_;
|
||||
|
@ -127,6 +130,12 @@ class Partition final {
|
|||
const std::vector<std::unique_ptr<Extent>>& extents() const { return extents_; }
|
||||
uint64_t size() const { return size_; }
|
||||
|
||||
// Return a copy of *this, but with extents that includes only the first
|
||||
// |aligned_size| bytes. |aligned_size| should be aligned to
|
||||
// logical_block_size() of the MetadataBuilder that this partition belongs
|
||||
// to.
|
||||
Partition GetBeginningExtents(uint64_t aligned_size) const;
|
||||
|
||||
private:
|
||||
void ShrinkTo(uint64_t aligned_size);
|
||||
void set_group_name(std::string_view group_name) { group_name_ = group_name; }
|
||||
|
@ -156,6 +165,8 @@ struct Interval {
|
|||
return (start == other.start) ? end < other.end : start < other.start;
|
||||
}
|
||||
|
||||
std::unique_ptr<Extent> AsExtent() const;
|
||||
|
||||
// Intersect |a| with |b|.
|
||||
// If no intersection, result has 0 length().
|
||||
static Interval Intersect(const Interval& a, const Interval& b);
|
||||
|
@ -325,6 +336,8 @@ class MetadataBuilder {
|
|||
// Return the list of free regions not occupied by extents in the metadata.
|
||||
std::vector<Interval> GetFreeRegions() const;
|
||||
|
||||
uint64_t logical_block_size() const;
|
||||
|
||||
private:
|
||||
MetadataBuilder();
|
||||
MetadataBuilder(const MetadataBuilder&) = delete;
|
||||
|
|
|
@ -25,10 +25,12 @@ cc_defaults {
|
|||
shared_libs: [
|
||||
"libbase",
|
||||
"liblog",
|
||||
"liblp",
|
||||
],
|
||||
static_libs: [
|
||||
"libdm",
|
||||
"libfs_mgr",
|
||||
"libfstab",
|
||||
"liblp",
|
||||
],
|
||||
whole_static_libs: [
|
||||
|
@ -49,6 +51,7 @@ filegroup {
|
|||
name: "libsnapshot_sources",
|
||||
srcs: [
|
||||
"snapshot.cpp",
|
||||
"partition_cow_creator.cpp",
|
||||
"utility.cpp",
|
||||
],
|
||||
}
|
||||
|
@ -77,6 +80,7 @@ cc_test {
|
|||
defaults: ["libsnapshot_defaults"],
|
||||
srcs: [
|
||||
"snapshot_test.cpp",
|
||||
"partition_cow_creator_test.cpp",
|
||||
"test_helpers.cpp",
|
||||
],
|
||||
shared_libs: [
|
||||
|
@ -90,5 +94,7 @@ cc_test {
|
|||
"libgmock",
|
||||
"liblp",
|
||||
"libsnapshot",
|
||||
"libsparse",
|
||||
"libz",
|
||||
],
|
||||
}
|
||||
|
|
|
@ -17,13 +17,16 @@
|
|||
#include <stdint.h>
|
||||
|
||||
#include <chrono>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <android-base/unique_fd.h>
|
||||
#include <fs_mgr_dm_linear.h>
|
||||
#include <libdm/dm.h>
|
||||
#include <libfiemap/image_manager.h>
|
||||
#include <liblp/builder.h>
|
||||
#include <liblp/liblp.h>
|
||||
|
||||
#ifndef FRIEND_TEST
|
||||
|
@ -45,6 +48,10 @@ class IPartitionOpener;
|
|||
|
||||
namespace snapshot {
|
||||
|
||||
struct AutoDeleteCowImage;
|
||||
struct AutoDeleteSnapshot;
|
||||
struct PartitionCowCreator;
|
||||
|
||||
enum class UpdateState : unsigned int {
|
||||
// No update or merge is in progress.
|
||||
None,
|
||||
|
@ -75,8 +82,9 @@ enum class UpdateState : unsigned int {
|
|||
|
||||
class SnapshotManager final {
|
||||
using CreateLogicalPartitionParams = android::fs_mgr::CreateLogicalPartitionParams;
|
||||
using LpMetadata = android::fs_mgr::LpMetadata;
|
||||
using IPartitionOpener = android::fs_mgr::IPartitionOpener;
|
||||
using LpMetadata = android::fs_mgr::LpMetadata;
|
||||
using MetadataBuilder = android::fs_mgr::MetadataBuilder;
|
||||
|
||||
public:
|
||||
// Dependency injection for testing.
|
||||
|
@ -153,6 +161,20 @@ class SnapshotManager final {
|
|||
// Other: 0
|
||||
UpdateState GetUpdateState(double* progress = nullptr);
|
||||
|
||||
// Create necessary COW device / files for OTA clients. New logical partitions will be added to
|
||||
// group "cow" in target_metadata. Regions of partitions of current_metadata will be
|
||||
// "write-protected" and snapshotted.
|
||||
bool CreateUpdateSnapshots(MetadataBuilder* target_metadata, const std::string& target_suffix,
|
||||
MetadataBuilder* current_metadata, const std::string& current_suffix,
|
||||
const std::map<std::string, uint64_t>& cow_sizes);
|
||||
|
||||
// Map a snapshotted partition for OTA clients to write to. Write-protected regions are
|
||||
// determined previously in CreateSnapshots.
|
||||
bool MapUpdateSnapshot(const CreateLogicalPartitionParams& params, std::string* snapshot_path);
|
||||
|
||||
// Unmap a snapshot device that's previously mapped with MapUpdateSnapshot.
|
||||
bool UnmapUpdateSnapshot(const std::string& target_partition_name);
|
||||
|
||||
// If this returns true, first-stage mount must call
|
||||
// CreateLogicalAndSnapshotPartitions rather than CreateLogicalPartitions.
|
||||
bool NeedSnapshotsInFirstStageMount();
|
||||
|
@ -174,6 +196,9 @@ class SnapshotManager final {
|
|||
FRIEND_TEST(SnapshotTest, MergeCannotRemoveCow);
|
||||
FRIEND_TEST(SnapshotTest, NoMergeBeforeReboot);
|
||||
friend class SnapshotTest;
|
||||
friend struct AutoDeleteCowImage;
|
||||
friend struct AutoDeleteSnapshot;
|
||||
friend struct PartitionCowCreator;
|
||||
|
||||
using DmTargetSnapshot = android::dm::DmTargetSnapshot;
|
||||
using IImageManager = android::fiemap::IImageManager;
|
||||
|
@ -344,6 +369,9 @@ class SnapshotManager final {
|
|||
bool MapPartitionWithSnapshot(LockedFile* lock, CreateLogicalPartitionParams params,
|
||||
std::string* path);
|
||||
|
||||
// The reverse of MapPartitionWithSnapshot.
|
||||
bool UnmapPartitionWithSnapshot(LockedFile* lock, const std::string& target_partition_name);
|
||||
|
||||
std::string gsid_dir_;
|
||||
std::string metadata_dir_;
|
||||
std::unique_ptr<IDeviceInfo> device_;
|
||||
|
|
148
fs_mgr/libsnapshot/partition_cow_creator.cpp
Normal file
148
fs_mgr/libsnapshot/partition_cow_creator.cpp
Normal file
|
@ -0,0 +1,148 @@
|
|||
// Copyright (C) 2019 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 "partition_cow_creator.h"
|
||||
|
||||
#include <math.h>
|
||||
|
||||
#include <android-base/logging.h>
|
||||
|
||||
#include "utility.h"
|
||||
|
||||
using android::fs_mgr::Extent;
|
||||
using android::fs_mgr::kDefaultBlockSize;
|
||||
using android::fs_mgr::Partition;
|
||||
|
||||
namespace android {
|
||||
namespace snapshot {
|
||||
|
||||
// Round |d| up to a multiple of |block_size|.
|
||||
static uint64_t RoundUp(double d, uint64_t block_size) {
|
||||
uint64_t ret = ((uint64_t)ceil(d) + block_size - 1) / block_size * block_size;
|
||||
CHECK(ret >= d) << "Can't round " << d << " up to a multiple of " << block_size;
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Intersect two linear extents. If no intersection, return an extent with length 0.
|
||||
static std::unique_ptr<Extent> Intersect(Extent* target_extent, Extent* existing_extent) {
|
||||
// Convert target_extent and existing_extent to linear extents. Zero extents
|
||||
// doesn't matter and doesn't result in any intersection.
|
||||
auto existing_linear_extent = existing_extent->AsLinearExtent();
|
||||
if (!existing_extent) return nullptr;
|
||||
|
||||
auto target_linear_extent = target_extent->AsLinearExtent();
|
||||
if (!target_linear_extent) return nullptr;
|
||||
|
||||
return android::fs_mgr::Interval::Intersect(target_linear_extent->AsInterval(),
|
||||
existing_linear_extent->AsInterval())
|
||||
.AsExtent();
|
||||
}
|
||||
|
||||
// Check that partition |p| contains |e| fully. Both of them should
|
||||
// be from |target_metadata|.
|
||||
// Returns true as long as |e| is a subrange of any extent of |p|.
|
||||
bool PartitionCowCreator::HasExtent(Partition* p, Extent* e) {
|
||||
for (auto& partition_extent : p->extents()) {
|
||||
auto intersection = Intersect(partition_extent.get(), e);
|
||||
if (intersection != nullptr && intersection->num_sectors() == e->num_sectors()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Return the number of sectors, N, where |target_partition|[0..N] (from
|
||||
// |target_metadata|) are the sectors that should be snapshotted. N is computed
|
||||
// so that this range of sectors are used by partitions in |current_metadata|.
|
||||
//
|
||||
// The client code (update_engine) should have computed target_metadata by
|
||||
// resizing partitions of current_metadata, so only the first N sectors should
|
||||
// be snapshotted, not a range with start index != 0.
|
||||
//
|
||||
// Note that if partition A has shrunk and partition B has grown, the new
|
||||
// extents of partition B may use the empty space that was used by partition A.
|
||||
// In this case, that new extent cannot be written directly, as it may be used
|
||||
// by the running system. Hence, all extents of the new partition B must be
|
||||
// intersected with all old partitions (including old partition A and B) to get
|
||||
// the region that needs to be snapshotted.
|
||||
std::optional<uint64_t> PartitionCowCreator::GetSnapshotSize() {
|
||||
// Compute the number of sectors that needs to be snapshotted.
|
||||
uint64_t snapshot_sectors = 0;
|
||||
std::vector<std::unique_ptr<Extent>> intersections;
|
||||
for (const auto& extent : target_partition->extents()) {
|
||||
for (auto* existing_partition :
|
||||
ListPartitionsWithSuffix(current_metadata, current_suffix)) {
|
||||
for (const auto& existing_extent : existing_partition->extents()) {
|
||||
auto intersection = Intersect(extent.get(), existing_extent.get());
|
||||
if (intersection != nullptr && intersection->num_sectors() > 0) {
|
||||
snapshot_sectors += intersection->num_sectors();
|
||||
intersections.emplace_back(std::move(intersection));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
uint64_t snapshot_size = snapshot_sectors * kSectorSize;
|
||||
|
||||
// Sanity check that all recorded intersections are indeed within
|
||||
// target_partition[0..snapshot_sectors].
|
||||
Partition target_partition_snapshot = target_partition->GetBeginningExtents(snapshot_size);
|
||||
for (const auto& intersection : intersections) {
|
||||
if (!HasExtent(&target_partition_snapshot, intersection.get())) {
|
||||
auto linear_intersection = intersection->AsLinearExtent();
|
||||
LOG(ERROR) << "Extent "
|
||||
<< (linear_intersection
|
||||
? (std::to_string(linear_intersection->physical_sector()) + "," +
|
||||
std::to_string(linear_intersection->end_sector()))
|
||||
: "")
|
||||
<< " is not part of Partition " << target_partition->name() << "[0.."
|
||||
<< snapshot_size
|
||||
<< "]. The metadata wasn't constructed correctly. This should not happen.";
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
|
||||
return snapshot_size;
|
||||
}
|
||||
|
||||
std::optional<PartitionCowCreator::Return> PartitionCowCreator::Run() {
|
||||
static constexpr double kCowEstimateFactor = 1.05;
|
||||
|
||||
CHECK(current_metadata->GetBlockDevicePartitionName(0) == LP_METADATA_DEFAULT_PARTITION_NAME &&
|
||||
target_metadata->GetBlockDevicePartitionName(0) == LP_METADATA_DEFAULT_PARTITION_NAME);
|
||||
|
||||
Return ret;
|
||||
ret.snapshot_status.device_size = target_partition->size();
|
||||
|
||||
auto snapshot_size = GetSnapshotSize();
|
||||
if (!snapshot_size.has_value()) return std::nullopt;
|
||||
|
||||
ret.snapshot_status.snapshot_size = *snapshot_size;
|
||||
|
||||
// TODO: always read from cow_size when the COW size is written in
|
||||
// update package. kCowEstimateFactor is good for prototyping but
|
||||
// we can't use that in production.
|
||||
if (!cow_size.has_value()) {
|
||||
cow_size =
|
||||
RoundUp(ret.snapshot_status.snapshot_size * kCowEstimateFactor, kDefaultBlockSize);
|
||||
}
|
||||
|
||||
// TODO: create COW partition in target_metadata to save space.
|
||||
ret.snapshot_status.cow_partition_size = 0;
|
||||
ret.snapshot_status.cow_file_size = (*cow_size) - ret.snapshot_status.cow_partition_size;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
} // namespace snapshot
|
||||
} // namespace android
|
61
fs_mgr/libsnapshot/partition_cow_creator.h
Normal file
61
fs_mgr/libsnapshot/partition_cow_creator.h
Normal file
|
@ -0,0 +1,61 @@
|
|||
// Copyright (C) 2019 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 <optional>
|
||||
#include <string>
|
||||
|
||||
#include <liblp/builder.h>
|
||||
|
||||
#include <libsnapshot/snapshot.h>
|
||||
|
||||
namespace android {
|
||||
namespace snapshot {
|
||||
|
||||
// Helper class that creates COW for a partition.
|
||||
struct PartitionCowCreator {
|
||||
using Extent = android::fs_mgr::Extent;
|
||||
using MetadataBuilder = android::fs_mgr::MetadataBuilder;
|
||||
using Partition = android::fs_mgr::Partition;
|
||||
|
||||
// The metadata that will be written to target metadata slot.
|
||||
MetadataBuilder* target_metadata;
|
||||
// The suffix of the target slot.
|
||||
std::string target_suffix;
|
||||
// The partition in target_metadata that needs to be snapshotted.
|
||||
Partition* target_partition;
|
||||
// The metadata at the current slot (that would be used if the device boots
|
||||
// normally). This is used to determine which extents are being used.
|
||||
MetadataBuilder* current_metadata;
|
||||
// The suffix of the current slot.
|
||||
std::string current_suffix;
|
||||
// The COW size given by client code.
|
||||
std::optional<uint64_t> cow_size;
|
||||
|
||||
struct Return {
|
||||
SnapshotManager::SnapshotStatus snapshot_status;
|
||||
};
|
||||
|
||||
std::optional<Return> Run();
|
||||
|
||||
private:
|
||||
bool HasExtent(Partition* p, Extent* e);
|
||||
std::optional<uint64_t> GetSnapshotSize();
|
||||
};
|
||||
|
||||
} // namespace snapshot
|
||||
} // namespace android
|
90
fs_mgr/libsnapshot/partition_cow_creator_test.cpp
Normal file
90
fs_mgr/libsnapshot/partition_cow_creator_test.cpp
Normal file
|
@ -0,0 +1,90 @@
|
|||
// Copyright (C) 2018 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 <gmock/gmock.h>
|
||||
#include <gtest/gtest.h>
|
||||
#include <liblp/builder.h>
|
||||
#include <liblp/property_fetcher.h>
|
||||
|
||||
#include "partition_cow_creator.h"
|
||||
|
||||
using ::android::fs_mgr::MetadataBuilder;
|
||||
using ::testing::_;
|
||||
using ::testing::AnyNumber;
|
||||
using ::testing::Return;
|
||||
|
||||
namespace android {
|
||||
namespace snapshot {
|
||||
|
||||
class MockPropertyFetcher : public fs_mgr::IPropertyFetcher {
|
||||
public:
|
||||
MOCK_METHOD2(GetProperty, std::string(const std::string&, const std::string&));
|
||||
MOCK_METHOD2(GetBoolProperty, bool(const std::string&, bool));
|
||||
};
|
||||
|
||||
class PartitionCowCreatorTest : ::testing::Test {
|
||||
public:
|
||||
void SetUp() override {
|
||||
fs_mgr::IPropertyFetcher::OverrideForTesting(std::make_unique<MockPropertyFetcher>());
|
||||
|
||||
EXPECT_CALL(fetcher(), GetProperty("ro.boot.slot_suffix", _))
|
||||
.Times(AnyNumber())
|
||||
.WillRepeatedly(Return("_a"));
|
||||
EXPECT_CALL(fetcher(), GetBoolProperty("ro.boot.dynamic_partitions", _))
|
||||
.Times(AnyNumber())
|
||||
.WillRepeatedly(Return(true));
|
||||
EXPECT_CALL(fetcher(), GetBoolProperty("ro.boot.dynamic_partitions_retrofit", _))
|
||||
.Times(AnyNumber())
|
||||
.WillRepeatedly(Return(false));
|
||||
EXPECT_CALL(fetcher(), GetBoolProperty("ro.virtual_ab.enabled", _))
|
||||
.Times(AnyNumber())
|
||||
.WillRepeatedly(Return(true));
|
||||
}
|
||||
void TearDown() override {
|
||||
fs_mgr::IPropertyFetcher::OverrideForTesting(std::make_unique<MockPropertyFetcher>());
|
||||
}
|
||||
MockPropertyFetcher& fetcher() {
|
||||
return *static_cast<MockPropertyFetcher*>(fs_mgr::IPropertyFetcher::GetInstance());
|
||||
}
|
||||
};
|
||||
|
||||
TEST(PartitionCowCreator, IntersectSelf) {
|
||||
auto builder_a = MetadataBuilder::New(1024 * 1024, 1024, 2);
|
||||
ASSERT_NE(builder_a, nullptr);
|
||||
auto system_a = builder_a->AddPartition("system_a", LP_PARTITION_ATTR_READONLY);
|
||||
ASSERT_NE(system_a, nullptr);
|
||||
ASSERT_TRUE(builder_a->ResizePartition(system_a, 40 * 1024));
|
||||
|
||||
auto builder_b = MetadataBuilder::New(1024 * 1024, 1024, 2);
|
||||
ASSERT_NE(builder_b, nullptr);
|
||||
auto system_b = builder_b->AddPartition("system_b", LP_PARTITION_ATTR_READONLY);
|
||||
ASSERT_NE(system_b, nullptr);
|
||||
ASSERT_TRUE(builder_b->ResizePartition(system_b, 40 * 1024));
|
||||
|
||||
PartitionCowCreator creator{.target_metadata = builder_b.get(),
|
||||
.target_suffix = "_b",
|
||||
.target_partition = system_b,
|
||||
.current_metadata = builder_a.get(),
|
||||
.current_suffix = "_a",
|
||||
.cow_size = 20 * 1024};
|
||||
auto ret = creator.Run();
|
||||
ASSERT_TRUE(ret.has_value());
|
||||
ASSERT_EQ(40 * 1024, ret->snapshot_status.device_size);
|
||||
ASSERT_EQ(40 * 1024, ret->snapshot_status.snapshot_size);
|
||||
ASSERT_EQ(20 * 1024,
|
||||
ret->snapshot_status.cow_file_size + ret->snapshot_status.cow_partition_size);
|
||||
}
|
||||
|
||||
} // namespace snapshot
|
||||
} // namespace android
|
|
@ -15,6 +15,7 @@
|
|||
#include <libsnapshot/snapshot.h>
|
||||
|
||||
#include <dirent.h>
|
||||
#include <math.h>
|
||||
#include <sys/file.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/unistd.h>
|
||||
|
@ -36,6 +37,7 @@
|
|||
#include <libfiemap/image_manager.h>
|
||||
#include <liblp/liblp.h>
|
||||
|
||||
#include "partition_cow_creator.h"
|
||||
#include "utility.h"
|
||||
|
||||
namespace android {
|
||||
|
@ -55,6 +57,7 @@ using android::fs_mgr::CreateLogicalPartition;
|
|||
using android::fs_mgr::CreateLogicalPartitionParams;
|
||||
using android::fs_mgr::GetPartitionName;
|
||||
using android::fs_mgr::LpMetadata;
|
||||
using android::fs_mgr::MetadataBuilder;
|
||||
using android::fs_mgr::SlotNumberForSlotSuffix;
|
||||
using std::chrono::duration_cast;
|
||||
using namespace std::chrono_literals;
|
||||
|
@ -413,22 +416,15 @@ bool SnapshotManager::MapCowImage(const std::string& name,
|
|||
bool SnapshotManager::UnmapSnapshot(LockedFile* lock, const std::string& name) {
|
||||
CHECK(lock);
|
||||
|
||||
SnapshotStatus status;
|
||||
if (!ReadSnapshotStatus(lock, name, &status)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto& dm = DeviceMapper::Instance();
|
||||
if (!dm.DeleteDeviceIfExists(name)) {
|
||||
LOG(ERROR) << "Could not delete snapshot device: " << name;
|
||||
return false;
|
||||
}
|
||||
|
||||
// There may be an extra device, since the kernel doesn't let us have a
|
||||
// snapshot and linear target in the same table.
|
||||
auto dm_name = GetSnapshotDeviceName(name, status);
|
||||
if (name != dm_name && !dm.DeleteDeviceIfExists(dm_name)) {
|
||||
LOG(ERROR) << "Could not delete inner snapshot device: " << dm_name;
|
||||
auto snapshot_extra_device = GetSnapshotExtraDeviceName(name);
|
||||
if (!dm.DeleteDeviceIfExists(snapshot_extra_device)) {
|
||||
LOG(ERROR) << "Could not delete snapshot inner device: " << snapshot_extra_device;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -1078,7 +1074,7 @@ bool SnapshotManager::RemoveAllSnapshots(LockedFile* lock) {
|
|||
|
||||
bool ok = true;
|
||||
for (const auto& name : snapshots) {
|
||||
ok &= DeleteSnapshot(lock, name);
|
||||
ok &= (UnmapPartitionWithSnapshot(lock, name) && DeleteSnapshot(lock, name));
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
|
@ -1221,6 +1217,12 @@ bool SnapshotManager::MapPartitionWithSnapshot(LockedFile* lock,
|
|||
CHECK(lock);
|
||||
path->clear();
|
||||
|
||||
if (params.GetPartitionName() != params.GetDeviceName()) {
|
||||
LOG(ERROR) << "Mapping snapshot with a different name is unsupported: partition_name = "
|
||||
<< params.GetPartitionName() << ", device_name = " << params.GetDeviceName();
|
||||
return false;
|
||||
}
|
||||
|
||||
// Fill out fields in CreateLogicalPartitionParams so that we have more information (e.g. by
|
||||
// reading super partition metadata).
|
||||
CreateLogicalPartitionParams::OwnedData params_owned_data;
|
||||
|
@ -1330,6 +1332,31 @@ bool SnapshotManager::MapPartitionWithSnapshot(LockedFile* lock,
|
|||
return true;
|
||||
}
|
||||
|
||||
bool SnapshotManager::UnmapPartitionWithSnapshot(LockedFile* lock,
|
||||
const std::string& target_partition_name) {
|
||||
CHECK(lock);
|
||||
|
||||
if (!UnmapSnapshot(lock, target_partition_name)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!UnmapCowImage(target_partition_name)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto& dm = DeviceMapper::Instance();
|
||||
|
||||
std::string base_name = GetBaseDeviceName(target_partition_name);
|
||||
if (!dm.DeleteDeviceIfExists(base_name)) {
|
||||
LOG(ERROR) << "Cannot delete base device: " << base_name;
|
||||
return false;
|
||||
}
|
||||
|
||||
LOG(INFO) << "Successfully unmapped snapshot " << target_partition_name;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
auto SnapshotManager::OpenFile(const std::string& file, int open_flags, int lock_flags)
|
||||
-> std::unique_ptr<LockedFile> {
|
||||
unique_fd fd(open(file.c_str(), open_flags | O_CLOEXEC | O_NOFOLLOW | O_SYNC, 0660));
|
||||
|
@ -1597,5 +1624,109 @@ bool SnapshotManager::ForceLocalImageManager() {
|
|||
return true;
|
||||
}
|
||||
|
||||
bool SnapshotManager::CreateUpdateSnapshots(MetadataBuilder* target_metadata,
|
||||
const std::string& target_suffix,
|
||||
MetadataBuilder* current_metadata,
|
||||
const std::string& current_suffix,
|
||||
const std::map<std::string, uint64_t>& cow_sizes) {
|
||||
auto lock = LockExclusive();
|
||||
if (!lock) return false;
|
||||
|
||||
// Add _{target_suffix} to COW size map.
|
||||
std::map<std::string, uint64_t> suffixed_cow_sizes;
|
||||
for (const auto& [name, size] : cow_sizes) {
|
||||
suffixed_cow_sizes[name + target_suffix] = size;
|
||||
}
|
||||
|
||||
// In case of error, automatically delete devices that are created along the way.
|
||||
// Note that "lock" is destroyed after "created_devices", so it is safe to use |lock| for
|
||||
// these devices.
|
||||
AutoDeviceList created_devices;
|
||||
|
||||
for (auto* target_partition : ListPartitionsWithSuffix(target_metadata, target_suffix)) {
|
||||
std::optional<uint64_t> cow_size = std::nullopt;
|
||||
auto it = suffixed_cow_sizes.find(target_partition->name());
|
||||
if (it != suffixed_cow_sizes.end()) {
|
||||
cow_size = it->second;
|
||||
LOG(INFO) << "Using provided COW size " << cow_size << " for partition "
|
||||
<< target_partition->name();
|
||||
}
|
||||
|
||||
// Compute the device sizes for the partition.
|
||||
PartitionCowCreator cow_creator{target_metadata, target_suffix, target_partition,
|
||||
current_metadata, current_suffix, cow_size};
|
||||
auto cow_creator_ret = cow_creator.Run();
|
||||
if (!cow_creator_ret.has_value()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
LOG(INFO) << "For partition " << target_partition->name()
|
||||
<< ", device size = " << cow_creator_ret->snapshot_status.device_size
|
||||
<< ", snapshot size = " << cow_creator_ret->snapshot_status.snapshot_size
|
||||
<< ", cow partition size = "
|
||||
<< cow_creator_ret->snapshot_status.cow_partition_size
|
||||
<< ", cow file size = " << cow_creator_ret->snapshot_status.cow_file_size;
|
||||
|
||||
// Delete any existing snapshot before re-creating one.
|
||||
if (!DeleteSnapshot(lock.get(), target_partition->name())) {
|
||||
LOG(ERROR) << "Cannot delete existing snapshot before creating a new one for partition "
|
||||
<< target_partition->name();
|
||||
return false;
|
||||
}
|
||||
|
||||
// It is possible that the whole partition uses free space in super, and snapshot / COW
|
||||
// would not be needed. In this case, skip the partition.
|
||||
bool needs_snapshot = cow_creator_ret->snapshot_status.snapshot_size > 0;
|
||||
bool needs_cow = (cow_creator_ret->snapshot_status.cow_partition_size +
|
||||
cow_creator_ret->snapshot_status.cow_file_size) > 0;
|
||||
CHECK(needs_snapshot == needs_cow);
|
||||
|
||||
if (!needs_snapshot) {
|
||||
LOG(INFO) << "Skip creating snapshot for partition " << target_partition->name()
|
||||
<< "because nothing needs to be snapshotted.";
|
||||
continue;
|
||||
}
|
||||
|
||||
// Store these device sizes to snapshot status file.
|
||||
if (!CreateSnapshot(lock.get(), target_partition->name(),
|
||||
cow_creator_ret->snapshot_status)) {
|
||||
return false;
|
||||
}
|
||||
created_devices.EmplaceBack<AutoDeleteSnapshot>(this, lock.get(), target_partition->name());
|
||||
|
||||
// Create the backing COW image if necessary.
|
||||
if (cow_creator_ret->snapshot_status.cow_file_size > 0) {
|
||||
if (!CreateCowImage(lock.get(), target_partition->name())) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
LOG(INFO) << "Successfully created snapshot for " << target_partition->name();
|
||||
}
|
||||
|
||||
created_devices.Release();
|
||||
LOG(INFO) << "Successfully created all snapshots for target slot " << target_suffix;
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
bool SnapshotManager::MapUpdateSnapshot(const CreateLogicalPartitionParams& params,
|
||||
std::string* snapshot_path) {
|
||||
auto lock = LockShared();
|
||||
if (!lock) return false;
|
||||
if (!UnmapPartitionWithSnapshot(lock.get(), params.GetPartitionName())) {
|
||||
LOG(ERROR) << "Cannot unmap existing snapshot before re-mapping it: "
|
||||
<< params.GetPartitionName();
|
||||
return false;
|
||||
}
|
||||
return MapPartitionWithSnapshot(lock.get(), params, snapshot_path);
|
||||
}
|
||||
|
||||
bool SnapshotManager::UnmapUpdateSnapshot(const std::string& target_partition_name) {
|
||||
auto lock = LockShared();
|
||||
if (!lock) return false;
|
||||
return UnmapPartitionWithSnapshot(lock.get(), target_partition_name);
|
||||
}
|
||||
|
||||
} // namespace snapshot
|
||||
} // namespace android
|
||||
|
|
|
@ -17,6 +17,9 @@
|
|||
#include <android-base/logging.h>
|
||||
#include <android-base/strings.h>
|
||||
|
||||
using android::fs_mgr::MetadataBuilder;
|
||||
using android::fs_mgr::Partition;
|
||||
|
||||
namespace android {
|
||||
namespace snapshot {
|
||||
|
||||
|
@ -52,5 +55,25 @@ AutoUnmapImage::~AutoUnmapImage() {
|
|||
}
|
||||
}
|
||||
|
||||
std::vector<Partition*> ListPartitionsWithSuffix(MetadataBuilder* builder,
|
||||
const std::string& suffix) {
|
||||
std::vector<Partition*> ret;
|
||||
for (const auto& group : builder->ListGroups()) {
|
||||
for (auto* partition : builder->ListPartitionsInGroup(group)) {
|
||||
if (!base::EndsWith(partition->name(), suffix)) {
|
||||
continue;
|
||||
}
|
||||
ret.push_back(partition);
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
AutoDeleteSnapshot::~AutoDeleteSnapshot() {
|
||||
if (!name_.empty() && !manager_->DeleteSnapshot(lock_, name_)) {
|
||||
LOG(ERROR) << "Failed to auto delete snapshot " << name_;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace snapshot
|
||||
} // namespace android
|
||||
|
|
|
@ -14,11 +14,14 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <functional>
|
||||
#include <string>
|
||||
|
||||
#include <android-base/macros.h>
|
||||
#include <libdm/dm.h>
|
||||
#include <libfiemap/image_manager.h>
|
||||
#include <liblp/builder.h>
|
||||
#include <libsnapshot/snapshot.h>
|
||||
|
||||
namespace android {
|
||||
namespace snapshot {
|
||||
|
@ -81,5 +84,24 @@ struct AutoUnmapImage : AutoDevice {
|
|||
android::fiemap::IImageManager* images_ = nullptr;
|
||||
};
|
||||
|
||||
// Automatically deletes a snapshot. |name| should be the name of the partition, e.g. "system_a".
|
||||
// Client is responsible for maintaining the lifetime of |manager| and |lock|.
|
||||
struct AutoDeleteSnapshot : AutoDevice {
|
||||
AutoDeleteSnapshot(SnapshotManager* manager, SnapshotManager::LockedFile* lock,
|
||||
const std::string& name)
|
||||
: AutoDevice(name), manager_(manager), lock_(lock) {}
|
||||
AutoDeleteSnapshot(AutoDeleteSnapshot&& other);
|
||||
~AutoDeleteSnapshot();
|
||||
|
||||
private:
|
||||
DISALLOW_COPY_AND_ASSIGN(AutoDeleteSnapshot);
|
||||
SnapshotManager* manager_ = nullptr;
|
||||
SnapshotManager::LockedFile* lock_ = nullptr;
|
||||
};
|
||||
|
||||
// Return a list of partitions in |builder| with the name ending in |suffix|.
|
||||
std::vector<android::fs_mgr::Partition*> ListPartitionsWithSuffix(
|
||||
android::fs_mgr::MetadataBuilder* builder, const std::string& suffix);
|
||||
|
||||
} // namespace snapshot
|
||||
} // namespace android
|
||||
|
|
Loading…
Reference in a new issue