Fix Ext4 filesystem support.

This patch fixes the way the emulator handles Ext4 partition images.

Before the patch:

  - The hardware property 'hw.useext4' was used to determine whether
    the partition images use yaffs2 (for 'no') or ext4 (for 'yes').
    The default value was based on the API level, since ext4 is
    used since Android 4.4 (API level 19), but it was easy to get
    confused, resulting in the inability to properly boot the
    AVD. The symptom was not trivial, i.e.:

      - Mounting /system with the disk image failed, so the
        system kept using the ramdisk mount.

      - Later, logcat contained plenty of complaints that some
        stuff couldn't be found under /system/bin/

After the patch:

  - The partition format is simply auto-detected by probing the
    start of the disk image. The hardware property has not been
    removed because doing this breaks snapshots, but it is now
    tagged as deprecated.

+ Move the ext4 probing code from android/avd/util.* to
  android/filesystems/ext4_utils.* and add proper unit tests.

Change-Id: Iab24191e491d4cb8589dd13408618a0d13efeb76
diff --git a/vl-android.c b/vl-android.c
index 009d2aa..47e1628 100644
--- a/vl-android.c
+++ b/vl-android.c
@@ -53,6 +53,7 @@
 #include "android/charpipe.h"
 #include "android/log-rotate.h"
 #include "modem_driver.h"
+#include "android/filesystems/ext4_utils.h"
 #include "android/gps.h"
 #include "android/hw-kmsg.h"
 #include "android/hw-pipe-net.h"
@@ -3268,6 +3269,10 @@
         }
     }
 
+    bool systemImageIsExt4 = false;
+    bool dataImageIsExt4 = false;
+    bool cacheImageIsExt4 = false;
+
     /* Initialize system partition image */
     {
         char        tmp[PATH_MAX+32];
@@ -3281,7 +3286,9 @@
 
         snprintf(tmp,sizeof(tmp),"system,size=0x%" PRIx64, sysBytes);
 
+        const char* imageFile = NULL;
         if (sysImage && *sysImage) {
+            imageFile = sysImage;
             if (filelock_create(sysImage) == NULL) {
                 fprintf(stderr,"WARNING: System image already in use, changes will not persist!\n");
                 /* If there is no file= parameters, nand_add_dev will create
@@ -3292,6 +3299,7 @@
             }
         }
         if (initImage && *initImage) {
+            imageFile = initImage;
             if (!path_exists(initImage)) {
                 PANIC("Invalid initial system image path: %s", initImage);
             }
@@ -3300,11 +3308,14 @@
         } else {
             PANIC("Missing initial system image path!");
         }
-        if (android_hw->hw_useext4) {
+        systemImageIsExt4 = imageFile && android_pathIsExt4PartitionImage(imageFile);
+        if (systemImageIsExt4) {
             /* Using a nand device to approximate a block device until full
              * support is added */
             pstrcat(tmp,sizeof(tmp),",pagesize=512,extrasize=0");
         }
+        dprint("System partition format: %s",
+               systemImageIsExt4 ? "ext4" : "yaffs2");
         nand_add_dev(tmp);
     }
 
@@ -3321,6 +3332,7 @@
 
         snprintf(tmp,sizeof(tmp),"userdata,size=0x%" PRIx64, dataBytes);
 
+        const char* imageFile = dataImage;
         if (dataImage && *dataImage) {
             if (filelock_create(dataImage) == NULL) {
                 fprintf(stderr, "WARNING: Data partition already in use. Changes will not persist!\n");
@@ -3338,14 +3350,18 @@
             }
         }
         if (initImage && *initImage) {
+            imageFile = initImage;
             pstrcat(tmp, sizeof(tmp), ",initfile=");
             pstrcat(tmp, sizeof(tmp), initImage);
         }
-        if (android_hw->hw_useext4) {
+        dataImageIsExt4 = imageFile && android_pathIsExt4PartitionImage(imageFile);
+        if (dataImageIsExt4) {
             /* Using a nand device to approximate a block device until full
              * support is added */
             pstrcat(tmp, sizeof(tmp), ",pagesize=512,extrasize=0");
         }
+        dprint("Data partition format: %s",
+               dataImageIsExt4 ? "ext4" : "yaffs2");
         nand_add_dev(tmp);
     }
 
@@ -3571,6 +3587,9 @@
 
         snprintf(tmp,sizeof(tmp),"cache,size=0x%" PRIx64, partSize);
 
+        // NOTE: Assume the /cache and /data partitions have the same format
+        cacheImageIsExt4 = dataImageIsExt4;
+
         if (partPath && *partPath && strcmp(partPath, "<temp>") != 0) {
             if (filelock_create(partPath) == NULL) {
                 fprintf(stderr, "WARNING: Cache partition already in use. Changes will not persist!\n");
@@ -3579,6 +3598,7 @@
             } else {
                 /* Create the file if needed */
                 if (!path_exists(partPath)) {
+                    // TODO(digit): For EXT4, create a real empty ext4 partition image.
                     if (path_empty_file(partPath) < 0) {
                         PANIC("Could not create cache image file %s: %s", partPath, strerror(errno));
                     }
@@ -3587,11 +3607,13 @@
                 pstrcat(tmp, sizeof(tmp), partPath);
             }
         }
-        if (android_hw->hw_useext4) {
+        if (cacheImageIsExt4) {
             /* Using a nand device to approximate a block device until full
              * support is added */
             pstrcat(tmp, sizeof(tmp), ",pagesize=512,extrasize=0");
         }
+        dprint("Cache partition format: %s",
+               cacheImageIsExt4 ? "ext4" : "yaffs2");
         nand_add_dev(tmp);
     }