Support bootconfig in first stage init and fs_mgr

Androidboot parameters are being moved from the kernel commandline to
bootconfig.
fs_mgr looks for these parameters in properties and falls back to
reading directly from /proc/cmdline. So both of these sources are
updated for bootconfig.
The androidboot parameters from /proc/bootconfig
are added as ro.boot properties, and fs_mgr will fall back to searching
/proc/bootconfig if it is too early.

Test: boot cuttlefish with androidboot.fstab_suffix and
androidboot.hardware in bootconfig and not in cmdline.
Test: atest CtsFsMgrTestCases
Bug: 173815685

Change-Id: Iea36a0da94c26e1aa37d97c576725e0ad77cd3ad
diff --git a/init/first_stage_init.cpp b/init/first_stage_init.cpp
index 83d2b6d..551cf19 100644
--- a/init/first_stage_init.cpp
+++ b/init/first_stage_init.cpp
@@ -209,6 +209,8 @@
     CHECKCALL(chmod("/proc/cmdline", 0440));
     std::string cmdline;
     android::base::ReadFileToString("/proc/cmdline", &cmdline);
+    // Don't expose the raw bootconfig to unprivileged processes.
+    chmod("/proc/bootconfig", 0440);
     gid_t groups[] = {AID_READPROC};
     CHECKCALL(setgroups(arraysize(groups), groups));
     CHECKCALL(mount("sysfs", "/sys", "sysfs", 0, NULL));
diff --git a/init/property_service.cpp b/init/property_service.cpp
index ce67386..b722702 100644
--- a/init/property_service.cpp
+++ b/init/property_service.cpp
@@ -1180,6 +1180,14 @@
     }
 }
 
+static void ProcessBootconfig() {
+    ImportBootconfig([&](const std::string& key, const std::string& value) {
+        if (StartsWith(key, "androidboot.")) {
+            InitPropertySet("ro.boot." + key.substr(12), value);
+        }
+    });
+}
+
 void PropertyInit() {
     selinux_callback cb;
     cb.func_audit = PropertyAuditCallback;
@@ -1198,6 +1206,7 @@
     // properties set in DT always have priority over the command-line ones.
     ProcessKernelDt();
     ProcessKernelCmdline();
+    ProcessBootconfig();
 
     // Propagate the kernel variables to internal variables
     // used by init as well as the current required properties.
diff --git a/init/util.cpp b/init/util.cpp
index 255434a..e69b43f 100644
--- a/init/util.cpp
+++ b/init/util.cpp
@@ -246,6 +246,19 @@
     }
 }
 
+void ImportBootconfig(const std::function<void(const std::string&, const std::string&)>& fn) {
+    std::string bootconfig;
+    android::base::ReadFileToString("/proc/bootconfig", &bootconfig);
+
+    for (const auto& entry : android::base::Split(bootconfig, "\n")) {
+        std::vector<std::string> pieces = android::base::Split(entry, "=");
+        if (pieces.size() == 2) {
+            pieces[1].erase(std::remove(pieces[1].begin(), pieces[1].end(), '"'), pieces[1].end());
+            fn(android::base::Trim(pieces[0]), android::base::Trim(pieces[1]));
+        }
+    }
+}
+
 bool make_dir(const std::string& path, mode_t mode) {
     std::string secontext;
     if (SelabelLookupFileContext(path, mode, &secontext) && !secontext.empty()) {
diff --git a/init/util.h b/init/util.h
index 3cdc9f4..7745d77 100644
--- a/init/util.h
+++ b/init/util.h
@@ -55,6 +55,7 @@
 bool mkdir_recursive(const std::string& pathname, mode_t mode);
 int wait_for_file(const char *filename, std::chrono::nanoseconds timeout);
 void ImportKernelCmdline(const std::function<void(const std::string&, const std::string&)>&);
+void ImportBootconfig(const std::function<void(const std::string&, const std::string&)>&);
 bool make_dir(const std::string& path, mode_t mode);
 bool is_dir(const char* pathname);
 Result<std::string> ExpandProps(const std::string& src);