Mount /vendor and /odm early
Right now these two partitions are mounted in the fs stage of the init
process. As a result, many vendor/ODM files needed earlier in the boot
process (e.g., init.<hardware>.rc, fstab.<hardware>.rc,
uevent.<hardware>.rc, SELinux policy files etc) can only live on the root
partition.
To prevent vendors/ODMs from polluting the root partition, this patch makes
it possible to mount the vendor and ODM partitions in the first stage of the
init process. The fstab info of both partitions to be mounted early is
composed from new kernel cmdline arguments android.early.prefix and
android.early.fstab.
For example, with:
android.early.prefix=/sys/devices/1010000.msdc0/mmc_host/mmc0/mmc0:0001/block/mmcblk0/
android.early.fstab=mmcblk0p10+/odm+ext4+ro+verify\nmmcblk0p09+/vendor+ext4+ro+verify
the final fstab string will be:
/sys/devices/1010000.msdc0/mmc_host/mmc0/mmc0:0001/block/mmcblk0/mmcblk0p10 /odm ext4 ro verify
/sys/devices/1010000.msdc0/mmc_host/mmc0/mmc0:0001/block/mmcblk0/mmcblk0p09 /vendor ext4 ro verify
The android.early.prefix is optional. When it is missing, the final fstab
string will be directly converted from android.early.fstab.
This patch also makes sure that the early mounted partitions are dm-verity
enabled so that they are trust worthy to store system files.
BUG=27805372
Change-Id: I3cf32482a5ec65445ba3aedab2164c7ba8f12694
diff --git a/init/devices.cpp b/init/devices.cpp
index 373177e..830b74c 100644
--- a/init/devices.cpp
+++ b/init/devices.cpp
@@ -44,6 +44,7 @@
#include <android-base/file.h>
#include <android-base/stringprintf.h>
+#include <android-base/unique_fd.h>
#include <cutils/list.h>
#include <cutils/uevent.h>
@@ -601,14 +602,17 @@
return name;
}
+#define DEVPATH_LEN 96
+#define MAX_DEV_NAME 64
+
static void handle_block_device_event(struct uevent *uevent)
{
const char *base = "/dev/block/";
const char *name;
- char devpath[96];
+ char devpath[DEVPATH_LEN];
char **links = NULL;
- name = parse_device_name(uevent, 64);
+ name = parse_device_name(uevent, MAX_DEV_NAME);
if (!name)
return;
@@ -622,8 +626,6 @@
uevent->major, uevent->minor, links);
}
-#define DEVPATH_LEN 96
-
static bool assemble_devpath(char *devpath, const char *dirname,
const char *devname)
{
@@ -657,7 +659,7 @@
char devpath[DEVPATH_LEN] = {0};
char **links = NULL;
- name = parse_device_name(uevent, 64);
+ name = parse_device_name(uevent, MAX_DEV_NAME);
if (!name)
return;
@@ -900,7 +902,8 @@
}
#define UEVENT_MSG_LEN 2048
-void handle_device_fd()
+
+static inline void handle_device_fd_with(void (handle_uevent)(struct uevent*))
{
char msg[UEVENT_MSG_LEN+2];
int n;
@@ -913,21 +916,28 @@
struct uevent uevent;
parse_event(msg, &uevent);
-
- if (selinux_status_updated() > 0) {
- struct selabel_handle *sehandle2;
- sehandle2 = selinux_android_file_context_handle();
- if (sehandle2) {
- selabel_close(sehandle);
- sehandle = sehandle2;
- }
- }
-
- handle_device_event(&uevent);
- handle_firmware_event(&uevent);
+ handle_uevent(&uevent);
}
}
+void handle_device_fd()
+{
+ handle_device_fd_with(
+ [](struct uevent *uevent) {
+ if (selinux_status_updated() > 0) {
+ struct selabel_handle *sehandle2;
+ sehandle2 = selinux_android_file_context_handle();
+ if (sehandle2) {
+ selabel_close(sehandle);
+ sehandle = sehandle2;
+ }
+ }
+
+ handle_device_event(uevent);
+ handle_firmware_event(uevent);
+ });
+}
+
/* Coldboot walks parts of the /sys tree and pokes the uevent files
** to cause the kernel to regenerate device add events that happened
** before init's device manager was started
@@ -979,6 +989,65 @@
}
}
+static void early_uevent_handler(struct uevent *uevent, const char *base, bool is_block)
+{
+ const char *name;
+ char devpath[DEVPATH_LEN];
+
+ if (is_block && strncmp(uevent->subsystem, "block", 5))
+ return;
+
+ name = parse_device_name(uevent, MAX_DEV_NAME);
+ if (!name) {
+ LOG(ERROR) << "Failed to parse dev name from uevent: " << uevent->action
+ << " " << uevent->partition_name << " " << uevent->partition_num
+ << " " << uevent->major << ":" << uevent->minor;
+ return;
+ }
+
+ snprintf(devpath, sizeof(devpath), "%s%s", base, name);
+ make_dir(base, 0755);
+
+ dev_t dev = makedev(uevent->major, uevent->minor);
+ mode_t mode = 0600 | (is_block ? S_IFBLK : S_IFCHR);
+ mknod(devpath, mode, dev);
+}
+
+void early_create_dev(const std::string& syspath, early_device_type dev_type)
+{
+ android::base::unique_fd dfd(open(syspath.c_str(), O_RDONLY));
+ if (dfd < 0) {
+ LOG(ERROR) << "Failed to open " << syspath;
+ return;
+ }
+
+ android::base::unique_fd fd(openat(dfd, "uevent", O_WRONLY));
+ if (fd < 0) {
+ LOG(ERROR) << "Failed to open " << syspath << "/uevent";
+ return;
+ }
+
+ fcntl(device_fd, F_SETFL, O_NONBLOCK);
+
+ write(fd, "add\n", 4);
+ handle_device_fd_with(dev_type == EARLY_BLOCK_DEV ?
+ [](struct uevent *uevent) {
+ early_uevent_handler(uevent, "/dev/block/", true);
+ } :
+ [](struct uevent *uevent) {
+ early_uevent_handler(uevent, "/dev/", false);
+ });
+}
+
+int early_device_socket_open() {
+ device_fd = uevent_open_socket(256*1024, true);
+ return device_fd < 0;
+}
+
+void early_device_socket_close() {
+ close(device_fd);
+}
+
void device_init() {
sehandle = selinux_android_file_context_handle();
selinux_status_open(true);
diff --git a/init/devices.h b/init/devices.h
index 6cb0a77..8e9ab7d 100644
--- a/init/devices.h
+++ b/init/devices.h
@@ -21,6 +21,13 @@
extern void handle_device_fd();
extern void device_init(void);
+
+enum early_device_type { EARLY_BLOCK_DEV, EARLY_CHAR_DEV };
+
+extern int early_device_socket_open();
+extern void early_device_socket_close();
+extern void early_create_dev(const std::string& syspath, early_device_type dev_type);
+
extern int add_dev_perms(const char *name, const char *attr,
mode_t perm, unsigned int uid,
unsigned int gid, unsigned short prefix,
diff --git a/init/init.cpp b/init/init.cpp
index 78d71a8..957527b 100644
--- a/init/init.cpp
+++ b/init/init.cpp
@@ -54,6 +54,7 @@
#include "action.h"
#include "bootchart.h"
#include "devices.h"
+#include "fs_mgr.h"
#include "import_parser.h"
#include "init.h"
#include "init_parser.h"
@@ -342,9 +343,6 @@
}
static void process_kernel_cmdline() {
- // Don't expose the raw commandline to unprivileged processes.
- chmod("/proc/cmdline", 0440);
-
// The first pass does the common stuff, and finds if we are in qemu.
// The second pass is only necessary for qemu to export all kernel params
// as properties.
@@ -461,6 +459,104 @@
}
}
+/* Returns a new path consisting of base_path and the file name in reference_path. */
+static std::string get_path(const std::string& base_path, const std::string& reference_path) {
+ std::string::size_type pos = reference_path.rfind('/');
+ if (pos == std::string::npos) {
+ return base_path + '/' + reference_path;
+ } else {
+ return base_path + reference_path.substr(pos);
+ }
+}
+
+/* Imports the fstab info from cmdline. */
+static std::string import_cmdline_fstab() {
+ std::string prefix, fstab, fstab_full;
+
+ import_kernel_cmdline(false,
+ [&](const std::string& key, const std::string& value, bool in_qemu __attribute__((__unused__))) {
+ if (key == "android.early.prefix") {
+ prefix = value;
+ } else if (key == "android.early.fstab") {
+ fstab = value;
+ }
+ });
+ if (!fstab.empty()) {
+ // Convert "mmcblk0p09+/odm+ext4+ro+verify" to "mmcblk0p09 /odm ext4 ro verify"
+ std::replace(fstab.begin(), fstab.end(), '+', ' ');
+ for (const auto& entry : android::base::Split(fstab, "\n")) {
+ fstab_full += prefix + entry + '\n';
+ }
+ }
+ return fstab_full;
+}
+
+/* Early mount vendor and ODM partitions. The fstab info is read from kernel cmdline. */
+static void early_mount() {
+ std::string fstab_string = import_cmdline_fstab();
+ if (fstab_string.empty()) {
+ LOG(INFO) << "Failed to load vendor fstab from kernel cmdline";
+ return;
+ }
+ FILE *fstab_file = fmemopen((void *)fstab_string.c_str(), fstab_string.length(), "r");
+ if (!fstab_file) {
+ PLOG(ERROR) << "Failed to open fstab string as FILE";
+ return;
+ }
+ std::unique_ptr<struct fstab, decltype(&fs_mgr_free_fstab)> fstab(fs_mgr_read_fstab_file(fstab_file), fs_mgr_free_fstab);
+ fclose(fstab_file);
+ if (!fstab) {
+ LOG(ERROR) << "Failed to parse fstab string: " << fstab_string;
+ return;
+ }
+ LOG(INFO) << "Loaded vendor fstab from cmdline";
+
+ if (early_device_socket_open()) {
+ LOG(ERROR) << "Failed to open device uevent socket";
+ return;
+ }
+
+ /* Create /dev/device-mapper for dm-verity */
+ early_create_dev("/sys/devices/virtual/misc/device-mapper", EARLY_CHAR_DEV);
+
+ for (int i = 0; i < fstab->num_entries; ++i) {
+ struct fstab_rec *rec = &fstab->recs[i];
+ std::string mount_point = rec->mount_point;
+ std::string syspath = rec->blk_device;
+
+ if (mount_point != "/vendor" && mount_point != "/odm")
+ continue;
+
+ /* Create mount target under /dev/block/ from sysfs via uevent */
+ LOG(INFO) << "Mounting " << mount_point << " from " << syspath << "...";
+ char *devpath = strdup(get_path("/dev/block", syspath).c_str());
+ if (!devpath) {
+ PLOG(ERROR) << "Failed to strdup dev path in early mount " << syspath;
+ continue;
+ }
+ rec->blk_device = devpath;
+ early_create_dev(syspath, EARLY_BLOCK_DEV);
+
+ int rc = fs_mgr_early_setup_verity(rec);
+ if (rc == FS_MGR_EARLY_SETUP_VERITY_SUCCESS) {
+ /* Mount target is changed to /dev/block/dm-<n>; initiate its creation from sysfs counterpart */
+ early_create_dev(get_path("/sys/devices/virtual/block", rec->blk_device), EARLY_BLOCK_DEV);
+ } else if (rc == FS_MGR_EARLY_SETUP_VERITY_FAIL) {
+ LOG(ERROR) << "Failed to set up dm-verity on " << rec->blk_device;
+ continue;
+ } else { /* FS_MGR_EARLY_SETUP_VERITY_NO_VERITY */
+ LOG(INFO) << "dm-verity disabled on debuggable device; mount directly on " << rec->blk_device;
+ }
+
+ mkdir(mount_point.c_str(), 0755);
+ rc = mount(rec->blk_device, mount_point.c_str(), rec->fs_type, rec->flags, rec->fs_options);
+ if (rc) {
+ PLOG(ERROR) << "Failed to mount on " << rec->blk_device;
+ }
+ }
+ early_device_socket_close();
+}
+
int main(int argc, char** argv) {
if (!strcmp(basename(argv[0]), "ueventd")) {
return ueventd_main(argc, argv);
@@ -477,6 +573,9 @@
bool is_first_stage = (argc == 1) || (strcmp(argv[1], "--second-stage") != 0);
+ // Don't expose the raw commandline to unprivileged processes.
+ chmod("/proc/cmdline", 0440);
+
// Get the basic filesystem setup we need put together in the initramdisk
// on / and then we'll let the rc file figure out the rest.
if (is_first_stage) {
@@ -489,6 +588,7 @@
mount("sysfs", "/sys", "sysfs", 0, NULL);
mount("selinuxfs", "/sys/fs/selinux", "selinuxfs", 0, NULL);
mknod("/dev/kmsg", S_IFCHR | 0600, makedev(1, 11));
+ early_mount();
}
// Now that tmpfs is mounted on /dev and we have /dev/kmsg, we can actually
@@ -541,6 +641,8 @@
restorecon("/dev/__properties__");
restorecon("/property_contexts");
restorecon_recursive("/sys");
+ restorecon_recursive("/dev/block");
+ restorecon("/dev/device-mapper");
epoll_fd = epoll_create1(EPOLL_CLOEXEC);
if (epoll_fd == -1) {