libsnapshot: Refactor CreateLogicalAndSnapshotPartitions

Create SnapshotManager::MapPartitionWithSnapshot that deals
with one partition so that the code can be re-used later.

Test: libsnapshot_test

Change-Id: Iba36e6c05f8541397323361f7ceb62a6472d2e1c
This commit is contained in:
Yifan Hong 2019-08-16 15:00:27 -07:00
parent a9ffef27e1
commit 31fda7e476
2 changed files with 90 additions and 58 deletions

View file

@ -329,6 +329,10 @@ class SnapshotManager final {
std::string GetSnapshotDeviceName(const std::string& snapshot_name,
const SnapshotStatus& status);
// Map the base device, COW devices, and snapshot device.
bool MapPartitionWithSnapshot(LockedFile* lock, CreateLogicalPartitionParams params,
std::string* path);
std::string gsid_dir_;
std::string metadata_dir_;
std::unique_ptr<IDeviceInfo> device_;

View file

@ -19,6 +19,7 @@
#include <sys/types.h>
#include <sys/unistd.h>
#include <optional>
#include <thread>
#include <unordered_set>
@ -1102,22 +1103,6 @@ bool SnapshotManager::CreateLogicalAndSnapshotPartitions(const std::string& supe
auto lock = LockExclusive();
if (!lock) return false;
std::vector<std::string> snapshot_list;
if (!ListSnapshots(lock.get(), &snapshot_list)) {
return false;
}
std::unordered_set<std::string> live_snapshots;
for (const auto& snapshot : snapshot_list) {
SnapshotStatus status;
if (!ReadSnapshotStatus(lock.get(), snapshot, &status)) {
return false;
}
if (status.state != SnapshotState::MergeCompleted) {
live_snapshots.emplace(snapshot);
}
}
const auto& opener = device_->GetPartitionOpener();
uint32_t slot = SlotNumberForSlotSuffix(device_->GetSlotSuffix());
auto metadata = android::fs_mgr::ReadMetadata(opener, super_device, slot);
@ -1126,59 +1111,102 @@ bool SnapshotManager::CreateLogicalAndSnapshotPartitions(const std::string& supe
return false;
}
// Map logical partitions.
auto& dm = DeviceMapper::Instance();
for (const auto& partition : metadata->partitions) {
auto partition_name = GetPartitionName(partition);
if (!partition.num_extents) {
LOG(INFO) << "Skipping zero-length logical partition: " << partition_name;
continue;
}
if (!(partition.attributes & LP_PARTITION_ATTR_UPDATED)) {
LOG(INFO) << "Detected re-flashing of partition, will skip snapshot: "
<< partition_name;
live_snapshots.erase(partition_name);
}
CreateLogicalPartitionParams params = {
.block_device = super_device,
.metadata = metadata.get(),
.partition = &partition,
.partition_opener = &opener,
};
if (auto iter = live_snapshots.find(partition_name); iter != live_snapshots.end()) {
// If the device has a snapshot, it'll need to be writable, and
// we'll need to create the logical partition with a marked-up name
// (since the snapshot will use the partition name).
params.force_writable = true;
params.device_name = GetBaseDeviceName(partition_name);
}
std::string ignore_path;
if (!CreateLogicalPartition(params, &ignore_path)) {
LOG(ERROR) << "Could not create logical partition " << partition_name << " as device "
<< params.GetDeviceName();
return false;
}
if (!params.force_writable) {
// No snapshot.
continue;
}
// We don't have ueventd in first-stage init, so use device major:minor
// strings instead.
std::string base_device;
if (!dm.GetDeviceString(params.GetDeviceName(), &base_device)) {
LOG(ERROR) << "Could not determine major/minor for: " << params.GetDeviceName();
return false;
}
if (!MapSnapshot(lock.get(), partition_name, base_device, {}, &ignore_path)) {
LOG(ERROR) << "Could not map snapshot for partition: " << partition_name;
if (!MapPartitionWithSnapshot(lock.get(), std::move(params), &ignore_path)) {
return false;
}
}
LOG(INFO) << "Created logical partitions with snapshot.";
return true;
}
bool SnapshotManager::MapPartitionWithSnapshot(LockedFile* lock,
CreateLogicalPartitionParams params,
std::string* path) {
CHECK(lock);
path->clear();
// Fill out fields in CreateLogicalPartitionParams so that we have more information (e.g. by
// reading super partition metadata).
CreateLogicalPartitionParams::OwnedData params_owned_data;
if (!params.InitDefaults(&params_owned_data)) {
return false;
}
if (!params.partition->num_extents) {
LOG(INFO) << "Skipping zero-length logical partition: " << params.GetPartitionName();
return true; // leave path empty to indicate that nothing is mapped.
}
// Determine if there is a live snapshot for the SnapshotStatus of the partition; i.e. if the
// partition still has a snapshot that needs to be mapped. If no live snapshot or merge
// completed, live_snapshot_status is set to nullopt.
std::optional<SnapshotStatus> live_snapshot_status;
do {
if (!(params.partition->attributes & LP_PARTITION_ATTR_UPDATED)) {
LOG(INFO) << "Detected re-flashing of partition, will skip snapshot: "
<< params.GetPartitionName();
break;
}
auto file_path = GetSnapshotStatusFilePath(params.GetPartitionName());
if (access(file_path.c_str(), F_OK) != 0) {
if (errno != ENOENT) {
PLOG(INFO) << "Can't map snapshot for " << params.GetPartitionName()
<< ": Can't access " << file_path;
return false;
}
break;
}
live_snapshot_status = std::make_optional<SnapshotStatus>();
if (!ReadSnapshotStatus(lock, params.GetPartitionName(), &*live_snapshot_status)) {
return false;
}
// No live snapshot if merge is completed.
if (live_snapshot_status->state == SnapshotState::MergeCompleted) {
live_snapshot_status.reset();
}
} while (0);
if (live_snapshot_status.has_value()) {
// dm-snapshot requires the base device to be writable.
params.force_writable = true;
// Map the base device with a different name to avoid collision.
params.device_name = GetBaseDeviceName(params.GetPartitionName());
}
auto& dm = DeviceMapper::Instance();
std::string ignore_path;
if (!CreateLogicalPartition(params, &ignore_path)) {
LOG(ERROR) << "Could not create logical partition " << params.GetPartitionName()
<< " as device " << params.GetDeviceName();
return false;
}
if (!live_snapshot_status.has_value()) {
return true;
}
// We don't have ueventd in first-stage init, so use device major:minor
// strings instead.
std::string base_device;
if (!dm.GetDeviceString(params.GetDeviceName(), &base_device)) {
LOG(ERROR) << "Could not determine major/minor for: " << params.GetDeviceName();
return false;
}
if (!MapSnapshot(lock, params.GetPartitionName(), base_device, {}, path)) {
LOG(ERROR) << "Could not map snapshot for partition: " << params.GetPartitionName();
return false;
}
LOG(INFO) << "Mapped " << params.GetPartitionName() << " as snapshot device at " << *path;
return true;
}