Merge "Include sys/select.h for fd_set"
diff --git a/fastboot/README.md b/fastboot/README.md
index c224448..d3b6c1a 100644
--- a/fastboot/README.md
+++ b/fastboot/README.md
@@ -27,16 +27,16 @@
 1. Host sends a command, which is an ascii string in a single
    packet no greater than 64 bytes.
 
-2. Client response with a single packet no greater than 64 bytes.
+2. Client response with a single packet no greater than 256 bytes.
    The first four bytes of the response are "OKAY", "FAIL", "DATA",
    or "INFO".  Additional bytes may contain an (ascii) informative
    message.
 
-   a. INFO -> the remaining 60 bytes are an informative message
+   a. INFO -> the remaining 252 bytes are an informative message
       (providing progress or diagnostic messages).  They should
       be displayed and then step #2 repeats
 
-   b. FAIL -> the requested command failed.  The remaining 60 bytes
+   b. FAIL -> the requested command failed.  The remaining 252 bytes
       of the response (if present) provide a textual failure message
       to present to the user.  Stop.
 
@@ -53,13 +53,13 @@
    until the client has sent or received the number of bytes indicated
    in the "DATA" response above.
 
-4. Client responds with a single packet no greater than 64 bytes.
+4. Client responds with a single packet no greater than 256 bytes.
    The first four bytes of the response are "OKAY", "FAIL", or "INFO".
    Similar to #2:
 
-   a. INFO -> display the remaining 60 bytes and return to #4
+   a. INFO -> display the remaining 252 bytes and return to #4
 
-   b. FAIL -> display the remaining 60 bytes (if present) as a failure
+   b. FAIL -> display the remaining 252 bytes (if present) as a failure
       reason and consider the command failed.  Stop.
 
    c. OKAY -> success.  Go to #5
