Split fstab mount into 2 phases

This will make it possible to start some key services before mounting
data partition

(cherry picked from commit abfbec342fdd2fc9d139a88a2d950953918e1b4e)

Bug: 30118894
Change-Id: Ia9f8cc035de6cc0df9a61605864915efa0266d7f
diff --git a/fs_mgr/fs_mgr.c b/fs_mgr/fs_mgr.c
index 6de8817..387f708 100644
--- a/fs_mgr/fs_mgr.c
+++ b/fs_mgr/fs_mgr.c
@@ -489,7 +489,7 @@
  * first successful mount.
  * Returns -1 on error, and  FS_MGR_MNTALL_* otherwise.
  */
-int fs_mgr_mount_all(struct fstab *fstab)
+int fs_mgr_mount_all(struct fstab *fstab, int mount_mode)
 {
     int i = 0;
     int encryptable = FS_MGR_MNTALL_DEV_NOT_ENCRYPTABLE;
@@ -503,8 +503,10 @@
     }
 
     for (i = 0; i < fstab->num_entries; i++) {
-        /* Don't mount entries that are managed by vold */
-        if (fstab->recs[i].fs_mgr_flags & (MF_VOLDMANAGED | MF_RECOVERYONLY)) {
+        /* Don't mount entries that are managed by vold or not for the mount mode*/
+        if ((fstab->recs[i].fs_mgr_flags & (MF_VOLDMANAGED | MF_RECOVERYONLY)) ||
+             ((mount_mode == MOUNT_MODE_LATE) && !fs_mgr_is_latemount(&fstab->recs[i])) ||
+             ((mount_mode == MOUNT_MODE_EARLY) && fs_mgr_is_latemount(&fstab->recs[i]))) {
             continue;
         }
 
diff --git a/fs_mgr/fs_mgr_fstab.c b/fs_mgr/fs_mgr_fstab.c
index 45adb34..21b4c74 100644
--- a/fs_mgr/fs_mgr_fstab.c
+++ b/fs_mgr/fs_mgr_fstab.c
@@ -78,6 +78,7 @@
     { "formattable", MF_FORMATTABLE },
     { "slotselect",  MF_SLOTSELECT },
     { "nofail",      MF_NOFAIL },
+    { "latemount",   MF_LATEMOUNT },
     { "defaults",    0 },
     { 0,             0 },
 };
@@ -545,3 +546,8 @@
 {
     return fstab->fs_mgr_flags & MF_NOFAIL;
 }
+
+int fs_mgr_is_latemount(struct fstab_rec *fstab)
+{
+    return fstab->fs_mgr_flags & MF_LATEMOUNT;
+}
diff --git a/fs_mgr/fs_mgr_main.c b/fs_mgr/fs_mgr_main.c
index e5a00d5..776c13e 100644
--- a/fs_mgr/fs_mgr_main.c
+++ b/fs_mgr/fs_mgr_main.c
@@ -96,7 +96,7 @@
     fstab = fs_mgr_read_fstab(fstab_file);
 
     if (a_flag) {
-        return fs_mgr_mount_all(fstab);
+        return fs_mgr_mount_all(fstab, MOUNT_MODE_DEFAULT);
     } else if (n_flag) {
         return fs_mgr_do_mount(fstab, n_name, n_blk_dev, 0);
     } else if (u_flag) {
diff --git a/fs_mgr/fs_mgr_priv.h b/fs_mgr/fs_mgr_priv.h
index 46975f1..6d9492b 100644
--- a/fs_mgr/fs_mgr_priv.h
+++ b/fs_mgr/fs_mgr_priv.h
@@ -48,7 +48,7 @@
  *
  *   <fs_mgr_options> is a comma separated list of flags that control the operation of
  *                     the fs_mgr program.  The list includes "wait", which will wait till
- *                     the <source> file exists, and "check", which requests that the fs_mgr 
+ *                     the <source> file exists, and "check", which requests that the fs_mgr
  *                     run an fscheck program on the <source> before mounting the filesystem.
  *                     If check is specifed on a read-only filesystem, it is ignored.
  *                     Also, "encryptable" means that filesystem can be encrypted.
@@ -83,6 +83,7 @@
 #define MF_FORMATTABLE  0x4000
 #define MF_SLOTSELECT   0x8000
 #define MF_FORCEFDEORFBE 0x10000
+#define MF_LATEMOUNT    0x20000
 #define MF_NOFAIL       0x40000
 
 #define DM_BUF_SIZE 4096
diff --git a/fs_mgr/include/fs_mgr.h b/fs_mgr/include/fs_mgr.h
index 46d8f97..7565965 100644
--- a/fs_mgr/include/fs_mgr.h
+++ b/fs_mgr/include/fs_mgr.h
@@ -40,6 +40,13 @@
     VERITY_MODE_DEFAULT = VERITY_MODE_RESTART
 };
 
+// Mount modes
+enum mount_mode {
+    MOUNT_MODE_DEFAULT = 0,
+    MOUNT_MODE_EARLY = 1,
+    MOUNT_MODE_LATE = 2
+};
+
 /*
  * The entries must be kept in the same order as they were seen in the fstab.
  * Unless explicitly requested, a lookup on mount point should always
@@ -82,7 +89,7 @@
 #define FS_MGR_MNTALL_DEV_NOT_ENCRYPTED 1
 #define FS_MGR_MNTALL_DEV_NOT_ENCRYPTABLE 0
 #define FS_MGR_MNTALL_FAIL -1
-int fs_mgr_mount_all(struct fstab *fstab);
+int fs_mgr_mount_all(struct fstab *fstab, int mount_mode);
 
 #define FS_MGR_DOMNT_FAILED -1
 #define FS_MGR_DOMNT_BUSY -2
@@ -110,6 +117,7 @@
 int fs_mgr_is_notrim(struct fstab_rec *fstab);
 int fs_mgr_is_formattable(struct fstab_rec *fstab);
 int fs_mgr_is_nofail(struct fstab_rec *fstab);
+int fs_mgr_is_latemount(struct fstab_rec *fstab);
 int fs_mgr_swapon_all(struct fstab *fstab);
 
 int fs_mgr_do_format(struct fstab_rec *fstab);
diff --git a/init/builtins.cpp b/init/builtins.cpp
index 70f9194..44217f0 100644
--- a/init/builtins.cpp
+++ b/init/builtins.cpp
@@ -479,9 +479,9 @@
  *
  * start_index: index of the first path in the args list
  */
-static void import_late(const std::vector<std::string>& args, size_t start_index) {
+static void import_late(const std::vector<std::string>& args, size_t start_index, size_t end_index) {
     Parser& parser = Parser::GetInstance();
-    if (args.size() <= start_index) {
+    if (end_index <= start_index) {
         // Use the default set if no path is given
         static const std::vector<std::string> init_directories = {
             "/system/etc/init",
@@ -493,25 +493,23 @@
             parser.ParseConfig(dir);
         }
     } else {
-        for (size_t i = start_index; i < args.size(); ++i) {
+        for (size_t i = start_index; i < end_index; ++i) {
             parser.ParseConfig(args[i]);
         }
     }
 }
 
-/* mount_all <fstab> [ <path> ]*
+/* mount_fstab
  *
- * This function might request a reboot, in which case it will
- * not return.
+ *  Call fs_mgr_mount_all() to mount the given fstab
  */
-static int do_mount_all(const std::vector<std::string>& args) {
+static int mount_fstab(const char* fstabfile, int mount_mode) {
     pid_t pid;
     int ret = -1;
     int child_ret = -1;
     int status;
     struct fstab *fstab;
 
-    const char* fstabfile = args[1].c_str();
     /*
      * Call fs_mgr_mount_all() to mount all filesystems.  We fork(2) and
      * do the call in the child to provide protection to the main init
@@ -536,7 +534,7 @@
         /* child, call fs_mgr_mount_all() */
         klog_set_level(6);  /* So we can see what fs_mgr_mount_all() does */
         fstab = fs_mgr_read_fstab(fstabfile);
-        child_ret = fs_mgr_mount_all(fstab);
+        child_ret = fs_mgr_mount_all(fstab, mount_mode);
         fs_mgr_free_fstab(fstab);
         if (child_ret == -1) {
             ERROR("fs_mgr_mount_all returned an error\n");
@@ -546,28 +544,38 @@
         /* fork failed, return an error */
         return -1;
     }
+    return ret;
+}
 
-    /* Paths of .rc files are specified at the 2nd argument and beyond */
-    import_late(args, 2);
-
-    if (ret == FS_MGR_MNTALL_DEV_NEEDS_ENCRYPTION) {
+/* Queue event based on fs_mgr return code.
+ *
+ * code: return code of fs_mgr_mount_all
+ *
+ * This function might request a reboot, in which case it will
+ * not return.
+ *
+ * return code is processed based on input code
+ */
+static int queue_fs_event(int code) {
+    int ret = code;
+    if (code == FS_MGR_MNTALL_DEV_NEEDS_ENCRYPTION) {
         ActionManager::GetInstance().QueueEventTrigger("encrypt");
-    } else if (ret == FS_MGR_MNTALL_DEV_MIGHT_BE_ENCRYPTED) {
+    } else if (code == FS_MGR_MNTALL_DEV_MIGHT_BE_ENCRYPTED) {
         property_set("ro.crypto.state", "encrypted");
         property_set("ro.crypto.type", "block");
         ActionManager::GetInstance().QueueEventTrigger("defaultcrypto");
-    } else if (ret == FS_MGR_MNTALL_DEV_NOT_ENCRYPTED) {
+    } else if (code == FS_MGR_MNTALL_DEV_NOT_ENCRYPTED) {
         property_set("ro.crypto.state", "unencrypted");
         ActionManager::GetInstance().QueueEventTrigger("nonencrypted");
-    } else if (ret == FS_MGR_MNTALL_DEV_NOT_ENCRYPTABLE) {
+    } else if (code == FS_MGR_MNTALL_DEV_NOT_ENCRYPTABLE) {
         property_set("ro.crypto.state", "unsupported");
         ActionManager::GetInstance().QueueEventTrigger("nonencrypted");
-    } else if (ret == FS_MGR_MNTALL_DEV_NEEDS_RECOVERY) {
+    } else if (code == FS_MGR_MNTALL_DEV_NEEDS_RECOVERY) {
         /* Setup a wipe via recovery, and reboot into recovery */
         ERROR("fs_mgr_mount_all suggested recovery, so wiping data via recovery.\n");
         ret = wipe_data_via_recovery("wipe_data_via_recovery");
         /* If reboot worked, there is no return. */
-    } else if (ret == FS_MGR_MNTALL_DEV_FILE_ENCRYPTED) {
+    } else if (code == FS_MGR_MNTALL_DEV_FILE_ENCRYPTED) {
         if (e4crypt_install_keyring()) {
             return -1;
         }
@@ -577,14 +585,55 @@
         // Although encrypted, we have device key, so we do not need to
         // do anything different from the nonencrypted case.
         ActionManager::GetInstance().QueueEventTrigger("nonencrypted");
-    } else if (ret > 0) {
-        ERROR("fs_mgr_mount_all returned unexpected error %d\n", ret);
+    } else if (code > 0) {
+        ERROR("fs_mgr_mount_all returned unexpected error %d\n", code);
     }
     /* else ... < 0: error */
 
     return ret;
 }
 
+/* mount_all <fstab> [ <path> ]* [--<options>]*
+ *
+ * This function might request a reboot, in which case it will
+ * not return.
+ */
+static int do_mount_all(const std::vector<std::string>& args) {
+    std::size_t na = 0;
+    bool import_rc = true;
+    bool queue_event = true;
+    int mount_mode = MOUNT_MODE_DEFAULT;
+    const char* fstabfile = args[1].c_str();
+    std::size_t path_arg_end = args.size();
+
+    for (na = args.size() - 1; na > 1; --na) {
+        if (args[na] == "--early") {
+             path_arg_end = na;
+             queue_event = false;
+             mount_mode = MOUNT_MODE_EARLY;
+        } else if (args[na] == "--late") {
+            path_arg_end = na;
+            import_rc = false;
+            mount_mode = MOUNT_MODE_LATE;
+        }
+    }
+
+    int ret =  mount_fstab(fstabfile, mount_mode);
+
+    if (import_rc) {
+        /* Paths of .rc files are specified at the 2nd argument and beyond */
+        import_late(args, 2, path_arg_end);
+    }
+
+    if (queue_event) {
+        /* queue_fs_event will queue event based on mount_fstab return code
+         * and return processed return code*/
+        ret = queue_fs_event(ret);
+    }
+
+    return ret;
+}
+
 static int do_swapon_all(const std::vector<std::string>& args) {
     struct fstab *fstab;
     int ret;
diff --git a/init/readme.txt b/init/readme.txt
index 4481e24..dad7e06 100644
--- a/init/readme.txt
+++ b/init/readme.txt
@@ -1,4 +1,3 @@
-
 Android Init Language
 ---------------------
 
@@ -78,6 +77,14 @@
 conflict resolution when multiple services are added to the system, as
 each one will go into a separate file.
 
+There are two options "early" and "late" in mount_all command
+which can be set after optional paths. With "--early" set, the
+init executable will skip mounting entries with "latemount" flag
+and triggering fs encryption state event. With "--late" set,
+init executable will only mount entries with "latemount" flag but skip
+importing rc files. By default, no option is set, and mount_all will
+mount_all will process all entries in the given fstab.
+
 Actions
 -------
 Actions are named sequences of commands.  Actions have a trigger which
@@ -291,10 +298,11 @@
    owned by the root user and root group. If provided, the mode, owner and group
    will be updated if the directory exists already.
 
-mount_all <fstab> [ <path> ]*
+mount_all <fstab> [ <path> ]* [--<option>]
    Calls fs_mgr_mount_all on the given fs_mgr-format fstab and imports .rc files
-   at the specified paths (e.g., on the partitions just mounted). Refer to the
-   section of "Init .rc Files" for detail.
+   at the specified paths (e.g., on the partitions just mounted) with optional
+   options "early" and "late".
+   Refer to the section of "Init .rc Files" for detail.
 
 mount <type> <device> <dir> [ <flag> ]* [<options>]
    Attempt to mount the named device at the directory <dir>
diff --git a/rootdir/init.rc b/rootdir/init.rc
index 56379db..a9b6af0 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -236,6 +236,9 @@
 
     export DOWNLOAD_CACHE /data/cache
 
+    # set RLIMIT_NICE to allow priorities from 19 to -20
+    setrlimit 13 40 40
+
 # Healthd can trigger a full boot from charger mode by signaling this
 # property when the power button is held.
 on property:sys.boot_from_charger_mode=1
@@ -258,6 +261,11 @@
 # Mount filesystems and start core system services.
 on late-init
     trigger early-fs
+
+    # Mount fstab in init.{$device}.rc by mount_all command. Optional parameter
+    # '--early' can be specified to skip entries with 'latemount'.
+    # /system and /vendor must be mounted by the end of the fs stage,
+    # while /data is optional.
     trigger fs
     trigger post-fs
 
@@ -266,9 +274,18 @@
     # issued fs triggers have completed.
     trigger load_system_props_action
 
+    # Mount fstab in init.{$device}.rc by mount_all with '--late' parameter
+    # to only mount entries with 'latemount'. This is needed if '--early' is
+    # specified in the previous mount_all command on the fs stage.
+    # With /system mounted and properties form /system + /factory available,
+    # some services can be started.
+    trigger late-fs
+
     # Now we can mount /data. File encryption requires keymaster to decrypt
-    # /data, which in turn can only be loaded when system properties are present
+    # /data, which in turn can only be loaded when system properties are present.
     trigger post-fs-data
+
+    # Load persist properties and override properties (if enabled) from /data.
     trigger load_persist_props_action
 
     # Remove a file to wake up anything waiting for firmware.
@@ -484,9 +501,6 @@
     hostname localhost
     domainname localdomain
 
-    # set RLIMIT_NICE to allow priorities from 19 to -20
-    setrlimit 13 40 40
-
     # Memory management.  Basic kernel parameters, and allow the high
     # level system server to be able to adjust the kernel OOM driver
     # parameters to match how it is managing things.