Support F2FS for the data partition

This adds F2FS support
- for wiping a device
- for the install "format" command.

Note: crypto data in "footer" with a default/negative length
is not supported, unlike with "ext4".

Change-Id: I8d141a0d4d14df9fe84d3b131484e9696fcd8870
Signed-off-by: JP Abgrall <jpa@google.com>
diff --git a/Android.mk b/Android.mk
index 577679c..1970956 100644
--- a/Android.mk
+++ b/Android.mk
@@ -32,6 +32,12 @@
 
 LOCAL_FORCE_STATIC_EXECUTABLE := true
 
+# Not working as expected?
+# LOCAL_ADDITIONAL_DEPENDENCIES := mkfs.f2fs
+# LOCAL_ADDITIONAL_DEPENDENCIES := $(TARGET_RECOVERY_ROOT_OUT)/sbin/mkfs.f2fs
+LOCAL_ADDITIONAL_DEPENDENCIES := $(TARGET_ROOT_OUT_SBIN)/mkfs.f2fs
+
+
 RECOVERY_API_VERSION := 3
 RECOVERY_FSTAB_VERSION := 2
 LOCAL_CFLAGS += -DRECOVERY_API_VERSION=$(RECOVERY_API_VERSION)
diff --git a/roots.cpp b/roots.cpp
index 66481a3..8f99019 100644
--- a/roots.cpp
+++ b/roots.cpp
@@ -19,6 +19,7 @@
 #include <sys/mount.h>
 #include <sys/stat.h>
 #include <sys/types.h>
+#include <sys/wait.h>
 #include <unistd.h>
 #include <ctype.h>
 #include <fcntl.h>
@@ -151,6 +152,20 @@
     return unmount_mounted_volume(mv);
 }
 
+static int exec_cmd(const char* path, char* const argv[]) {
+    int status;
+    pid_t child;
+    if ((child = vfork()) == 0) {
+        execv(path, argv);
+        _exit(-1);
+    }
+    waitpid(child, &status, 0);
+    if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
+        LOGE("%s failed with status %d\n", path, WEXITSTATUS(status));
+    }
+    return WEXITSTATUS(status);
+}
+
 int format_volume(const char* volume) {
     Volume* v = volume_for_path(volume);
     if (v == NULL) {
@@ -195,19 +210,7 @@
         return 0;
     }
 
-    if (strcmp(v->fs_type, "ext4") == 0) {
-        ssize_t length = 0;
-        if (v->length != 0) {
-            length = v->length;
-        } else if (v->key_loc != NULL && strcmp(v->key_loc, "footer") == 0) {
-            length = -CRYPT_FOOTER_OFFSET;
-        }
-        int result = make_ext4fs(v->blk_device, length, volume, sehandle);
-        if (result != 0) {
-            LOGE("format_volume: make_extf4fs failed on %s\n", v->blk_device);
-            return -1;
-        }
-
+    if (strcmp(v->fs_type, "ext4") == 0 || strcmp(v->fs_type, "f2fs") == 0) {
         // if there's a key_loc that looks like a path, it should be a
         // block device for storing encryption metadata.  wipe it too.
         if (v->key_loc != NULL && v->key_loc[0] == '/') {
@@ -221,6 +224,39 @@
             close(fd);
         }
 
+        ssize_t length = 0;
+        if (v->length != 0) {
+            length = v->length;
+        } else if (v->key_loc != NULL && strcmp(v->key_loc, "footer") == 0) {
+            length = -CRYPT_FOOTER_OFFSET;
+        }
+        int result;
+        if (strcmp(v->fs_type, "ext4") == 0) {
+            result = make_ext4fs(v->blk_device, length, volume, sehandle);
+        } else {   /* Has to be f2fs because we checked earlier. */
+            if (v->key_loc != NULL && strcmp(v->key_loc, "footer") == 0 && length < 0) {
+                LOGE("format_volume: crypt footer + negative length (%lld) not supported on %s\n", v->fs_type, length);
+                return -1;
+            }
+            if (length < 0) {
+                LOGE("format_volume: negative length (%ld) not supported on %s\n", length, v->fs_type);
+                return -1;
+            }
+            char *num_sectors;
+            if (asprintf(&num_sectors, "%ld", length / 512) <= 0) {
+                LOGE("format_volume: failed to create %s command for %s\n", v->fs_type, v->blk_device);
+                return -1;
+            }
+            const char *f2fs_path = "/sbin/mkfs.f2fs";
+            const char* const f2fs_argv[] = {"mkfs.f2fs", "-t", "-d1", v->blk_device, num_sectors, NULL};
+
+            result = exec_cmd(f2fs_path, (char* const*)f2fs_argv);
+            free(num_sectors);
+        }
+        if (result != 0) {
+            LOGE("format_volume: make %s failed on %s with %d(%s)\n", v->fs_type, v->blk_device, result, strerror(errno));
+            return -1;
+        }
         return 0;
     }
 
diff --git a/updater/install.c b/updater/install.c
index 42f2289..edc386d 100644
--- a/updater/install.c
+++ b/updater/install.c
@@ -211,14 +211,29 @@
     return StringValue(result);
 }
 
+static int exec_cmd(const char* path, char* const argv[]) {
+    int status;
+    pid_t child;
+    if ((child = vfork()) == 0) {
+        execv(path, argv);
+        _exit(-1);
+    }
+    waitpid(child, &status, 0);
+    if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
+        printf("%s failed with status %d\n", path, WEXITSTATUS(status));
+    }
+    return WEXITSTATUS(status);
+}
+
 
 // format(fs_type, partition_type, location, fs_size, mount_point)
 //
 //    fs_type="yaffs2" partition_type="MTD"     location=partition fs_size=<bytes> mount_point=<location>
 //    fs_type="ext4"   partition_type="EMMC"    location=device    fs_size=<bytes> mount_point=<location>
-//    if fs_size == 0, then make_ext4fs uses the entire partition.
+//    fs_type="f2fs"   partition_type="EMMC"    location=device    fs_size=<bytes> mount_point=<location>
+//    if fs_size == 0, then make fs uses the entire partition.
 //    if fs_size > 0, that is the size to use
-//    if fs_size < 0, then reserve that many bytes at the end of the partition
+//    if fs_size < 0, then reserve that many bytes at the end of the partition (not for "f2fs")
 Value* FormatFn(const char* name, State* state, int argc, Expr* argv[]) {
     char* result = NULL;
     if (argc != 5) {
@@ -290,6 +305,24 @@
             goto done;
         }
         result = location;
+    } else if (strcmp(fs_type, "f2fs") == 0) {
+        char *num_sectors;
+        if (asprintf(&num_sectors, "%lld", atoll(fs_size) / 512) <= 0) {
+            printf("format_volume: failed to create %s command for %s\n", fs_type, location);
+            result = strdup("");
+            goto done;
+        }
+        const char *f2fs_path = "/sbin/mkfs.f2fs";
+        const char* const f2fs_argv[] = {"mkfs.f2fs", "-t", "-d1", location, num_sectors, NULL};
+        int status = exec_cmd(f2fs_path, (char* const*)f2fs_argv);
+        free(num_sectors);
+        if (status != 0) {
+            printf("%s: mkfs.f2fs failed (%d) on %s",
+                    name, status, location);
+            result = strdup("");
+            goto done;
+        }
+        result = location;
 #endif
     } else {
         printf("%s: unsupported fs_type \"%s\" partition_type \"%s\"",