Merge "fs_mgr: init: adb: add fstab argument to fs_mgr_overlayfs_mount_all"
diff --git a/adb/daemon/services.cpp b/adb/daemon/services.cpp
index 8417690..2bac486 100644
--- a/adb/daemon/services.cpp
+++ b/adb/daemon/services.cpp
@@ -103,7 +103,9 @@
     if (reboot_arg.empty()) reboot_arg = "adb";
     std::string reboot_string = android::base::StringPrintf("reboot,%s", reboot_arg.c_str());
 
-    if (reboot_arg == "fastboot" && access("/dev/socket/recovery", F_OK) == 0) {
+    if (reboot_arg == "fastboot" &&
+        android::base::GetBoolProperty("ro.boot.logical_partitions", false) &&
+        access("/dev/socket/recovery", F_OK) == 0) {
         LOG(INFO) << "Recovery specific reboot fastboot";
         /*
          * The socket is created to allow switching between recovery and
diff --git a/debuggerd/crasher/crasher.cpp b/debuggerd/crasher/crasher.cpp
index f0fe1d0..f0bdfbf 100644
--- a/debuggerd/crasher/crasher.cpp
+++ b/debuggerd/crasher/crasher.cpp
@@ -224,7 +224,7 @@
     // Prefixes.
     if (!strncmp(arg, "wait-", strlen("wait-"))) {
       char buf[1];
-      TEMP_FAILURE_RETRY(read(STDIN_FILENO, buf, sizeof(buf)));
+      UNUSED(TEMP_FAILURE_RETRY(read(STDIN_FILENO, buf, sizeof(buf))));
       return do_action(arg + strlen("wait-"));
     } else if (!strncmp(arg, "exhaustfd-", strlen("exhaustfd-"))) {
       errno = 0;
@@ -258,10 +258,14 @@
       __assert("some_file.c", 123, "false");
     } else if (!strcasecmp(arg, "assert2")) {
       __assert2("some_file.c", 123, "some_function", "false");
+#if !defined(__clang_analyzer__)
     } else if (!strcasecmp(arg, "fortify")) {
+      // FORTIFY is disabled when running clang-tidy and other tools, so this
+      // shouldn't depend on internal implementation details of it.
       char buf[10];
       __read_chk(-1, buf, 32, 10);
       while (true) pause();
+#endif
     } else if (!strcasecmp(arg, "fdsan_file")) {
       FILE* f = fopen("/dev/null", "r");
       close(fileno(f));
diff --git a/fastboot/bootimg_utils.cpp b/fastboot/bootimg_utils.cpp
index 1152007..e433787 100644
--- a/fastboot/bootimg_utils.cpp
+++ b/fastboot/bootimg_utils.cpp
@@ -39,27 +39,27 @@
     strcpy(reinterpret_cast<char*>(h->cmdline), cmdline.c_str());
 }
 
-boot_img_hdr_v1* mkbootimg(void* kernel, int64_t kernel_size, void* ramdisk, int64_t ramdisk_size,
-                           void* second, int64_t second_size, size_t base,
-                           const boot_img_hdr_v1& src, int64_t* bootimg_size) {
+boot_img_hdr_v1* mkbootimg(const std::vector<char>& kernel, const std::vector<char>& ramdisk,
+                           const std::vector<char>& second, size_t base, const boot_img_hdr_v1& src,
+                           std::vector<char>* out) {
     const size_t page_mask = src.page_size - 1;
 
     int64_t header_actual = (sizeof(boot_img_hdr_v1) + page_mask) & (~page_mask);
-    int64_t kernel_actual = (kernel_size + page_mask) & (~page_mask);
-    int64_t ramdisk_actual = (ramdisk_size + page_mask) & (~page_mask);
-    int64_t second_actual = (second_size + page_mask) & (~page_mask);
+    int64_t kernel_actual = (kernel.size() + page_mask) & (~page_mask);
+    int64_t ramdisk_actual = (ramdisk.size() + page_mask) & (~page_mask);
+    int64_t second_actual = (second.size() + page_mask) & (~page_mask);
 
-    *bootimg_size = header_actual + kernel_actual + ramdisk_actual + second_actual;
+    int64_t bootimg_size = header_actual + kernel_actual + ramdisk_actual + second_actual;
+    out->resize(bootimg_size);
 
-    boot_img_hdr_v1* hdr = reinterpret_cast<boot_img_hdr_v1*>(calloc(*bootimg_size, 1));
-    if (hdr == nullptr) die("couldn't allocate boot image: %" PRId64 " bytes", *bootimg_size);
+    boot_img_hdr_v1* hdr = reinterpret_cast<boot_img_hdr_v1*>(out->data());
 
     *hdr = src;
     memcpy(hdr->magic, BOOT_MAGIC, BOOT_MAGIC_SIZE);
 
-    hdr->kernel_size =  kernel_size;
-    hdr->ramdisk_size = ramdisk_size;
-    hdr->second_size =  second_size;
+    hdr->kernel_size = kernel.size();
+    hdr->ramdisk_size = ramdisk.size();
+    hdr->second_size = second.size();
 
     hdr->kernel_addr += base;
     hdr->ramdisk_addr += base;
@@ -70,8 +70,9 @@
         hdr->header_size = sizeof(boot_img_hdr_v1);
     }
 
-    memcpy(hdr->magic + hdr->page_size, kernel, kernel_size);
-    memcpy(hdr->magic + hdr->page_size + kernel_actual, ramdisk, ramdisk_size);
-    memcpy(hdr->magic + hdr->page_size + kernel_actual + ramdisk_actual, second, second_size);
+    memcpy(hdr->magic + hdr->page_size, kernel.data(), kernel.size());
+    memcpy(hdr->magic + hdr->page_size + kernel_actual, ramdisk.data(), ramdisk.size());
+    memcpy(hdr->magic + hdr->page_size + kernel_actual + ramdisk_actual, second.data(),
+           second.size());
     return hdr;
 }
diff --git a/fastboot/bootimg_utils.h b/fastboot/bootimg_utils.h
index fe805b0..a4e8870 100644
--- a/fastboot/bootimg_utils.h
+++ b/fastboot/bootimg_utils.h
@@ -33,8 +33,9 @@
 #include <sys/types.h>
 
 #include <string>
+#include <vector>
 
-boot_img_hdr_v1* mkbootimg(void* kernel, int64_t kernel_size, void* ramdisk, int64_t ramdisk_size,
-                           void* second, int64_t second_size, size_t base,
-                           const boot_img_hdr_v1& src, int64_t* bootimg_size);
+boot_img_hdr_v1* mkbootimg(const std::vector<char>& kernel, const std::vector<char>& ramdisk,
+                           const std::vector<char>& second, size_t base, const boot_img_hdr_v1& src,
+                           std::vector<char>* out);
 void bootimg_set_cmdline(boot_img_hdr_v1* h, const std::string& cmdline);
diff --git a/fastboot/engine.cpp b/fastboot/engine.cpp
index 0ac57af..a43e7a6 100644
--- a/fastboot/engine.cpp
+++ b/fastboot/engine.cpp
@@ -103,9 +103,9 @@
     RUN_COMMAND(fb->Flash(partition));
 }
 
-void fb_flash(const std::string& partition, void* data, uint32_t sz) {
-    Status(StringPrintf("Sending '%s' (%u KB)", partition.c_str(), sz / 1024));
-    RUN_COMMAND(fb->Download(static_cast<char*>(data), sz));
+void fb_flash(const std::string& partition, const std::vector<char>& data) {
+    Status(StringPrintf("Sending '%s' (%zu KB)", partition.c_str(), data.size() / 1024));
+    RUN_COMMAND(fb->Download(data));
 
     Status("Writing '" + partition + "'");
     RUN_COMMAND(fb->Flash(partition));
@@ -136,69 +136,6 @@
     RUN_COMMAND(fb->RawCommand(FB_CMD_RESIZE_PARTITION ":" + partition + ":" + size));
 }
 
-static int match(const char* str, const char** value, unsigned count) {
-    unsigned n;
-
-    for (n = 0; n < count; n++) {
-        const char *val = value[n];
-        int len = strlen(val);
-        int match;
-
-        if ((len > 1) && (val[len-1] == '*')) {
-            len--;
-            match = !strncmp(val, str, len);
-        } else {
-            match = !strcmp(val, str);
-        }
-
-        if (match) return 1;
-    }
-
-    return 0;
-}
-
-void fb_require(const std::string& product, const std::string& var, bool invert, size_t count,
-                const char** values) {
-    Status("Checking '" + var + "'");
-
-    double start = now();
-
-    std::string var_value;
-    auto status = fb->GetVar(var, &var_value);
-
-    if (status) {
-        fprintf(stderr, "getvar:%s FAILED (%s)\n", var.c_str(), fb->Error().c_str());
-        die("requirements not met!");
-    }
-
-    if (!product.empty()) {
-        if (product != cur_product) {
-            double split = now();
-            fprintf(stderr, "IGNORE, product is %s required only for %s [%7.3fs]\n", cur_product,
-                    product.c_str(), (split - start));
-            return;
-        }
-    }
-
-    int yes = match(var_value.c_str(), values, count);
-    if (invert) yes = !yes;
-
-    if (yes) {
-        double split = now();
-        fprintf(stderr, "OKAY [%7.3fs]\n", (split - start));
-        return;
-    }
-
-    fprintf(stderr, "FAILED\n\n");
-    fprintf(stderr, "Device %s is '%s'.\n", var.c_str(), var_value.c_str());
-    fprintf(stderr, "Update %s '%s'", invert ? "rejects" : "requires", values[0]);
-    for (size_t n = 1; n < count; n++) {
-        fprintf(stderr, " or '%s'", values[n]);
-    }
-    fprintf(stderr, ".\n\n");
-    die("requirements not met!");
-}
-
 void fb_display(const std::string& label, const std::string& var) {
     std::string value;
     auto status = fb->GetVar(var, &value);
@@ -210,18 +147,6 @@
     fprintf(stderr, "%s: %s\n", label.c_str(), value.c_str());
 }
 
-void fb_query_save(const std::string& var, char* dest, uint32_t dest_size) {
-    std::string value;
-    auto status = fb->GetVar(var, &value);
-
-    if (status) {
-        fprintf(stderr, "getvar:%s FAILED (%s)\n", var.c_str(), fb->Error().c_str());
-        return;
-    }
-
-    strncpy(dest, value.c_str(), dest_size);
-}
-
 void fb_reboot() {
     fprintf(stderr, "Rebooting");
     fb->Reboot();
@@ -233,9 +158,9 @@
     RUN_COMMAND(fb->RawCommand(cmd));
 }
 
-void fb_download(const std::string& name, void* data, uint32_t size) {
+void fb_download(const std::string& name, const std::vector<char>& data) {
     Status("Downloading '" + name + "'");
-    RUN_COMMAND(fb->Download(static_cast<char*>(data), size));
+    RUN_COMMAND(fb->Download(data));
 }
 
 void fb_download_fd(const std::string& name, int fd, uint32_t sz) {
diff --git a/fastboot/engine.h b/fastboot/engine.h
index d78cb13..b681f5a 100644
--- a/fastboot/engine.h
+++ b/fastboot/engine.h
@@ -48,18 +48,15 @@
 void fb_reinit(Transport* transport);
 
 bool fb_getvar(const std::string& key, std::string* value);
-void fb_flash(const std::string& partition, void* data, uint32_t sz);
+void fb_flash(const std::string& partition, const std::vector<char>& data);
 void fb_flash_fd(const std::string& partition, int fd, uint32_t sz);
 void fb_flash_sparse(const std::string& partition, struct sparse_file* s, uint32_t sz,
                      size_t current, size_t total);
 void fb_erase(const std::string& partition);
-void fb_require(const std::string& prod, const std::string& var, bool invert, size_t nvalues,
-                const char** values);
 void fb_display(const std::string& label, const std::string& var);
-void fb_query_save(const std::string& var, char* dest, uint32_t dest_size);
 void fb_reboot();
 void fb_command(const std::string& cmd, const std::string& msg);
-void fb_download(const std::string& name, void* data, uint32_t size);
+void fb_download(const std::string& name, const std::vector<char>& data);
 void fb_download_fd(const std::string& name, int fd, uint32_t sz);
 void fb_upload(const std::string& outfile);
 void fb_notice(const std::string& notice);
diff --git a/fastboot/fastboot.cpp b/fastboot/fastboot.cpp
index 8e6c125..5173bab 100644
--- a/fastboot/fastboot.cpp
+++ b/fastboot/fastboot.cpp
@@ -43,6 +43,7 @@
 
 #include <chrono>
 #include <functional>
+#include <regex>
 #include <thread>
 #include <utility>
 #include <vector>
@@ -69,14 +70,15 @@
 #include "udp.h"
 #include "usb.h"
 
+using android::base::ReadFully;
+using android::base::Split;
+using android::base::Trim;
 using android::base::unique_fd;
 
 #ifndef O_BINARY
 #define O_BINARY 0
 #endif
 
-char cur_product[FB_RESPONSE_SZ + 1];
-
 static const char* serial = nullptr;
 
 static bool g_long_listing = false;
@@ -179,38 +181,22 @@
 
 static int64_t get_file_size(int fd) {
     struct stat sb;
-    return fstat(fd, &sb) == -1 ? -1 : sb.st_size;
+    if (fstat(fd, &sb) == -1) {
+        die("could not get file size");
+    }
+    return sb.st_size;
 }
 
-static void* load_fd(int fd, int64_t* sz) {
-    int errno_tmp;
-    char* data = nullptr;
+bool ReadFileToVector(const std::string& file, std::vector<char>* out) {
+    out->clear();
 
-    *sz = get_file_size(fd);
-    if (*sz < 0) {
-        goto oops;
+    unique_fd fd(TEMP_FAILURE_RETRY(open(file.c_str(), O_RDONLY | O_CLOEXEC | O_BINARY)));
+    if (fd == -1) {
+        return false;
     }
 
-    data = (char*) malloc(*sz);
-    if (data == nullptr) goto oops;
-
-    if(read(fd, data, *sz) != *sz) goto oops;
-    close(fd);
-
-    return data;
-
-oops:
-    errno_tmp = errno;
-    close(fd);
-    if(data != 0) free(data);
-    errno = errno_tmp;
-    return 0;
-}
-
-static void* load_file(const std::string& path, int64_t* sz) {
-    int fd = open(path.c_str(), O_RDONLY | O_BINARY);
-    if (fd == -1) return nullptr;
-    return load_fd(fd, sz);
+    out->resize(get_file_size(fd));
+    return ReadFully(fd, out->data(), out->size());
 }
 
 static int match_fastboot_with_serial(usb_ifc_info* info, const char* local_serial) {
@@ -418,70 +404,71 @@
     return 0;
 }
 
-static void* load_bootable_image(const std::string& kernel, const std::string& ramdisk,
-                                 const std::string& second_stage, int64_t* sz) {
-    int64_t ksize;
-    void* kdata = load_file(kernel.c_str(), &ksize);
-    if (kdata == nullptr) die("cannot load '%s': %s", kernel.c_str(), strerror(errno));
+static std::vector<char> LoadBootableImage(const std::string& kernel, const std::string& ramdisk,
+                                           const std::string& second_stage) {
+    std::vector<char> kernel_data;
+    if (!ReadFileToVector(kernel, &kernel_data)) {
+        die("cannot load '%s': %s", kernel.c_str(), strerror(errno));
+    }
 
     // Is this actually a boot image?
-    if (ksize < static_cast<int64_t>(sizeof(boot_img_hdr_v1))) {
+    if (kernel_data.size() < sizeof(boot_img_hdr_v1)) {
         die("cannot load '%s': too short", kernel.c_str());
     }
-    if (!memcmp(kdata, BOOT_MAGIC, BOOT_MAGIC_SIZE)) {
+    if (!memcmp(kernel_data.data(), BOOT_MAGIC, BOOT_MAGIC_SIZE)) {
         if (!g_cmdline.empty()) {
-            bootimg_set_cmdline(reinterpret_cast<boot_img_hdr_v1*>(kdata), g_cmdline);
+            bootimg_set_cmdline(reinterpret_cast<boot_img_hdr_v1*>(kernel_data.data()), g_cmdline);
         }
 
         if (!ramdisk.empty()) die("cannot boot a boot.img *and* ramdisk");
 
-        *sz = ksize;
-        return kdata;
+        return kernel_data;
     }
 
-    void* rdata = nullptr;
-    int64_t rsize = 0;
+    std::vector<char> ramdisk_data;
     if (!ramdisk.empty()) {
-        rdata = load_file(ramdisk.c_str(), &rsize);
-        if (rdata == nullptr) die("cannot load '%s': %s", ramdisk.c_str(), strerror(errno));
+        if (!ReadFileToVector(ramdisk, &ramdisk_data)) {
+            die("cannot load '%s': %s", ramdisk.c_str(), strerror(errno));
+        }
     }
 
-    void* sdata = nullptr;
-    int64_t ssize = 0;
+    std::vector<char> second_stage_data;
     if (!second_stage.empty()) {
-        sdata = load_file(second_stage.c_str(), &ssize);
-        if (sdata == nullptr) die("cannot load '%s': %s", second_stage.c_str(), strerror(errno));
+        if (!ReadFileToVector(second_stage, &second_stage_data)) {
+            die("cannot load '%s': %s", second_stage.c_str(), strerror(errno));
+        }
     }
-
     fprintf(stderr,"creating boot image...\n");
-    boot_img_hdr_v1* bdata = mkbootimg(kdata, ksize, rdata, rsize, sdata, ssize,
-                                       g_base_addr, g_boot_img_hdr, sz);
-    if (bdata == nullptr) die("failed to create boot.img");
 
-    if (!g_cmdline.empty()) bootimg_set_cmdline(bdata, g_cmdline);
-    fprintf(stderr, "creating boot image - %" PRId64 " bytes\n", *sz);
+    std::vector<char> out;
+    boot_img_hdr_v1* boot_image_data = mkbootimg(kernel_data, ramdisk_data, second_stage_data,
+                                                 g_base_addr, g_boot_img_hdr, &out);
 
-    return bdata;
+    if (!g_cmdline.empty()) bootimg_set_cmdline(boot_image_data, g_cmdline);
+    fprintf(stderr, "creating boot image - %zu bytes\n", out.size());
+
+    return out;
 }
 
-static void* unzip_to_memory(ZipArchiveHandle zip, const char* entry_name, int64_t* sz) {
-    ZipString zip_entry_name(entry_name);
+static bool UnzipToMemory(ZipArchiveHandle zip, const std::string& entry_name,
+                          std::vector<char>* out) {
+    ZipString zip_entry_name(entry_name.c_str());
     ZipEntry zip_entry;
     if (FindEntry(zip, zip_entry_name, &zip_entry) != 0) {
-        fprintf(stderr, "archive does not contain '%s'\n", entry_name);
-        return nullptr;
+        fprintf(stderr, "archive does not contain '%s'\n", entry_name.c_str());
+        return false;
     }
 
-    *sz = zip_entry.uncompressed_length;
+    out->resize(zip_entry.uncompressed_length);
 
-    fprintf(stderr, "extracting %s (%" PRId64 " MB) to RAM...\n", entry_name, *sz / 1024 / 1024);
-    uint8_t* data = reinterpret_cast<uint8_t*>(malloc(zip_entry.uncompressed_length));
-    if (data == nullptr) die("failed to allocate %" PRId64 " bytes for '%s'", *sz, entry_name);
+    fprintf(stderr, "extracting %s (%zu MB) to RAM...\n", entry_name.c_str(),
+            out->size() / 1024 / 1024);
 
-    int error = ExtractToMemory(zip, &zip_entry, data, zip_entry.uncompressed_length);
-    if (error != 0) die("failed to extract '%s': %s", entry_name, ErrorCodeString(error));
+    int error = ExtractToMemory(zip, &zip_entry, reinterpret_cast<uint8_t*>(out->data()),
+                                out->size());
+    if (error != 0) die("failed to extract '%s': %s", entry_name.c_str(), ErrorCodeString(error));
 
-    return data;
+    return true;
 }
 
 #if defined(_WIN32)
@@ -602,109 +589,145 @@
     return fd.release();
 }
 
-static char* strip(char* s) {
-    while (*s && isspace(*s)) s++;
+static void CheckRequirement(const std::string& cur_product, const std::string& var,
+                             const std::string& product, bool invert,
+                             const std::vector<std::string>& options) {
+    Status("Checking '" + var + "'");
 
-    int n = strlen(s);
-    while (n-- > 0) {
-        if (!isspace(s[n])) break;
-        s[n] = 0;
+    double start = now();
+
+    if (!product.empty()) {
+        if (product != cur_product) {
+            double split = now();
+            fprintf(stderr, "IGNORE, product is %s required only for %s [%7.3fs]\n",
+                    cur_product.c_str(), product.c_str(), (split - start));
+            return;
+        }
     }
-    return s;
+
+    std::string var_value;
+    if (!fb_getvar(var, &var_value)) {
+        fprintf(stderr, "FAILED\n\n");
+        fprintf(stderr, "Could not getvar for '%s' (%s)\n\n", var.c_str(), fb_get_error().c_str());
+        die("requirements not met!");
+    }
+
+    bool match = false;
+    for (const auto& option : options) {
+        if (option == var_value || (option.back() == '*' &&
+                                    !var_value.compare(0, option.length() - 1, option, 0,
+                                                       option.length() - 1))) {
+            match = true;
+            break;
+        }
+    }
+
+    if (invert) {
+        match = !match;
+    }
+
+    if (match) {
+        double split = now();
+        fprintf(stderr, "OKAY [%7.3fs]\n", (split - start));
+        return;
+    }
+
+    fprintf(stderr, "FAILED\n\n");
+    fprintf(stderr, "Device %s is '%s'.\n", var.c_str(), var_value.c_str());
+    fprintf(stderr, "Update %s '%s'", invert ? "rejects" : "requires", options[0].c_str());
+    for (auto it = std::next(options.begin()); it != options.end(); ++it) {
+        fprintf(stderr, " or '%s'", it->c_str());
+    }
+    fprintf(stderr, ".\n\n");
+    die("requirements not met!");
 }
 
-#define MAX_OPTIONS 32
-static void check_requirement(char* line) {
-    char *val[MAX_OPTIONS];
-    unsigned count;
-    char *x;
-    int invert = 0;
-
+bool ParseRequirementLine(const std::string& line, std::string* name, std::string* product,
+                          bool* invert, std::vector<std::string>* options) {
     // "require product=alpha|beta|gamma"
     // "require version-bootloader=1234"
     // "require-for-product:gamma version-bootloader=istanbul|constantinople"
     // "require partition-exists=vendor"
+    *product = "";
+    *invert = false;
 
-    char* name = line;
-    const char* product = "";
-    if (!strncmp(name, "reject ", 7)) {
-        name += 7;
-        invert = 1;
-    } else if (!strncmp(name, "require ", 8)) {
-        name += 8;
-        invert = 0;
-    } else if (!strncmp(name, "require-for-product:", 20)) {
-        // Get the product and point name past it
-        product = name + 20;
-        name = strchr(name, ' ');
-        if (!name) die("android-info.txt syntax error: %s", line);
-        *name = 0;
-        name += 1;
-        invert = 0;
+    auto require_reject_regex = std::regex{"(require\\s+|reject\\s+)?\\s*(\\S+)\\s*=\\s*(.*)"};
+    auto require_product_regex =
+            std::regex{"require-for-product:\\s*(\\S+)\\s+(\\S+)\\s*=\\s*(.*)"};
+    std::smatch match_results;
+
+    if (std::regex_match(line, match_results, require_reject_regex)) {
+        *invert = Trim(match_results[1]) == "reject";
+    } else if (std::regex_match(line, match_results, require_product_regex)) {
+        *product = match_results[1];
+    } else {
+        return false;
     }
 
-    x = strchr(name, '=');
-    if (x == 0) return;
-    *x = 0;
-    val[0] = x + 1;
-
-    name = strip(name);
-
-    // "require partition-exists=x" is a special case, added because of the trouble we had when
-    // Pixel 2 shipped with new partitions and users used old versions of fastboot to flash them,
-    // missing out new partitions. A device with new partitions can use "partition-exists" to
-    // override the fields `optional_if_no_image` in the `images` array.
-    if (!strcmp(name, "partition-exists")) {
-        const char* partition_name = val[0];
-        std::string has_slot;
-        if (!fb_getvar(std::string("has-slot:") + partition_name, &has_slot) ||
-            (has_slot != "yes" && has_slot != "no")) {
-            die("device doesn't have required partition %s!", partition_name);
-        }
-        bool known_partition = false;
-        for (size_t i = 0; i < arraysize(images); ++i) {
-            if (images[i].nickname && !strcmp(images[i].nickname, partition_name)) {
-                images[i].optional_if_no_image = false;
-                known_partition = true;
-            }
-        }
-        if (!known_partition) {
-            die("device requires partition %s which is not known to this version of fastboot",
-                partition_name);
-        }
-        return;
-    }
-
-    for(count = 1; count < MAX_OPTIONS; count++) {
-        x = strchr(val[count - 1],'|');
-        if (x == 0) break;
-        *x = 0;
-        val[count] = x + 1;
-    }
-
+    *name = match_results[2];
     // Work around an unfortunate name mismatch.
-    const char* var = name;
-    if (!strcmp(name, "board")) var = "product";
-
-    const char** out = reinterpret_cast<const char**>(malloc(sizeof(char*) * count));
-    if (out == nullptr) die("out of memory");
-
-    for (size_t i = 0; i < count; ++i) {
-        out[i] = xstrdup(strip(val[i]));
+    if (*name == "board") {
+        *name = "product";
     }
 
-    fb_require(product, var, invert, count, out);
+    auto raw_options = Split(match_results[3], "|");
+    for (const auto& option : raw_options) {
+        auto trimmed_option = Trim(option);
+        options->emplace_back(trimmed_option);
+    }
+
+    return true;
 }
 
-static void check_requirements(char* data, int64_t sz) {
-    char* s = data;
-    while (sz-- > 0) {
-        if (*s == '\n') {
-            *s++ = 0;
-            check_requirement(data);
-            data = s;
+// "require partition-exists=x" is a special case, added because of the trouble we had when
+// Pixel 2 shipped with new partitions and users used old versions of fastboot to flash them,
+// missing out new partitions. A device with new partitions can use "partition-exists" to
+// override the fields `optional_if_no_image` in the `images` array.
+static void HandlePartitionExists(const std::vector<std::string>& options) {
+    const std::string& partition_name = options[0];
+    std::string has_slot;
+    if (!fb_getvar("has-slot:" + partition_name, &has_slot) ||
+        (has_slot != "yes" && has_slot != "no")) {
+        die("device doesn't have required partition %s!", partition_name.c_str());
+    }
+    bool known_partition = false;
+    for (size_t i = 0; i < arraysize(images); ++i) {
+        if (images[i].nickname && images[i].nickname == partition_name) {
+            images[i].optional_if_no_image = false;
+            known_partition = true;
+        }
+    }
+    if (!known_partition) {
+        die("device requires partition %s which is not known to this version of fastboot",
+            partition_name.c_str());
+    }
+}
+
+static void CheckRequirements(const std::string& data) {
+    std::string cur_product;
+    if (!fb_getvar("product", &cur_product)) {
+        fprintf(stderr, "getvar:product FAILED (%s)\n", fb_get_error().c_str());
+    }
+
+    auto lines = Split(data, "\n");
+    for (const auto& line : lines) {
+        if (line.empty()) {
+            continue;
+        }
+
+        std::string name;
+        std::string product;
+        bool invert;
+        std::vector<std::string> options;
+
+        if (!ParseRequirementLine(line, &name, &product, &invert, &options)) {
+            fprintf(stderr, "android-info.txt syntax error: %s\n", line.c_str());
+            continue;
+        }
+        if (name == "partition-exists") {
+            HandlePartitionExists(options);
         } else {
-            s++;
+            CheckRequirement(cur_product, name, product, invert, options);
         }
     }
 }
@@ -1097,7 +1120,7 @@
 
 class ImageSource {
   public:
-    virtual void* ReadFile(const std::string& name, int64_t* size) const = 0;
+    virtual bool ReadFile(const std::string& name, std::vector<char>* out) const = 0;
     virtual int OpenFile(const std::string& name) const = 0;
 };
 
@@ -1166,12 +1189,11 @@
 }
 
 void FlashAllTool::CheckRequirements() {
-    int64_t sz;
-    void* data = source_.ReadFile("android-info.txt", &sz);
-    if (data == nullptr) {
+    std::vector<char> contents;
+    if (!source_.ReadFile("android-info.txt", &contents)) {
         die("could not read android-info.txt");
     }
-    check_requirements(reinterpret_cast<char*>(data), sz);
+    ::CheckRequirements({contents.data(), contents.size()});
 }
 
 void FlashAllTool::DetermineSecondarySlot() {
@@ -1224,10 +1246,9 @@
 
 void FlashAllTool::FlashImage(const Image& image, const std::string& slot, fastboot_buffer* buf) {
     auto flash = [&, this](const std::string& partition_name) {
-        int64_t sz;
-        void* data = source_.ReadFile(image.sig_name, &sz);
-        if (data) {
-            fb_download("signature", data, sz);
+        std::vector<char> signature_data;
+        if (source_.ReadFile(image.sig_name, &signature_data)) {
+            fb_download("signature", signature_data);
             fb_command("signature", "installing signature");
         }
 
@@ -1263,15 +1284,15 @@
 class ZipImageSource final : public ImageSource {
   public:
     explicit ZipImageSource(ZipArchiveHandle zip) : zip_(zip) {}
-    void* ReadFile(const std::string& name, int64_t* size) const override;
+    bool ReadFile(const std::string& name, std::vector<char>* out) const override;
     int OpenFile(const std::string& name) const override;
 
   private:
     ZipArchiveHandle zip_;
 };
 
-void* ZipImageSource::ReadFile(const std::string& name, int64_t* size) const {
-    return unzip_to_memory(zip_, name.c_str(), size);
+bool ZipImageSource::ReadFile(const std::string& name, std::vector<char>* out) const {
+    return UnzipToMemory(zip_, name, out);
 }
 
 int ZipImageSource::OpenFile(const std::string& name) const {
@@ -1281,8 +1302,6 @@
 static void do_update(const char* filename, const std::string& slot_override, bool skip_secondary) {
     dump_info();
 
-    fb_query_save("product", cur_product, sizeof(cur_product));
-
     ZipArchiveHandle zip;
     int error = OpenArchive(filename, &zip);
     if (error != 0) {
@@ -1297,16 +1316,16 @@
 
 class LocalImageSource final : public ImageSource {
   public:
-    void* ReadFile(const std::string& name, int64_t* size) const override;
+    bool ReadFile(const std::string& name, std::vector<char>* out) const override;
     int OpenFile(const std::string& name) const override;
 };
 
-void* LocalImageSource::ReadFile(const std::string& name, int64_t* size) const {
+bool LocalImageSource::ReadFile(const std::string& name, std::vector<char>* out) const {
     auto path = find_item_given_name(name);
     if (path.empty()) {
-        return nullptr;
+        return false;
     }
-    return load_file(path.c_str(), size);
+    return ReadFileToVector(path, out);
 }
 
 int LocalImageSource::OpenFile(const std::string& name) const {
@@ -1318,8 +1337,6 @@
     std::string fname;
     dump_info();
 
-    fb_query_save("product", cur_product, sizeof(cur_product));
-
     FlashAllTool tool(LocalImageSource(), slot_override, skip_secondary, wipe);
     tool.Flash();
 }
@@ -1473,8 +1490,6 @@
     bool wants_set_active = false;
     bool skip_secondary = false;
     bool set_fbe_marker = false;
-    void *data;
-    int64_t sz;
     int longindex;
     std::string slot_override;
     std::string next_active;
@@ -1678,10 +1693,12 @@
             do_for_partitions(partition.c_str(), slot_override, format, true);
         } else if (command == "signature") {
             std::string filename = next_arg(&args);
-            data = load_file(filename.c_str(), &sz);
-            if (data == nullptr) die("could not load '%s': %s", filename.c_str(), strerror(errno));
-            if (sz != 256) die("signature must be 256 bytes (got %" PRId64 ")", sz);
-            fb_download("signature", data, sz);
+            std::vector<char> data;
+            if (!ReadFileToVector(filename, &data)) {
+                die("could not load '%s': %s", filename.c_str(), strerror(errno));
+            }
+            if (data.size() != 256) die("signature must be 256 bytes (got %zu)", data.size());
+            fb_download("signature", data);
             fb_command("signature", "installing signature");
         } else if (command == "reboot") {
             wants_reboot = true;
@@ -1718,8 +1735,8 @@
             std::string second_stage;
             if (!args.empty()) second_stage = next_arg(&args);
 
-            data = load_bootable_image(kernel, ramdisk, second_stage, &sz);
-            fb_download("boot.img", data, sz);
+            auto data = LoadBootableImage(kernel, ramdisk, second_stage);
+            fb_download("boot.img", data);
             fb_command("boot", "booting");
         } else if (command == "flash") {
             std::string pname = next_arg(&args);
@@ -1744,9 +1761,9 @@
             std::string second_stage;
             if (!args.empty()) second_stage = next_arg(&args);
 
-            data = load_bootable_image(kernel, ramdisk, second_stage, &sz);
-            auto flashraw = [&](const std::string& partition) {
-                fb_flash(partition, data, sz);
+            auto data = LoadBootableImage(kernel, ramdisk, second_stage);
+            auto flashraw = [&data](const std::string& partition) {
+                fb_flash(partition, data);
             };
             do_for_partitions(partition, slot_override, flashraw, true);
         } else if (command == "flashall") {
diff --git a/fastboot/fastboot_driver.cpp b/fastboot/fastboot_driver.cpp
index 4a14131..72ba619 100644
--- a/fastboot/fastboot_driver.cpp
+++ b/fastboot/fastboot_driver.cpp
@@ -147,28 +147,6 @@
     return SUCCESS;
 }
 
-RetCode FastBootDriver::Require(const std::string& var, const std::vector<std::string>& allowed,
-                                bool* reqmet, bool invert) {
-    *reqmet = invert;
-    RetCode ret;
-    std::string response;
-    if ((ret = GetVar(var, &response))) {
-        return ret;
-    }
-
-    // Now check if we have a match
-    for (const auto s : allowed) {
-        // If it ends in *, and starting substring match
-        if (response == s || (s.length() && s.back() == '*' &&
-                              !response.compare(0, s.length() - 1, s, 0, s.length() - 1))) {
-            *reqmet = !invert;
-            break;
-        }
-    }
-
-    return SUCCESS;
-}
-
 RetCode FastBootDriver::Download(int fd, size_t size, std::string* response,
                                  std::vector<std::string>* info) {
     RetCode ret;
@@ -194,24 +172,19 @@
 
 RetCode FastBootDriver::Download(const std::vector<char>& buf, std::string* response,
                                  std::vector<std::string>* info) {
-    return Download(buf.data(), buf.size(), response, info);
-}
-
-RetCode FastBootDriver::Download(const char* buf, uint32_t size, std::string* response,
-                                 std::vector<std::string>* info) {
     RetCode ret;
     error_ = "";
-    if ((size == 0 || size > MAX_DOWNLOAD_SIZE) && !disable_checks_) {
+    if ((buf.size() == 0 || buf.size() > MAX_DOWNLOAD_SIZE) && !disable_checks_) {
         error_ = "Buffer is too large or 0 bytes";
         return BAD_ARG;
     }
 
-    if ((ret = DownloadCommand(size, response, info))) {
+    if ((ret = DownloadCommand(buf.size(), response, info))) {
         return ret;
     }
 
     // Write the buffer
-    if ((ret = SendBuffer(buf, size))) {
+    if ((ret = SendBuffer(buf))) {
         return ret;
     }
 
diff --git a/fastboot/fastboot_driver.h b/fastboot/fastboot_driver.h
index 4d85ba0..2d45085 100644
--- a/fastboot/fastboot_driver.h
+++ b/fastboot/fastboot_driver.h
@@ -74,9 +74,6 @@
                      std::vector<std::string>* info = nullptr);
     RetCode Download(const std::vector<char>& buf, std::string* response = nullptr,
                      std::vector<std::string>* info = nullptr);
-    // This will be removed after fastboot is modified to use a vector
-    RetCode Download(const char* buf, uint32_t size, std::string* response = nullptr,
-                     std::vector<std::string>* info = nullptr);
     RetCode Download(sparse_file* s, bool use_crc = false, std::string* response = nullptr,
                      std::vector<std::string>* info = nullptr);
     RetCode Erase(const std::string& part, std::string* response = nullptr,
diff --git a/fastboot/fastboot_test.cpp b/fastboot/fastboot_test.cpp
index 43201fa..e0bbd56 100644
--- a/fastboot/fastboot_test.cpp
+++ b/fastboot/fastboot_test.cpp
@@ -59,3 +59,145 @@
     EXPECT_DEATH(fb.ParseOsVersion(&hdr, "1.128.3"), "bad OS version");
     EXPECT_DEATH(fb.ParseOsVersion(&hdr, "1.2.128"), "bad OS version");
 }
+
+extern bool ParseRequirementLine(const std::string& line, std::string* name, std::string* product,
+                                 bool* invert, std::vector<std::string>* options);
+
+static void ParseRequirementLineTest(const std::string& line, const std::string& expected_name,
+                                     const std::string& expected_product, bool expected_invert,
+                                     const std::vector<std::string>& expected_options) {
+    std::string name;
+    std::string product;
+    bool invert;
+    std::vector<std::string> options;
+
+    EXPECT_TRUE(ParseRequirementLine(line, &name, &product, &invert, &options)) << line;
+
+    EXPECT_EQ(expected_name, name) << line;
+    EXPECT_EQ(expected_product, product) << line;
+    EXPECT_EQ(expected_invert, invert) << line;
+    EXPECT_EQ(expected_options, options) << line;
+}
+
+TEST(FastBoot, ParseRequirementLineSuccesses) {
+    // Examples provided in the code + slight variations.
+    ParseRequirementLineTest("require product=alpha", "product", "", false, {"alpha"});
+    ParseRequirementLineTest("require product=alpha|beta|gamma", "product", "", false,
+                             {"alpha", "beta", "gamma"});
+    ParseRequirementLineTest("require version-bootloader=1234", "version-bootloader", "", false,
+                             {"1234"});
+    ParseRequirementLineTest("require-for-product:gamma version-bootloader=istanbul",
+                             "version-bootloader", "gamma", false, {"istanbul"});
+    ParseRequirementLineTest("require-for-product:gamma version-bootloader=istanbul|constantinople",
+                             "version-bootloader", "gamma", false, {"istanbul", "constantinople"});
+    ParseRequirementLineTest("require partition-exists=vendor", "partition-exists", "", false,
+                             {"vendor"});
+    ParseRequirementLineTest("reject product=alpha", "product", "", true, {"alpha"});
+    ParseRequirementLineTest("reject product=alpha|beta|gamma", "product", "", true,
+                             {"alpha", "beta", "gamma"});
+
+    // Without any prefix, assume 'require'
+    ParseRequirementLineTest("product=alpha|beta|gamma", "product", "", false,
+                             {"alpha", "beta", "gamma"});
+    // Including if the variable name is otherwise a prefix keyword
+    ParseRequirementLineTest("require = alpha", "require", "", false, {"alpha"});
+    ParseRequirementLineTest("reject = alpha", "reject", "", false, {"alpha"});
+    ParseRequirementLineTest("require-for-product:gamma = alpha", "require-for-product:gamma", "",
+                             false, {"alpha"});
+
+    // Extra spaces are allowed.
+    ParseRequirementLineTest("require    product=alpha|beta|gamma", "product", "", false,
+                             {"alpha", "beta", "gamma"});
+    ParseRequirementLineTest("require product    =alpha|beta|gamma", "product", "", false,
+                             {"alpha", "beta", "gamma"});
+    ParseRequirementLineTest("require product=   alpha|beta|gamma", "product", "", false,
+                             {"alpha", "beta", "gamma"});
+    ParseRequirementLineTest("require product   =   alpha|beta|gamma", "product", "", false,
+                             {"alpha", "beta", "gamma"});
+    ParseRequirementLineTest("require product=alpha  |beta|gamma", "product", "", false,
+                             {"alpha", "beta", "gamma"});
+    ParseRequirementLineTest("require product=alpha|  beta|gamma", "product", "", false,
+                             {"alpha", "beta", "gamma"});
+    ParseRequirementLineTest("require product=alpha  |  beta|gamma", "product", "", false,
+                             {"alpha", "beta", "gamma"});
+    ParseRequirementLineTest("require product=alpha|beta|gamma   ", "product", "", false,
+                             {"alpha", "beta", "gamma"});
+    ParseRequirementLineTest("product  =  alpha  |  beta  |  gamma   ", "product", "", false,
+                             {"alpha", "beta", "gamma"});
+    ParseRequirementLineTest("require-for-product:  gamma version-bootloader=istanbul",
+                             "version-bootloader", "gamma", false, {"istanbul"});
+
+    // Extraneous ending | is okay, implies accepting an empty string.
+    ParseRequirementLineTest("require product=alpha|", "product", "", false, {"alpha", ""});
+    ParseRequirementLineTest("require product=alpha|beta|gamma|", "product", "", false,
+                             {"alpha", "beta", "gamma", ""});
+
+    // Accept empty options, double ||, etc, implies accepting an empty string.
+    ParseRequirementLineTest("require product=alpha||beta|   |gamma", "product", "", false,
+                             {"alpha", "", "beta", "", "gamma"});
+    ParseRequirementLineTest("require product=alpha||beta|gamma", "product", "", false,
+                             {"alpha", "", "beta", "gamma"});
+    ParseRequirementLineTest("require product=alpha|beta|   |gamma", "product", "", false,
+                             {"alpha", "beta", "", "gamma"});
+    ParseRequirementLineTest("require product=alpha||", "product", "", false, {"alpha", "", ""});
+    ParseRequirementLineTest("require product=alpha|| ", "product", "", false, {"alpha", "", ""});
+    ParseRequirementLineTest("require product=alpha| ", "product", "", false, {"alpha", ""});
+    ParseRequirementLineTest("require product=alpha|beta| ", "product", "", false,
+                             {"alpha", "beta", ""});
+
+    // No option string is also treating as accepting an empty string.
+    ParseRequirementLineTest("require =", "require", "", false, {""});
+    ParseRequirementLineTest("require = |", "require", "", false, {"", ""});
+    ParseRequirementLineTest("reject =", "reject", "", false, {""});
+    ParseRequirementLineTest("reject = |", "reject", "", false, {"", ""});
+    ParseRequirementLineTest("require-for-product: =", "require-for-product:", "", false, {""});
+    ParseRequirementLineTest("require-for-product: = | ", "require-for-product:", "", false,
+                             {"", ""});
+    ParseRequirementLineTest("require product=", "product", "", false, {""});
+    ParseRequirementLineTest("require product = ", "product", "", false, {""});
+    ParseRequirementLineTest("require product = | ", "product", "", false, {"", ""});
+    ParseRequirementLineTest("reject product=", "product", "", true, {""});
+    ParseRequirementLineTest("reject product = ", "product", "", true, {""});
+    ParseRequirementLineTest("reject product = | ", "product", "", true, {"", ""});
+    ParseRequirementLineTest("require-for-product:gamma product=", "product", "gamma", false, {""});
+    ParseRequirementLineTest("require-for-product:gamma product = ", "product", "gamma", false,
+                             {""});
+    ParseRequirementLineTest("require-for-product:gamma product = |", "product", "gamma", false,
+                             {"", ""});
+
+    // Check for board -> product substitution.
+    ParseRequirementLineTest("require board=alpha", "product", "", false, {"alpha"});
+    ParseRequirementLineTest("board=alpha", "product", "", false, {"alpha"});
+}
+
+static void ParseRequirementLineTestMalformed(const std::string& line) {
+    std::string name;
+    std::string product;
+    bool invert;
+    std::vector<std::string> options;
+
+    EXPECT_FALSE(ParseRequirementLine(line, &name, &product, &invert, &options)) << line;
+}
+
+TEST(FastBoot, ParseRequirementLineMalformed) {
+    ParseRequirementLineTestMalformed("nothing");
+    ParseRequirementLineTestMalformed("");
+    ParseRequirementLineTestMalformed("=");
+    ParseRequirementLineTestMalformed("|");
+
+    ParseRequirementLineTestMalformed("require");
+    ParseRequirementLineTestMalformed("require ");
+    ParseRequirementLineTestMalformed("reject");
+    ParseRequirementLineTestMalformed("reject ");
+    ParseRequirementLineTestMalformed("require-for-product:");
+    ParseRequirementLineTestMalformed("require-for-product: ");
+
+    ParseRequirementLineTestMalformed("require product");
+    ParseRequirementLineTestMalformed("reject product");
+
+    ParseRequirementLineTestMalformed("require-for-product:gamma");
+    ParseRequirementLineTestMalformed("require-for-product:gamma product");
+
+    // No spaces allowed before between require-for-product and :.
+    ParseRequirementLineTestMalformed("require-for-product :");
+}
diff --git a/fastboot/util.cpp b/fastboot/util.cpp
index 125182d..7d15047 100644
--- a/fastboot/util.cpp
+++ b/fastboot/util.cpp
@@ -74,9 +74,3 @@
     static constexpr char kStatusFormat[] = "%-50s ";
     fprintf(stderr, kStatusFormat, message.c_str());
 }
-
-char* xstrdup(const char* s) {
-    char* result = strdup(s);
-    if (!result) die("out of memory");
-    return result;
-}
diff --git a/fastboot/util.h b/fastboot/util.h
index 20be461..533d2c7 100644
--- a/fastboot/util.h
+++ b/fastboot/util.h
@@ -9,7 +9,6 @@
 
 /* util stuff */
 double now();
-char* xstrdup(const char*);
 void set_verbose();
 
 void Status(const std::string& message);
diff --git a/init/reboot.cpp b/init/reboot.cpp
index 2f88121..b84bfd3 100644
--- a/init/reboot.cpp
+++ b/init/reboot.cpp
@@ -464,6 +464,12 @@
         cmd = ANDROID_RB_RESTART2;
         if (cmd_params.size() >= 2) {
             reboot_target = cmd_params[1];
+            // adb reboot fastboot should boot into bootloader for devices not
+            // supporting logical partitions.
+            if (reboot_target == "fastboot" &&
+                !android::base::GetBoolProperty("ro.boot.logical_partitions", false)) {
+                reboot_target = "bootloader";
+            }
             // When rebooting to the bootloader notify the bootloader writing
             // also the BCB.
             if (reboot_target == "bootloader") {