Add a new "virtual disk" feature.

It's extremely difficult to test storage related logic on devices
that don't have physical SD card slots.  So to support better
debugging and testing, add a new "virtual disk" feature which mounts
a 512MB file through loop device.

It relies on the kernel having the "loop.max_part" value set to
something other than 0 via the boot command line, since that allows
all the existing partition logic to fall into place.

Bug: 34903607
Test: builds, boots, virtual disk works
Change-Id: I04c5b33e37319d867542985a56b7999a9b7cf35d
diff --git a/Loop.cpp b/Loop.cpp
index 1127817..7e243de 100644
--- a/Loop.cpp
+++ b/Loop.cpp
@@ -32,12 +32,19 @@
 
 #include <cutils/log.h>
 
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+#include <android-base/unique_fd.h>
+
 #include <sysutils/SocketClient.h>
 #include "Loop.h"
 #include "Asec.h"
 #include "VoldUtil.h"
 #include "sehandle.h"
 
+using android::base::StringPrintf;
+using android::base::unique_fd;
+
 int Loop::dumpState(SocketClient *c) {
     int i;
     int fd;
@@ -229,6 +236,40 @@
     return 0;
 }
 
+int Loop::create(const std::string& target, std::string& out_device) {
+    unique_fd ctl_fd(open("/dev/loop-control", O_RDWR));
+    if (ctl_fd.get() == -1) {
+        PLOG(ERROR) << "Failed to open loop-control";
+        return -errno;
+    }
+
+    int num = ioctl(ctl_fd.get(), LOOP_CTL_GET_FREE);
+    if (num == -1) {
+        PLOG(ERROR) << "Failed LOOP_CTL_GET_FREE";
+        return -errno;
+    }
+
+    out_device = StringPrintf("/dev/block/loop%d", num);
+
+    unique_fd target_fd(open(target.c_str(), O_RDWR));
+    if (target_fd.get() == -1) {
+        PLOG(ERROR) << "Failed to open " << target;
+        return -errno;
+    }
+    unique_fd device_fd(open(out_device.c_str(), O_RDWR));
+    if (device_fd.get() == -1) {
+        PLOG(ERROR) << "Failed to open " << out_device;
+        return -errno;
+    }
+
+    if (ioctl(device_fd.get(), LOOP_SET_FD, target_fd.get()) == -1) {
+        PLOG(ERROR) << "Failed to LOOP_SET_FD";
+        return -errno;
+    }
+
+    return 0;
+}
+
 int Loop::destroyByDevice(const char *loopDevice) {
     int device_fd;
 
@@ -254,20 +295,37 @@
 }
 
 int Loop::createImageFile(const char *file, unsigned long numSectors) {
-    int fd;
+    int res = 0;
 
-    if ((fd = creat(file, 0600)) < 0) {
-        SLOGE("Error creating imagefile (%s)", strerror(errno));
-        return -1;
+    char* secontext = nullptr;
+    if (sehandle) {
+        if (!selabel_lookup(sehandle, &secontext, file, S_IFREG)) {
+            setfscreatecon(secontext);
+        }
     }
 
-    if (ftruncate(fd, numSectors * 512) < 0) {
-        SLOGE("Error truncating imagefile (%s)", strerror(errno));
-        close(fd);
-        return -1;
+    unique_fd fd(creat(file, 0600));
+    if (fd.get() == -1) {
+        PLOG(ERROR) << "Failed to create image " << file;
+        res = -errno;
+        goto done;
     }
-    close(fd);
-    return 0;
+
+    if (fallocate(fd.get(), 0, 0, numSectors * 512) == -1) {
+        PLOG(WARNING) << "Failed to fallocate; falling back to ftruncate";
+        if (ftruncate(fd, numSectors * 512) == -1) {
+            PLOG(ERROR) << "Failed to ftruncate";
+            res = -errno;
+        }
+    }
+
+done:
+    if (secontext) {
+        setfscreatecon(nullptr);
+        freecon(secontext);
+    }
+
+    return res;
 }
 
 int Loop::resizeImageFile(const char *file, unsigned long numSectors) {