Revise loopback device handling to be parallelization safe.

Update engine tests were allocating loopback devices in a non-atomic fashion.
When they were run in parallel with other tests that were using loopback
devices, this could lead to two tests using the same device and failing.

This change follows jrbarnette's advice and allocates loopback devices
atomically using "losetup --show -f <file to bind>".

NOTE: integration_unittest.cc is not currently being built or run, and
so the change here has not been verified. I only discovered it wasn't being
built while making this change, and fixing/deleting it seemed out of scope
for the change.

BUG=chromium-os:24975
TEST=Ran unittests

Change-Id: Ica060c8add009cac134e22b46b4e87588d0246e0
Reviewed-on: https://gerrit.chromium.org/gerrit/15128
Reviewed-by: Richard Barnette <jrbarnette@chromium.org>
Reviewed-by: Darin Petkov <petkov@chromium.org>
Tested-by: Don Garrett <dgarrett@chromium.org>
Commit-Ready: Don Garrett <dgarrett@chromium.org>
diff --git a/test_utils.cc b/test_utils.cc
index 85c73d7..fcaae46 100644
--- a/test_utils.cc
+++ b/test_utils.cc
@@ -136,9 +136,11 @@
   return ret;
 }
 
-std::string GetUnusedLoopDevice() {
+string BindToUnusedLoopDevice(const string &filename) {
   // get a loop device we can use for the install device
-  FILE* find_dev_cmd = popen("losetup -f", "r");
+  string cmd = "losetup --show -f " + filename;
+
+  FILE* find_dev_cmd = popen(cmd.c_str(), "r");
   CHECK(find_dev_cmd);
 
   string ret;
@@ -156,6 +158,8 @@
   if (*ret.rbegin() == '\n')
     ret.resize(ret.size() - 1);
 
+  // Ensure that the device starts with "/dev/loop"
+  EXPECT_TRUE(StartsWithASCII(ret, "/dev/loop", true));
   return ret;
 }
 
@@ -281,16 +285,14 @@
   }
 }
 
-ScopedLoopMounter::ScopedLoopMounter(const std::string& file_path,
-                                     std::string* mnt_path,
+ScopedLoopMounter::ScopedLoopMounter(const string& file_path,
+                                     string* mnt_path,
                                      unsigned long flags) {
   EXPECT_TRUE(utils::MakeTempDirectory("/tmp/mnt.XXXXXX", mnt_path));
   dir_remover_.reset(new ScopedDirRemover(*mnt_path));
 
-  std::string loop_dev = GetUnusedLoopDevice();
-  EXPECT_EQ(0, system(StringPrintf("losetup %s %s", loop_dev.c_str(),
-                                   file_path.c_str()).c_str()));
-  loop_releaser_.reset(new ScopedLoopbackDeviceReleaser(loop_dev));
+  string loop_dev;
+  loop_binder_.reset(new ScopedLoopbackDeviceBinder(file_path, &loop_dev));
 
   EXPECT_TRUE(utils::MountFilesystem(loop_dev, *mnt_path, flags));
   unmounter_.reset(new ScopedFilesystemUnmounter(*mnt_path));