Merge changes from topic 'pre-early-mount' am: 1549257ff5 am: c027aac7c8
am: 9e140bb206

Change-Id: Idda7bee037b87bdb0aa8a26d2cdb9ee7b90b0cbd
diff --git a/fs_mgr/fs_mgr_slotselect.cpp b/fs_mgr/fs_mgr_slotselect.cpp
index 94b43e4..e957f6b 100644
--- a/fs_mgr/fs_mgr_slotselect.cpp
+++ b/fs_mgr/fs_mgr_slotselect.cpp
@@ -25,6 +25,9 @@
 #include <stdlib.h>
 #include <string.h>
 
+#include <android-base/file.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
 #include <cutils/properties.h>
 
 #include "fs_mgr.h"
@@ -77,8 +80,51 @@
     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<std::string> 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 @@
         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 d959798..a9deed9 100644
--- a/fs_mgr/include/fs_mgr.h
+++ b/fs_mgr/include/fs_mgr.h
@@ -123,6 +123,7 @@
 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 6af237c..b3b808b 100644
--- a/init/devices.cpp
+++ b/init/devices.cpp
@@ -62,19 +62,7 @@
 
 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;
-};
+static android::base::unique_fd device_fd;
 
 struct perms_ {
     char *name;
@@ -249,11 +237,13 @@
 
     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 +254,7 @@
     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 +275,10 @@
     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)
@@ -349,6 +341,19 @@
     }
 }
 
+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 */
@@ -515,7 +520,7 @@
         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);
 
@@ -875,9 +880,15 @@
     }
 }
 
+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*))
+static inline coldboot_action_t handle_device_fd_with(
+        std::function<coldboot_action_t(uevent* uevent)> handle_uevent)
 {
     char msg[UEVENT_MSG_LEN+2];
     int n;
@@ -890,14 +901,18 @@
 
         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();
@@ -907,9 +922,21 @@
                 }
             }
 
-            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
@@ -921,21 +948,24 @@
 ** 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] == '.')
@@ -949,85 +979,31 @@
         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<DIR, decltype(&closedir)> d(opendir(path), closedir);
-    if(d) {
-        do_coldboot(d.get());
-    }
-}
-
-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;
+    if (d) {
+        return do_coldboot(d.get(), fn);
     }
 
-    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_CONTINUE;
 }
 
-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() {
+void device_init(const char* path, coldboot_callback fn) {
     sehandle = selinux_android_file_context_handle();
     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;
     }
@@ -1039,13 +1015,33 @@
     }
 
     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;
 }
 
+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 8e9ab7d..26a064b 100644
--- a/init/devices.h
+++ b/init/devices.h
@@ -17,16 +17,37 @@
 #ifndef _INIT_DEVICES_H
 #define _INIT_DEVICES_H
 
+#include <functional>
 #include <sys/stat.h>
 
-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
+};
 
-enum early_device_type { EARLY_BLOCK_DEV, EARLY_CHAR_DEV };
+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;
+};
 
-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);
+typedef std::function<coldboot_action_t(struct uevent* uevent)> 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 43f601f..9c1e23b 100644
--- a/init/init.cpp
+++ b/init/init.cpp
@@ -495,28 +495,48 @@
     }
 }
 
-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_ptr<DIR, int(*)(DIR*)>dir(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_ptr<DIR, int(*)(DIR*)>dir(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(), ',', '.');
@@ -643,102 +663,208 @@
     }
 }
 
-/* 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);
+static std::string import_dt_fstab() {
+    std::string fstab;
+    if (!is_dt_compatible() || !is_dt_fstab_compatible()) {
+        return fstab;
     }
-}
 
-/* Imports the fstab info from cmdline. */
-static std::string import_cmdline_fstab() {
-    std::string prefix, fstab, fstab_full;
+    std::string fstabdir_name = StringPrintf("%s/fstab", android_dt_dir);
+    std::unique_ptr<DIR, int (*)(DIR*)> fstabdir(opendir(fstabdir_name.c_str()), closedir);
+    if (!fstabdir) return fstab;
 
-    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';
+    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 <dev> <mnt_point>  <type>  <mnt_flags>  <fsmgr_flags>\n
+        std::vector<std::string> 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_full;
+
+    return fstab;
 }
 
-/* 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;
+/* Early mount vendor and ODM partitions. The fstab is read from device-tree. */
+static bool early_mount() {
+    std::string fstab = import_dt_fstab();
+    if (fstab.empty()) {
+        LOG(INFO) << "Early mount skipped (missing fstab in device tree)";
+        return true;
     }
-    FILE *fstab_file = fmemopen((void *)fstab_string.c_str(), fstab_string.length(), "r");
+
+    std::unique_ptr<FILE, decltype(&fclose)> fstab_file(
+        fmemopen(static_cast<void*>(const_cast<char*>(fstab.c_str())), fstab.length(), "r"), fclose);
     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;
+        PLOG(ERROR) << "Early mount failed to open fstab file in memory";
+        return false;
     }
 
-    /* Create /dev/device-mapper for dm-verity */
-    early_create_dev("/sys/devices/virtual/misc/device-mapper", EARLY_CHAR_DEV);
+    std::unique_ptr<struct fstab, decltype(&fs_mgr_free_fstab)> tab(
+        fs_mgr_read_fstab_file(fstab_file.get()), fs_mgr_free_fstab);
+    if (!tab) {
+        LOG(ERROR) << "Early mount fsmgr failed to load fstab from kernel:" << std::endl << fstab;
+        return false;
+    }
 
-    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;
+    // 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;
+    }
 
-        if (mount_point != "/vendor" && mount_point != "/odm")
-            continue;
+    // 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 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;
+    // create the devices we need..
+    device_init(nullptr, [&](uevent* uevent) -> coldboot_action_t {
+        if (!strncmp(uevent->subsystem, "firmware", 8)) {
+            return COLDBOOT_CONTINUE;
         }
 
-        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;
+        // 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;
     }
-    early_device_socket_close();
+
+    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) {
@@ -787,8 +913,10 @@
     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);