otautil: add support for unmounting entire volumes

When wiping /system, the partition isn't actually mounted at /system
or / - it's mounted at /mnt/system. This breaks 'format system' from
recovery if the partition has been mounted.

This patch adds an ensure_volume_unmounted function that finds all
mounts of a given device and unmounts them, meaning the device
can be safely formatted.

Change-Id: Id4f727f845308a89e865f1ba60dc284f5ebc66e1
diff --git a/install/wipe_data.cpp b/install/wipe_data.cpp
index 765a815..ed67207 100644
--- a/install/wipe_data.cpp
+++ b/install/wipe_data.cpp
@@ -52,7 +52,11 @@
 
   ui->Print("Formatting %s...\n", volume);
 
-  ensure_path_unmounted(volume);
+  Volume* vol = volume_for_mount_point(volume);
+  if (ensure_volume_unmounted(vol->blk_device) == -1) {
+    PLOG(ERROR) << "Failed to unmount volume!";
+    return false;
+  }
 
   int result;
   if (is_data && convert_fbe) {
diff --git a/otautil/include/otautil/roots.h b/otautil/include/otautil/roots.h
index 482f3d0..670bcbe 100644
--- a/otautil/include/otautil/roots.h
+++ b/otautil/include/otautil/roots.h
@@ -39,6 +39,10 @@
 // success (volume is unmounted);
 int ensure_path_unmounted(const std::string& path);
 
+// Make sure that the volume at 'blk_device' is unmounted.
+// Returns 0 on success.
+int ensure_volume_unmounted(const std::string& blk_device);
+
 // Reformat the given volume (must be the mount point only, eg
 // "/cache"), no paths permitted.  Attempts to unmount the volume if
 // it is mounted.
diff --git a/otautil/roots.cpp b/otautil/roots.cpp
index 815d644..c5ca476 100644
--- a/otautil/roots.cpp
+++ b/otautil/roots.cpp
@@ -88,6 +88,27 @@
   return android::fs_mgr::EnsurePathUnmounted(&fstab, path) ? 0 : -1;
 }
 
+int ensure_volume_unmounted(const std::string& blk_device) {
+  android::fs_mgr::Fstab mounted_fstab;
+  if (!android::fs_mgr::ReadFstabFromFile("/proc/mounts", &mounted_fstab)) {
+    LOG(ERROR) << "Failed to read /proc/mounts";
+    return -1;
+  }
+
+  /* find any entries with the volume */
+  for (auto& entry : mounted_fstab) {
+    if (entry.blk_device == blk_device) {
+      int result = umount(entry.mount_point.c_str());
+      if (result == -1) {
+        LOG(ERROR) << "Failed to unmount " << blk_device << " from " << entry.mount_point << ": "
+                   << errno;
+        return -1;
+      }
+    }
+  }
+  return 0;
+}
+
 static int exec_cmd(const std::vector<std::string>& args) {
   CHECK(!args.empty());
   auto argv = StringVectorToNullTerminatedArray(args);