Switch to use libdm to bind loop devices
am: 173e619eae

Change-Id: I8ad6cd13e1da5aef85470f5aa53cc516bac45e31
diff --git a/Android.bp b/Android.bp
index 8418697..9d6ec7c 100644
--- a/Android.bp
+++ b/Android.bp
@@ -652,6 +652,7 @@
         "libgmock",
         "libchrome_test_helpers",
         "libupdate_engine_android",
+        "libdm",
     ],
 
     header_libs: [
diff --git a/common/test_utils.cc b/common/test_utils.cc
index 50b0962..bd69d03 100644
--- a/common/test_utils.cc
+++ b/common/test_utils.cc
@@ -37,6 +37,10 @@
 #include <base/files/file_util.h>
 #include <base/logging.h>
 
+#ifdef __ANDROID__
+#include <libdm/loop_control.h>
+#endif
+
 #include "update_engine/common/error_code_utils.h"
 #include "update_engine/common/utils.h"
 #include "update_engine/payload_consumer/file_writer.h"
@@ -44,16 +48,7 @@
 using std::set;
 using std::string;
 using std::vector;
-
-namespace {
-
-#ifdef __ANDROID__
-#define kLoopDevicePrefix "/dev/block/loop"
-#else
-#define kLoopDevicePrefix "/dev/loop"
-#endif  // __ANDROID__
-
-}  // namespace
+using namespace std::chrono_literals;
 
 namespace chromeos_update_engine {
 
@@ -112,17 +107,43 @@
   return utils::WriteFile(path.c_str(), data.data(), data.size());
 }
 
-bool BindToUnusedLoopDevice(const string& filename,
-                            bool writable,
-                            string* out_lo_dev_name) {
-  CHECK(out_lo_dev_name);
+bool SetLoopDeviceStatus(int loop_device_fd,
+                         const std::string& filename,
+                         int loop_number,
+                         bool writable) {
+  struct loop_info64 device_info {};
+  device_info.lo_offset = 0;
+  device_info.lo_sizelimit = 0;  // 0 means whole file.
+  device_info.lo_flags = (writable ? 0 : LO_FLAGS_READ_ONLY);
+  device_info.lo_number = loop_number;
+  strncpy(reinterpret_cast<char*>(device_info.lo_file_name),
+          base::FilePath(filename).BaseName().value().c_str(),
+          LO_NAME_SIZE - 1);
+  device_info.lo_file_name[LO_NAME_SIZE - 1] = '\0';
+  TEST_AND_RETURN_FALSE_ERRNO(
+      ioctl(loop_device_fd, LOOP_SET_STATUS64, &device_info) == 0);
+  if (writable) {
+    // Make sure loop device isn't read only.
+    int ro = 0;
+    if (ioctl(loop_device_fd, BLKROSET, &ro) != 0) {
+      PLOG(WARNING) << "Failed to mark loop device writable.";
+    }
+  }
+
+  return true;
+}
+
+bool BindToUnusedLoopDeviceLegacy(int data_fd,
+                                  const string& filename,
+                                  bool writable,
+                                  string* out_lo_dev_name) {
   // Get the next available loop-device.
   int control_fd =
       HANDLE_EINTR(open("/dev/loop-control", O_RDWR | O_LARGEFILE));
   TEST_AND_RETURN_FALSE_ERRNO(control_fd >= 0);
   int loop_number = ioctl(control_fd, LOOP_CTL_GET_FREE);
   IGNORE_EINTR(close(control_fd));
-  *out_lo_dev_name = kLoopDevicePrefix + std::to_string(loop_number);
+  *out_lo_dev_name = "/dev/loop" + std::to_string(loop_number);
 
   // Double check that the loop exists and is free.
   int loop_device_fd =
@@ -146,32 +167,35 @@
     return false;
   }
 
-  // Open our data file and assign it to the loop device.
+  // Assign the data fd to the loop device.
+  TEST_AND_RETURN_FALSE_ERRNO(ioctl(loop_device_fd, LOOP_SET_FD, data_fd) == 0);
+  return SetLoopDeviceStatus(loop_device_fd, filename, loop_number, writable);
+}
+
+bool BindToUnusedLoopDevice(const string& filename,
+                            bool writable,
+                            string* out_lo_dev_name) {
+  CHECK(out_lo_dev_name);
   int data_fd = open(filename.c_str(),
                      (writable ? O_RDWR : O_RDONLY) | O_LARGEFILE | O_CLOEXEC);
   TEST_AND_RETURN_FALSE_ERRNO(data_fd >= 0);
   ScopedFdCloser data_fd_closer(&data_fd);
-  TEST_AND_RETURN_FALSE_ERRNO(ioctl(loop_device_fd, LOOP_SET_FD, data_fd) == 0);
 
-  memset(&device_info, 0, sizeof(device_info));
-  device_info.lo_offset = 0;
-  device_info.lo_sizelimit = 0;  // 0 means whole file.
-  device_info.lo_flags = (writable ? 0 : LO_FLAGS_READ_ONLY);
-  device_info.lo_number = loop_number;
-  strncpy(reinterpret_cast<char*>(device_info.lo_file_name),
-          base::FilePath(filename).BaseName().value().c_str(),
-          LO_NAME_SIZE - 1);
-  device_info.lo_file_name[LO_NAME_SIZE - 1] = '\0';
-  TEST_AND_RETURN_FALSE_ERRNO(
-      ioctl(loop_device_fd, LOOP_SET_STATUS64, &device_info) == 0);
-  if (writable) {
-    // Make sure loop device isn't read only.
-    int ro = 0;
-    if (ioctl(loop_device_fd, BLKROSET, &ro) != 0) {
-      PLOG(WARNING) << "Failed to mark loop device writable.";
-    }
-  }
-  return true;
+#ifdef __ANDROID__
+  // Use libdm to bind a free loop device. The library internally handles the
+  // race condition.
+  android::dm::LoopControl loop_control;
+  TEST_AND_RETURN_FALSE(loop_control.Attach(data_fd, 5s, out_lo_dev_name));
+  int loop_device_fd = open(out_lo_dev_name->c_str(), O_RDWR | O_CLOEXEC);
+  ScopedFdCloser loop_fd_closer(&loop_device_fd);
+  int loop_number;
+  TEST_AND_RETURN_FALSE(
+      sscanf(out_lo_dev_name->c_str(), "/dev/block/loop%d", &loop_number) == 1);
+  return SetLoopDeviceStatus(loop_device_fd, filename, loop_number, writable);
+#else
+  return BindToUnusedLoopDeviceLegacy(
+      data_fd, filename, writable, out_lo_dev_name);
+#endif
 }
 
 bool UnbindLoopDevice(const string& lo_dev_name) {