Merge "libadf: delete libadf & libadfhwc"
diff --git a/fs_mgr/libsnapshot/android/snapshot/snapshot.proto b/fs_mgr/libsnapshot/android/snapshot/snapshot.proto
index 38c6bf8..36e1169 100644
--- a/fs_mgr/libsnapshot/android/snapshot/snapshot.proto
+++ b/fs_mgr/libsnapshot/android/snapshot/snapshot.proto
@@ -34,7 +34,19 @@
MERGE_COMPLETED = 3;
}
-// Next: 10
+// Next: 3
+enum MergePhase {
+ // No merge is in progress.
+ NO_MERGE = 0;
+
+ // Shrunk partitions can merge.
+ FIRST_PHASE = 1;
+
+ // Grown partitions can merge.
+ SECOND_PHASE = 2;
+}
+
+// Next: 11
message SnapshotStatus {
// Name of the snapshot. This is usually the name of the snapshotted
// logical partition; for example, "system_b".
@@ -87,6 +99,9 @@
// True if compression is enabled, false otherwise.
bool compression_enabled = 9;
+
+ // The old partition size (if none existed, this will be zero).
+ uint64 old_partition_size = 10;
}
// Next: 8
@@ -118,7 +133,7 @@
Cancelled = 7;
};
-// Next: 6
+// Next: 7
message SnapshotUpdateStatus {
UpdateState state = 1;
@@ -136,6 +151,9 @@
// Whether compression/dm-user was used for any snapshots.
bool compression_enabled = 5;
+
+ // Merge phase (if state == MERGING).
+ MergePhase merge_phase = 6;
}
// Next: 4
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
index 397ff2e..0a8567f 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
@@ -463,6 +463,10 @@
const std::string& base_device, const std::chrono::milliseconds& timeout_ms,
std::string* path);
+ // Map the source device used for dm-user.
+ bool MapSourceDevice(LockedFile* lock, const std::string& name,
+ const std::chrono::milliseconds& timeout_ms, std::string* path);
+
// Map a COW image that was previous created with CreateCowImage.
std::optional<std::string> MapCowImage(const std::string& name,
const std::chrono::milliseconds& timeout_ms);
@@ -521,11 +525,13 @@
std::string GetMergeStateFilePath() const;
// Helpers for merging.
+ bool MergeSecondPhaseSnapshots(LockedFile* lock);
bool SwitchSnapshotToMerge(LockedFile* lock, const std::string& name);
bool RewriteSnapshotDeviceTable(const std::string& dm_name);
bool MarkSnapshotMergeCompleted(LockedFile* snapshot_lock, const std::string& snapshot_name);
void AcknowledgeMergeSuccess(LockedFile* lock);
void AcknowledgeMergeFailure();
+ MergePhase DecideMergePhase(const SnapshotStatus& status);
std::unique_ptr<LpMetadata> ReadCurrentMetadata();
enum class MetadataPartitionState {
@@ -558,7 +564,8 @@
// UpdateState::MergeNeedsReboot
UpdateState CheckMergeState(const std::function<bool()>& before_cancel);
UpdateState CheckMergeState(LockedFile* lock, const std::function<bool()>& before_cancel);
- UpdateState CheckTargetMergeState(LockedFile* lock, const std::string& name);
+ UpdateState CheckTargetMergeState(LockedFile* lock, const std::string& name,
+ const SnapshotUpdateStatus& update_status);
// Interact with status files under /metadata/ota/snapshots.
bool WriteSnapshotStatus(LockedFile* lock, const SnapshotStatus& status);
@@ -568,12 +575,9 @@
std::string GetSnapshotBootIndicatorPath();
std::string GetRollbackIndicatorPath();
std::string GetForwardMergeIndicatorPath();
+ std::string GetOldPartitionMetadataPath();
- // Return the name of the device holding the "snapshot" or "snapshot-merge"
- // target. This may not be the final device presented via MapSnapshot(), if
- // for example there is a linear segment.
- std::string GetSnapshotDeviceName(const std::string& snapshot_name,
- const SnapshotStatus& status);
+ const LpMetadata* ReadOldPartitionMetadata(LockedFile* lock);
bool MapAllPartitions(LockedFile* lock, const std::string& super_device, uint32_t slot,
const std::chrono::milliseconds& timeout_ms);
@@ -673,8 +677,8 @@
// Helper for RemoveAllSnapshots.
// Check whether |name| should be deleted as a snapshot name.
- bool ShouldDeleteSnapshot(LockedFile* lock, const std::map<std::string, bool>& flashing_status,
- Slot current_slot, const std::string& name);
+ bool ShouldDeleteSnapshot(const std::map<std::string, bool>& flashing_status, Slot current_slot,
+ const std::string& name);
// Create or delete forward merge indicator given |wipe|. Iff wipe is scheduled,
// allow forward merge on FDR.
@@ -722,6 +726,7 @@
bool in_factory_data_reset_ = false;
std::function<bool(const std::string&)> uevent_regen_callback_;
std::unique_ptr<SnapuserdClient> snapuserd_client_;
+ std::unique_ptr<LpMetadata> old_partition_metadata_;
};
} // namespace snapshot
diff --git a/fs_mgr/libsnapshot/snapshot.cpp b/fs_mgr/libsnapshot/snapshot.cpp
index 21b720e..ebda430 100644
--- a/fs_mgr/libsnapshot/snapshot.cpp
+++ b/fs_mgr/libsnapshot/snapshot.cpp
@@ -132,8 +132,8 @@
return partition_name + "-base";
}
-static std::string GetSnapshotExtraDeviceName(const std::string& snapshot_name) {
- return snapshot_name + "-inner";
+static std::string GetSourceDeviceName(const std::string& partition_name) {
+ return partition_name + "-src";
}
bool SnapshotManager::BeginUpdate() {
@@ -157,6 +157,9 @@
images_->RemoveAllImages();
}
+ // Clear any cached metadata (this allows re-using one manager across tests).
+ old_partition_metadata_ = nullptr;
+
auto state = ReadUpdateState(file.get());
if (state != UpdateState::None) {
LOG(ERROR) << "An update is already in progress, cannot begin a new update";
@@ -255,6 +258,7 @@
GetSnapshotBootIndicatorPath(),
GetRollbackIndicatorPath(),
GetForwardMergeIndicatorPath(),
+ GetOldPartitionMetadataPath(),
};
for (const auto& file : files) {
RemoveFileIfExists(file);
@@ -465,8 +469,13 @@
LOG(ERROR) << "Invalid snapshot size for " << base_device << ": " << status.snapshot_size();
return false;
}
+ if (status.device_size() != status.snapshot_size()) {
+ LOG(ERROR) << "Device size and snapshot size must be the same (device size = "
+ << status.device_size() << ", snapshot size = " << status.snapshot_size();
+ return false;
+ }
+
uint64_t snapshot_sectors = status.snapshot_size() / kSectorSize;
- uint64_t linear_sectors = (status.device_size() - status.snapshot_size()) / kSectorSize;
auto& dm = DeviceMapper::Instance();
@@ -474,7 +483,8 @@
// have completed merging, but the start of the merge process is considered
// atomic.
SnapshotStorageMode mode;
- switch (ReadUpdateState(lock)) {
+ SnapshotUpdateStatus update_status = ReadSnapshotUpdateStatus(lock);
+ switch (update_status.state()) {
case UpdateState::MergeCompleted:
case UpdateState::MergeNeedsReboot:
LOG(ERROR) << "Should not create a snapshot device for " << name
@@ -484,52 +494,24 @@
case UpdateState::MergeFailed:
// Note: MergeFailed indicates that a merge is in progress, but
// is possibly stalled. We still have to honor the merge.
- mode = SnapshotStorageMode::Merge;
+ if (DecideMergePhase(status) == update_status.merge_phase()) {
+ mode = SnapshotStorageMode::Merge;
+ } else {
+ mode = SnapshotStorageMode::Persistent;
+ }
break;
default:
mode = SnapshotStorageMode::Persistent;
break;
}
- // The kernel (tested on 4.19) crashes horribly if a device has both a snapshot
- // and a linear target in the same table. Instead, we stack them, and give the
- // snapshot device a different name. It is not exposed to the caller in this
- // case.
- auto snap_name = (linear_sectors > 0) ? GetSnapshotExtraDeviceName(name) : name;
-
DmTable table;
table.Emplace<DmTargetSnapshot>(0, snapshot_sectors, base_device, cow_device, mode,
kSnapshotChunkSize);
- if (!dm.CreateDevice(snap_name, table, dev_path, timeout_ms)) {
- LOG(ERROR) << "Could not create snapshot device: " << snap_name;
+ if (!dm.CreateDevice(name, table, dev_path, timeout_ms)) {
+ LOG(ERROR) << "Could not create snapshot device: " << name;
return false;
}
-
- if (linear_sectors) {
- std::string snap_dev;
- if (!dm.GetDeviceString(snap_name, &snap_dev)) {
- LOG(ERROR) << "Cannot determine major/minor for: " << snap_name;
- return false;
- }
-
- // Our stacking will looks like this:
- // [linear, linear] ; to snapshot, and non-snapshot region of base device
- // [snapshot-inner]
- // [base device] [cow]
- DmTable table;
- table.Emplace<DmTargetLinear>(0, snapshot_sectors, snap_dev, 0);
- table.Emplace<DmTargetLinear>(snapshot_sectors, linear_sectors, base_device,
- snapshot_sectors);
- if (!dm.CreateDevice(name, table, dev_path, timeout_ms)) {
- LOG(ERROR) << "Could not create outer snapshot device: " << name;
- dm.DeleteDevice(snap_name);
- return false;
- }
- }
-
- // :TODO: when merging is implemented, we need to add an argument to the
- // status indicating how much progress is left to merge. (device-mapper
- // does not retain the initial values, so we can't derive them.)
return true;
}
@@ -557,6 +539,36 @@
return std::nullopt;
}
+bool SnapshotManager::MapSourceDevice(LockedFile* lock, const std::string& name,
+ const std::chrono::milliseconds& timeout_ms,
+ std::string* path) {
+ CHECK(lock);
+
+ auto metadata = ReadOldPartitionMetadata(lock);
+ if (!metadata) {
+ LOG(ERROR) << "Could not map source device due to missing or corrupt metadata";
+ return false;
+ }
+
+ auto old_name = GetOtherPartitionName(name);
+ auto slot_suffix = device_->GetSlotSuffix();
+ auto slot = SlotNumberForSlotSuffix(slot_suffix);
+
+ CreateLogicalPartitionParams params = {
+ .block_device = device_->GetSuperDevice(slot),
+ .metadata = metadata,
+ .partition_name = old_name,
+ .timeout_ms = timeout_ms,
+ .device_name = GetSourceDeviceName(name),
+ .partition_opener = &device_->GetPartitionOpener(),
+ };
+ if (!CreateLogicalPartition(std::move(params), path)) {
+ LOG(ERROR) << "Could not create source device for snapshot " << name;
+ return false;
+ }
+ return true;
+}
+
bool SnapshotManager::UnmapSnapshot(LockedFile* lock, const std::string& name) {
CHECK(lock);
@@ -565,13 +577,6 @@
LOG(ERROR) << "Could not delete snapshot device: " << name;
return false;
}
-
- auto snapshot_extra_device = GetSnapshotExtraDeviceName(name);
- if (!dm.DeleteDeviceIfExists(snapshot_extra_device)) {
- LOG(ERROR) << "Could not delete snapshot inner device: " << snapshot_extra_device;
- return false;
- }
-
return true;
}
@@ -678,6 +683,8 @@
bool compression_enabled = false;
+ std::vector<std::string> first_merge_group;
+
uint64_t total_cow_file_size = 0;
DmTargetSnapshot::Status initial_target_values = {};
for (const auto& snapshot : snapshots) {
@@ -696,6 +703,9 @@
total_cow_file_size += snapshot_status.cow_file_size();
compression_enabled |= snapshot_status.compression_enabled();
+ if (DecideMergePhase(snapshot_status) == MergePhase::FIRST_PHASE) {
+ first_merge_group.emplace_back(snapshot);
+ }
}
if (cow_file_size) {
@@ -709,14 +719,26 @@
initial_status.set_metadata_sectors(initial_target_values.metadata_sectors);
initial_status.set_compression_enabled(compression_enabled);
+ // If any partitions shrunk, we need to merge them before we merge any other
+ // partitions (see b/177935716). Otherwise, a merge from another partition
+ // may overwrite the source block of a copy operation.
+ const std::vector<std::string>* merge_group;
+ if (first_merge_group.empty()) {
+ merge_group = &snapshots;
+ initial_status.set_merge_phase(MergePhase::SECOND_PHASE);
+ } else {
+ merge_group = &first_merge_group;
+ initial_status.set_merge_phase(MergePhase::FIRST_PHASE);
+ }
+
// Point of no return - mark that we're starting a merge. From now on every
- // snapshot must be a merge target.
+ // eligible snapshot must be a merge target.
if (!WriteSnapshotUpdateStatus(lock.get(), initial_status)) {
return false;
}
bool rewrote_all = true;
- for (const auto& snapshot : snapshots) {
+ for (const auto& snapshot : *merge_group) {
// If this fails, we have no choice but to continue. Everything must
// be merged. This is not an ideal state to be in, but it is safe,
// because we the next boot will try again.
@@ -749,16 +771,15 @@
// After this, we return true because we technically did switch to a merge
// target. Everything else we do here is just informational.
- auto dm_name = GetSnapshotDeviceName(name, status);
- if (!RewriteSnapshotDeviceTable(dm_name)) {
+ if (!RewriteSnapshotDeviceTable(name)) {
return false;
}
status.set_state(SnapshotState::MERGING);
DmTargetSnapshot::Status dm_status;
- if (!QuerySnapshotStatus(dm_name, nullptr, &dm_status)) {
- LOG(ERROR) << "Could not query merge status for snapshot: " << dm_name;
+ if (!QuerySnapshotStatus(name, nullptr, &dm_status)) {
+ LOG(ERROR) << "Could not query merge status for snapshot: " << name;
}
status.set_sectors_allocated(dm_status.sectors_allocated);
status.set_metadata_sectors(dm_status.metadata_sectors);
@@ -768,33 +789,33 @@
return true;
}
-bool SnapshotManager::RewriteSnapshotDeviceTable(const std::string& dm_name) {
+bool SnapshotManager::RewriteSnapshotDeviceTable(const std::string& name) {
auto& dm = DeviceMapper::Instance();
std::vector<DeviceMapper::TargetInfo> old_targets;
- if (!dm.GetTableInfo(dm_name, &old_targets)) {
- LOG(ERROR) << "Could not read snapshot device table: " << dm_name;
+ if (!dm.GetTableInfo(name, &old_targets)) {
+ LOG(ERROR) << "Could not read snapshot device table: " << name;
return false;
}
if (old_targets.size() != 1 || DeviceMapper::GetTargetType(old_targets[0].spec) != "snapshot") {
- LOG(ERROR) << "Unexpected device-mapper table for snapshot: " << dm_name;
+ LOG(ERROR) << "Unexpected device-mapper table for snapshot: " << name;
return false;
}
std::string base_device, cow_device;
if (!DmTargetSnapshot::GetDevicesFromParams(old_targets[0].data, &base_device, &cow_device)) {
- LOG(ERROR) << "Could not derive underlying devices for snapshot: " << dm_name;
+ LOG(ERROR) << "Could not derive underlying devices for snapshot: " << name;
return false;
}
DmTable table;
table.Emplace<DmTargetSnapshot>(0, old_targets[0].spec.length, base_device, cow_device,
SnapshotStorageMode::Merge, kSnapshotChunkSize);
- if (!dm.LoadTableAndActivate(dm_name, table)) {
- LOG(ERROR) << "Could not swap device-mapper tables on snapshot device " << dm_name;
+ if (!dm.LoadTableAndActivate(name, table)) {
+ LOG(ERROR) << "Could not swap device-mapper tables on snapshot device " << name;
return false;
}
- LOG(INFO) << "Successfully switched snapshot device to a merge target: " << dm_name;
+ LOG(INFO) << "Successfully switched snapshot device to a merge target: " << name;
return true;
}
@@ -908,13 +929,13 @@
UpdateState SnapshotManager::CheckMergeState(LockedFile* lock,
const std::function<bool()>& before_cancel) {
- UpdateState state = ReadUpdateState(lock);
- switch (state) {
+ SnapshotUpdateStatus update_status = ReadSnapshotUpdateStatus(lock);
+ switch (update_status.state()) {
case UpdateState::None:
case UpdateState::MergeCompleted:
// Harmless races are allowed between two callers of WaitForMerge,
// so in both of these cases we just propagate the state.
- return state;
+ return update_status.state();
case UpdateState::Merging:
case UpdateState::MergeNeedsReboot:
@@ -931,10 +952,10 @@
if (HandleCancelledUpdate(lock, before_cancel)) {
return UpdateState::Cancelled;
}
- return state;
+ return update_status.state();
default:
- return state;
+ return update_status.state();
}
std::vector<std::string> snapshots;
@@ -946,8 +967,9 @@
bool failed = false;
bool merging = false;
bool needs_reboot = false;
+ bool wrong_phase = false;
for (const auto& snapshot : snapshots) {
- UpdateState snapshot_state = CheckTargetMergeState(lock, snapshot);
+ UpdateState snapshot_state = CheckTargetMergeState(lock, snapshot, update_status);
switch (snapshot_state) {
case UpdateState::MergeFailed:
failed = true;
@@ -963,6 +985,9 @@
case UpdateState::Cancelled:
cancelled = true;
break;
+ case UpdateState::None:
+ wrong_phase = true;
+ break;
default:
LOG(ERROR) << "Unknown merge status for \"" << snapshot << "\": "
<< "\"" << snapshot_state << "\"";
@@ -982,6 +1007,14 @@
// it in WaitForMerge rather than here and elsewhere.
return UpdateState::MergeFailed;
}
+ if (wrong_phase) {
+ // If we got here, no other partitions are being merged, and nothing
+ // failed to merge. It's safe to move to the next merge phase.
+ if (!MergeSecondPhaseSnapshots(lock)) {
+ return UpdateState::MergeFailed;
+ }
+ return UpdateState::Merging;
+ }
if (needs_reboot) {
WriteUpdateState(lock, UpdateState::MergeNeedsReboot);
return UpdateState::MergeNeedsReboot;
@@ -997,17 +1030,16 @@
return UpdateState::MergeCompleted;
}
-UpdateState SnapshotManager::CheckTargetMergeState(LockedFile* lock, const std::string& name) {
+UpdateState SnapshotManager::CheckTargetMergeState(LockedFile* lock, const std::string& name,
+ const SnapshotUpdateStatus& update_status) {
SnapshotStatus snapshot_status;
if (!ReadSnapshotStatus(lock, name, &snapshot_status)) {
return UpdateState::MergeFailed;
}
- std::string dm_name = GetSnapshotDeviceName(name, snapshot_status);
-
std::unique_ptr<LpMetadata> current_metadata;
- if (!IsSnapshotDevice(dm_name)) {
+ if (!IsSnapshotDevice(name)) {
if (!current_metadata) {
current_metadata = ReadCurrentMetadata();
}
@@ -1021,7 +1053,7 @@
// During a check, we decided the merge was complete, but we were unable to
// collapse the device-mapper stack and perform COW cleanup. If we haven't
// rebooted after this check, the device will still be a snapshot-merge
- // target. If the have rebooted, the device will now be a linear target,
+ // target. If we have rebooted, the device will now be a linear target,
// and we can try cleanup again.
if (snapshot_status.state() == SnapshotState::MERGE_COMPLETED) {
// NB: It's okay if this fails now, we gave cleanup our best effort.
@@ -1029,7 +1061,7 @@
return UpdateState::MergeCompleted;
}
- LOG(ERROR) << "Expected snapshot or snapshot-merge for device: " << dm_name;
+ LOG(ERROR) << "Expected snapshot or snapshot-merge for device: " << name;
return UpdateState::MergeFailed;
}
@@ -1039,9 +1071,15 @@
std::string target_type;
DmTargetSnapshot::Status status;
- if (!QuerySnapshotStatus(dm_name, &target_type, &status)) {
+ if (!QuerySnapshotStatus(name, &target_type, &status)) {
return UpdateState::MergeFailed;
}
+ if (target_type == "snapshot" &&
+ DecideMergePhase(snapshot_status) == MergePhase::SECOND_PHASE &&
+ update_status.merge_phase() == MergePhase::FIRST_PHASE) {
+ // The snapshot is not being merged because it's in the wrong phase.
+ return UpdateState::None;
+ }
if (target_type != "snapshot-merge") {
// We can get here if we failed to rewrite the target type in
// InitiateMerge(). If we failed to create the target in first-stage
@@ -1077,6 +1115,38 @@
return UpdateState::MergeCompleted;
}
+bool SnapshotManager::MergeSecondPhaseSnapshots(LockedFile* lock) {
+ std::vector<std::string> snapshots;
+ if (!ListSnapshots(lock, &snapshots)) {
+ return UpdateState::MergeFailed;
+ }
+
+ SnapshotUpdateStatus update_status = ReadSnapshotUpdateStatus(lock);
+ CHECK(update_status.state() == UpdateState::Merging);
+ CHECK(update_status.merge_phase() == MergePhase::FIRST_PHASE);
+
+ update_status.set_merge_phase(MergePhase::SECOND_PHASE);
+ if (!WriteSnapshotUpdateStatus(lock, update_status)) {
+ return false;
+ }
+
+ bool rewrote_all = true;
+ for (const auto& snapshot : snapshots) {
+ SnapshotStatus snapshot_status;
+ if (!ReadSnapshotStatus(lock, snapshot, &snapshot_status)) {
+ return UpdateState::MergeFailed;
+ }
+ if (DecideMergePhase(snapshot_status) != MergePhase::SECOND_PHASE) {
+ continue;
+ }
+ if (!SwitchSnapshotToMerge(lock, snapshot)) {
+ LOG(ERROR) << "Failed to switch snapshot to a second-phase merge target: " << snapshot;
+ rewrote_all = false;
+ }
+ }
+ return rewrote_all;
+}
+
std::string SnapshotManager::GetSnapshotBootIndicatorPath() {
return metadata_dir_ + "/" + android::base::Basename(kBootIndicatorPath);
}
@@ -1089,6 +1159,10 @@
return metadata_dir_ + "/allow-forward-merge";
}
+std::string SnapshotManager::GetOldPartitionMetadataPath() {
+ return metadata_dir_ + "/old-partition-metadata";
+}
+
void SnapshotManager::AcknowledgeMergeSuccess(LockedFile* lock) {
// It's not possible to remove update state in recovery, so write an
// indicator that cleanup is needed on reboot. If a factory data reset
@@ -1124,21 +1198,20 @@
bool SnapshotManager::OnSnapshotMergeComplete(LockedFile* lock, const std::string& name,
const SnapshotStatus& status) {
- auto dm_name = GetSnapshotDeviceName(name, status);
- if (IsSnapshotDevice(dm_name)) {
+ if (IsSnapshotDevice(name)) {
// We are extra-cautious here, to avoid deleting the wrong table.
std::string target_type;
DmTargetSnapshot::Status dm_status;
- if (!QuerySnapshotStatus(dm_name, &target_type, &dm_status)) {
+ if (!QuerySnapshotStatus(name, &target_type, &dm_status)) {
return false;
}
if (target_type != "snapshot-merge") {
LOG(ERROR) << "Unexpected target type " << target_type
- << " for snapshot device: " << dm_name;
+ << " for snapshot device: " << name;
return false;
}
if (dm_status.sectors_allocated != dm_status.metadata_sectors) {
- LOG(ERROR) << "Merge is unexpectedly incomplete for device " << dm_name;
+ LOG(ERROR) << "Merge is unexpectedly incomplete for device " << name;
return false;
}
if (!CollapseSnapshotDevice(name, status)) {
@@ -1159,23 +1232,21 @@
bool SnapshotManager::CollapseSnapshotDevice(const std::string& name,
const SnapshotStatus& status) {
auto& dm = DeviceMapper::Instance();
- auto dm_name = GetSnapshotDeviceName(name, status);
// Verify we have a snapshot-merge device.
DeviceMapper::TargetInfo target;
- if (!GetSingleTarget(dm_name, TableQuery::Table, &target)) {
+ if (!GetSingleTarget(name, TableQuery::Table, &target)) {
return false;
}
if (DeviceMapper::GetTargetType(target.spec) != "snapshot-merge") {
// This should be impossible, it was checked earlier.
- LOG(ERROR) << "Snapshot device has invalid target type: " << dm_name;
+ LOG(ERROR) << "Snapshot device has invalid target type: " << name;
return false;
}
std::string base_device, cow_device;
if (!DmTargetSnapshot::GetDevicesFromParams(target.data, &base_device, &cow_device)) {
- LOG(ERROR) << "Could not parse snapshot device " << dm_name
- << " parameters: " << target.data;
+ LOG(ERROR) << "Could not parse snapshot device " << name << " parameters: " << target.data;
return false;
}
@@ -1186,42 +1257,6 @@
return false;
}
- if (dm_name != name) {
- // We've derived the base device, but we actually need to replace the
- // table of the outermost device. Do a quick verification that this
- // device looks like we expect it to.
- std::vector<DeviceMapper::TargetInfo> outer_table;
- if (!dm.GetTableInfo(name, &outer_table)) {
- LOG(ERROR) << "Could not validate outer snapshot table: " << name;
- return false;
- }
- if (outer_table.size() != 2) {
- LOG(ERROR) << "Expected 2 dm-linear targets for table " << name
- << ", got: " << outer_table.size();
- return false;
- }
- for (const auto& target : outer_table) {
- auto target_type = DeviceMapper::GetTargetType(target.spec);
- if (target_type != "linear") {
- LOG(ERROR) << "Outer snapshot table may only contain linear targets, but " << name
- << " has target: " << target_type;
- return false;
- }
- }
- if (outer_table[0].spec.length != snapshot_sectors) {
- LOG(ERROR) << "dm-snapshot " << name << " should have " << snapshot_sectors
- << " sectors, got: " << outer_table[0].spec.length;
- return false;
- }
- uint64_t expected_device_sectors = status.device_size() / kSectorSize;
- uint64_t actual_device_sectors = outer_table[0].spec.length + outer_table[1].spec.length;
- if (expected_device_sectors != actual_device_sectors) {
- LOG(ERROR) << "Outer device " << name << " should have " << expected_device_sectors
- << " sectors, got: " << actual_device_sectors;
- return false;
- }
- }
-
uint32_t slot = SlotNumberForSlotSuffix(device_->GetSlotSuffix());
// Create a DmTable that is identical to the base device.
CreateLogicalPartitionParams base_device_params{
@@ -1236,7 +1271,6 @@
return false;
}
- // Note: we are replacing the *outer* table here, so we do not use dm_name.
if (!dm.LoadTableAndActivate(name, table)) {
return false;
}
@@ -1246,22 +1280,17 @@
// flushed remaining I/O. We could in theory replace with dm-zero (or
// re-use the table above), but for now it's better to know why this
// would fail.
- if (dm_name != name && !dm.DeleteDeviceIfExists(dm_name)) {
- LOG(ERROR) << "Unable to delete snapshot device " << dm_name << ", COW cannot be "
- << "reclaimed until after reboot.";
- return false;
- }
-
if (status.compression_enabled()) {
UnmapDmUserDevice(name);
}
-
- // Cleanup the base device as well, since it is no longer used. This does
- // not block cleanup.
auto base_name = GetBaseDeviceName(name);
if (!dm.DeleteDeviceIfExists(base_name)) {
LOG(ERROR) << "Unable to delete base device for snapshot: " << base_name;
}
+ auto source_name = GetSourceDeviceName(name);
+ if (!dm.DeleteDeviceIfExists(source_name)) {
+ LOG(ERROR) << "Unable to delete source device for snapshot: " << source_name;
+ }
return true;
}
@@ -1364,9 +1393,9 @@
continue;
}
- std::string backing_device;
- if (!dm.GetDmDevicePathByName(GetBaseDeviceName(snapshot), &backing_device)) {
- LOG(ERROR) << "Could not get device path for " << GetBaseDeviceName(snapshot);
+ std::string source_device;
+ if (!dm.GetDmDevicePathByName(GetSourceDeviceName(snapshot), &source_device)) {
+ LOG(ERROR) << "Could not get device path for " << GetSourceDeviceName(snapshot);
continue;
}
@@ -1392,7 +1421,7 @@
}
if (transition == InitTransition::SELINUX_DETACH) {
- auto message = misc_name + "," + cow_image_device + "," + backing_device;
+ auto message = misc_name + "," + cow_image_device + "," + source_device;
snapuserd_argv->emplace_back(std::move(message));
// Do not attempt to connect to the new snapuserd yet, it hasn't
@@ -1403,7 +1432,7 @@
}
uint64_t base_sectors =
- snapuserd_client_->InitDmUserCow(misc_name, cow_image_device, backing_device);
+ snapuserd_client_->InitDmUserCow(misc_name, cow_image_device, source_device);
if (base_sectors == 0) {
// Unrecoverable as metadata reads from cow device failed
LOG(FATAL) << "Failed to retrieve base_sectors from Snapuserd";
@@ -1548,7 +1577,7 @@
// Otherwise (UPDATED flag), only delete snapshots if they are not mapped
// as dm-snapshot (for example, after merge completes).
bool should_unmap = current_slot != Slot::Target;
- bool should_delete = ShouldDeleteSnapshot(lock, flashing_status, current_slot, name);
+ bool should_delete = ShouldDeleteSnapshot(flashing_status, current_slot, name);
bool partition_ok = true;
if (should_unmap && !UnmapPartitionWithSnapshot(lock, name)) {
@@ -1582,8 +1611,7 @@
}
// See comments in RemoveAllSnapshots().
-bool SnapshotManager::ShouldDeleteSnapshot(LockedFile* lock,
- const std::map<std::string, bool>& flashing_status,
+bool SnapshotManager::ShouldDeleteSnapshot(const std::map<std::string, bool>& flashing_status,
Slot current_slot, const std::string& name) {
if (current_slot != Slot::Target) {
return true;
@@ -1597,16 +1625,7 @@
// partition flashed, okay to delete obsolete snapshots
return true;
}
- // partition updated, only delete if not dm-snapshot
- SnapshotStatus status;
- if (!ReadSnapshotStatus(lock, name, &status)) {
- LOG(WARNING) << "Unable to read snapshot status for " << name
- << ", guessing snapshot device name";
- auto extra_name = GetSnapshotExtraDeviceName(name);
- return !IsSnapshotDevice(name) && !IsSnapshotDevice(extra_name);
- }
- auto dm_name = GetSnapshotDeviceName(name, status);
- return !IsSnapshotDevice(dm_name);
+ return !IsSnapshotDevice(name);
}
UpdateState SnapshotManager::GetUpdateState(double* progress) {
@@ -1922,24 +1941,35 @@
}
if (live_snapshot_status->compression_enabled()) {
- auto name = GetDmUserCowName(params.GetPartitionName());
+ // Get the source device (eg the view of the partition from before it was resized).
+ std::string source_device_path;
+ if (!MapSourceDevice(lock, params.GetPartitionName(), remaining_time,
+ &source_device_path)) {
+ LOG(ERROR) << "Could not map source device for: " << cow_name;
+ return false;
+ }
+
+ auto source_device = GetSourceDeviceName(params.GetPartitionName());
+ created_devices.EmplaceBack<AutoUnmapDevice>(&dm, source_device);
+
+ if (!WaitForDevice(source_device_path, remaining_time)) {
+ return false;
+ }
std::string cow_path;
if (!GetMappedImageDevicePath(cow_name, &cow_path)) {
LOG(ERROR) << "Could not determine path for: " << cow_name;
return false;
}
-
- // Ensure both |base_path| and |cow_path| are created, for snapuserd.
- if (!WaitForDevice(base_path, remaining_time)) {
- return false;
- }
if (!WaitForDevice(cow_path, remaining_time)) {
return false;
}
+ auto name = GetDmUserCowName(params.GetPartitionName());
+
std::string new_cow_device;
- if (!MapDmUserCow(lock, name, cow_path, base_path, remaining_time, &new_cow_device)) {
+ if (!MapDmUserCow(lock, name, cow_path, source_device_path, remaining_time,
+ &new_cow_device)) {
LOG(ERROR) << "Could not map dm-user device for partition "
<< params.GetPartitionName();
return false;
@@ -1983,12 +2013,18 @@
}
auto& dm = DeviceMapper::Instance();
- std::string base_name = GetBaseDeviceName(target_partition_name);
+ auto base_name = GetBaseDeviceName(target_partition_name);
if (!dm.DeleteDeviceIfExists(base_name)) {
LOG(ERROR) << "Cannot delete base device: " << base_name;
return false;
}
+ auto source_name = GetSourceDeviceName(target_partition_name);
+ if (!dm.DeleteDeviceIfExists(source_name)) {
+ LOG(ERROR) << "Cannot delete source device: " << source_name;
+ return false;
+ }
+
LOG(INFO) << "Successfully unmapped snapshot " << target_partition_name;
return true;
@@ -2409,14 +2445,6 @@
return true;
}
-std::string SnapshotManager::GetSnapshotDeviceName(const std::string& snapshot_name,
- const SnapshotStatus& status) {
- if (status.device_size() != status.snapshot_size()) {
- return GetSnapshotExtraDeviceName(snapshot_name);
- }
- return snapshot_name;
-}
-
bool SnapshotManager::EnsureImageManager() {
if (images_) return true;
@@ -2588,6 +2616,24 @@
return Return::Error();
}
+ // If compression is enabled, we need to retain a copy of the old metadata
+ // so we can access original blocks in case they are moved around. We do
+ // not want to rely on the old super metadata slot because we don't
+ // guarantee its validity after the slot switch is successful.
+ if (cow_creator.compression_enabled) {
+ auto metadata = current_metadata->Export();
+ if (!metadata) {
+ LOG(ERROR) << "Could not export current metadata";
+ return Return::Error();
+ }
+
+ auto path = GetOldPartitionMetadataPath();
+ if (!android::fs_mgr::WriteToImageFile(path, *metadata.get())) {
+ LOG(ERROR) << "Cannot write old metadata to " << path;
+ return Return::Error();
+ }
+ }
+
SnapshotUpdateStatus status = {};
status.set_state(update_state);
status.set_compression_enabled(cow_creator.compression_enabled);
@@ -2688,6 +2734,15 @@
continue;
}
+ // Find the original partition size.
+ auto name = target_partition->name();
+ auto old_partition_name =
+ name.substr(0, name.size() - target_suffix.size()) + cow_creator->current_suffix;
+ auto old_partition = cow_creator->current_metadata->FindPartition(old_partition_name);
+ if (old_partition) {
+ cow_creator_ret->snapshot_status.set_old_partition_size(old_partition->size());
+ }
+
// Store these device sizes to snapshot status file.
if (!CreateSnapshot(lock, &cow_creator_ret->snapshot_status)) {
return Return::Error();
@@ -3375,5 +3430,26 @@
return PerformInitTransition(InitTransition::SECOND_STAGE);
}
+const LpMetadata* SnapshotManager::ReadOldPartitionMetadata(LockedFile* lock) {
+ CHECK(lock);
+
+ if (!old_partition_metadata_) {
+ auto path = GetOldPartitionMetadataPath();
+ old_partition_metadata_ = android::fs_mgr::ReadFromImageFile(path);
+ if (!old_partition_metadata_) {
+ LOG(ERROR) << "Could not read old partition metadata from " << path;
+ return nullptr;
+ }
+ }
+ return old_partition_metadata_.get();
+}
+
+MergePhase SnapshotManager::DecideMergePhase(const SnapshotStatus& status) {
+ if (status.compression_enabled() && status.device_size() < status.old_partition_size()) {
+ return MergePhase::FIRST_PHASE;
+ }
+ return MergePhase::SECOND_PHASE;
+}
+
} // namespace snapshot
} // namespace android
diff --git a/fs_mgr/libsnapshot/snapshot_test.cpp b/fs_mgr/libsnapshot/snapshot_test.cpp
index 80a6074..4c209ec 100644
--- a/fs_mgr/libsnapshot/snapshot_test.cpp
+++ b/fs_mgr/libsnapshot/snapshot_test.cpp
@@ -234,7 +234,8 @@
.partition_opener = &opener,
};
- auto result = sm->OpenSnapshotWriter(params, {});
+ auto old_partition = "/dev/block/mapper/" + GetOtherPartitionName(name);
+ auto result = sm->OpenSnapshotWriter(params, {old_partition});
if (!result) {
return AssertionFailure() << "Cannot open snapshot for writing: " << name;
}
@@ -467,31 +468,6 @@
ASSERT_TRUE(android::base::StartsWith(snap_device, "/dev/block/dm-"));
}
-TEST_F(SnapshotTest, MapPartialSnapshot) {
- ASSERT_TRUE(AcquireLock());
-
- static const uint64_t kSnapshotSize = 1024 * 1024;
- static const uint64_t kDeviceSize = 1024 * 1024 * 2;
- SnapshotStatus status;
- status.set_name("test-snapshot");
- status.set_device_size(kDeviceSize);
- status.set_snapshot_size(kSnapshotSize);
- status.set_cow_file_size(kSnapshotSize);
- ASSERT_TRUE(sm->CreateSnapshot(lock_.get(), &status));
- ASSERT_TRUE(CreateCowImage("test-snapshot"));
-
- std::string base_device;
- ASSERT_TRUE(CreatePartition("base-device", kDeviceSize, &base_device));
-
- std::string cow_device;
- ASSERT_TRUE(MapCowImage("test-snapshot", 10s, &cow_device));
-
- std::string snap_device;
- ASSERT_TRUE(sm->MapSnapshot(lock_.get(), "test-snapshot", base_device, cow_device, 10s,
- &snap_device));
- ASSERT_TRUE(android::base::StartsWith(snap_device, "/dev/block/dm-"));
-}
-
TEST_F(SnapshotTest, NoMergeBeforeReboot) {
ASSERT_TRUE(sm->FinishedSnapshotWrites(false));
@@ -590,8 +566,7 @@
ASSERT_EQ(status.state(), SnapshotState::CREATED);
DeviceMapper::TargetInfo target;
- auto dm_name = init->GetSnapshotDeviceName("test_partition_b", status);
- ASSERT_TRUE(init->IsSnapshotDevice(dm_name, &target));
+ ASSERT_TRUE(init->IsSnapshotDevice("test_partition_b", &target));
ASSERT_EQ(DeviceMapper::GetTargetType(target.spec), "snapshot");
}
@@ -618,8 +593,7 @@
// We should not get a snapshot device now.
DeviceMapper::TargetInfo target;
- auto dm_name = init->GetSnapshotDeviceName("test_partition_b", status);
- ASSERT_FALSE(init->IsSnapshotDevice(dm_name, &target));
+ ASSERT_FALSE(init->IsSnapshotDevice("test_partition_b", &target));
// We should see a cancelled update as well.
lock_ = nullptr;
diff --git a/fs_mgr/libsnapshot/utility.cpp b/fs_mgr/libsnapshot/utility.cpp
index 7342fd4..4a2af1c 100644
--- a/fs_mgr/libsnapshot/utility.cpp
+++ b/fs_mgr/libsnapshot/utility.cpp
@@ -187,5 +187,13 @@
return android::base::GetBoolProperty("ro.virtual_ab.compression.enabled", false);
}
+std::string GetOtherPartitionName(const std::string& name) {
+ auto suffix = android::fs_mgr::GetPartitionSlotSuffix(name);
+ CHECK(suffix == "_a" || suffix == "_b");
+
+ auto other_suffix = (suffix == "_a") ? "_b" : "_a";
+ return name.substr(0, name.size() - suffix.size()) + other_suffix;
+}
+
} // namespace snapshot
} // namespace android
diff --git a/fs_mgr/libsnapshot/utility.h b/fs_mgr/libsnapshot/utility.h
index 3e6873b..671de9d 100644
--- a/fs_mgr/libsnapshot/utility.h
+++ b/fs_mgr/libsnapshot/utility.h
@@ -131,5 +131,8 @@
bool IsCompressionEnabled();
+// Swap the suffix of a partition name.
+std::string GetOtherPartitionName(const std::string& name);
+
} // namespace snapshot
} // namespace android
diff --git a/fs_mgr/tests/adb-remount-test.sh b/fs_mgr/tests/adb-remount-test.sh
index f5bbe35..2433833 100755
--- a/fs_mgr/tests/adb-remount-test.sh
+++ b/fs_mgr/tests/adb-remount-test.sh
@@ -740,7 +740,7 @@
grep -v \
-e "^\(overlay\|tmpfs\|none\|sysfs\|proc\|selinuxfs\|debugfs\|bpf\) " \
-e "^\(binfmt_misc\|cg2_bpf\|pstore\|tracefs\|adb\|mtp\|ptp\|devpts\) " \
- -e "^\(ramdumpfs\|binder\|/sys/kernel/debug\) " \
+ -e "^\(ramdumpfs\|binder\|/sys/kernel/debug\|securityfs\) " \
-e " functionfs " \
-e "^\(/data/media\|/dev/block/loop[0-9]*\) " \
-e "^rootfs / rootfs rw," \
diff --git a/trusty/keymaster/TrustyKeymaster.cpp b/trusty/keymaster/TrustyKeymaster.cpp
index 5690031..23e0433 100644
--- a/trusty/keymaster/TrustyKeymaster.cpp
+++ b/trusty/keymaster/TrustyKeymaster.cpp
@@ -37,6 +37,12 @@
if (versionRsp.error != KM_ERROR_OK) {
ALOGW("TA appears not to support GetVersion2, falling back (err = %d)", versionRsp.error);
+ err = trusty_keymaster_connect();
+ if (err) {
+ ALOGE("Failed to connect to trusty keymaster %d", err);
+ return err;
+ }
+
GetVersionRequest versionReq;
GetVersionResponse versionRsp;
GetVersion(versionReq, &versionRsp);
diff --git a/trusty/secure_dpu/Android.bp b/trusty/secure_dpu/Android.bp
new file mode 100644
index 0000000..0d57cea
--- /dev/null
+++ b/trusty/secure_dpu/Android.bp
@@ -0,0 +1,20 @@
+// Copyright (C) 2021 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.
+
+cc_library_headers {
+ name: "secure_dpu_headers",
+ vendor: true,
+
+ export_include_dirs: ["include"],
+}
diff --git a/trusty/secure_dpu/include/trusty/secure_dpu/SecureDpu.h b/trusty/secure_dpu/include/trusty/secure_dpu/SecureDpu.h
new file mode 100644
index 0000000..b939d44
--- /dev/null
+++ b/trusty/secure_dpu/include/trusty/secure_dpu/SecureDpu.h
@@ -0,0 +1,113 @@
+/*
+ * Copyright 2020, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <stdint.h>
+
+/**
+ * DOC: Secure DPU
+ *
+ * The Secure DPU works as the persistent channel between the non-secure and the
+ * secure world. The channel is established during the boot up stage of the
+ * non-secure world system. In general, the established channel allows the
+ * secure world applications initiate requests or notifications to the non-secure
+ * world.
+ *
+ * For particular devices, the secure world can only perform operations on the
+ * display when in the TUI session if device-specific setup is done by the
+ * non-secure world. Besides, the non-secure world could allocate framebuffer
+ * for the secure world application if the memory is limited in the secure world
+ * on specific devices.
+ *
+ * Currently, supported requests are to start / stop the secure display mode and
+ * to allocate framebuffer.
+ *
+ * This header file needs to be synced on both the Trusty and the Android
+ * codebase.
+ */
+
+#define SECURE_DPU_PORT_NAME "com.android.trusty.secure_dpu"
+#define SECURE_DPU_MAX_MSG_SIZE 64
+
+/**
+ * enum secure_dpu_cmd - command identifiers for secure_fb interface
+ * @SECURE_DPU_CMD_RESP_BIT:
+ * Message is a response.
+ * @SECURE_DPU_CMD_REQ_SHIFT:
+ * Number of bits used by @SECURE_DPU_CMD_RESP_BIT.
+ * @SECURE_DPU_CMD_START_SECURE_DISPLAY:
+ * Notify the system to start secure display mode
+ * @SECURE_DPU_CMD_STOP_SECURE_DISPLAY:
+ * Notify the system to stop secure display mode
+ * @SECURE_DPU_CMD_ALLOCATE_BUFFER:
+ * Request non-secure world to allocate the buffer
+ */
+enum secure_dpu_cmd {
+ SECURE_DPU_CMD_RESP_BIT = 1,
+ SECURE_DPU_CMD_REQ_SHIFT = 1,
+ SECURE_DPU_CMD_START_SECURE_DISPLAY = (1 << SECURE_DPU_CMD_REQ_SHIFT),
+ SECURE_DPU_CMD_STOP_SECURE_DISPLAY = (2 << SECURE_DPU_CMD_REQ_SHIFT),
+ SECURE_DPU_CMD_ALLOCATE_BUFFER = (3 << SECURE_DPU_CMD_REQ_SHIFT),
+};
+
+/**
+ * struct secure_dpu_allocate_buffer_req - payload for
+ * %SECURE_DPU_CMD_ALLOCATE_BUFFER
+ * request
+ * @buffer_len: Requested length
+ */
+struct secure_dpu_allocate_buffer_req {
+ uint64_t buffer_len;
+};
+
+/**
+ * struct secure_dpu_allocate_buffer_resp - payload for
+ * %SECURE_DPU_CMD_ALLOCATE_BUFFER
+ * response
+ * @buffer_len: Allocated length
+ */
+struct secure_dpu_allocate_buffer_resp {
+ uint64_t buffer_len;
+};
+
+/**
+ * struct secure_fb_req - common structure for secure_fb requests.
+ * @cmd: Command identifier - one of &enum secure_dpu_cmd.
+ */
+struct secure_dpu_req {
+ uint32_t cmd;
+};
+
+/**
+ * struct secure_dpu_resp - common structure for secure_dpu responses.
+ * @cmd: Command identifier - %SECURE_DPU_CMD_RESP_BIT or'ed with the
+ * command identifier of the corresponding
+ * request.
+ * @status: Status of requested operation. One of &enum secure_dpu_error.
+ */
+struct secure_dpu_resp {
+ uint32_t cmd;
+ int32_t status;
+};
+
+enum secure_dpu_error {
+ SECURE_DPU_ERROR_OK = 0,
+ SECURE_DPU_ERROR_FAIL = -1,
+ SECURE_DPU_ERROR_UNINITIALIZED = -2,
+ SECURE_DPU_ERROR_PARAMETERS = -3,
+ SECURE_DPU_ERROR_NO_MEMORY = -4,
+};