libsnapshot: Move snapshot metadata to super partition.

snapshot metadata files are stored in /metadata. This means, we cannot
wipe after installing any update.

This patch does the following:

1: Create a scratch space in super partition. The scratch space for ota
   metadata is just about 1MB.

2: Create ext4 filesystem on top of scratch block device.

3: Mount the scratch on /mnt/scratch_super

4: When snapshot-manager instance is created, point the /mnt/scratch/ota
to metadata_dir_ so that all the snapshot files are stored in the new
path.

All the logic of OTA remains the same. This flow is enabled only on userdebug builds for now and the only consumer would be snapshotctl

$snapshotctl apply-update /data/nbd/ -w

During init, we would have to mount the scratch partition to detect
if there is any pending updates.

With this, we would now be able to wipe the device along with the update flow. This will help incremental flashing wherein we would end up saving ~35-40 seconds on Pixel devices.

With this flow, the end-to-end update for incremental builds takes
~20-30 seconds.

Bug: 330744468
Test: Pixel 6 incremental flashing with wipe, Full OTA, vts_libsnapshot
Change-Id: Iac6ce2cf37b70ea221cd18175c8962988d03d95b
Signed-off-by: Akilesh Kailash <akailash@google.com>
This commit is contained in:
Akilesh Kailash 2024-03-13 14:17:25 -07:00
parent adcba86848
commit 398203d1da
16 changed files with 618 additions and 23 deletions

View file

