Merge "libsnapshot: Move snapshot metadata to super partition." into main
This commit is contained in:
commit
40f2bfd604
16 changed files with 618 additions and 23 deletions
|
|
@ -186,6 +186,7 @@ cc_binary {
|
|||
"libprotobuf-cpp-lite",
|
||||
"libsparse",
|
||||
"libutils",
|
||||
"libselinux",
|
||||
],
|
||||
|
||||
static_libs: [
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
417
fs_mgr/libsnapshot/scratch_super.cpp
Normal file
417
fs_mgr/libsnapshot/scratch_super.cpp
Normal 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
|
||||
33
fs_mgr/libsnapshot/scratch_super.h
Normal file
33
fs_mgr/libsnapshot/scratch_super.h
Normal 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
|
||||
|
|
@ -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(),
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -42,6 +42,7 @@ cc_library_static {
|
|||
static_libs: [
|
||||
"libcutils_sockets",
|
||||
"libfs_mgr_file_wait",
|
||||
"libdm",
|
||||
],
|
||||
shared_libs: [
|
||||
"libbase",
|
||||
|
|
|
|||
|
|
@ -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() {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue