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