@ -186,6 +186,7 @@ cc_binary {
"libprotobuf-cpp-lite",
"libsparse",
"libutils",
"libselinux",
],
static_libs: [

View file

@ -80,10 +80,8 @@
using namespace std::chrono_literals;
bool fs_mgr_set_blk_ro(const std::string& blockdev, bool readonly = true);
bool fs_mgr_is_device_unlocked();
bool fs_mgr_is_ext4(const std::string& blk_device);
bool fs_mgr_is_f2fs(const std::string& blk_device);
bool fs_mgr_filesystem_available(const std::string& filesystem);

View file

@ -104,6 +104,12 @@ int fs_mgr_setup_verity(android::fs_mgr::FstabEntry* fstab, bool wait_for_verity
// returned. Otherwise, it will use the current slot.
std::string fs_mgr_get_super_partition_name(int slot = -1);
// Set readonly for the block device
bool fs_mgr_set_blk_ro(const std::string& blockdev, bool readonly = true);
// Check if the block device has ext4 filesystem
bool fs_mgr_is_ext4(const std::string& blk_device);
enum FsMgrUmountStatus : int {
SUCCESS = 0,
ERROR_UNKNOWN = 1 << 0,

View file

@ -46,6 +46,7 @@ cc_defaults {
"libfstab",
"libsnapuserd_client",
"libz",
"libselinux",
],
header_libs: [
"libfiemap_headers",
@ -91,6 +92,7 @@ filegroup {
"partition_cow_creator.cpp",
"return.cpp",
"utility.cpp",
"scratch_super.cpp",
],
}
@ -351,6 +353,7 @@ cc_binary {
],
srcs: [
"snapshotctl.cpp",
"scratch_super.cpp",
],
static_libs: [
"libbrotli",

View file

@ -13,6 +13,7 @@
// limitations under the License.
#include "device_info.h"
#include "scratch_super.h"
#include <android-base/logging.h>
#include <fs_mgr.h>
@ -37,8 +38,24 @@ constexpr bool kIsRecovery = true;
constexpr bool kIsRecovery = false;
#endif
DeviceInfo::DeviceInfo() {
std::string scratch_device = android::snapshot::GetScratchOtaMetadataPartition();
if (!scratch_device.empty()) {
std::string scratch_metadata =
android::snapshot::MapScratchOtaMetadataPartition(scratch_device);
if (!scratch_metadata.empty()) {
SetMetadataDir(scratch_metadata);
SetTempMetadata();
}
}
}
std::string DeviceInfo::GetMetadataDir() const {
return "/metadata/ota"s;
return metadata_dir_;
}
void DeviceInfo::SetMetadataDir(const std::string& value) {
metadata_dir_ = value;
}
std::string DeviceInfo::GetSlotSuffix() const {

View file

@ -29,6 +29,7 @@ class DeviceInfo final : public SnapshotManager::IDeviceInfo {
using MergeStatus = ::aidl::android::hardware::boot::MergeStatus;
public:
DeviceInfo();
std::string GetMetadataDir() const override;
std::string GetSlotSuffix() const override;
std::string GetOtherSlotSuffix() const override;
@ -42,14 +43,19 @@ class DeviceInfo final : public SnapshotManager::IDeviceInfo {
std::unique_ptr<IImageManager> OpenImageManager() const override;
bool IsFirstStageInit() const override;
android::dm::IDeviceMapper& GetDeviceMapper() override;
void SetMetadataDir(const std::string& value);
void set_first_stage_init(bool value) { first_stage_init_ = value; }
bool IsTempMetadata() const override { return temp_metadata_; }
void SetTempMetadata() { temp_metadata_ = true; }
private:
bool EnsureBootHal();
android::fs_mgr::PartitionOpener opener_;
bool first_stage_init_ = false;
// Default value
std::string metadata_dir_ = "/metadata/ota";
bool temp_metadata_ = false;
#ifdef LIBSNAPSHOT_USE_HAL
std::unique_ptr<::android::hal::BootControlClient> boot_control_;
#endif

View file

@ -111,6 +111,7 @@ class ISnapshotManager {
virtual bool IsFirstStageInit() const = 0;
virtual std::unique_ptr<IImageManager> OpenImageManager() const = 0;
virtual android::dm::IDeviceMapper& GetDeviceMapper() = 0;
virtual bool IsTempMetadata() const = 0;
// Helper method for implementing OpenImageManager.
std::unique_ptr<IImageManager> OpenImageManager(const std::string& gsid_dir) const;
@ -329,6 +330,10 @@ class SnapshotManager final : public ISnapshotManager {
// might be needed to perform first-stage mounts.
static bool IsSnapshotManagerNeeded();
// Map the temp OTA metadata partition from super
static bool MapTempOtaMetadataPartitionIfNeeded(
const std::function<bool(const std::string&)>& init);
// Helper function for second stage init to restorecon on the rollback indicator.
static std::string GetGlobalRollbackIndicatorPath();

View file

@ -79,7 +79,7 @@ class TestDeviceInfo : public SnapshotManager::IDeviceInfo {
: TestDeviceInfo(fake_super) {
set_slot_suffix(slot_suffix);
}
std::string GetMetadataDir() const override { return "/metadata/ota/test"s; }
std::string GetMetadataDir() const override { return metadata_dir_; }
std::string GetSlotSuffix() const override { return slot_suffix_; }
std::string GetOtherSlotSuffix() const override { return slot_suffix_ == "_a" ? "_b" : "_a"; }
std::string GetSuperDevice([[maybe_unused]] uint32_t slot) const override { return "super"; }
@ -120,6 +120,7 @@ class TestDeviceInfo : public SnapshotManager::IDeviceInfo {
void set_dm(android::dm::IDeviceMapper* dm) { dm_ = dm; }
MergeStatus merge_status() const { return merge_status_; }
bool IsTempMetadata() const override { return temp_metadata_; }
private:
std::string slot_suffix_ = "_a";
@ -129,6 +130,8 @@ class TestDeviceInfo : public SnapshotManager::IDeviceInfo {
bool first_stage_init_ = false;
std::unordered_set<uint32_t> unbootable_slots_;
android::dm::IDeviceMapper* dm_ = nullptr;
std::string metadata_dir_ = "/metadata/ota/test";
bool temp_metadata_ = false;
};
class DeviceMapperWrapper : public android::dm::IDeviceMapper {

View file

@ -0,0 +1,417 @@
// Copyright (C) 2024 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 <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <linux/fs.h>
#include <selinux/selinux.h>
#include <stdlib.h>
#include <sys/mount.h>
#include <sys/stat.h>
#include <sys/statvfs.h>
#include <sys/types.h>
#include <sys/vfs.h>
#include <unistd.h>
#include <android-base/file.h>
#include <android-base/logging.h>
#include <android-base/macros.h>
#include <android-base/properties.h>
#include <android-base/scopeguard.h>
#include <android-base/strings.h>
#include <android-base/unique_fd.h>
#include <ext4_utils/ext4_utils.h>
#include <libsnapshot/snapshot.h>
#include <fs_mgr.h>
#include <fs_mgr_dm_linear.h>
#include <fstab/fstab.h>
#include <liblp/builder.h>
#include <storage_literals/storage_literals.h>
#include <algorithm>
#include <filesystem>
#include <memory>
#include <optional>
#include <string>
#include <vector>
#include "device_info.h"
#include "scratch_super.h"
using namespace std::literals;
using namespace android::dm;
using namespace android::fs_mgr;
using namespace android::storage_literals;
namespace android {
namespace snapshot {
static bool UmountScratch() {
auto ota_dir = std::string(kOtaMetadataMount) + "/" + "ota";
std::error_code ec;
if (std::filesystem::remove_all(ota_dir, ec) == static_cast<std::uintmax_t>(-1)) {
LOG(ERROR) << "Failed to remove OTA directory: " << ec.message();
return false;
}
if (umount(kOtaMetadataMount) != 0) {
PLOG(ERROR) << "UmountScratch failed";
return false;
}
LOG(INFO) << "umount scratch_super success";
return true;
}
bool CleanupScratchOtaMetadataIfPresent(const ISnapshotManager::IDeviceInfo* info) {
if (!UmountScratch()) {
return false;
}
std::unique_ptr<MetadataBuilder> builder;
const auto partition_name = android::base::Basename(kOtaMetadataMount);
const std::vector<int> slots = {0, 1};
if (info == nullptr) {
info = new android::snapshot::DeviceInfo();
}
std::string super_device;
if (info->IsTestDevice()) {
super_device = "super";
} else {
super_device = kPhysicalDevice + fs_mgr_get_super_partition_name();
}
const auto& opener = info->GetPartitionOpener();
std::string slot_suffix = info->GetSlotSuffix();
int slot = SlotNumberForSlotSuffix(slot_suffix);
// Walk both the slots and clean up metadata related to scratch space from
// both the slots.
for (auto slot : slots) {
std::unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(opener, super_device, slot);
if (!builder) {
return false;
}
if (builder->FindPartition(partition_name) != nullptr) {
builder->RemovePartition(partition_name);
auto metadata = builder->Export();
if (!metadata) {
return false;
}
if (!UpdatePartitionTable(info->GetPartitionOpener(), super_device, *metadata.get(),
slot)) {
LOG(ERROR) << "UpdatePartitionTable failed for slot: " << slot;
return false;
}
if (DestroyLogicalPartition(partition_name)) {
LOG(INFO) << "CleanupScratchOtaMetadata success for slot: " << slot;
}
}
}
return true;
}
static bool SetupOTADirs() {
if (setfscreatecon(android::snapshot::kOtaMetadataFileContext)) {
PLOG(ERROR) << "setfscreatecon failed: " << android::snapshot::kOtaMetadataFileContext;
return false;
}
const auto ota_dir = std::string(kOtaMetadataMount) + "/" + "ota";
if (mkdir(ota_dir.c_str(), 0755) != 0 && errno != EEXIST) {
PLOG(ERROR) << "mkdir " << ota_dir;
return false;
}
const auto snapshot_dir = ota_dir + "/" + "snapshots";
if (mkdir(snapshot_dir.c_str(), 0755) != 0 && errno != EEXIST) {
PLOG(ERROR) << "mkdir " << snapshot_dir;
return false;
}
if (setfscreatecon(nullptr)) {
PLOG(ERROR) << "setfscreatecon null";
return false;
}
return true;
}
static bool MountScratch(const std::string& device_path) {
if (access(device_path.c_str(), R_OK | W_OK)) {
LOG(ERROR) << "Path does not exist or is not readwrite: " << device_path;
return false;
}
std::string filesystem_candidate;
if (fs_mgr_is_ext4(device_path)) {
filesystem_candidate = "ext4";
} else {
LOG(ERROR) << "Scratch partition is not ext4";
return false;
}
if (setfscreatecon(android::snapshot::kOtaMetadataFileContext)) {
PLOG(ERROR) << "setfscreatecon failed: " << android::snapshot::kOtaMetadataFileContext;
return false;
}
if (mkdir(kOtaMetadataMount, 0755) && (errno != EEXIST)) {
PLOG(ERROR) << "create " << kOtaMetadataMount;
return false;
}
android::fs_mgr::FstabEntry entry;
entry.blk_device = device_path;
entry.mount_point = kOtaMetadataMount;
entry.flags = MS_NOATIME;
entry.flags |= MS_SYNCHRONOUS;
entry.fs_options = "nodiscard";
fs_mgr_set_blk_ro(device_path, false);
entry.fs_mgr_flags.check = true;
bool mounted = false;
entry.fs_type = filesystem_candidate.c_str();
if (fs_mgr_do_mount_one(entry) == 0) {
mounted = true;
}
if (setfscreatecon(nullptr)) {
PLOG(ERROR) << "setfscreatecon null";
return false;
}
if (!mounted) {
rmdir(kOtaMetadataMount);
return false;
}
return true;
}
static bool MakeScratchFilesystem(const std::string& scratch_device) {
std::string fs_type;
std::string command;
if (!access(kMkExt4, X_OK)) {
fs_type = "ext4";
command = kMkExt4 + " -F -b 4096 -t ext4 -m 0 -O has_journal -M "s + kOtaMetadataMount;
} else {
LOG(ERROR) << "No supported mkfs command or filesystem driver available, supported "
"filesystems "
"are: f2fs, ext4";
return false;
}
command += " " + scratch_device + " >/dev/null 2>/dev/null </dev/null";
fs_mgr_set_blk_ro(scratch_device, false);
auto ret = system(command.c_str());
if (ret) {
LOG(ERROR) << "make " << fs_type << " filesystem on " << scratch_device
<< " return=" << ret;
return false;
}
return true;
}
static bool CreateDynamicScratch(const ISnapshotManager::IDeviceInfo* info,
std::string* scratch_device) {
const auto partition_name = android::base::Basename(kOtaMetadataMount);
auto& dm = DeviceMapper::Instance();
if (info == nullptr) {
info = new android::snapshot::DeviceInfo();
}
std::string super_device;
if (info->IsTestDevice()) {
super_device = "super";
} else {
super_device = kPhysicalDevice + fs_mgr_get_super_partition_name();
}
bool partition_exists = dm.GetState(partition_name) != DmDeviceState::INVALID;
if (partition_exists) {
LOG(ERROR) << "Partition already exists: " << partition_name;
return false;
}
const auto& opener = info->GetPartitionOpener();
std::string slot_suffix = info->GetSlotSuffix();
int slot = SlotNumberForSlotSuffix(slot_suffix);
std::unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(opener, super_device, slot);
if (!builder) {
LOG(ERROR) << "open " << super_device << " failed";
return false;
}
auto partition = builder->FindPartition(partition_name);
partition_exists = partition != nullptr;
if (partition_exists) {
LOG(ERROR) << "Partition exists in super metadata";
return false;
}
partition = builder->AddPartition(partition_name, LP_PARTITION_ATTR_NONE);
if (!partition) {
LOG(ERROR) << "AddPartition failed " << partition_name;
return false;
}
auto free_space = builder->AllocatableSpace() - builder->UsedSpace();
if (free_space < kOtaMetadataPartitionSize) {
LOG(ERROR) << "No space in super partition. Free space: " << free_space
<< " Requested space: " << kOtaMetadataPartitionSize;
return false;
}
LOG(INFO) << "CreateDynamicScratch: free_space: " << free_space
<< " scratch_size: " << kOtaMetadataPartitionSize << " slot_number: " << slot;
if (!builder->ResizePartition(partition, kOtaMetadataPartitionSize)) {
LOG(ERROR) << "ResizePartition failed: " << partition_name << " free_space: " << free_space
<< " scratch_size: " << kOtaMetadataPartitionSize;
return false;
}
auto metadata = builder->Export();
CreateLogicalPartitionParams params;
if (!metadata ||
!UpdatePartitionTable(info->GetPartitionOpener(), super_device, *metadata.get(), slot)) {
LOG(ERROR) << "UpdatePartitionTable failed: " << partition_name;
return false;
}
params = {
.block_device = super_device,
.metadata_slot = slot,
.partition_name = partition_name,
.force_writable = true,
.timeout_ms = 10s,
.partition_opener = &info->GetPartitionOpener(),
};
if (!CreateLogicalPartition(params, scratch_device)) {
LOG(ERROR) << "CreateLogicalPartition failed";
return false;
}
LOG(INFO) << "Scratch device created successfully: " << *scratch_device << " slot: " << slot;
return true;
}
bool IsScratchOtaMetadataOnSuper() {
auto partition_name = android::base::Basename(kOtaMetadataMount);
auto source_slot = fs_mgr_get_slot_suffix();
auto source_slot_number = SlotNumberForSlotSuffix(source_slot);
const auto super_device =
kPhysicalDevice + fs_mgr_get_super_partition_name(!source_slot_number);
auto metadata = android::fs_mgr::ReadMetadata(super_device, !source_slot_number);
if (!metadata) {
return false;
}
auto partition = android::fs_mgr::FindPartition(*metadata.get(), partition_name);
if (!partition) {
return false;
}
auto& dm = DeviceMapper::Instance();
if (dm.GetState(partition_name) == DmDeviceState::ACTIVE) {
LOG(INFO) << "Partition: " << partition_name << " is active";
return true;
}
CreateLogicalPartitionParams params = {
.block_device = super_device,
.metadata = metadata.get(),
.partition = partition,
};
std::string scratch_path;
if (!CreateLogicalPartition(params, &scratch_path)) {
LOG(ERROR) << "Could not create logical partition: " << partition_name;
return false;
}
LOG(INFO) << "Scratch device: " << scratch_path << " created successfully";
return true;
}
std::string GetScratchOtaMetadataPartition() {
std::string device;
auto& dm = DeviceMapper::Instance();
auto partition_name = android::base::Basename(kOtaMetadataMount);
bool invalid_partition = (dm.GetState(partition_name) == DmDeviceState::INVALID);
if (!invalid_partition && dm.GetDmDevicePathByName(partition_name, &device)) {
return device;
}
return "";
}
static bool ScratchAlreadyMounted(const std::string& mount_point) {
android::fs_mgr::Fstab fstab;
if (!ReadFstabFromProcMounts(&fstab)) {
return false;
}
for (const auto& entry : GetEntriesForMountPoint(&fstab, mount_point)) {
if (entry->fs_type == "ext4") {
return true;
}
}
return false;
}
std::string MapScratchOtaMetadataPartition(const std::string& scratch_device) {
if (!ScratchAlreadyMounted(kOtaMetadataMount)) {
if (!MountScratch(scratch_device)) {
return "";
}
}
auto ota_dir = std::string(kOtaMetadataMount) + "/" + "ota";
if (access(ota_dir.c_str(), F_OK) != 0) {
return "";
}
return ota_dir;
}
// Entry point to create a scratch device on super partition
// This will create a 1MB space in super. The space will be
// from the current active slot. Ext4 filesystem will be created
// on this scratch device and all the OTA related directories
// will be created.
bool CreateScratchOtaMetadataOnSuper(const ISnapshotManager::IDeviceInfo* info) {
std::string scratch_device;
if (!CreateDynamicScratch(info, &scratch_device)) {
LOG(ERROR) << "CreateDynamicScratch failed";
return false;
}
if (!MakeScratchFilesystem(scratch_device)) {
LOG(ERROR) << "MakeScratchFilesystem failed";
return false;
}
if (!MountScratch(scratch_device)) {
LOG(ERROR) << "MountScratch failed";
return false;
}
if (!SetupOTADirs()) {
LOG(ERROR) << "SetupOTADirs failed";
return false;
}
return true;
}
} // namespace snapshot
} // namespace android

View file

@ -0,0 +1,33 @@
// Copyright (C) 2024 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
namespace android {
namespace snapshot {
constexpr char kMkExt4[] = "/system/bin/mke2fs";
constexpr char kOtaMetadataFileContext[] = "u:object_r:ota_metadata_file:s0";
constexpr char kOtaMetadataMount[] = "/mnt/scratch_ota_metadata_super";
const size_t kOtaMetadataPartitionSize = uint64_t(1 * 1024 * 1024);
constexpr char kPhysicalDevice[] = "/dev/block/by-name/";
bool IsScratchOtaMetadataOnSuper();
std::string GetScratchOtaMetadataPartition();
std::string MapScratchOtaMetadataPartition(const std::string& device);
bool CreateScratchOtaMetadataOnSuper(const ISnapshotManager::IDeviceInfo* info = nullptr);
bool CleanupScratchOtaMetadataIfPresent(const ISnapshotManager::IDeviceInfo* info = nullptr);
} // namespace snapshot
} // namespace android

View file

@ -48,6 +48,7 @@
#include <libsnapshot/snapshot_stats.h>
#include "device_info.h"
#include "partition_cow_creator.h"
#include "scratch_super.h"
#include "snapshot_metadata_updater.h"
#include "utility.h"
@ -117,7 +118,11 @@ std::unique_ptr<SnapshotManager> SnapshotManager::New(IDeviceInfo* info) {
info = new DeviceInfo();
}
return std::unique_ptr<SnapshotManager>(new SnapshotManager(info));
auto sm = std::unique_ptr<SnapshotManager>(new SnapshotManager(info));
if (info->IsTempMetadata()) {
LOG(INFO) << "Using temp metadata from super";
}
return sm;
}
std::unique_ptr<SnapshotManager> SnapshotManager::NewForFirstStageMount(IDeviceInfo* info) {
@ -1110,6 +1115,13 @@ UpdateState SnapshotManager::ProcessUpdateState(const std::function<bool()>& cal
if (result.state == UpdateState::MergeFailed) {
AcknowledgeMergeFailure(result.failure_code);
}
if (result.state == UpdateState::MergeCompleted) {
if (device_->IsTempMetadata()) {
CleanupScratchOtaMetadataIfPresent();
}
}
if (result.state != UpdateState::Merging) {
// Either there is no merge, or the merge was finished, so no need
// to keep waiting.
@ -2310,7 +2322,27 @@ bool SnapshotManager::ListSnapshots(LockedFile* lock, std::vector<std::string>*
}
bool SnapshotManager::IsSnapshotManagerNeeded() {
return access(kBootIndicatorPath, F_OK) == 0;
if (access(kBootIndicatorPath, F_OK) == 0) {
return true;
}
if (IsScratchOtaMetadataOnSuper()) {
return true;
}
return false;
}
bool SnapshotManager::MapTempOtaMetadataPartitionIfNeeded(
const std::function<bool(const std::string&)>& init) {
auto device = android::snapshot::GetScratchOtaMetadataPartition();
if (!device.empty()) {
init(device);
if (android::snapshot::MapScratchOtaMetadataPartition(device).empty()) {
return false;
}
}
return true;
}
std::string SnapshotManager::GetGlobalRollbackIndicatorPath() {
@ -2397,6 +2429,12 @@ bool SnapshotManager::MapAllPartitions(LockedFile* lock, const std::string& supe
continue;
}
if (GetPartitionName(partition) ==
android::base::Basename(android::snapshot::kOtaMetadataMount)) {
LOG(INFO) << "Partition: " << GetPartitionName(partition) << " skipping";
continue;
}
CreateLogicalPartitionParams params = {
.block_device = super_device,
.metadata = metadata.get(),

View file

@ -47,6 +47,7 @@
#include <android/snapshot/snapshot.pb.h>
#include <libsnapshot/test_helpers.h>
#include "partition_cow_creator.h"
#include "scratch_super.h"
#include "utility.h"
// Mock classes are not used. Header included to ensure mocked class definition aligns with the
@ -1342,6 +1343,15 @@ class SnapshotUpdateTest : public SnapshotTest {
DynamicPartitionGroup* group_ = nullptr;
};
TEST_F(SnapshotUpdateTest, SuperOtaMetadataTest) {
auto info = new TestDeviceInfo(fake_super);
ASSERT_TRUE(CreateScratchOtaMetadataOnSuper(info));
std::string scratch_device = GetScratchOtaMetadataPartition();
ASSERT_NE(scratch_device, "");
ASSERT_NE(MapScratchOtaMetadataPartition(scratch_device), "");
ASSERT_TRUE(CleanupScratchOtaMetadataIfPresent(info));
}
// Test full update flow executed by update_engine. Some partitions uses super empty space,
// some uses images, and some uses both.
// Also test UnmapUpdateSnapshot unmaps everything.

View file

@ -16,7 +16,6 @@
#include <sysexits.h>
#include <unistd.h>
#include <chrono>
#include <filesystem>
#include <fstream>
@ -46,6 +45,7 @@
#include <storage_literals/storage_literals.h>
#include "partition_cow_creator.h"
#include "scratch_super.h"
#ifdef SNAPSHOTCTL_USERDEBUG_OR_ENG
#include <BootControlClient.h>
@ -57,6 +57,8 @@ using namespace android::storage_literals;
using android::base::LogdLogger;
using android::base::StderrLogger;
using android::base::TeeLogger;
using namespace android::dm;
using namespace android::fs_mgr;
using android::fs_mgr::CreateLogicalPartitionParams;
using android::fs_mgr::FindPartition;
using android::fs_mgr::GetPartitionSize;
@ -97,7 +99,7 @@ namespace snapshot {
#ifdef SNAPSHOTCTL_USERDEBUG_OR_ENG
class MapSnapshots {
public:
MapSnapshots(std::string path = "");
MapSnapshots(std::string path = "", bool metadata_super = false);
bool CreateSnapshotDevice(std::string& partition_name, std::string& patch);
bool InitiateThreadedSnapshotWrite(std::string& pname, std::string& snapshot_patch);
bool FinishSnapshotWrites();
@ -122,15 +124,12 @@ class MapSnapshots {
std::vector<std::string> patchfiles_;
chromeos_update_engine::DeltaArchiveManifest manifest_;
bool metadata_super_ = false;
};
MapSnapshots::MapSnapshots(std::string path) {
sm_ = SnapshotManager::New();
if (!sm_) {
std::cout << "Failed to create snapshotmanager";
exit(1);
}
MapSnapshots::MapSnapshots(std::string path, bool metadata_super) {
snapshot_dir_path_ = path + "/";
metadata_super_ = metadata_super;
}
std::string MapSnapshots::GetGroupName(const android::fs_mgr::LpMetadata& pt,
@ -150,6 +149,12 @@ std::string MapSnapshots::GetGroupName(const android::fs_mgr::LpMetadata& pt,
}
bool MapSnapshots::PrepareUpdate() {
if (metadata_super_ && !CreateScratchOtaMetadataOnSuper()) {
LOG(ERROR) << "Failed to create OTA metadata on super";
return false;
}
sm_ = SnapshotManager::New();
auto source_slot = fs_mgr_get_slot_suffix();
auto source_slot_number = SlotNumberForSlotSuffix(source_slot);
auto super_source = fs_mgr_get_super_partition_name(source_slot_number);
@ -234,14 +239,22 @@ bool MapSnapshots::PrepareUpdate() {
bool MapSnapshots::GetCowDevicePath(std::string partition_name, std::string* cow_path) {
auto& dm = android::dm::DeviceMapper::Instance();
std::string cow_device = partition_name + "-cow";
std::string cow_device = partition_name + "-cow-img";
if (metadata_super_) {
// If COW device exists on /data, then data wipe cannot be done.
if (dm.GetDmDevicePathByName(cow_device, cow_path)) {
LOG(ERROR) << "COW device exists on /data: " << *cow_path;
return false;
}
}
cow_device = partition_name + "-cow";
if (dm.GetDmDevicePathByName(cow_device, cow_path)) {
return true;
}
LOG(INFO) << "Failed to find cow path: " << cow_device << " Checking the device for -img path";
// If the COW device exists only on /data
cow_device = partition_name + "-cow-img";
if (!dm.GetDmDevicePathByName(cow_device, cow_path)) {
LOG(ERROR) << "Failed to cow path: " << cow_device;
return false;
@ -321,6 +334,12 @@ bool MapSnapshots::ApplyUpdate() {
}
bool MapSnapshots::BeginUpdate() {
if (metadata_super_ && !CreateScratchOtaMetadataOnSuper()) {
LOG(ERROR) << "Failed to create OTA metadata on super";
return false;
}
sm_ = SnapshotManager::New();
lock_ = sm_->LockExclusive();
std::vector<std::string> snapshots;
sm_->ListSnapshots(lock_.get(), &snapshots);
@ -470,10 +489,12 @@ bool MapSnapshots::FinishSnapshotWrites() {
}
bool MapSnapshots::UnmapCowImagePath(std::string& name) {
sm_ = SnapshotManager::New();
return sm_->UnmapCowImage(name);
}
bool MapSnapshots::DeleteSnapshots() {
sm_ = SnapshotManager::New();
lock_ = sm_->LockExclusive();
if (!sm_->RemoveAllUpdateState(lock_.get())) {
LOG(ERROR) << "Remove All Update State failed";
@ -583,13 +604,19 @@ bool ApplyUpdate(int argc, char** argv) {
}
if (argc < 3) {
std::cerr << " apply-update <directory location where snapshot patches are present>"
std::cerr << " apply-update <directory location where snapshot patches are present> {-w}"
" Apply the snapshots to the COW block device\n";
return false;
}
std::string path = std::string(argv[2]);
MapSnapshots cow(path);
bool metadata_on_super = false;
if (argc == 4) {
if (std::string(argv[3]) == "-w") {
metadata_on_super = true;
}
}
MapSnapshots cow(path, metadata_on_super);
if (!cow.ApplyUpdate()) {
return false;
}
@ -607,7 +634,7 @@ bool MapPrecreatedSnapshots(int argc, char** argv) {
}
if (argc < 3) {
std::cerr << " map-snapshots <directory location where snapshot patches are present>"
std::cerr << " map-snapshots <directory location where snapshot patches are present> {-w}"
" Map all snapshots based on patches present in the directory\n";
return false;
}
@ -638,7 +665,14 @@ bool MapPrecreatedSnapshots(int argc, char** argv) {
}
}
MapSnapshots cow(path);
bool metadata_on_super = false;
if (argc == 4) {
if (std::string(argv[3]) == "-w") {
metadata_on_super = true;
}
}
MapSnapshots cow(path, metadata_on_super);
if (!cow.BeginUpdate()) {
LOG(ERROR) << "BeginUpdate failed";
return false;

View file

@ -42,6 +42,7 @@ cc_library_static {
static_libs: [
"libcutils_sockets",
"libfs_mgr_file_wait",
"libdm",
],
shared_libs: [
"libbase",

View file

@ -35,6 +35,7 @@
#include <android-base/properties.h>
#include <android-base/strings.h>
#include <fs_mgr/file_wait.h>
#include <libdm/dm.h>
#include <snapuserd/snapuserd_client.h>
namespace android {
@ -333,7 +334,21 @@ bool SnapuserdClient::QueryUpdateVerification() {
}
std::string SnapuserdClient::GetDaemonAliveIndicatorPath() {
return "/metadata/ota/" + std::string(kDaemonAliveIndicator);
std::string metadata_dir;
std::string temp_metadata_mnt = "/mnt/scratch_ota_metadata_super";
auto& dm = ::android::dm::DeviceMapper::Instance();
auto partition_name = android::base::Basename(temp_metadata_mnt);
bool invalid_partition = (dm.GetState(partition_name) == dm::DmDeviceState::INVALID);
std::string temp_device;
if (!invalid_partition && dm.GetDmDevicePathByName(partition_name, &temp_device)) {
metadata_dir = temp_metadata_mnt + "/" + "ota/";
} else {
metadata_dir = "/metadata/ota/";
}
return metadata_dir + std::string(kDaemonAliveIndicator);
}
bool SnapuserdClient::IsTransitionedDaemonReady() {

View file

@ -371,6 +371,14 @@ bool FirstStageMountVBootV2::CreateLogicalPartitions() {
}
if (SnapshotManager::IsSnapshotManagerNeeded()) {
auto init_devices = [this](const std::string& device) -> bool {
if (android::base::StartsWith(device, "/dev/block/dm-")) {
return block_dev_init_.InitDmDevice(device);
}
return block_dev_init_.InitDevices({device});
};
SnapshotManager::MapTempOtaMetadataPartitionIfNeeded(init_devices);
auto sm = SnapshotManager::NewForFirstStageMount();
if (!sm) {
return false;