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
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
index 351dce7..ab258bc 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
@@ -345,6 +345,14 @@
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 @@
// 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 @@
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_;
};
diff --git a/fs_mgr/libsnapshot/snapshot.cpp b/fs_mgr/libsnapshot/snapshot.cpp
index 6595707..cee1de3 100644
--- a/fs_mgr/libsnapshot/snapshot.cpp
+++ b/fs_mgr/libsnapshot/snapshot.cpp
@@ -408,10 +408,12 @@
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 @@
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 @@
}
// 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 @@
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 @@
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 @@
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 @@
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