Use fsck.f2fs -a instead of -f for faster boot

and run fsck with -f on clean shutdown instead.

With -f, fsck.f2fs always performs a full scan of the /data
partition regardless of whether the partition is clean or not.
The full scan takes more than 2 seconds on volantis-userdebug
and delays the OS boot.

With -a, the command does almost nothing when the partition
is clean and finishes within 20-30ms on volantis-userdebug.
When the partition has an error or its check point has
CP_FSCK_FLAG (aka "need_fsck"), the command does exactly the
same full scan as -f to fix it.

Bug: 21853106
Change-Id: I126263caf34c0f5bb8f5e6794454d4e72526ce38
diff --git a/init/builtins.cpp b/init/builtins.cpp
index ca31c50..d5d3faf 100644
--- a/init/builtins.cpp
+++ b/init/builtins.cpp
@@ -16,7 +16,9 @@
 
 #include <errno.h>
 #include <fcntl.h>
+#include <mntent.h>
 #include <net/if.h>
+#include <signal.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
@@ -38,6 +40,7 @@
 #include <base/stringprintf.h>
 #include <cutils/partition_utils.h>
 #include <cutils/android_reboot.h>
+#include <logwrap/logwrap.h>
 #include <private/android_filesystem_config.h>
 
 #include "init.h"
@@ -49,6 +52,8 @@
 #include "log.h"
 
 #define chmod DO_NOT_USE_CHMOD_USE_FCHMODAT_SYMLINK_NOFOLLOW
+#define UNMOUNT_CHECK_MS 5000
+#define UNMOUNT_CHECK_TIMES 10
 
 int add_environment(const char *name, const char *value);
 
@@ -109,6 +114,67 @@
     }
 }
 
+static void unmount_and_fsck(const struct mntent *entry)
+{
+    if (strcmp(entry->mnt_type, "f2fs") && strcmp(entry->mnt_type, "ext4"))
+        return;
+
+    /* First, lazily unmount the directory. This unmount request finishes when
+     * all processes that open a file or directory in |entry->mnt_dir| exit.
+     */
+    TEMP_FAILURE_RETRY(umount2(entry->mnt_dir, MNT_DETACH));
+
+    /* Next, kill all processes except init, kthreadd, and kthreadd's
+     * children to finish the lazy unmount. Killing all processes here is okay
+     * because this callback function is only called right before reboot().
+     * It might be cleaner to selectively kill processes that actually use
+     * |entry->mnt_dir| rather than killing all, probably by reusing a function
+     * like killProcessesWithOpenFiles() in vold/, but the selinux policy does
+     * not allow init to scan /proc/<pid> files which the utility function
+     * heavily relies on. The policy does not allow the process to execute
+     * killall/pkill binaries either. Note that some processes might
+     * automatically restart after kill(), but that is not really a problem
+     * because |entry->mnt_dir| is no longer visible to such new processes.
+     */
+    service_for_each(service_stop);
+    TEMP_FAILURE_RETRY(kill(-1, SIGKILL));
+
+    int count = 0;
+    while (count++ < UNMOUNT_CHECK_TIMES) {
+        int fd = TEMP_FAILURE_RETRY(open(entry->mnt_fsname, O_RDONLY | O_EXCL));
+        if (fd >= 0) {
+            /* |entry->mnt_dir| has sucessfully been unmounted. */
+            close(fd);
+            break;
+        } else if (errno == EBUSY) {
+            /* Some processes using |entry->mnt_dir| are still alive. Wait for a
+             * while then retry.
+             */
+            TEMP_FAILURE_RETRY(
+                usleep(UNMOUNT_CHECK_MS * 1000 / UNMOUNT_CHECK_TIMES));
+            continue;
+        } else {
+            /* Cannot open the device. Give up. */
+            return;
+        }
+    }
+
+    int st;
+    if (!strcmp(entry->mnt_type, "f2fs")) {
+        const char *f2fs_argv[] = {
+            "/system/bin/fsck.f2fs", "-f", entry->mnt_fsname,
+        };
+        android_fork_execvp_ext(ARRAY_SIZE(f2fs_argv), (char **)f2fs_argv,
+                                &st, true, LOG_KLOG, true, NULL);
+    } else if (!strcmp(entry->mnt_type, "ext4")) {
+        const char *ext4_argv[] = {
+            "/system/bin/e2fsck", "-f", "-y", entry->mnt_fsname,
+        };
+        android_fork_execvp_ext(ARRAY_SIZE(ext4_argv), (char **)ext4_argv,
+                                &st, true, LOG_KLOG, true, NULL);
+    }
+}
+
 int do_class_start(int nargs, char **args)
 {
         /* Starting a class does not start services
@@ -559,6 +625,7 @@
     int len = 0;
     int cmd = 0;
     const char *reboot_target;
+    void (*callback_on_ro_remount)(const struct mntent*) = NULL;
 
     res = expand_props(command, args[1], sizeof(command));
     if (res) {
@@ -569,6 +636,7 @@
     if (strncmp(command, "shutdown", 8) == 0) {
         cmd = ANDROID_RB_POWEROFF;
         len = 8;
+        callback_on_ro_remount = unmount_and_fsck;
     } else if (strncmp(command, "reboot", 6) == 0) {
         cmd = ANDROID_RB_RESTART2;
         len = 6;
@@ -586,7 +654,8 @@
         return -EINVAL;
     }
 
-    return android_reboot(cmd, 0, reboot_target);
+    return android_reboot_with_callback(cmd, 0, reboot_target,
+                                        callback_on_ro_remount);
 }
 
 int do_trigger(int nargs, char **args)