libsnapshot: Add a helper for waiting for device paths.

Normally, DeviceMapper::CreateDevice() handles this for us. However, it
does not work in first-stage init, because ueventd is not running.
Therefore this patch adds a way for first-stage init to set a callback
to manually regenerate and process uevents.

Additionally, even with ueventd, dm-user misc device creation needs a
WaitForFile() call, since ueventd is asynchronous.

The WaitForDevice() helper in this patch accounts for both of these
scenarios.

Bug: 173476209
Test: device boots into first-stage init after full VABC ota
Change-Id: Ib7a9bfc2a5a5095aa00b358072f9cb1743c19ab2
This commit is contained in:
David Anderson 2020-11-21 13:44:00 -08:00
parent 46d1844377
commit 189e8e3a25
2 changed files with 70 additions and 11 deletions

View file

@ -345,6 +345,14 @@ class SnapshotManager final : public ISnapshotManager {
bool MapAllSnapshots(const std::chrono::milliseconds& timeout_ms = {}) override;
bool UnmapAllSnapshots() override;
// We can't use WaitForFile during first-stage init, because ueventd is not
// running and therefore will not automatically create symlinks. Instead,
// we let init provide us with the correct function to use to ensure
// uevents have been processed and symlink/mknod calls completed.
void SetUeventRegenCallback(std::function<bool(const std::string&)> callback) {
uevent_regen_callback_ = callback;
}
private:
FRIEND_TEST(SnapshotTest, CleanFirstStageMount);
FRIEND_TEST(SnapshotTest, CreateSnapshot);
@ -676,6 +684,12 @@ class SnapshotManager final : public ISnapshotManager {
// Same as above, but for paths only (no major:minor device strings).
bool GetMappedImageDevicePath(const std::string& device_name, std::string* device_path);
// Wait for a device to be created by ueventd (eg, its symlink or node to be populated).
// This is needed for any code that uses device-mapper path in first-stage init. If
// |timeout_ms| is empty or the given device is not a path, WaitForDevice immediately
// returns true.
bool WaitForDevice(const std::string& device, std::chrono::milliseconds timeout_ms);
std::string gsid_dir_;
std::string metadata_dir_;
std::unique_ptr<IDeviceInfo> device_;
@ -683,6 +697,7 @@ class SnapshotManager final : public ISnapshotManager {
bool has_local_image_manager_ = false;
bool use_first_stage_snapuserd_ = false;
bool in_factory_data_reset_ = false;
std::function<bool(const std::string&)> uevent_regen_callback_;
std::unique_ptr<SnapuserdClient> snapuserd_client_;
};

View file

@ -408,10 +408,12 @@ bool SnapshotManager::MapDmUserCow(LockedFile* lock, const std::string& name,
if (!dm.CreateDevice(name, table, path, timeout_ms)) {
return false;
}
if (!WaitForDevice(*path, timeout_ms)) {
return false;
}
auto control_device = "/dev/dm-user/" + misc_name;
if (!android::fs_mgr::WaitForFile(control_device, timeout_ms)) {
LOG(ERROR) << "Timed out waiting for dm-user misc device: " << control_device;
if (!WaitForDevice(control_device, timeout_ms)) {
return false;
}
@ -1339,10 +1341,12 @@ bool SnapshotManager::PerformSecondStageTransition() {
continue;
}
auto misc_name = user_cow_name;
DmTable table;
table.Emplace<DmTargetUser>(0, target.spec.length, user_cow_name);
table.Emplace<DmTargetUser>(0, target.spec.length, misc_name);
if (!dm.LoadTableAndActivate(user_cow_name, table)) {
LOG(ERROR) << "Unable to swap tables for " << user_cow_name;
LOG(ERROR) << "Unable to swap tables for " << misc_name;
continue;
}
@ -1368,14 +1372,13 @@ bool SnapshotManager::PerformSecondStageTransition() {
}
// Wait for ueventd to acknowledge and create the control device node.
std::string control_device = "/dev/dm-user/" + user_cow_name;
if (!android::fs_mgr::WaitForFile(control_device, 10s)) {
LOG(ERROR) << "Could not find control device: " << control_device;
std::string control_device = "/dev/dm-user/" + misc_name;
if (!WaitForDevice(control_device, 10s)) {
continue;
}
uint64_t base_sectors =
snapuserd_client_->InitDmUserCow(user_cow_name, cow_image_device, backing_device);
snapuserd_client_->InitDmUserCow(misc_name, cow_image_device, backing_device);
if (base_sectors == 0) {
// Unrecoverable as metadata reads from cow device failed
LOG(FATAL) << "Failed to retrieve base_sectors from Snapuserd";
@ -1384,7 +1387,7 @@ bool SnapshotManager::PerformSecondStageTransition() {
CHECK(base_sectors == target.spec.length);
if (!snapuserd_client_->AttachDmUser(user_cow_name)) {
if (!snapuserd_client_->AttachDmUser(misc_name)) {
// This error is unrecoverable. We cannot proceed because reads to
// the underlying device will fail.
LOG(FATAL) << "Could not initialize snapuserd for " << user_cow_name;
@ -1923,13 +1926,20 @@ bool SnapshotManager::MapPartitionWithSnapshot(LockedFile* lock,
if (IsCompressionEnabled()) {
auto name = GetDmUserCowName(params.GetPartitionName());
// :TODO: need to force init to process uevents for these in first-stage.
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;
}
std::string new_cow_device;
if (!MapDmUserCow(lock, name, cow_path, base_path, remaining_time, &new_cow_device)) {
LOG(ERROR) << "Could not map dm-user device for partition "
@ -2069,7 +2079,7 @@ bool SnapshotManager::UnmapCowDevices(LockedFile* lock, const std::string& name)
if (!EnsureSnapuserdConnected()) {
return false;
}
if (!dm.DeleteDevice(dm_user_name)) {
if (!dm.DeleteDeviceIfExists(dm_user_name)) {
LOG(ERROR) << "Cannot unmap " << dm_user_name;
return false;
}
@ -3293,5 +3303,39 @@ bool SnapshotManager::GetMappedImageDeviceStringOrPath(const std::string& device
return true;
}
bool SnapshotManager::WaitForDevice(const std::string& device,
std::chrono::milliseconds timeout_ms) {
if (!android::base::StartsWith(device, "/")) {
return true;
}
// In first-stage init, we rely on init setting a callback which can
// regenerate uevents and populate /dev for us.
if (uevent_regen_callback_) {
if (!uevent_regen_callback_(device)) {
LOG(ERROR) << "Failed to find device after regenerating uevents: " << device;
return false;
}
return true;
}
// Otherwise, the only kind of device we need to wait for is a dm-user
// misc device. Normal calls to DeviceMapper::CreateDevice() guarantee
// the path has been created.
if (!android::base::StartsWith(device, "/dev/dm-user/")) {
return true;
}
if (timeout_ms.count() == 0) {
LOG(ERROR) << "No timeout was specified to wait for device: " << device;
return false;
}
if (!android::fs_mgr::WaitForFile(device, timeout_ms)) {
LOG(ERROR) << "Timed out waiting for device to appear: " << device;
return false;
}
return true;
}
} // namespace snapshot
} // namespace android