Add ro.boot.fstab_suffix and modify mount_all to use it

Currently the ReadDefaultFstab function, which calls GetFstabPath,
makes some assumptions about what the fstab will be called and where
it is located. This is being used by vold to set up userdata encryption
and for gsid, and is even used in the default boot control HAL, so it
has become quite baked.

The original way for a board to specify things to mount was to use the
"mount_all /path/to/fstab" command in init.rc. However, due to the
above functionality, the path after mount_all is no longer very useful,
as it cannot differ from the inferred path, or userdata encryption and
other features will be broken.

On Cuttlefish, we have an interest in being able to test alternative
userdata configurations (ext4 vs f2fs, encryption on/off, etc.) and
currently the only way to achieve this is to either a) modify the
ro.hardware or ro.hardware.platform properties, which breaks a bunch
of things like default HAL filenames, or regenerate our odm.img or
vendor.img filesystems. We can't simply install another fstab and
point to it with "mount_all".

This change allows the fstab path to be omitted from "mount_all", and
adds another property which overrides the existing checks for
fstab.${ro.hardware} and fstab.${ro.hardware.platform}. Specifying
${ro.boot.fstab_suffix} will cause fstab.${ro.boot.fstab_suffix}
to be checked first.

Bug: 142424832
Test: booted cuttlefish with 'mount_all ${ro.hardware} --late'
Test: booted cuttlefish with 'mount_all --late'
Test: booted cuttlefish with 'mount_all --late' and fstab_suffix=f2fs
Test: partially booted cuttlefish with 'mount_all ${ro.hardware}'
Test: partially booted cuttlefish with 'mount_all'
Change-Id: I3e10f66aecfcd48bdb9ebf1d304b7aae745cbd3c
diff --git a/fs_mgr/fs_mgr_fstab.cpp b/fs_mgr/fs_mgr_fstab.cpp
index 0825a77..f333a85 100644
--- a/fs_mgr/fs_mgr_fstab.cpp
+++ b/fs_mgr/fs_mgr_fstab.cpp
@@ -408,16 +408,17 @@
     return fstab_result;
 }
 
