From ea23983a9c84db151478c0c5a8b41f7d0c4c3fde Mon Sep 17 00:00:00 2001 From: Sandeep Patil Date: Fri, 3 Feb 2017 07:51:55 -0800 Subject: [PATCH 1/5] ueventd: make selinux labeling optional for device creation This is to setup a way for us to run coldboot during init first stage and also at ueventd startup. We do not have all of the file context during the first stage, so the "early" coldboot needs to proceed without labelling the device nodes. However, the follow up in ueventd must label these nodes. This change allows us to do both. b/27805372 Test: Boot angler successfully and compare do before/after comparison of the output of 'ls -AclpqRZ /dev' to ensure there are no differences. Change-Id: I5e88bd7da8a1d2cc41e3abba30dda463ecbde32e Signed-off-by: Sandeep Patil --- init/devices.cpp | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/init/devices.cpp b/init/devices.cpp index 6af237cf9..fbc035954 100644 --- a/init/devices.cpp +++ b/init/devices.cpp @@ -249,11 +249,13 @@ static void make_device(const char *path, mode = get_device_perm(path, links, &uid, &gid) | (block ? S_IFBLK : S_IFCHR); - if (selabel_lookup_best_match(sehandle, &secontext, path, links, mode)) { - PLOG(ERROR) << "Device '" << path << "' not created; cannot find SELinux label"; - return; + if (sehandle) { + if (selabel_lookup_best_match(sehandle, &secontext, path, links, mode)) { + PLOG(ERROR) << "Device '" << path << "' not created; cannot find SELinux label"; + return; + } + setfscreatecon(secontext); } - setfscreatecon(secontext); dev = makedev(major, minor); /* Temporarily change egid to avoid race condition setting the gid of the @@ -264,7 +266,7 @@ static void make_device(const char *path, setegid(gid); /* If the node already exists update its SELinux label to handle cases when * it was created with the wrong context during coldboot procedure. */ - if (mknod(path, mode, dev) && (errno == EEXIST)) { + if (mknod(path, mode, dev) && (errno == EEXIST) && secontext) { char* fcon = nullptr; int rc = lgetfilecon(path, &fcon); @@ -285,8 +287,10 @@ out: chown(path, uid, -1); setegid(AID_ROOT); - freecon(secontext); - setfscreatecon(NULL); + if (secontext) { + freecon(secontext); + setfscreatecon(NULL); + } } static void add_platform_device(const char *path) From 957e4ab0b5c5f0ed006a287ba7cc387ce7db4d27 Mon Sep 17 00:00:00 2001 From: Sandeep Patil Date: Tue, 7 Feb 2017 18:11:37 -0800 Subject: [PATCH 2/5] init: refactor: add support for doing early coldboot We don't want to spend time creating devices that are unncessesary during early (init first-stage) mount. So, refactor the devices code tha allows us to call into coldboot and has the - ability to only create devices that are specified by the caller - ability to stop coldboot cycle when all devices that the caller is interested in - ability to run coldboot for a specific syspath - ability to run ueventd code unmodified Test: Tested boot on angler, sailfish Change-Id: Id8f3492380696760414eadc20d624d300c904f8e Signed-off-by: Sandeep Patil --- init/devices.cpp | 113 +++++++++++++++++++++++++++++++---------------- init/devices.h | 30 ++++++++++++- 2 files changed, 102 insertions(+), 41 deletions(-) diff --git a/init/devices.cpp b/init/devices.cpp index fbc035954..c7f3efa71 100644 --- a/init/devices.cpp +++ b/init/devices.cpp @@ -64,18 +64,6 @@ extern struct selabel_handle *sehandle; static int device_fd = -1; -struct uevent { - const char *action; - const char *path; - const char *subsystem; - const char *firmware; - const char *partition_name; - const char *device_name; - int partition_num; - int major; - int minor; -}; - struct perms_ { char *name; char *attr; @@ -879,9 +867,15 @@ static void handle_firmware_event(uevent* uevent) { } } +static bool inline should_stop_coldboot(coldboot_action_t act) +{ + return (act == COLDBOOT_STOP || act == COLDBOOT_FINISH); +} + #define UEVENT_MSG_LEN 2048 -static inline void handle_device_fd_with(void (handle_uevent)(struct uevent*)) +template +static inline coldboot_action_t handle_device_fd_with(const T& handle_uevent) { char msg[UEVENT_MSG_LEN+2]; int n; @@ -894,14 +888,18 @@ static inline void handle_device_fd_with(void (handle_uevent)(struct uevent*)) struct uevent uevent; parse_event(msg, &uevent); - handle_uevent(&uevent); + coldboot_action_t act = handle_uevent(&uevent); + if (should_stop_coldboot(act)) + return act; } + + return COLDBOOT_CONTINUE; } -void handle_device_fd() +coldboot_action_t handle_device_fd(coldboot_callback fn) { - handle_device_fd_with( - [](struct uevent *uevent) { + coldboot_action_t ret = handle_device_fd_with( + [&](uevent* uevent) -> coldboot_action_t { if (selinux_status_updated() > 0) { struct selabel_handle *sehandle2; sehandle2 = selinux_android_file_context_handle(); @@ -911,9 +909,21 @@ void handle_device_fd() } } - handle_device_event(uevent); - handle_firmware_event(uevent); + // default is to always create the devices + coldboot_action_t act = COLDBOOT_CREATE; + if (fn) { + act = fn(uevent); + } + + if (act == COLDBOOT_CREATE || act == COLDBOOT_STOP) { + handle_device_event(uevent); + handle_firmware_event(uevent); + } + + return act; }); + + return ret; } /* Coldboot walks parts of the /sys tree and pokes the uevent files @@ -925,21 +935,24 @@ void handle_device_fd() ** socket's buffer. */ -static void do_coldboot(DIR *d) +static coldboot_action_t do_coldboot(DIR *d, coldboot_callback fn) { struct dirent *de; int dfd, fd; + coldboot_action_t act = COLDBOOT_CONTINUE; dfd = dirfd(d); fd = openat(dfd, "uevent", O_WRONLY); - if(fd >= 0) { + if (fd >= 0) { write(fd, "add\n", 4); close(fd); - handle_device_fd(); + act = handle_device_fd(fn); + if (should_stop_coldboot(act)) + return act; } - while((de = readdir(d))) { + while (!should_stop_coldboot(act) && (de = readdir(d))) { DIR *d2; if(de->d_type != DT_DIR || de->d_name[0] == '.') @@ -953,34 +966,39 @@ static void do_coldboot(DIR *d) if(d2 == 0) close(fd); else { - do_coldboot(d2); + act = do_coldboot(d2, fn); closedir(d2); } } + + // default is always to continue looking for uevents + return act; } -static void coldboot(const char *path) +static coldboot_action_t coldboot(const char *path, coldboot_callback fn) { std::unique_ptr d(opendir(path), closedir); - if(d) { - do_coldboot(d.get()); + if (d) { + return do_coldboot(d.get(), fn); } + + return COLDBOOT_CONTINUE; } -static void early_uevent_handler(struct uevent *uevent, const char *base, bool is_block) +static coldboot_action_t 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; + return COLDBOOT_STOP; 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; + return COLDBOOT_STOP; } snprintf(devpath, sizeof(devpath), "%s%s", base, name); @@ -989,6 +1007,8 @@ static void early_uevent_handler(struct uevent *uevent, const char *base, bool i dev_t dev = makedev(uevent->major, uevent->minor); mode_t mode = 0600 | (is_block ? S_IFBLK : S_IFCHR); mknod(devpath, mode, dev); + + return COLDBOOT_STOP; } void early_create_dev(const std::string& syspath, early_device_type dev_type) @@ -1009,11 +1029,11 @@ void early_create_dev(const std::string& syspath, early_device_type dev_type) write(fd, "add\n", 4); handle_device_fd_with(dev_type == EARLY_BLOCK_DEV ? - [](struct uevent *uevent) { - early_uevent_handler(uevent, "/dev/block/", true); + [](uevent* uevent) -> coldboot_action_t { + return early_uevent_handler(uevent, "/dev/block/", true); } : - [](struct uevent *uevent) { - early_uevent_handler(uevent, "/dev/", false); + [](uevent* uevent) -> coldboot_action_t { + return early_uevent_handler(uevent, "/dev/", false); }); } @@ -1026,7 +1046,7 @@ void early_device_socket_close() { close(device_fd); } -void device_init() { +void device_init(const char* path, coldboot_callback fn) { sehandle = selinux_android_file_context_handle(); selinux_status_open(true); @@ -1043,10 +1063,25 @@ void device_init() { } Timer t; - coldboot("/sys/class"); - coldboot("/sys/block"); - coldboot("/sys/devices"); - close(open(COLDBOOT_DONE, O_WRONLY|O_CREAT|O_CLOEXEC, 0000)); + coldboot_action_t act; + if (!path) { + act = coldboot("/sys/class", fn); + if (!should_stop_coldboot(act)) { + act = coldboot("/sys/block", fn); + if (!should_stop_coldboot(act)) { + act = coldboot("/sys/devices", fn); + } + } + } else { + act = coldboot(path, fn); + } + + // If we have a callback, then do as it says. If no, then the default is + // to always create COLDBOOT_DONE file. + if (!fn || (act == COLDBOOT_FINISH)) { + close(open(COLDBOOT_DONE, O_WRONLY|O_CREAT|O_CLOEXEC, 0000)); + } + LOG(INFO) << "Coldboot took " << t; } diff --git a/init/devices.h b/init/devices.h index 8e9ab7d2e..d1e489b06 100644 --- a/init/devices.h +++ b/init/devices.h @@ -17,10 +17,36 @@ #ifndef _INIT_DEVICES_H #define _INIT_DEVICES_H +#include #include -extern void handle_device_fd(); -extern void device_init(void); +enum coldboot_action_t { + // coldboot continues without creating the device for the uevent + COLDBOOT_CONTINUE = 0, + // coldboot continues after creating the device for the uevent + COLDBOOT_CREATE, + // coldboot stops after creating the device for uevent but doesn't + // create the COLDBOOT_DONE file + COLDBOOT_STOP, + // same as COLDBOOT_STOP, but creates the COLDBOOT_DONE file + COLDBOOT_FINISH +}; + +struct uevent { + const char* action; + const char* path; + const char* subsystem; + const char* firmware; + const char* partition_name; + const char* device_name; + int partition_num; + int major; + int minor; +}; + +typedef std::function coldboot_callback; +extern coldboot_action_t handle_device_fd(coldboot_callback fn = nullptr); +extern void device_init(const char* path = nullptr, coldboot_callback fn = nullptr); enum early_device_type { EARLY_BLOCK_DEV, EARLY_CHAR_DEV }; From 44a3ee2cd1d73e8577d5612285c9000b47e848b6 Mon Sep 17 00:00:00 2001 From: Sandeep Patil Date: Wed, 8 Feb 2017 15:49:47 -0800 Subject: [PATCH 3/5] init: remove the existing early_mount code keeps parts of the code that are still needed for the fs_mgr + dt based implementation b/27805372 Test: boot angler, sailfish without regressions Change-Id: I1b08f8b7b4f2e67118d328443a5011c0f5ead919 Signed-off-by: Sandeep Patil --- init/devices.cpp | 65 ++------------------------------- init/devices.h | 6 ---- init/init.cpp | 93 +++++------------------------------------------- 3 files changed, 11 insertions(+), 153 deletions(-) diff --git a/init/devices.cpp b/init/devices.cpp index c7f3efa71..2224c1313 100644 --- a/init/devices.cpp +++ b/init/devices.cpp @@ -874,8 +874,8 @@ static bool inline should_stop_coldboot(coldboot_action_t act) #define UEVENT_MSG_LEN 2048 -template -static inline coldboot_action_t handle_device_fd_with(const T& handle_uevent) +static inline coldboot_action_t handle_device_fd_with( + std::function handle_uevent) { char msg[UEVENT_MSG_LEN+2]; int n; @@ -985,67 +985,6 @@ static coldboot_action_t coldboot(const char *path, coldboot_callback fn) return COLDBOOT_CONTINUE; } -static coldboot_action_t 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 COLDBOOT_STOP; - - 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 COLDBOOT_STOP; - } - - 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); - - return COLDBOOT_STOP; -} - -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 ? - [](uevent* uevent) -> coldboot_action_t { - return early_uevent_handler(uevent, "/dev/block/", true); - } : - [](uevent* uevent) -> coldboot_action_t { - return 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(const char* path, coldboot_callback fn) { sehandle = selinux_android_file_context_handle(); selinux_status_open(true); diff --git a/init/devices.h b/init/devices.h index d1e489b06..4f9791286 100644 --- a/init/devices.h +++ b/init/devices.h @@ -48,12 +48,6 @@ typedef std::function coldboot_callbac extern coldboot_action_t handle_device_fd(coldboot_callback fn = nullptr); extern void device_init(const char* path = nullptr, coldboot_callback fn = nullptr); -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 c8c18d2f3..48d63e96a 100644 --- a/init/init.cpp +++ b/init/init.cpp @@ -625,102 +625,27 @@ static void set_usb_controller() { } } -/* 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; - + std::string fstabfile; 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 (key == "androidboot.fstab") { + fstabfile = 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; + return fstabfile; } /* 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"; + // TODO: read fstab entries from device tree, so early mount + // entries can be specified for A/B devices where system = root + std::string fstab = import_cmdline_fstab(); + if (fstab.empty()) { + LOG(INFO) << "Early mount skipped (missing fstab argument)"; 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 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-; 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) { From 35403ebaf85aa229973275be9f5229d453799811 Mon Sep 17 00:00:00 2001 From: Sandeep Patil Date: Wed, 8 Feb 2017 20:27:12 -0800 Subject: [PATCH 4/5] init: early_mount: add support to mount non-verity partitions early This is done by parsing 'androidboot.fstab=' kernel cmdline option to get the fstab file that *only* specifies partitions to be mounted early (i.e. in init's first stage). Note that, the same fstab file may not be used as an argument to mount_all later in the boot as that will cause fs_mgr to fail with EBUSY. TODO: - Possibly add a new mount_mode so the same fstab can be used for early_mount, 'mount_all --early/--late' etc. - Add support for dm-verity enabled partitions to mount early. - Add support for getting fstab arguments through DT instead of kernel cmdline. Bug: 27805372 Test: Boot angler by passing a seperate fstab file using the kernel cmdline option to mount vendor partition early, remove the vendor partition entry from the main fstab file for the test. Boot sailfish by passing a seperate fstab entry via device tree to mount vendor partition early. Remove vendor partition entry from the main fstab file for the test Change-Id: I18785b893c54c8cee960ab44d5e8f83e5d624aa8 Signed-off-by: Sandeep Patil --- fs_mgr/fs_mgr_slotselect.cpp | 59 ++++++++++++++- fs_mgr/include/fs_mgr.h | 1 + init/devices.cpp | 24 +++++- init/devices.h | 1 + init/init.cpp | 139 +++++++++++++++++++++++++++++++++-- 5 files changed, 211 insertions(+), 13 deletions(-) diff --git a/fs_mgr/fs_mgr_slotselect.cpp b/fs_mgr/fs_mgr_slotselect.cpp index 94b43e4f8..e957f6b2e 100644 --- a/fs_mgr/fs_mgr_slotselect.cpp +++ b/fs_mgr/fs_mgr_slotselect.cpp @@ -25,6 +25,9 @@ #include #include +#include +#include +#include #include #include "fs_mgr.h" @@ -77,8 +80,51 @@ static int get_active_slot_suffix_from_misc(struct fstab *fstab, return 0; } -// Gets slot_suffix from either the kernel cmdline / firmware or the -// misc partition. Sets |out_suffix| on success and returns 0. Returns +// finds slot_suffix in androidboot.slot_suffix kernel command line argument +// or in the device tree node at /firmware/android/slot_suffix property +static int get_active_slot_suffix_from_kernel(char *out_suffix, + size_t suffix_len) +{ + std::string cmdline; + if (android::base::ReadFileToString("/proc/cmdline", &cmdline)) { + for (const auto& entry : android::base::Split(android::base::Trim(cmdline), " ")) { + std::vector pieces = android::base::Split(entry, "="); + if (pieces.size() == 2) { + if (pieces[0] == "androidboot.slot_suffix") { + strncpy(out_suffix, pieces[1].c_str(), suffix_len); + return 0; + } + } + } + } + + // if we can't find slot_suffix in cmdline, check the DT + static constexpr char android_dt_dir[] = "/proc/device-tree/firmware/android"; + std::string file_name = android::base::StringPrintf("%s/compatible", android_dt_dir); + std::string dt_value; + if (android::base::ReadFileToString(file_name, &dt_value)) { + if (!dt_value.compare("android,firmware")) { + LERROR << "Error finding compatible android DT node"; + return -1; + } + + file_name = android::base::StringPrintf("%s/%s", android_dt_dir, "slot_suffix"); + if (!android::base::ReadFileToString(file_name, &dt_value)) { + LERROR << "Error finding slot_suffix in device tree"; + return -1; + } + + // DT entries have a terminating '\0', so 'suffix_len' is safe. + strncpy(out_suffix, dt_value.c_str(), suffix_len); + return 0; + } + + // slot_suffix missing in kernel cmdline or device tree + return -1; +} + +// Gets slot_suffix from either the kernel cmdline / device tree / firmware +// or the misc partition. Sets |out_suffix| on success and returns 0. Returns // -1 if slot_suffix could not be determined. static int get_active_slot_suffix(struct fstab *fstab, char *out_suffix, size_t suffix_len) @@ -94,6 +140,15 @@ static int get_active_slot_suffix(struct fstab *fstab, char *out_suffix, return 0; } + // if the property is not set, we are either being invoked too early + // or the slot suffix in mentioned in the misc partition. If its + // "too early", try to find the slotsuffix ourselves in the kernel command + // line or the device tree + if (get_active_slot_suffix_from_kernel(out_suffix, suffix_len) == 0) { + LINFO << "Using slot suffix '" << out_suffix << "' from kernel"; + return 0; + } + // If we couldn't get the suffix from the kernel cmdline, try the // the misc partition. if (get_active_slot_suffix_from_misc(fstab, out_suffix, suffix_len) == 0) { diff --git a/fs_mgr/include/fs_mgr.h b/fs_mgr/include/fs_mgr.h index d959798d9..a9deed948 100644 --- a/fs_mgr/include/fs_mgr.h +++ b/fs_mgr/include/fs_mgr.h @@ -123,6 +123,7 @@ int fs_mgr_is_convertible_to_fbe(const struct fstab_rec *fstab); int fs_mgr_is_noemulatedsd(const struct fstab_rec *fstab); int fs_mgr_is_notrim(struct fstab_rec *fstab); int fs_mgr_is_formattable(struct fstab_rec *fstab); +int fs_mgr_is_slotselect(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_is_quota(struct fstab_rec *fstab); diff --git a/init/devices.cpp b/init/devices.cpp index 2224c1313..b3b808b89 100644 --- a/init/devices.cpp +++ b/init/devices.cpp @@ -62,7 +62,7 @@ static const char *firmware_dirs[] = { "/etc/firmware", extern struct selabel_handle *sehandle; -static int device_fd = -1; +static android::base::unique_fd device_fd; struct perms_ { char *name; @@ -341,6 +341,19 @@ static void remove_platform_device(const char *path) } } +static void destroy_platform_devices() { + struct listnode* node; + struct listnode* n; + struct platform_node* bus; + + list_for_each_safe(node, n, &platform_names) { + list_remove(node); + bus = node_to_item(node, struct platform_node, list); + free(bus->path); + free(bus); + } +} + /* Given a path that may start with a PCI device, populate the supplied buffer * with the PCI domain/bus number and the peripheral ID and return 0. * If it doesn't start with a PCI device, or there is some error, return -1 */ @@ -507,7 +520,7 @@ static char **get_block_device_symlinks(struct uevent *uevent) return NULL; memset(links, 0, sizeof(char *) * 4); - LOG(INFO) << "found " << type << " device " << device; + LOG(VERBOSE) << "found " << type << " device " << device; snprintf(link_path, sizeof(link_path), "/dev/block/%s/%s", type, device); @@ -990,7 +1003,7 @@ void device_init(const char* path, coldboot_callback fn) { selinux_status_open(true); /* is 256K enough? udev uses 16MB! */ - device_fd = uevent_open_socket(256*1024, true); + device_fd.reset(uevent_open_socket(256*1024, true)); if (device_fd == -1) { return; } @@ -1024,6 +1037,11 @@ void device_init(const char* path, coldboot_callback fn) { LOG(INFO) << "Coldboot took " << t; } +void device_close() { + destroy_platform_devices(); + device_fd.reset(); +} + int get_device_fd() { return device_fd; } diff --git a/init/devices.h b/init/devices.h index 4f9791286..26a064bac 100644 --- a/init/devices.h +++ b/init/devices.h @@ -47,6 +47,7 @@ struct uevent { typedef std::function coldboot_callback; extern coldboot_action_t handle_device_fd(coldboot_callback fn = nullptr); extern void device_init(const char* path = nullptr, coldboot_callback fn = nullptr); +extern void device_close(); extern int add_dev_perms(const char *name, const char *attr, mode_t perm, unsigned int uid, diff --git a/init/init.cpp b/init/init.cpp index 48d63e96a..973573f91 100644 --- a/init/init.cpp +++ b/init/init.cpp @@ -638,14 +638,135 @@ static std::string import_cmdline_fstab() { } /* Early mount vendor and ODM partitions. The fstab info is read from kernel cmdline. */ -static void early_mount() { - // TODO: read fstab entries from device tree, so early mount - // entries can be specified for A/B devices where system = root - std::string fstab = import_cmdline_fstab(); - if (fstab.empty()) { +static bool early_mount() { + // TODO: read fstab entries from device tree instead of + // kernel cmdline + std::string fstab_file = import_cmdline_fstab(); + if (fstab_file.empty()) { LOG(INFO) << "Early mount skipped (missing fstab argument)"; - return; + return true; } + + std::unique_ptr tab(fs_mgr_read_fstab(fstab_file.c_str()), + fs_mgr_free_fstab); + if (!tab) { + LOG(ERROR) << "Early mount failed to read fstab: " << fstab_file; + // continue to allow booting normally if this happened. + return true; + } + + // find out fstab records for odm, system and vendor + fstab_rec* odm_rec = fs_mgr_get_entry_for_mount_point(tab.get(), "/odm"); + fstab_rec* system_rec = fs_mgr_get_entry_for_mount_point(tab.get(), "/system"); + fstab_rec* vendor_rec = fs_mgr_get_entry_for_mount_point(tab.get(), "/vendor"); + if (!odm_rec && !system_rec && !vendor_rec) { + // nothing to early mount + return true; + } + + // assume A/B device if we find 'slotselect' in any fstab entry + bool is_ab = ((odm_rec && fs_mgr_is_slotselect(odm_rec)) || + (system_rec && fs_mgr_is_slotselect(system_rec)) || + (vendor_rec && fs_mgr_is_slotselect(vendor_rec))); + bool found_odm = !odm_rec; + bool found_system = !system_rec; + bool found_vendor = !vendor_rec; + int count_odm = 0, count_vendor = 0, count_system = 0; + + // create the devices we need.. + device_init(nullptr, + [&](uevent* uevent) -> coldboot_action_t { + if (!strncmp(uevent->subsystem, "firmware", 8)) { + return COLDBOOT_CONTINUE; + } + + // we need platform devices to create symlinks + if (!strncmp(uevent->subsystem, "platform", 8)) { + return COLDBOOT_CREATE; + } + + // Ignore everything that is not a block device + if (strncmp(uevent->subsystem, "block", 5)) { + return COLDBOOT_CONTINUE; + } + + coldboot_action_t ret; + bool create_this_node = false; + if (uevent->partition_name) { + // prefix match partition names so we create device nodes for + // A/B-ed partitions + if (!found_odm && !strncmp(uevent->partition_name, "odm", 3)) { + LOG(VERBOSE) << "early_mount: found (" << uevent->partition_name + << ") partition"; + + // wait twice for A/B-ed partitions + count_odm++; + if (!is_ab) { + found_odm = true; + } else if (count_odm == 2) { + found_odm = true; + } + + create_this_node = true; + } else if (!found_system && !strncmp(uevent->partition_name, "system", 6)) { + LOG(VERBOSE) << "early_mount: found (" << uevent->partition_name + << ") partition"; + + count_system++; + if (!is_ab) { + found_system = true; + } else if (count_system == 2) { + found_system = true; + } + + create_this_node = true; + } else if (!found_vendor && !strncmp(uevent->partition_name, "vendor", 6)) { + LOG(VERBOSE) << "early_mount: found (" << uevent->partition_name + << ") partition"; + count_vendor++; + if (!is_ab) { + found_vendor = true; + } else if (count_vendor == 2) { + found_vendor = true; + } + + create_this_node = true; + } + } + + // if we found all other partitions already, create this + // node and stop coldboot. If this is a prefix matched + // partition, create device node and continue. For everything + // else skip the device node + if (found_odm && found_system && found_vendor) { + ret = COLDBOOT_STOP; + } else if (create_this_node) { + ret = COLDBOOT_CREATE; + } else { + ret = COLDBOOT_CONTINUE; + } + + return ret; + }); + + // TODO: add support to mount partitions w/ verity + + int ret = 0; + if (odm_rec && + (ret = fs_mgr_do_mount(tab.get(), odm_rec->mount_point, odm_rec->blk_device, NULL))) { + PLOG(ERROR) << "early_mount: fs_mgr_do_mount returned error for mounting odm"; + return false; + } + + if (vendor_rec && + (ret = fs_mgr_do_mount(tab.get(), vendor_rec->mount_point, vendor_rec->blk_device, NULL))) { + PLOG(ERROR) << "early_mount: fs_mgr_do_mount returned error for mounting vendor"; + return false; + } + + device_close(); + + return true; } int main(int argc, char** argv) { @@ -694,8 +815,10 @@ int main(int argc, char** argv) { LOG(INFO) << "init " << (is_first_stage ? "first" : "second") << " stage started!"; if (is_first_stage) { - // Mount devices defined in android.early.* kernel commandline - early_mount(); + if (!early_mount()) { + LOG(ERROR) << "Failed to mount required partitions early ..."; + panic(); + } // Set up SELinux, loading the SELinux policy. selinux_initialize(true); From fc86f2442f99fcfc43527531768f69f56596f24a Mon Sep 17 00:00:00 2001 From: Sandeep Patil Date: Thu, 9 Feb 2017 19:19:31 -0800 Subject: [PATCH 5/5] init: fstab: add support to read fstab entries from device tree for early mount, we need a way to tell init where to find vendor, odm partitions (also system in case of non-A/B devices). Also, that needs to be independent of kernel cmdline since the cmdline will likely exceed its limit. The change adds support for parse and create fstab entries that can be directly sent to the fs_mgr for mounting partitions early in init first stage. Sample DT entry to mount vendor partition early on angler- firmware { android { compatible = "android,firmware"; fstab { compatible = "android,fstab"; vendor { compatible = "android,vendor"; dev = "/dev/block/platform/soc.0/f9824900.sdhci/by-name/vendor"; type = "ext4"; mnt_flags = "ro,barrier=1,inode_readahead_blks=8"; fsmgr_flags = "wait"; }; }; }; }; b/27805372 Test: Boot angler and sailfish with early "vendor" partition mount by adding aforementioned DT node and enable CONFIG_PROC_DEVICETREE in kernel Change-Id: I669013e3fdb157e88719436534f63989dec95d60 Signed-off-by: Sandeep Patil --- init/init.cpp | 278 ++++++++++++++++++++++++++++++++------------------ 1 file changed, 179 insertions(+), 99 deletions(-) diff --git a/init/init.cpp b/init/init.cpp index 973573f91..eb7431c10 100644 --- a/init/init.cpp +++ b/init/init.cpp @@ -477,28 +477,48 @@ static void export_kernel_boot_props() { } } -static void process_kernel_dt() { - static const char android_dir[] = "/proc/device-tree/firmware/android"; +static constexpr char android_dt_dir[] = "/proc/device-tree/firmware/android"; - std::string file_name = StringPrintf("%s/compatible", android_dir); +static bool is_dt_compatible() { + std::string dt_value; + std::string file_name = StringPrintf("%s/compatible", android_dt_dir); - std::string dt_file; - android::base::ReadFileToString(file_name, &dt_file); - if (!dt_file.compare("android,firmware")) { + android::base::ReadFileToString(file_name, &dt_value); + if (!dt_value.compare("android,firmware")) { LOG(ERROR) << "firmware/android is not compatible with 'android,firmware'"; - return; + return false; } - std::unique_ptrdir(opendir(android_dir), closedir); + return true; +} + +static bool is_dt_fstab_compatible() { + std::string dt_value; + std::string file_name = StringPrintf("%s/%s/compatible", android_dt_dir, "fstab"); + + android::base::ReadFileToString(file_name, &dt_value); + if (!dt_value.compare("android,fstab")) { + LOG(ERROR) << "firmware/android/fstab is not compatible with 'android,fstab'"; + return false; + } + + return true; +} + +static void process_kernel_dt() { + if (!is_dt_compatible()) return; + + std::unique_ptrdir(opendir(android_dt_dir), closedir); if (!dir) return; + std::string dt_file; struct dirent *dp; while ((dp = readdir(dir.get())) != NULL) { if (dp->d_type != DT_REG || !strcmp(dp->d_name, "compatible") || !strcmp(dp->d_name, "name")) { continue; } - file_name = StringPrintf("%s/%s", android_dir, dp->d_name); + std::string file_name = StringPrintf("%s/%s", android_dt_dir, dp->d_name); android::base::ReadFileToString(file_name, &dt_file); std::replace(dt_file.begin(), dt_file.end(), ',', '.'); @@ -625,34 +645,98 @@ static void set_usb_controller() { } } -/* Imports the fstab info from cmdline. */ -static std::string import_cmdline_fstab() { - std::string fstabfile; - import_kernel_cmdline(false, - [&](const std::string& key, const std::string& value, bool in_qemu __attribute__((__unused__))) { - if (key == "androidboot.fstab") { - fstabfile = value; - } - }); - return fstabfile; +static std::string import_dt_fstab() { + std::string fstab; + if (!is_dt_compatible() || !is_dt_fstab_compatible()) { + return fstab; + } + + std::string fstabdir_name = StringPrintf("%s/fstab", android_dt_dir); + std::unique_ptr fstabdir(opendir(fstabdir_name.c_str()), closedir); + if (!fstabdir) return fstab; + + dirent* dp; + while ((dp = readdir(fstabdir.get())) != NULL) { + // skip over name and compatible + if (dp->d_type != DT_DIR) { + continue; + } + + // skip if its not 'vendor', 'odm' or 'system' + if (strcmp(dp->d_name, "odm") && strcmp(dp->d_name, "system") && + strcmp(dp->d_name, "vendor")) { + continue; + } + + // create \n + std::vector fstab_entry; + std::string file_name; + std::string value; + file_name = StringPrintf("%s/%s/dev", fstabdir_name.c_str(), dp->d_name); + if (!android::base::ReadFileToString(file_name, &value)) { + LOG(ERROR) << "dt_fstab: Failed to find device for partition " << dp->d_name; + fstab.clear(); + break; + } + // trim the terminating '\0' out + value.resize(value.size() - 1); + fstab_entry.push_back(value); + fstab_entry.push_back(StringPrintf("/%s", dp->d_name)); + + file_name = StringPrintf("%s/%s/type", fstabdir_name.c_str(), dp->d_name); + if (!android::base::ReadFileToString(file_name, &value)) { + LOG(ERROR) << "dt_fstab: Failed to find type for partition " << dp->d_name; + fstab.clear(); + break; + } + value.resize(value.size() - 1); + fstab_entry.push_back(value); + + file_name = StringPrintf("%s/%s/mnt_flags", fstabdir_name.c_str(), dp->d_name); + if (!android::base::ReadFileToString(file_name, &value)) { + LOG(ERROR) << "dt_fstab: Failed to find type for partition " << dp->d_name; + fstab.clear(); + break; + } + value.resize(value.size() - 1); + fstab_entry.push_back(value); + + file_name = StringPrintf("%s/%s/fsmgr_flags", fstabdir_name.c_str(), dp->d_name); + if (!android::base::ReadFileToString(file_name, &value)) { + LOG(ERROR) << "dt_fstab: Failed to find type for partition " << dp->d_name; + fstab.clear(); + break; + } + value.resize(value.size() - 1); + fstab_entry.push_back(value); + + fstab += android::base::Join(fstab_entry, " "); + fstab += '\n'; + } + + return fstab; } -/* Early mount vendor and ODM partitions. The fstab info is read from kernel cmdline. */ +/* Early mount vendor and ODM partitions. The fstab is read from device-tree. */ static bool early_mount() { - // TODO: read fstab entries from device tree instead of - // kernel cmdline - std::string fstab_file = import_cmdline_fstab(); - if (fstab_file.empty()) { - LOG(INFO) << "Early mount skipped (missing fstab argument)"; + std::string fstab = import_dt_fstab(); + if (fstab.empty()) { + LOG(INFO) << "Early mount skipped (missing fstab in device tree)"; return true; } - std::unique_ptr tab(fs_mgr_read_fstab(fstab_file.c_str()), - fs_mgr_free_fstab); + std::unique_ptr fstab_file( + fmemopen(static_cast(const_cast(fstab.c_str())), fstab.length(), "r"), fclose); + if (!fstab_file) { + PLOG(ERROR) << "Early mount failed to open fstab file in memory"; + return false; + } + + std::unique_ptr tab( + fs_mgr_read_fstab_file(fstab_file.get()), fs_mgr_free_fstab); if (!tab) { - LOG(ERROR) << "Early mount failed to read fstab: " << fstab_file; - // continue to allow booting normally if this happened. - return true; + LOG(ERROR) << "Early mount fsmgr failed to load fstab from kernel:" << std::endl << fstab; + return false; } // find out fstab records for odm, system and vendor @@ -674,92 +758,88 @@ static bool early_mount() { int count_odm = 0, count_vendor = 0, count_system = 0; // create the devices we need.. - device_init(nullptr, - [&](uevent* uevent) -> coldboot_action_t { - if (!strncmp(uevent->subsystem, "firmware", 8)) { - return COLDBOOT_CONTINUE; - } + device_init(nullptr, [&](uevent* uevent) -> coldboot_action_t { + if (!strncmp(uevent->subsystem, "firmware", 8)) { + return COLDBOOT_CONTINUE; + } - // we need platform devices to create symlinks - if (!strncmp(uevent->subsystem, "platform", 8)) { - return COLDBOOT_CREATE; - } + // we need platform devices to create symlinks + if (!strncmp(uevent->subsystem, "platform", 8)) { + return COLDBOOT_CREATE; + } - // Ignore everything that is not a block device - if (strncmp(uevent->subsystem, "block", 5)) { - return COLDBOOT_CONTINUE; - } + // Ignore everything that is not a block device + if (strncmp(uevent->subsystem, "block", 5)) { + return COLDBOOT_CONTINUE; + } - coldboot_action_t ret; - bool create_this_node = false; - if (uevent->partition_name) { - // prefix match partition names so we create device nodes for - // A/B-ed partitions - if (!found_odm && !strncmp(uevent->partition_name, "odm", 3)) { - LOG(VERBOSE) << "early_mount: found (" << uevent->partition_name - << ") partition"; + coldboot_action_t ret; + bool create_this_node = false; + if (uevent->partition_name) { + // prefix match partition names so we create device nodes for + // A/B-ed partitions + if (!found_odm && !strncmp(uevent->partition_name, "odm", 3)) { + LOG(VERBOSE) << "early_mount: found (" << uevent->partition_name << ") partition"; - // wait twice for A/B-ed partitions - count_odm++; - if (!is_ab) { - found_odm = true; - } else if (count_odm == 2) { - found_odm = true; - } - - create_this_node = true; - } else if (!found_system && !strncmp(uevent->partition_name, "system", 6)) { - LOG(VERBOSE) << "early_mount: found (" << uevent->partition_name - << ") partition"; - - count_system++; - if (!is_ab) { - found_system = true; - } else if (count_system == 2) { - found_system = true; - } - - create_this_node = true; - } else if (!found_vendor && !strncmp(uevent->partition_name, "vendor", 6)) { - LOG(VERBOSE) << "early_mount: found (" << uevent->partition_name - << ") partition"; - count_vendor++; - if (!is_ab) { - found_vendor = true; - } else if (count_vendor == 2) { - found_vendor = true; - } - - create_this_node = true; + // wait twice for A/B-ed partitions + count_odm++; + if (!is_ab) { + found_odm = true; + } else if (count_odm == 2) { + found_odm = true; } - } - // if we found all other partitions already, create this - // node and stop coldboot. If this is a prefix matched - // partition, create device node and continue. For everything - // else skip the device node - if (found_odm && found_system && found_vendor) { - ret = COLDBOOT_STOP; - } else if (create_this_node) { - ret = COLDBOOT_CREATE; - } else { - ret = COLDBOOT_CONTINUE; - } + create_this_node = true; + } else if (!found_system && !strncmp(uevent->partition_name, "system", 6)) { + LOG(VERBOSE) << "early_mount: found (" << uevent->partition_name << ") partition"; - return ret; - }); + count_system++; + if (!is_ab) { + found_system = true; + } else if (count_system == 2) { + found_system = true; + } + + create_this_node = true; + } else if (!found_vendor && !strncmp(uevent->partition_name, "vendor", 6)) { + LOG(VERBOSE) << "early_mount: found (" << uevent->partition_name << ") partition"; + count_vendor++; + if (!is_ab) { + found_vendor = true; + } else if (count_vendor == 2) { + found_vendor = true; + } + + create_this_node = true; + } + } + + // if we found all other partitions already, create this + // node and stop coldboot. If this is a prefix matched + // partition, create device node and continue. For everything + // else skip the device node + if (found_odm && found_system && found_vendor) { + ret = COLDBOOT_STOP; + } else if (create_this_node) { + ret = COLDBOOT_CREATE; + } else { + ret = COLDBOOT_CONTINUE; + } + + return ret; + }); // TODO: add support to mount partitions w/ verity int ret = 0; if (odm_rec && - (ret = fs_mgr_do_mount(tab.get(), odm_rec->mount_point, odm_rec->blk_device, NULL))) { + (ret = fs_mgr_do_mount(tab.get(), odm_rec->mount_point, odm_rec->blk_device, NULL))) { PLOG(ERROR) << "early_mount: fs_mgr_do_mount returned error for mounting odm"; return false; } if (vendor_rec && - (ret = fs_mgr_do_mount(tab.get(), vendor_rec->mount_point, vendor_rec->blk_device, NULL))) { + (ret = fs_mgr_do_mount(tab.get(), vendor_rec->mount_point, vendor_rec->blk_device, NULL))) { PLOG(ERROR) << "early_mount: fs_mgr_do_mount returned error for mounting vendor"; return false; }