cryptohome: Add UdevAdmSettle

Add a function for UdevAdmSettle(): `udevadm settle` is used to
wait for any outstanding udev requests and optionally uses a
device path to wait. This guarantees the existence of the device
path on completion of the call.

BUG=b:172344853
TEST=unittests

Change-Id: Id508bf8ba4c205d3a02cb552a461d583bef0f439
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform2/+/2599220
Reviewed-by: Leo Lai <cylai@google.com>
Reviewed-by: Daniil Lunev <dlunev@chromium.org>
Tested-by: Sarthak Kukreti <sarthakkukreti@chromium.org>
Commit-Queue: Sarthak Kukreti <sarthakkukreti@chromium.org>
diff --git a/cryptohome/mock_platform.h b/cryptohome/mock_platform.h
index ed134b8..8eacafe 100644
--- a/cryptohome/mock_platform.h
+++ b/cryptohome/mock_platform.h
@@ -393,6 +393,7 @@
               SafeCreateDirAndSetOwnership,
               (const base::FilePath&, uid_t, gid_t),
               (override));
+  MOCK_METHOD(bool, UdevAdmSettle, (const base::FilePath&, bool), (override));
 
   brillo::ProcessMock* mock_process() { return mock_process_.get(); }
 
diff --git a/cryptohome/platform.cc b/cryptohome/platform.cc
index eb54d5f..c59a6b9b 100644
--- a/cryptohome/platform.cc
+++ b/cryptohome/platform.cc
@@ -804,6 +804,29 @@
   return path_result.second == brillo::SafeFD::Error::kNoError;
 }
 
+bool Platform::UdevAdmSettle(const base::FilePath& device_path,
+                             bool wait_for_device) {
+  brillo::ProcessImpl udevadm_process;
+  udevadm_process.AddArg("/bin/udevadm");
+  udevadm_process.AddArg("settle");
+
+  if (wait_for_device) {
+    udevadm_process.AddArg("-t");
+    udevadm_process.AddArg("10");
+    udevadm_process.AddArg("-E");
+    udevadm_process.AddArg(device_path.value());
+  }
+  // Close unused file descriptors in child process.
+  udevadm_process.SetCloseUnusedFileDescriptors(true);
+
+  // Start the process and return.
+  int rc = udevadm_process.Run();
+  if (rc != 0)
+    return false;
+
+  return true;
+}
+
 bool Platform::DeleteFile(const FilePath& path) {
   return base::DeleteFile(path);
 }
@@ -1650,10 +1673,8 @@
 }
 
 FileEnumerator::FileInfo::FileInfo(const FilePath& name,
-                                   const base::stat_wrapper_t& stat
-                                   )
-    : name_(name), stat_(stat) {
-}
+                                   const base::stat_wrapper_t& stat)
+    : name_(name), stat_(stat) {}
 
 FileEnumerator::FileInfo::FileInfo(const FileEnumerator::FileInfo& other) {
   if (other.info_.get()) {
diff --git a/cryptohome/platform.h b/cryptohome/platform.h
index 787891a..3ba28a4 100644
--- a/cryptohome/platform.h
+++ b/cryptohome/platform.h
@@ -917,6 +917,10 @@
                                             uid_t user_id,
                                             gid_t gid);
 
+  // Calls "udevadm settle", optionally waiting for the device path to appear.
+  virtual bool UdevAdmSettle(const base::FilePath& device_path,
+                             bool wait_for_device);
+
  private:
   // Returns the process and open file information for the specified process id
   // with files open on the given path