-// Identify path to fstab file. Lookup is based on pattern fstab.<hardware>,
-// fstab.<hardware.platform> in folders /odm/etc, vendor/etc, or /.
+// Identify path to fstab file. Lookup is based on pattern
+// fstab.<fstab_suffix>, fstab.<hardware>, fstab.<hardware.platform> in
+// folders /odm/etc, vendor/etc, or /.
 std::string GetFstabPath() {
-    for (const char* prop : {"hardware", "hardware.platform"}) {
-        std::string hw;
+    for (const char* prop : {"fstab_suffix", "hardware", "hardware.platform"}) {
+        std::string suffix;
 
-        if (!fs_mgr_get_boot_config(prop, &hw)) continue;
+        if (!fs_mgr_get_boot_config(prop, &suffix)) continue;
 
         for (const char* prefix : {"/odm/etc/fstab.", "/vendor/etc/fstab.", "/fstab."}) {
-            std::string fstab_path = prefix + hw;
+            std::string fstab_path = prefix + suffix;
             if (access(fstab_path.c_str(), F_OK) == 0) {
                 return fstab_path;
             }
diff --git a/init/README.md b/init/README.md
index b70366b..0dd1490 100644
--- a/init/README.md
+++ b/init/README.md
@@ -547,13 +547,16 @@
   * `ref`: use the systemwide DE key
   * `per_boot_ref`: use the key freshly generated on each boot.
 
-`mount_all <fstab> [ <path> ]\* [--<option>]`
+`mount_all [ <fstab> ] [--<option>]`
 > Calls fs\_mgr\_mount\_all on the given fs\_mgr-format fstab with optional
   options "early" and "late".
   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. By default,
   no option is set, and mount\_all will process all entries in the given fstab.
+  If the fstab parameter is not specified, fstab.${ro.boot.fstab_suffix},
+  fstab.${ro.hardware} or fstab.${ro.hardware.platform} will be scanned for
+  under /odm/etc, /vendor/etc, or / at runtime, in that order.
 
 `mount <type> <device> <dir> [ <flag>\* ] [<options>]`
 > Attempt to mount the named device at the directory _dir_
diff --git a/init/builtins.cpp b/init/builtins.cpp
index 149a766..e918e12 100644
--- a/init/builtins.cpp
+++ b/init/builtins.cpp
@@ -518,21 +518,21 @@
 
 /* Imports .rc files from the specified paths. Default ones are applied if none is given.
  *
- * start_index: index of the first path in the args list
+ * rc_paths: list of paths to rc files to import
  */
-static void import_late(const std::vector<std::string>& args, size_t start_index, size_t end_index) {
+static void import_late(const std::vector<std::string>& rc_paths) {
     auto& action_manager = ActionManager::GetInstance();
     auto& service_list = ServiceList::GetInstance();
     Parser parser = CreateParser(action_manager, service_list);
-    if (end_index <= start_index) {
+    if (rc_paths.empty()) {
         // Fallbacks for partitions on which early mount isn't enabled.
         for (const auto& path : late_import_paths) {
             parser.ParseConfig(path);
         }
         late_import_paths.clear();
     } else {
-        for (size_t i = start_index; i < end_index; ++i) {
-            parser.ParseConfig(args[i]);
+        for (const auto& rc_path : rc_paths) {
+            parser.ParseConfig(rc_path);
         }
     }
 
@@ -633,48 +633,44 @@
 
 static int initial_mount_fstab_return_code = -1;
 
-/* mount_all <fstab> [ <path> ]* [--<options>]*
+/* <= Q: mount_all <fstab> [ <path> ]* [--<options>]*
+ * >= R: mount_all [ <fstab> ] [--<options>]*
  *
  * This function might request a reboot, in which case it will
  * not return.
  */
 static Result<void> do_mount_all(const BuiltinArguments& args) {
-    std::size_t na = 0;
-    bool import_rc = true;
-    bool queue_event = true;
-    int mount_mode = MOUNT_MODE_DEFAULT;
-    const auto& fstab_file = args[1];
-    std::size_t path_arg_end = args.size();
-    const char* prop_post_fix = "default";
+    auto mount_all = ParseMountAll(args.args);
+    if (!mount_all.ok()) return mount_all.error();
 
-    for (na = args.size() - 1; na > 1; --na) {
-        if (args[na] == "--early") {
-            path_arg_end = na;
-            queue_event = false;
-            mount_mode = MOUNT_MODE_EARLY;
-            prop_post_fix = "early";
-        } else if (args[na] == "--late") {
-            path_arg_end = na;
-            import_rc = false;
-            mount_mode = MOUNT_MODE_LATE;
-            prop_post_fix = "late";
-        }
+    const char* prop_post_fix = "default";
+    bool queue_event = true;
+    if (mount_all->mode == MOUNT_MODE_EARLY) {
+        prop_post_fix = "early";
+        queue_event = false;
+    } else if (mount_all->mode == MOUNT_MODE_LATE) {
+        prop_post_fix = "late";
     }
 
     std::string prop_name = "ro.boottime.init.mount_all."s + prop_post_fix;
     android::base::Timer t;
 
     Fstab fstab;
-    if (!ReadFstabFromFile(fstab_file, &fstab)) {
-        return Error() << "Could not read fstab";
+    if (mount_all->fstab_path.empty()) {
+        if (!ReadDefaultFstab(&fstab)) {
+            return Error() << "Could not read default fstab";
+        }
+    } else {
+        if (!ReadFstabFromFile(mount_all->fstab_path, &fstab)) {
+            return Error() << "Could not read fstab";
+        }
     }
 
-    auto mount_fstab_return_code = fs_mgr_mount_all(&fstab, mount_mode);
+    auto mount_fstab_return_code = fs_mgr_mount_all(&fstab, mount_all->mode);
     SetProperty(prop_name, std::to_string(t.duration().count()));
 
-    if (import_rc && SelinuxGetVendorAndroidVersion() <= __ANDROID_API_Q__) {
-        /* Paths of .rc files are specified at the 2nd argument and beyond */
-        import_late(args.args, 2, path_arg_end);
+    if (mount_all->import_rc) {
+        import_late(mount_all->rc_paths);
     }
 
     if (queue_event) {
@@ -690,11 +686,20 @@
     return {};
 }
 
-/* umount_all <fstab> */
+/* umount_all [ <fstab> ] */
 static Result<void> do_umount_all(const BuiltinArguments& args) {
+    auto umount_all = ParseUmountAll(args.args);
+    if (!umount_all.ok()) return umount_all.error();
+
     Fstab fstab;
-    if (!ReadFstabFromFile(args[1], &fstab)) {
-        return Error() << "Could not read fstab";
+    if (umount_all->empty()) {
+        if (!ReadDefaultFstab(&fstab)) {
+            return Error() << "Could not read default fstab";
+        }
+    } else {
+        if (!ReadFstabFromFile(*umount_all, &fstab)) {
+            return Error() << "Could not read fstab";
+        }
     }
 
     if (auto result = fs_mgr_umount_all(&fstab); result != 0) {
@@ -1349,11 +1354,11 @@
         // mount_all is currently too complex to run in vendor_init as it queues action triggers,
         // imports rc scripts, etc.  It should be simplified and run in vendor_init context.
         // mount and umount are run in the same context as mount_all for symmetry.
-        {"mount_all",               {1,     kMax, {false,  do_mount_all}}},
+        {"mount_all",               {0,     kMax, {false,  do_mount_all}}},
         {"mount",                   {3,     kMax, {false,  do_mount}}},
         {"perform_apex_config",     {0,     0,    {false,  do_perform_apex_config}}},
         {"umount",                  {1,     1,    {false,  do_umount}}},
-        {"umount_all",              {1,     1,    {false,  do_umount_all}}},
+        {"umount_all",              {0,     1,    {false,  do_umount_all}}},
         {"update_linker_config",    {0,     0,    {false,  do_update_linker_config}}},
         {"readahead",               {1,     2,    {true,   do_readahead}}},
         {"remount_userdata",        {0,     0,    {false,  do_remount_userdata}}},
diff --git a/init/check_builtins.cpp b/init/check_builtins.cpp
index d1a84f3..450c079 100644
--- a/init/check_builtins.cpp
+++ b/init/check_builtins.cpp
@@ -123,6 +123,14 @@
     return {};
 }
 
+Result<void> check_mount_all(const BuiltinArguments& args) {
+    auto options = ParseMountAll(args.args);
+    if (!options.ok()) {
+        return options.error();
+    }
+    return {};
+}
+
 Result<void> check_mkdir(const BuiltinArguments& args) {
     auto options = ParseMkdir(args.args);
     if (!options.ok()) {
@@ -204,6 +212,14 @@
     return {};
 }
 
+Result<void> check_umount_all(const BuiltinArguments& args) {
+    auto options = ParseUmountAll(args.args);
+    if (!options.ok()) {
+        return options.error();
+    }
+    return {};
+}
+
 Result<void> check_wait(const BuiltinArguments& args) {
     if (args.size() == 3 && !args[2].empty()) {
         double timeout_double;
diff --git a/init/check_builtins.h b/init/check_builtins.h
index fb34556..725a6fd 100644
--- a/init/check_builtins.h
+++ b/init/check_builtins.h
@@ -32,11 +32,13 @@
 Result<void> check_load_system_props(const BuiltinArguments& args);
 Result<void> check_loglevel(const BuiltinArguments& args);
 Result<void> check_mkdir(const BuiltinArguments& args);
+Result<void> check_mount_all(const BuiltinArguments& args);
 Result<void> check_restorecon(const BuiltinArguments& args);
 Result<void> check_restorecon_recursive(const BuiltinArguments& args);
 Result<void> check_setprop(const BuiltinArguments& args);
 Result<void> check_setrlimit(const BuiltinArguments& args);
 Result<void> check_sysclktz(const BuiltinArguments& args);
+Result<void> check_umount_all(const BuiltinArguments& args);
 Result<void> check_wait(const BuiltinArguments& args);
 Result<void> check_wait_for_prop(const BuiltinArguments& args);
 
diff --git a/init/util.cpp b/init/util.cpp
index f9be055..40f24b1 100644
--- a/init/util.cpp
+++ b/init/util.cpp
@@ -572,6 +572,48 @@
     return MkdirOptions{args[1], mode, *uid, *gid, fscrypt_action, ref_option};
 }
 
+Result<MountAllOptions> ParseMountAll(const std::vector<std::string>& args) {
+    bool compat_mode = false;
+    bool import_rc = false;
+    if (SelinuxGetVendorAndroidVersion() <= __ANDROID_API_Q__) {
+        if (args.size() <= 1) {
+            return Error() << "mount_all requires at least 1 argument";
+        }
+        compat_mode = true;
+        import_rc = true;
+    }
+
+    std::size_t first_option_arg = args.size();
+    enum mount_mode mode = MOUNT_MODE_DEFAULT;
+
+    // If we are <= Q, then stop looking for non-fstab arguments at slot 2.
+    // Otherwise, stop looking at slot 1 (as the fstab path argument is optional >= R).
+    for (std::size_t na = args.size() - 1; na > (compat_mode ? 1 : 0); --na) {
+        if (args[na] == "--early") {
+            first_option_arg = na;
+            mode = MOUNT_MODE_EARLY;
+        } else if (args[na] == "--late") {
+            first_option_arg = na;
+            mode = MOUNT_MODE_LATE;
+            import_rc = false;
+        }
+    }
+
+    std::string fstab_path;
+    if (first_option_arg > 1) {
+        fstab_path = args[1];
+    } else if (compat_mode) {
+        return Error() << "mount_all argument 1 must be the fstab path";
+    }
+
+    std::vector<std::string> rc_paths;
+    for (std::size_t na = 2; na < first_option_arg; ++na) {
+        rc_paths.push_back(args[na]);
+    }
+
+    return MountAllOptions{rc_paths, fstab_path, mode, import_rc};
+}
+
 Result<std::pair<int, std::vector<std::string>>> ParseRestorecon(
         const std::vector<std::string>& args) {
     struct flag_type {
@@ -612,6 +654,15 @@
     return std::pair(flag, paths);
 }
 
+Result<std::string> ParseUmountAll(const std::vector<std::string>& args) {
+    if (SelinuxGetVendorAndroidVersion() <= __ANDROID_API_Q__) {
+        if (args.size() <= 1) {
+            return Error() << "umount_all requires at least 1 argument";
+        }
+    }
+    return args[1];
+}
+
 static void InitAborter(const char* abort_message) {
     // When init forks, it continues to use this aborter for LOG(FATAL), but we want children to
     // simply abort instead of trying to reboot the system.
diff --git a/init/util.h b/init/util.h
index 8167b02..28f6b18 100644
--- a/init/util.h
+++ b/init/util.h
@@ -22,6 +22,7 @@
 #include <chrono>
 #include <functional>
 #include <string>
+#include <vector>
 
 #include <android-base/chrono_utils.h>
 
@@ -33,6 +34,12 @@
 namespace android {
 namespace init {
 
+enum mount_mode {
+    MOUNT_MODE_DEFAULT = 0,
+    MOUNT_MODE_EARLY = 1,
+    MOUNT_MODE_LATE = 2,
+};
+
 static const char kColdBootDoneProp[] = "ro.cold_boot_done";
 
 extern void (*trigger_shutdown)(const std::string& command);
@@ -73,9 +80,20 @@
 
 Result<MkdirOptions> ParseMkdir(const std::vector<std::string>& args);
 
+struct MountAllOptions {
+    std::vector<std::string> rc_paths;
+    std::string fstab_path;
+    mount_mode mode;
+    bool import_rc;
+};
+
+Result<MountAllOptions> ParseMountAll(const std::vector<std::string>& args);
+
 Result<std::pair<int, std::vector<std::string>>> ParseRestorecon(
         const std::vector<std::string>& args);
 
+Result<std::string> ParseUmountAll(const std::vector<std::string>& args);
+
 void SetStdioToDevNull(char** argv);
 void InitKernelLogging(char** argv);
 void InitSecondStageLogging(char** argv);