diff --git a/fs_mgr/Android.bp b/fs_mgr/Android.bp
index d4fc2b9..6170aee 100644
--- a/fs_mgr/Android.bp
+++ b/fs_mgr/Android.bp
@@ -65,6 +65,7 @@
         "-D_FILE_OFFSET_BITS=64",
     ],
     srcs: [
+        "blockdev.cpp",
         "file_wait.cpp",
         "fs_mgr.cpp",
         "fs_mgr_format.cpp",
diff --git a/fs_mgr/blockdev.cpp b/fs_mgr/blockdev.cpp
new file mode 100644
index 0000000..14b217c
--- /dev/null
+++ b/fs_mgr/blockdev.cpp
@@ -0,0 +1,159 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
+
+#include <dirent.h>
+#include <libdm/dm.h>
+#include <sys/stat.h>
+#include <sys/sysmacros.h>
+#include <sys/types.h>
+#include "blockdev.h"
+
+using android::base::Basename;
+using android::base::ErrnoError;
+using android::base::Error;
+using android::base::Result;
+using android::base::ResultError;
+using android::base::StartsWith;
+using android::base::StringPrintf;
+using android::base::unique_fd;
+using android::dm::DeviceMapper;
+
+// Return the parent device of a partition. Converts e.g. "sda26" into "sda".
+static std::string PartitionParent(const std::string& blockdev) {
+    if (blockdev.find('/') != std::string::npos) {
+        LOG(ERROR) << __func__ << ": invalid argument " << blockdev;
+        return blockdev;
+    }
+    auto dir = std::unique_ptr<DIR, decltype(&closedir)>{opendir("/sys/class/block"), closedir};
+    if (!dir) {
+        return blockdev;
+    }
+    for (struct dirent* ent = readdir(dir.get()); ent; ent = readdir(dir.get())) {
+        if (ent->d_name[0] == '.') {
+            continue;
+        }
+        std::string path = StringPrintf("/sys/class/block/%s/%s", ent->d_name, blockdev.c_str());
+        struct stat statbuf;
+        if (stat(path.c_str(), &statbuf) >= 0) {
+            return ent->d_name;
+        }
+    }
+    return blockdev;
+}
+
+// Convert a major:minor pair into a block device name.
+static std::string BlockdevName(dev_t dev) {
+    auto dir = std::unique_ptr<DIR, decltype(&closedir)>{opendir("/dev/block"), closedir};
+    if (!dir) {
+        return {};
+    }
+    for (struct dirent* ent = readdir(dir.get()); ent; ent = readdir(dir.get())) {
+        if (ent->d_name[0] == '.') {
+            continue;
+        }
+        const std::string path = std::string("/dev/block/") + ent->d_name;
+        struct stat statbuf;
+        if (stat(path.c_str(), &statbuf) >= 0 && dev == statbuf.st_rdev) {
+            return ent->d_name;
+        }
+    }
+    return {};
+}
+
+// Trim whitespace from the end of a string.
+static void rtrim(std::string& s) {
+    s.erase(s.find_last_not_of('\n') + 1, s.length());
+}
+
+// For file `file_path`, retrieve the block device backing the filesystem on
+// which the file exists and return the queue depth of the block device.
+static Result<uint32_t> BlockDeviceQueueDepth(const std::string& file_path) {
+    struct stat statbuf;
+    int res = stat(file_path.c_str(), &statbuf);
+    if (res < 0) {
+        return ErrnoError() << "stat(" << file_path << ")";
+    }
+    std::string blockdev = "/dev/block/" + BlockdevName(statbuf.st_dev);
+    LOG(DEBUG) << __func__ << ": " << file_path << " -> " << blockdev;
+    if (blockdev.empty()) {
+        const std::string err_msg =
+                StringPrintf("Failed to convert %u:%u (path %s)", major(statbuf.st_dev),
+                             minor(statbuf.st_dev), file_path.c_str());
+        return ResultError(err_msg, 0);
+    }
+    auto& dm = DeviceMapper::Instance();
+    for (;;) {
+        std::optional<std::string> child = dm.GetParentBlockDeviceByPath(blockdev);
+        if (!child) {
+            break;
+        }
+        LOG(DEBUG) << __func__ << ": " << blockdev << " -> " << *child;
+        blockdev = *child;
+    }
+    std::optional<std::string> maybe_blockdev = android::dm::ExtractBlockDeviceName(blockdev);
+    if (!maybe_blockdev) {
+        return ResultError("Failed to remove /dev/block/ prefix from " + blockdev, 0);
+    }
+    blockdev = PartitionParent(*maybe_blockdev);
+    LOG(DEBUG) << __func__ << ": "
+               << "Partition parent: " << blockdev;
+    const std::string nr_tags_path =
+            StringPrintf("/sys/class/block/%s/mq/0/nr_tags", blockdev.c_str());
+    std::string nr_tags;
+    if (!android::base::ReadFileToString(nr_tags_path, &nr_tags)) {
+        return ResultError("Failed to read " + nr_tags_path, 0);
+    }
+    rtrim(nr_tags);
+    LOG(DEBUG) << __func__ << ": " << file_path << " is backed by /dev/" << blockdev
+               << " and that block device supports queue depth " << nr_tags;
+    return strtol(nr_tags.c_str(), NULL, 0);
+}
+
+// Set 'nr_requests' of `loop_device_path` to the queue depth of the block
+// device backing `file_path`.
+Result<void> ConfigureQueueDepth(const std::string& loop_device_path,
+                                 const std::string& file_path) {
+    if (!StartsWith(loop_device_path, "/dev/")) {
+        return Error() << "Invalid argument " << loop_device_path;
+    }
+
+    const std::string loop_device_name = Basename(loop_device_path);
+
+    const Result<uint32_t> qd = BlockDeviceQueueDepth(file_path);
+    if (!qd.ok()) {
+        LOG(DEBUG) << __func__ << ": "
+                   << "BlockDeviceQueueDepth() returned " << qd.error();
+        return ResultError(qd.error());
+    }
+    const std::string nr_requests = StringPrintf("%u", *qd);
+    const std::string sysfs_path =
+            StringPrintf("/sys/class/block/%s/queue/nr_requests", loop_device_name.c_str());
+    unique_fd sysfs_fd(open(sysfs_path.c_str(), O_RDWR | O_CLOEXEC));
+    if (sysfs_fd == -1) {
+        return ErrnoError() << "Failed to open " << sysfs_path;
+    }
+
+    const int res = write(sysfs_fd.get(), nr_requests.data(), nr_requests.length());
+    if (res < 0) {
+        return ErrnoError() << "Failed to write to " << sysfs_path;
+    }
+    return {};
+}
diff --git a/fs_mgr/blockdev.h b/fs_mgr/blockdev.h
new file mode 100644
index 0000000..2c0d68a
--- /dev/null
+++ b/fs_mgr/blockdev.h
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <android-base/result.h>
+#include <string>
+
+android::base::Result<void> ConfigureQueueDepth(const std::string& loop_device_path,
+                                                const std::string& file_path);
diff --git a/fs_mgr/fs_mgr.cpp b/fs_mgr/fs_mgr.cpp
index ec66144..5f15aad 100644
--- a/fs_mgr/fs_mgr.cpp
+++ b/fs_mgr/fs_mgr.cpp
@@ -72,6 +72,7 @@
 #include <log/log_properties.h>
 #include <logwrap/logwrap.h>
 
+#include "blockdev.h"
 #include "fs_mgr_priv.h"
 
 #define KEY_LOC_PROP   "ro.crypto.keyfile.userdata"
@@ -2110,6 +2111,8 @@
 
     ConfigureIoScheduler(loop_device);
 
+    ConfigureQueueDepth(loop_device, "/");
+
     // set block size & direct IO
     unique_fd loop_fd(TEMP_FAILURE_RETRY(open(loop_device.c_str(), O_RDWR | O_CLOEXEC)));
     if (loop_fd.get() == -1) {
diff --git a/fs_mgr/fs_mgr_roots.cpp b/fs_mgr/fs_mgr_roots.cpp
index fdaffbe..d275320 100644
--- a/fs_mgr/fs_mgr_roots.cpp
+++ b/fs_mgr/fs_mgr_roots.cpp
@@ -14,6 +14,7 @@
  * limitations under the License.
  */
 
+#include "android-base/file.h"
 #include "fs_mgr/roots.h"
 
 #include <sys/mount.h>
@@ -39,18 +40,26 @@
     while (true) {
         auto entry = GetEntryForMountPoint(fstab, str);
         if (entry != nullptr) return entry;
-        if (str == "/") break;
-        auto slash = str.find_last_of('/');
-        if (slash == std::string::npos) break;
-        if (slash == 0) {
-            str = "/";
-        } else {
-            str = str.substr(0, slash);
-        }
+        str = android::base::Dirname(str);
+        if (!str.compare(".") || !str.compare("/")) break;
     }
     return nullptr;
 }
 
+std::vector<FstabEntry*> GetEntriesForPath(Fstab* fstab, const std::string& path) {
+    std::vector<FstabEntry*> entries;
+    if (path.empty()) return entries;
+
+    std::string str(path);
+    while (true) {
+        entries = GetEntriesForMountPoint(fstab, str);
+        if (!entries.empty()) return entries;
+        str = android::base::Dirname(str);
+        if (!str.compare(".") || !str.compare("/")) break;
+    }
+    return entries;
+}
+
 enum class MountState {
     ERROR = -1,
     NOT_MOUNTED = 0,
@@ -71,12 +80,7 @@
     return MountState::NOT_MOUNTED;
 }
 
-bool EnsurePathMounted(Fstab* fstab, const std::string& path, const std::string& mount_pt) {
-    auto rec = GetEntryForPath(fstab, path);
-    if (rec == nullptr) {
-        LERROR << "unknown volume for path [" << path << "]";
-        return false;
-    }
+bool TryPathMount(FstabEntry* rec, const std::string& mount_pt) {
     if (rec->fs_type == "ramdisk") {
         // The ramdisk is always mounted.
         return true;
@@ -136,6 +140,21 @@
     return true;
 }
 
+bool EnsurePathMounted(Fstab* fstab, const std::string& path, const std::string& mount_point) {
+    auto entries = GetEntriesForPath(fstab, path);
+    if (entries.empty()) {
+        LERROR << "unknown volume for path [" << path << "]";
+        return false;
+    }
+
+    for (auto entry : entries) {
+        if (TryPathMount(entry, mount_point)) return true;
+    }
+
+    LERROR << "Failed to mount for path [" << path << "]";
+    return false;
+}
+
 bool EnsurePathUnmounted(Fstab* fstab, const std::string& path) {
     auto rec = GetEntryForPath(fstab, path);
     if (rec == nullptr) {
diff --git a/fs_mgr/libdm/dm.cpp b/fs_mgr/libdm/dm.cpp
index 8e3d5a5..4034e30 100644
--- a/fs_mgr/libdm/dm.cpp
+++ b/fs_mgr/libdm/dm.cpp
@@ -578,34 +578,30 @@
     return std::string{spec.target_type, sizeof(spec.target_type)};
 }
 
-static bool ExtractBlockDeviceName(const std::string& path, std::string* name) {
+std::optional<std::string> ExtractBlockDeviceName(const std::string& path) {
     static constexpr std::string_view kDevBlockPrefix("/dev/block/");
     if (android::base::StartsWith(path, kDevBlockPrefix)) {
-        *name = path.substr(kDevBlockPrefix.length());
-        return true;
+        return path.substr(kDevBlockPrefix.length());
     }
-    return false;
+    return {};
 }
 
 bool DeviceMapper::IsDmBlockDevice(const std::string& path) {
-    std::string name;
-    if (!ExtractBlockDeviceName(path, &name)) {
-        return false;
-    }
-    return android::base::StartsWith(name, "dm-");
+    std::optional<std::string> name = ExtractBlockDeviceName(path);
+    return name && android::base::StartsWith(*name, "dm-");
 }
 
 std::optional<std::string> DeviceMapper::GetDmDeviceNameByPath(const std::string& path) {
-    std::string name;
-    if (!ExtractBlockDeviceName(path, &name)) {
+    std::optional<std::string> name = ExtractBlockDeviceName(path);
+    if (!name) {
         LOG(WARNING) << path << " is not a block device";
         return std::nullopt;
     }
-    if (!android::base::StartsWith(name, "dm-")) {
+    if (!android::base::StartsWith(*name, "dm-")) {
         LOG(WARNING) << path << " is not a dm device";
         return std::nullopt;
     }
-    std::string dm_name_file = "/sys/block/" + name + "/dm/name";
+    std::string dm_name_file = "/sys/block/" + *name + "/dm/name";
     std::string dm_name;
     if (!android::base::ReadFileToString(dm_name_file, &dm_name)) {
         PLOG(ERROR) << "Failed to read file " << dm_name_file;
@@ -616,16 +612,16 @@
 }
 
 std::optional<std::string> DeviceMapper::GetParentBlockDeviceByPath(const std::string& path) {
-    std::string name;
-    if (!ExtractBlockDeviceName(path, &name)) {
+    std::optional<std::string> name = ExtractBlockDeviceName(path);
+    if (!name) {
         LOG(WARNING) << path << " is not a block device";
         return std::nullopt;
     }
-    if (!android::base::StartsWith(name, "dm-")) {
+    if (!android::base::StartsWith(*name, "dm-")) {
         // Reached bottom of the device mapper stack.
         return std::nullopt;
     }
-    auto slaves_dir = "/sys/block/" + name + "/slaves";
+    auto slaves_dir = "/sys/block/" + *name + "/slaves";
     auto dir = std::unique_ptr<DIR, decltype(&closedir)>(opendir(slaves_dir.c_str()), closedir);
     if (dir == nullptr) {
         PLOG(ERROR) << "Failed to open: " << slaves_dir;
diff --git a/fs_mgr/libdm/include/libdm/dm.h b/fs_mgr/libdm/include/libdm/dm.h
index 31c3003..7e01b85 100644
--- a/fs_mgr/libdm/include/libdm/dm.h
+++ b/fs_mgr/libdm/include/libdm/dm.h
@@ -51,6 +51,10 @@
 
 static constexpr uint64_t kSectorSize = 512;
 
+// Returns `path` without /dev/block prefix if and only if `path` starts with
+// that prefix.
+std::optional<std::string> ExtractBlockDeviceName(const std::string& path);
+
 class DeviceMapper final {
   public:
     class DmBlockDevice final {
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/mock_snapshot_writer.h b/fs_mgr/libsnapshot/include/libsnapshot/mock_snapshot_writer.h
index 0457986..3655c01 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/mock_snapshot_writer.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/mock_snapshot_writer.h
@@ -36,6 +36,8 @@
 
     MOCK_METHOD(bool, EmitCopy, (uint64_t, uint64_t), (override));
     MOCK_METHOD(bool, EmitRawBlocks, (uint64_t, const void*, size_t), (override));
+    MOCK_METHOD(bool, EmitXorBlocks, (uint32_t, const void*, size_t, uint32_t, uint16_t),
+                (override));
     MOCK_METHOD(bool, EmitZeroBlocks, (uint64_t, uint64_t), (override));
     MOCK_METHOD(bool, EmitLabel, (uint64_t), (override));
     MOCK_METHOD(bool, EmitSequenceData, (size_t, const uint32_t*), (override));
diff --git a/init/reboot.cpp b/init/reboot.cpp
index 0e788e4..c5f1ee4 100644
--- a/init/reboot.cpp
+++ b/init/reboot.cpp
@@ -1035,6 +1035,20 @@
                         return;
                     }
                 }
+            } else if (reboot_target == "quiescent") {
+                bootloader_message boot = {};
+                if (std::string err; !read_bootloader_message(&boot, &err)) {
+                    LOG(ERROR) << "Failed to read bootloader message: " << err;
+                }
+                // Update the boot command field if it's empty, and preserve
+                // the other arguments in the bootloader message.
+                if (!CommandIsPresent(&boot)) {
+                    strlcpy(boot.command, "boot-quiescent", sizeof(boot.command));
+                    if (std::string err; !write_bootloader_message(boot, &err)) {
+                        LOG(ERROR) << "Failed to set bootloader message: " << err;
+                        return;
+                    }
+                }
             } else if (reboot_target == "sideload" || reboot_target == "sideload-auto-reboot" ||
                        reboot_target == "fastboot") {
                 std::string arg = reboot_target == "sideload-auto-reboot" ? "sideload_auto_reboot"