diff --git a/fastboot/Android.bp b/fastboot/Android.bp index 774af284c..bfe0768f8 100644 --- a/fastboot/Android.bp +++ b/fastboot/Android.bp @@ -186,6 +186,7 @@ cc_binary { "libprotobuf-cpp-lite", "libsparse", "libutils", + "libselinux", ], static_libs: [ diff --git a/fs_mgr/fs_mgr_priv.h b/fs_mgr/fs_mgr_priv.h index 7e4d5e54d..84e4924fb 100644 --- a/fs_mgr/fs_mgr_priv.h +++ b/fs_mgr/fs_mgr_priv.h @@ -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); diff --git a/fs_mgr/include/fs_mgr.h b/fs_mgr/include/fs_mgr.h index af5ae2d16..9cfa93f78 100644 --- a/fs_mgr/include/fs_mgr.h +++ b/fs_mgr/include/fs_mgr.h @@ -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, diff --git a/fs_mgr/libsnapshot/Android.bp b/fs_mgr/libsnapshot/Android.bp index f297125c6..4828c4c15 100644 --- a/fs_mgr/libsnapshot/Android.bp +++ b/fs_mgr/libsnapshot/Android.bp @@ -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", diff --git a/fs_mgr/libsnapshot/device_info.cpp b/fs_mgr/libsnapshot/device_info.cpp index e0969f4c7..19f3e0293 100644 --- a/fs_mgr/libsnapshot/device_info.cpp +++ b/fs_mgr/libsnapshot/device_info.cpp @@ -13,6 +13,7 @@ // limitations under the License. #include "device_info.h" +#include "scratch_super.h" #include #include @@ -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 { diff --git a/fs_mgr/libsnapshot/device_info.h b/fs_mgr/libsnapshot/device_info.h index 9153abb01..e93ec4960 100644 --- a/fs_mgr/libsnapshot/device_info.h +++ b/fs_mgr/libsnapshot/device_info.h @@ -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 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 diff --git a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h index deb2d6e92..7ae55db31 100644 --- a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h +++ b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h @@ -111,6 +111,7 @@ class ISnapshotManager { virtual bool IsFirstStageInit() const = 0; virtual std::unique_ptr OpenImageManager() const = 0; virtual android::dm::IDeviceMapper& GetDeviceMapper() = 0; + virtual bool IsTempMetadata() const = 0; // Helper method for implementing OpenImageManager. std::unique_ptr 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& init); + // Helper function for second stage init to restorecon on the rollback indicator. static std::string GetGlobalRollbackIndicatorPath(); diff --git a/fs_mgr/libsnapshot/include_test/libsnapshot/test_helpers.h b/fs_mgr/libsnapshot/include_test/libsnapshot/test_helpers.h index 620b03c14..1cd66515e 100644 --- a/fs_mgr/libsnapshot/include_test/libsnapshot/test_helpers.h +++ b/fs_mgr/libsnapshot/include_test/libsnapshot/test_helpers.h @@ -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 unbootable_slots_; android::dm::IDeviceMapper* dm_ = nullptr; + std::string metadata_dir_ = "/metadata/ota/test"; + bool temp_metadata_ = false; }; class DeviceMapperWrapper : public android::dm::IDeviceMapper { diff --git a/fs_mgr/libsnapshot/scratch_super.cpp b/fs_mgr/libsnapshot/scratch_super.cpp new file mode 100644 index 000000000..805abf3f9 --- /dev/null +++ b/fs_mgr/libsnapshot/scratch_super.cpp @@ -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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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(-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 builder; + const auto partition_name = android::base::Basename(kOtaMetadataMount); + const std::vector 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 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 GetPartitionOpener(); + std::string slot_suffix = info->GetSlotSuffix(); + int slot = SlotNumberForSlotSuffix(slot_suffix); + std::unique_ptr 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 diff --git a/fs_mgr/libsnapshot/scratch_super.h b/fs_mgr/libsnapshot/scratch_super.h new file mode 100644 index 000000000..3e6fe702f --- /dev/null +++ b/fs_mgr/libsnapshot/scratch_super.h @@ -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 diff --git a/fs_mgr/libsnapshot/snapshot.cpp b/fs_mgr/libsnapshot/snapshot.cpp index 108fd903b..6c3bedd86 100644 --- a/fs_mgr/libsnapshot/snapshot.cpp +++ b/fs_mgr/libsnapshot/snapshot.cpp @@ -48,6 +48,7 @@ #include #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::New(IDeviceInfo* info) { info = new DeviceInfo(); } - return std::unique_ptr(new SnapshotManager(info)); + auto sm = std::unique_ptr(new SnapshotManager(info)); + if (info->IsTempMetadata()) { + LOG(INFO) << "Using temp metadata from super"; + } + return sm; } std::unique_ptr SnapshotManager::NewForFirstStageMount(IDeviceInfo* info) { @@ -1110,6 +1115,13 @@ UpdateState SnapshotManager::ProcessUpdateState(const std::function& 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* } 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& 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(), diff --git a/fs_mgr/libsnapshot/snapshot_test.cpp b/fs_mgr/libsnapshot/snapshot_test.cpp index 16c247fdc..46c3a35e9 100644 --- a/fs_mgr/libsnapshot/snapshot_test.cpp +++ b/fs_mgr/libsnapshot/snapshot_test.cpp @@ -47,6 +47,7 @@ #include #include #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. diff --git a/fs_mgr/libsnapshot/snapshotctl.cpp b/fs_mgr/libsnapshot/snapshotctl.cpp index 0158d4d9c..23c3ccf58 100644 --- a/fs_mgr/libsnapshot/snapshotctl.cpp +++ b/fs_mgr/libsnapshot/snapshotctl.cpp @@ -16,7 +16,6 @@ #include #include - #include #include #include @@ -46,6 +45,7 @@ #include #include "partition_cow_creator.h" +#include "scratch_super.h" #ifdef SNAPSHOTCTL_USERDEBUG_OR_ENG #include @@ -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 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 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 " + std::cerr << " apply-update {-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 " + std::cerr << " map-snapshots {-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; diff --git a/fs_mgr/libsnapshot/snapuserd/Android.bp b/fs_mgr/libsnapshot/snapuserd/Android.bp index 9e1415612..734066baa 100644 --- a/fs_mgr/libsnapshot/snapuserd/Android.bp +++ b/fs_mgr/libsnapshot/snapuserd/Android.bp @@ -42,6 +42,7 @@ cc_library_static { static_libs: [ "libcutils_sockets", "libfs_mgr_file_wait", + "libdm", ], shared_libs: [ "libbase", diff --git a/fs_mgr/libsnapshot/snapuserd/snapuserd_client.cpp b/fs_mgr/libsnapshot/snapuserd/snapuserd_client.cpp index 789c98071..ddefb9f91 100644 --- a/fs_mgr/libsnapshot/snapuserd/snapuserd_client.cpp +++ b/fs_mgr/libsnapshot/snapuserd/snapuserd_client.cpp @@ -35,6 +35,7 @@ #include #include #include +#include #include 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() { diff --git a/init/first_stage_mount.cpp b/init/first_stage_mount.cpp index 927b45f22..ece430b70 100644 --- a/init/first_stage_mount.cpp +++ b/init/first_stage_mount.cpp @@ -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;