Merge "Add safety comments." into main
diff --git a/fastboot/fastboot.cpp b/fastboot/fastboot.cpp
index 2dae1ab..0bd07ed 100644
--- a/fastboot/fastboot.cpp
+++ b/fastboot/fastboot.cpp
@@ -634,6 +634,9 @@
             " --skip-reboot              Don't reboot device after flashing.\n"
             " --disable-verity           Sets disable-verity when flashing vbmeta.\n"
             " --disable-verification     Sets disable-verification when flashing vbmeta.\n"
+            " --disable-super-optimization\n"
+            "                            Disables optimizations on flashing super partition.\n"
+            " --disable-fastboot-info    Will collects tasks from image list rather than $OUT/fastboot-info.txt.\n"
             " --fs-options=OPTION[,OPTION]\n"
             "                            Enable filesystem features. OPTION supports casefold, projid, compress\n"
             // TODO: remove --unbuffered?
diff --git a/fs_mgr/fs_mgr_fstab.cpp b/fs_mgr/fs_mgr_fstab.cpp
index c85e831..ca27034 100644
--- a/fs_mgr/fs_mgr_fstab.cpp
+++ b/fs_mgr/fs_mgr_fstab.cpp
@@ -328,8 +328,7 @@
     // some recovery fstabs still contain the FDE options since they didn't do
     // anything in recovery mode anyway (except possibly to cause the
     // reservation of a crypto footer) and thus never got removed.
-    if (entry->fs_mgr_flags.crypt && !entry->fs_mgr_flags.vold_managed &&
-        access("/system/bin/recovery", F_OK) != 0) {
+    if (entry->fs_mgr_flags.crypt && !entry->fs_mgr_flags.vold_managed && !InRecovery()) {
         LERROR << "FDE is no longer supported; 'encryptable' can only be used for adoptable "
                   "storage";
         return false;
@@ -520,6 +519,9 @@
 // ramdisk's copy of the fstab had to be located in the root directory, but now
 // the system/etc directory is supported too and is the preferred location.
 std::string GetFstabPath() {
+    if (InRecovery()) {
+        return "/etc/recovery.fstab";
+    }
     for (const char* prop : {"fstab_suffix", "hardware", "hardware.platform"}) {
         std::string suffix;
 
@@ -835,15 +837,8 @@
     fstab->clear();
     ReadFstabFromDt(fstab, false /* verbose */);
 
-    std::string default_fstab_path;
-    // Use different fstab paths for normal boot and recovery boot, respectively
-    if ((access("/sbin/recovery", F_OK) == 0) || (access("/system/bin/recovery", F_OK) == 0)) {
-        default_fstab_path = "/etc/recovery.fstab";
-    } else {  // normal boot
-        default_fstab_path = GetFstabPath();
-    }
-
     Fstab default_fstab;
+    const std::string default_fstab_path = GetFstabPath();
     if (!default_fstab_path.empty() && ReadFstabFromFile(default_fstab_path, &default_fstab)) {
         for (auto&& entry : default_fstab) {
             fstab->emplace_back(std::move(entry));
@@ -936,6 +931,17 @@
     return base_device + "-verity";
 }
 
+bool InRecovery() {
+    // Check the existence of recovery binary instead of using the compile time
+    // __ANDROID_RECOVERY__ macro.
+    // If BOARD_USES_RECOVERY_AS_BOOT is true, both normal and recovery boot
+    // mode would use the same init binary, which would mean during normal boot
+    // the '/init' binary is actually a symlink pointing to
+    // init_second_stage.recovery, which would be compiled with
+    // __ANDROID_RECOVERY__ defined.
+    return access("/system/bin/recovery", F_OK) == 0 || access("/sbin/recovery", F_OK) == 0;
+}
+
 }  // namespace fs_mgr
 }  // namespace android
 
diff --git a/fs_mgr/fs_mgr_overlayfs_control.cpp b/fs_mgr/fs_mgr_overlayfs_control.cpp
index 69a2ac0..68576f2 100644
--- a/fs_mgr/fs_mgr_overlayfs_control.cpp
+++ b/fs_mgr/fs_mgr_overlayfs_control.cpp
@@ -18,17 +18,10 @@
 #include <errno.h>
 #include <fcntl.h>
 #include <linux/fs.h>
-#include <selinux/selinux.h>
-#include <stdio.h>
 #include <stdlib.h>
-#include <string.h>
-#include <sys/mount.h>
-#include <sys/param.h>
 #include <sys/stat.h>
 #include <sys/statvfs.h>
 #include <sys/types.h>
-#include <sys/utsname.h>
-#include <sys/vfs.h>
 #include <unistd.h>
 
 #include <algorithm>
@@ -38,13 +31,9 @@
 #include <vector>
 
 #include <android-base/file.h>
-#include <android-base/macros.h>
 #include <android-base/properties.h>
 #include <android-base/strings.h>
-#include <android-base/unique_fd.h>
-#include <ext4_utils/ext4_utils.h>
 #include <fs_mgr.h>
-#include <fs_mgr/file_wait.h>
 #include <fs_mgr_dm_linear.h>
 #include <fs_mgr_overlayfs.h>
 #include <fstab/fstab.h>
@@ -71,22 +60,25 @@
 
 constexpr char kDataScratchSizeMbProp[] = "fs_mgr.overlayfs.data_scratch_size_mb";
 
+constexpr char kPhysicalDevice[] = "/dev/block/by-name/";
+constexpr char kScratchImageMetadata[] = "/metadata/gsi/remount/lp_metadata";
+
+constexpr char kMkF2fs[] = "/system/bin/make_f2fs";
+constexpr char kMkExt4[] = "/system/bin/mke2fs";
+
 // Return true if everything is mounted, but before adb is started.  Right
 // after 'trigger load_persist_props_action' is done.
 static bool fs_mgr_boot_completed() {
     return android::base::GetBoolProperty("ro.persistent_properties.ready", false);
 }
 
-constexpr auto kPhysicalDevice = "/dev/block/by-name/";
-constexpr char kScratchImageMetadata[] = "/metadata/gsi/remount/lp_metadata";
-
 // Note: this is meant only for recovery/first-stage init.
 static bool ScratchIsOnData() {
     // The scratch partition of DSU is managed by gsid.
     if (fs_mgr_is_dsu_running()) {
         return false;
     }
-    return fs_mgr_access(kScratchImageMetadata);
+    return access(kScratchImageMetadata, F_OK) == 0;
 }
 
 static bool fs_mgr_rm_all(const std::string& path, bool* change = nullptr, int level = 0) {
@@ -131,7 +123,7 @@
 }
 
 std::string fs_mgr_overlayfs_setup_dir(const std::string& dir) {
-    auto top = dir + kOverlayTopDir;
+    auto top = dir + "/" + kOverlayTopDir;
 
     AutoSetFsCreateCon createcon(kOverlayfsFileContext);
     if (!createcon.Ok()) {
@@ -195,10 +187,6 @@
     return SlotNumberForSlotSuffix(fs_mgr_get_slot_suffix());
 }
 
-static std::string fs_mgr_overlayfs_super_device(uint32_t slot_number) {
-    return kPhysicalDevice + fs_mgr_get_super_partition_name(slot_number);
-}
-
 static bool fs_mgr_overlayfs_has_logical(const Fstab& fstab) {
     for (const auto& entry : fstab) {
         if (entry.fs_mgr_flags.logical) {
@@ -258,8 +246,8 @@
     }
 
     auto slot_number = fs_mgr_overlayfs_slot_number();
-    auto super_device = fs_mgr_overlayfs_super_device(slot_number);
-    if (!fs_mgr_rw_access(super_device)) {
+    const auto super_device = kPhysicalDevice + fs_mgr_get_super_partition_name();
+    if (access(super_device.c_str(), R_OK | W_OK)) {
         return OverlayfsTeardownResult::Ok;
     }
 
@@ -290,9 +278,9 @@
 
 bool fs_mgr_overlayfs_teardown_one(const std::string& overlay, const std::string& mount_point,
                                    bool* change, bool* should_destroy_scratch = nullptr) {
-    const auto top = overlay + kOverlayTopDir;
+    const auto top = overlay + "/" + kOverlayTopDir;
 
-    if (!fs_mgr_access(top)) {
+    if (access(top.c_str(), F_OK)) {
         if (should_destroy_scratch) *should_destroy_scratch = true;
         return true;
     }
@@ -300,7 +288,7 @@
     auto cleanup_all = mount_point.empty();
     const auto partition_name = android::base::Basename(mount_point);
     const auto oldpath = top + (cleanup_all ? "" : ("/" + partition_name));
-    const auto newpath = cleanup_all ? overlay + "/." + (kOverlayTopDir + 1) + ".teardown"
+    const auto newpath = cleanup_all ? overlay + "/." + kOverlayTopDir + ".teardown"
                                      : top + "/." + partition_name + ".teardown";
     auto ret = fs_mgr_rm_all(newpath);
     if (!rename(oldpath.c_str(), newpath.c_str())) {
@@ -346,72 +334,6 @@
     return ret;
 }
 
-// Mount kScratchMountPoint
-bool MountScratch(const std::string& device_path, bool readonly = false) {
-    if (readonly) {
-        if (!fs_mgr_access(device_path)) {
-            LOG(ERROR) << "Path does not exist: " << device_path;
-            return false;
-        }
-    } else if (!fs_mgr_rw_access(device_path)) {
-        LOG(ERROR) << "Path does not exist or is not readwrite: " << device_path;
-        return false;
-    }
-
-    std::vector<const char*> filesystem_candidates;
-    if (fs_mgr_is_f2fs(device_path)) {
-        filesystem_candidates = {"f2fs", "ext4"};
-    } else if (fs_mgr_is_ext4(device_path)) {
-        filesystem_candidates = {"ext4", "f2fs"};
-    } else {
-        LOG(ERROR) << "Scratch partition is not f2fs or ext4";
-        return false;
-    }
-
-    AutoSetFsCreateCon createcon(kOverlayfsFileContext);
-    if (!createcon.Ok()) {
-        return false;
-    }
-    if (mkdir(kScratchMountPoint, 0755) && (errno != EEXIST)) {
-        PERROR << "create " << kScratchMountPoint;
-        return false;
-    }
-
-    FstabEntry entry;
-    entry.blk_device = device_path;
-    entry.mount_point = kScratchMountPoint;
-    entry.flags = MS_NOATIME | MS_RDONLY;
-    if (!readonly) {
-        entry.flags &= ~MS_RDONLY;
-        entry.flags |= MS_SYNCHRONOUS;
-        entry.fs_options = "nodiscard";
-        fs_mgr_set_blk_ro(device_path, false);
-    }
-    // check_fs requires apex runtime library
-    if (fs_mgr_overlayfs_already_mounted("/data", false)) {
-        entry.fs_mgr_flags.check = true;
-    }
-    bool mounted = false;
-    for (auto fs_type : filesystem_candidates) {
-        entry.fs_type = fs_type;
-        if (fs_mgr_do_mount_one(entry) == 0) {
-            mounted = true;
-            break;
-        }
-    }
-    if (!createcon.Restore()) {
-        return false;
-    }
-    if (!mounted) {
-        rmdir(kScratchMountPoint);
-        return false;
-    }
-    return true;
-}
-
-const std::string kMkF2fs("/system/bin/make_f2fs");
-const std::string kMkExt4("/system/bin/mke2fs");
-
 // Note: The scratch partition of DSU is managed by gsid, and should be initialized during
 // first-stage-mount. Just check if the DM device for DSU scratch partition is created or not.
 static std::string GetDsuScratchDevice() {
@@ -456,14 +378,14 @@
     // thus do not rely on fsck to correct problems that could creep in.
     auto fs_type = ""s;
     auto command = ""s;
-    if (!access(kMkF2fs.c_str(), X_OK) && fs_mgr_filesystem_available("f2fs")) {
+    if (!access(kMkF2fs, X_OK) && fs_mgr_filesystem_available("f2fs")) {
         fs_type = "f2fs";
-        command = kMkF2fs + " -w ";
+        command = kMkF2fs + " -w "s;
         command += std::to_string(getpagesize());
         command += " -f -d1 -l" + android::base::Basename(kScratchMountPoint);
-    } else if (!access(kMkExt4.c_str(), X_OK) && fs_mgr_filesystem_available("ext4")) {
+    } else if (!access(kMkExt4, X_OK) && fs_mgr_filesystem_available("ext4")) {
         fs_type = "ext4";
-        command = kMkExt4 + " -F -b 4096 -t ext4 -m 0 -O has_journal -M " + kScratchMountPoint;
+        command = kMkExt4 + " -F -b 4096 -t ext4 -m 0 -O has_journal -M "s + kScratchMountPoint;
     } else {
         LERROR << "No supported mkfs command or filesystem driver available, supported filesystems "
                   "are: f2fs, ext4";
@@ -506,7 +428,7 @@
 
     auto partition_create = !*partition_exists;
     auto slot_number = fs_mgr_overlayfs_slot_number();
-    auto super_device = fs_mgr_overlayfs_super_device(slot_number);
+    const auto super_device = kPhysicalDevice + fs_mgr_get_super_partition_name();
     auto builder = MetadataBuilder::New(super_device, slot_number);
     if (!builder) {
         LERROR << "open " << super_device << " metadata";
@@ -646,8 +568,8 @@
 
 static bool CanUseSuperPartition(const Fstab& fstab) {
     auto slot_number = fs_mgr_overlayfs_slot_number();
-    auto super_device = fs_mgr_overlayfs_super_device(slot_number);
-    if (!fs_mgr_rw_access(super_device) || !fs_mgr_overlayfs_has_logical(fstab)) {
+    const auto super_device = kPhysicalDevice + fs_mgr_get_super_partition_name();
+    if (access(super_device.c_str(), R_OK | W_OK) || !fs_mgr_overlayfs_has_logical(fstab)) {
         return false;
     }
     auto metadata = ReadMetadata(super_device, slot_number);
@@ -697,8 +619,8 @@
     // If the partition exists, assume first that it can be mounted.
     if (partition_exists) {
         if (MountScratch(scratch_device)) {
-            if (fs_mgr_access(std::string(kScratchMountPoint) + kOverlayTopDir) ||
-                fs_mgr_filesystem_has_space(kScratchMountPoint)) {
+            const auto top = kScratchMountPoint + "/"s + kOverlayTopDir;
+            if (access(top.c_str(), F_OK) == 0 || fs_mgr_filesystem_has_space(kScratchMountPoint)) {
                 return true;
             }
             // declare it useless, no overrides and no free space
@@ -717,32 +639,6 @@
     return MountScratch(scratch_device);
 }
 
-// NOTE: OverlayfsSetupAllowed() must be "stricter" than OverlayfsTeardownAllowed().
-// Setup is allowed only if teardown is also allowed.
-bool OverlayfsSetupAllowed(bool verbose = false) {
-    if (!kAllowOverlayfs) {
-        if (verbose) {
-            LOG(ERROR) << "Overlayfs remounts can only be used in debuggable builds";
-        }
-        return false;
-    }
-    // Check mandatory kernel patches.
-    if (fs_mgr_overlayfs_valid() == OverlayfsValidResult::kNotSupported) {
-        if (verbose) {
-            LOG(ERROR) << "Kernel does not support overlayfs";
-        }
-        return false;
-    }
-    // in recovery or fastbootd, not allowed!
-    if (fs_mgr_in_recovery()) {
-        if (verbose) {
-            LOG(ERROR) << "Unsupported overlayfs setup from recovery";
-        }
-        return false;
-    }
-    return true;
-}
-
 constexpr bool OverlayfsTeardownAllowed() {
     // Never allow on non-debuggable build.
     return kAllowOverlayfs;
@@ -844,7 +740,7 @@
     if (!info.device.empty()) {
         return {std::move(info)};
     }
-    if (!fs_mgr_in_recovery()) {
+    if (!InRecovery()) {
         return {};
     }
 
@@ -867,8 +763,7 @@
     }
 
     // Avoid uart spam by first checking for a scratch partition.
-    auto metadata_slot = fs_mgr_overlayfs_slot_number();
-    auto super_device = fs_mgr_overlayfs_super_device(metadata_slot);
+    const auto super_device = kPhysicalDevice + fs_mgr_get_super_partition_name();
     auto metadata = ReadCurrentMetadata(super_device);
     if (!metadata) {
         return {};
@@ -1030,7 +925,7 @@
     if (!OverlayfsTeardownAllowed()) {
         return;
     }
-    if (!fs_mgr_in_recovery()) {
+    if (!InRecovery()) {
         LERROR << __FUNCTION__ << "(): must be called within recovery.";
         return;
     }
diff --git a/fs_mgr/fs_mgr_overlayfs_control.h b/fs_mgr/fs_mgr_overlayfs_control.h
index 50e83e8..b175101 100644
--- a/fs_mgr/fs_mgr_overlayfs_control.h
+++ b/fs_mgr/fs_mgr_overlayfs_control.h
@@ -14,8 +14,6 @@
 
 #pragma once
 
-#include <string>
-
 #include <fstab/fstab.h>
 
 // If "mount_point" is non-null, set up exactly one overlay.
diff --git a/fs_mgr/fs_mgr_overlayfs_mount.cpp b/fs_mgr/fs_mgr_overlayfs_mount.cpp
index c057c2b..37e3058 100644
--- a/fs_mgr/fs_mgr_overlayfs_mount.cpp
+++ b/fs_mgr/fs_mgr_overlayfs_mount.cpp
@@ -14,16 +14,12 @@
  * limitations under the License.
  */
 
-#include <dirent.h>
 #include <errno.h>
 #include <fcntl.h>
 #include <linux/fs.h>
 #include <selinux/selinux.h>
-#include <stdio.h>
 #include <stdlib.h>
-#include <string.h>
 #include <sys/mount.h>
-#include <sys/param.h>
 #include <sys/stat.h>
 #include <sys/statvfs.h>
 #include <sys/types.h>
@@ -33,7 +29,6 @@
 
 #include <algorithm>
 #include <memory>
-#include <optional>
 #include <string>
 #include <vector>
 
@@ -45,7 +40,6 @@
 #include <ext4_utils/ext4_utils.h>
 #include <fs_mgr.h>
 #include <fs_mgr/file_wait.h>
-#include <fs_mgr_dm_linear.h>
 #include <fs_mgr_overlayfs.h>
 #include <fstab/fstab.h>
 #include <libdm/dm.h>
@@ -62,39 +56,21 @@
 
 constexpr char kPreferCacheBackingStorageProp[] = "fs_mgr.overlayfs.prefer_cache_backing_storage";
 
-bool fs_mgr_access(const std::string& path) {
-    return access(path.c_str(), F_OK) == 0;
-}
+constexpr char kCacheMountPoint[] = "/cache";
+constexpr char kPhysicalDevice[] = "/dev/block/by-name/";
 
-const auto kLowerdirOption = "lowerdir=";
-const auto kUpperdirOption = "upperdir=";
-
-bool fs_mgr_in_recovery() {
-    // Check the existence of recovery binary instead of using the compile time
-    // __ANDROID_RECOVERY__ macro.
-    // If BOARD_USES_RECOVERY_AS_BOOT is true, both normal and recovery boot
-    // mode would use the same init binary, which would mean during normal boot
-    // the '/init' binary is actually a symlink pointing to
-    // init_second_stage.recovery, which would be compiled with
-    // __ANDROID_RECOVERY__ defined.
-    return fs_mgr_access("/system/bin/recovery");
-}
+constexpr char kLowerdirOption[] = "lowerdir=";
+constexpr char kUpperdirOption[] = "upperdir=";
 
 bool fs_mgr_is_dsu_running() {
     // Since android::gsi::CanBootIntoGsi() or android::gsi::MarkSystemAsGsi() is
     // never called in recovery, the return value of android::gsi::IsGsiRunning()
     // is not well-defined. In this case, just return false as being in recovery
     // implies not running a DSU system.
-    if (fs_mgr_in_recovery()) return false;
+    if (InRecovery()) return false;
     return android::gsi::IsGsiRunning();
 }
 
-const auto kCacheMountPoint = "/cache";
-
-static bool IsABDevice() {
-    return !android::base::GetProperty("ro.boot.slot_suffix", "").empty();
-}
-
 std::vector<const std::string> OverlayMountPoints() {
     // Never fallback to legacy cache mount point if within a DSU system,
     // because running a DSU system implies the device supports dynamic
@@ -105,7 +81,8 @@
 
     // For non-A/B devices prefer cache backing storage if
     // kPreferCacheBackingStorageProp property set.
-    if (!IsABDevice() && android::base::GetBoolProperty(kPreferCacheBackingStorageProp, false) &&
+    if (fs_mgr_get_slot_suffix().empty() &&
+        android::base::GetBoolProperty(kPreferCacheBackingStorageProp, false) &&
         android::base::GetIntProperty("ro.vendor.api_level", -1) < __ANDROID_API_T__) {
         return {kCacheMountPoint, kScratchMountPoint};
     }
@@ -118,11 +95,6 @@
     return !stat(path.c_str(), &st) && S_ISDIR(st.st_mode);
 }
 
-bool fs_mgr_rw_access(const std::string& path) {
-    if (path.empty()) return false;
-    return access(path.c_str(), R_OK | W_OK) == 0;
-}
-
 // At less than 1% or 8MB of free space return value of false,
 // means we will try to wrap with overlayfs.
 bool fs_mgr_filesystem_has_space(const std::string& mount_point) {
@@ -141,13 +113,11 @@
            (static_cast<uint64_t>(vst.f_bfree) * vst.f_frsize) >= kSizeThreshold;
 }
 
-const auto kPhysicalDevice = "/dev/block/by-name/";
-
 static bool fs_mgr_update_blk_device(FstabEntry* entry) {
     if (entry->fs_mgr_flags.logical) {
         fs_mgr_update_logical_partition(entry);
     }
-    if (fs_mgr_access(entry->blk_device)) {
+    if (access(entry->blk_device.c_str(), F_OK) == 0) {
         return true;
     }
     if (entry->blk_device != "/dev/root") {
@@ -155,10 +125,10 @@
     }
 
     // special case for system-as-root (taimen and others)
-    auto blk_device = std::string(kPhysicalDevice) + "system";
-    if (!fs_mgr_access(blk_device)) {
+    auto blk_device = kPhysicalDevice + "system"s;
+    if (access(blk_device.c_str(), F_OK)) {
         blk_device += fs_mgr_get_slot_suffix();
-        if (!fs_mgr_access(blk_device)) {
+        if (access(blk_device.c_str(), F_OK)) {
             return false;
         }
     }
@@ -237,12 +207,12 @@
     if (!fs_mgr_is_dir(mount_point)) return "";
     const auto base = android::base::Basename(mount_point) + "/";
     for (const auto& overlay_mount_point : OverlayMountPoints()) {
-        auto dir = overlay_mount_point + kOverlayTopDir + "/" + base;
+        auto dir = overlay_mount_point + "/" + kOverlayTopDir + "/" + base;
         auto upper = dir + kUpperName;
         if (!fs_mgr_is_dir(upper)) continue;
         auto work = dir + kWorkName;
         if (!fs_mgr_is_dir(work)) continue;
-        if (!fs_mgr_rw_access(work)) continue;
+        if (access(work.c_str(), R_OK | W_OK)) continue;
         return dir;
     }
     return "";
@@ -527,13 +497,13 @@
 }
 
 // Mount kScratchMountPoint
-static bool MountScratch(const std::string& device_path, bool readonly = false) {
+bool MountScratch(const std::string& device_path, bool readonly) {
     if (readonly) {
-        if (!fs_mgr_access(device_path)) {
+        if (access(device_path.c_str(), F_OK)) {
             LOG(ERROR) << "Path does not exist: " << device_path;
             return false;
         }
-    } else if (!fs_mgr_rw_access(device_path)) {
+    } else if (access(device_path.c_str(), R_OK | W_OK)) {
         LOG(ERROR) << "Path does not exist or is not readwrite: " << device_path;
         return false;
     }
@@ -589,9 +559,6 @@
     return true;
 }
 
-const std::string kMkF2fs("/system/bin/make_f2fs");
-const std::string kMkExt4("/system/bin/mke2fs");
-
 // Note: The scratch partition of DSU is managed by gsid, and should be initialized during
 // first-stage-mount. Just check if the DM device for DSU scratch partition is created or not.
 static std::string GetDsuScratchDevice() {
@@ -633,7 +600,7 @@
 
 // NOTE: OverlayfsSetupAllowed() must be "stricter" than OverlayfsTeardownAllowed().
 // Setup is allowed only if teardown is also allowed.
-bool OverlayfsSetupAllowed(bool verbose = false) {
+bool OverlayfsSetupAllowed(bool verbose) {
     if (!kAllowOverlayfs) {
         if (verbose) {
             LOG(ERROR) << "Overlayfs remounts can only be used in debuggable builds";
@@ -648,7 +615,7 @@
         return false;
     }
     // in recovery or fastbootd, not allowed!
-    if (fs_mgr_in_recovery()) {
+    if (InRecovery()) {
         if (verbose) {
             LOG(ERROR) << "Unsupported overlayfs setup from recovery";
         }
@@ -728,7 +695,7 @@
     // if verity is still disabled, i.e. no reboot occurred), and skips calling
     // fs_mgr_overlayfs_mount_all().
     auto scratch_device = GetBootScratchDevice();
-    if (!fs_mgr_rw_access(scratch_device)) {
+    if (access(scratch_device.c_str(), R_OK | W_OK)) {
         return;
     }
     if (!WaitForFile(scratch_device, 10s)) {
@@ -737,7 +704,8 @@
     if (!MountScratch(scratch_device, true /* readonly */)) {
         return;
     }
-    auto has_overlayfs_dir = fs_mgr_access(std::string(kScratchMountPoint) + kOverlayTopDir);
+    const auto top = kScratchMountPoint + "/"s + kOverlayTopDir;
+    const bool has_overlayfs_dir = access(top.c_str(), F_OK) == 0;
     fs_mgr_overlayfs_umount_scratch();
     if (has_overlayfs_dir) {
         MountScratch(scratch_device);
diff --git a/fs_mgr/fs_mgr_overlayfs_mount.h b/fs_mgr/fs_mgr_overlayfs_mount.h
index ae3ea84..f0afac1 100644
--- a/fs_mgr/fs_mgr_overlayfs_mount.h
+++ b/fs_mgr/fs_mgr_overlayfs_mount.h
@@ -20,9 +20,12 @@
 
 #include <fstab/fstab.h>
 
-bool fs_mgr_overlayfs_already_mounted(const std::string& mount_point, bool overlay_only = true);
-bool fs_mgr_wants_overlayfs(android::fs_mgr::FstabEntry* entry);
-android::fs_mgr::Fstab fs_mgr_overlayfs_candidate_list(const android::fs_mgr::Fstab& fstab);
+constexpr char kOverlayfsFileContext[] = "u:object_r:overlayfs_file:s0";
+
+constexpr char kScratchMountPoint[] = "/mnt/scratch";
+constexpr char kOverlayTopDir[] = "overlay";
+constexpr char kUpperName[] = "upper";
+constexpr char kWorkName[] = "work";
 
 #if ALLOW_ADBD_DISABLE_VERITY
 constexpr bool kAllowOverlayfs = true;
@@ -45,18 +48,13 @@
     bool restored_ = false;
 };
 
-constexpr auto kScratchMountPoint = "/mnt/scratch";
-constexpr char kOverlayfsFileContext[] = "u:object_r:overlayfs_file:s0";
-
-constexpr auto kUpperName = "upper";
-constexpr auto kWorkName = "work";
-constexpr auto kOverlayTopDir = "/overlay";
-
 bool fs_mgr_is_dsu_running();
-bool fs_mgr_in_recovery();
-bool fs_mgr_access(const std::string& path);
-bool fs_mgr_rw_access(const std::string& path);
 bool fs_mgr_filesystem_has_space(const std::string& mount_point);
 const std::string fs_mgr_mount_point(const std::string& mount_point);
+bool OverlayfsSetupAllowed(bool verbose = false);
+bool MountScratch(const std::string& device_path, bool readonly = false);
 bool fs_mgr_overlayfs_umount_scratch();
 std::vector<const std::string> OverlayMountPoints();
+bool fs_mgr_overlayfs_already_mounted(const std::string& mount_point, bool overlay_only = true);
+bool fs_mgr_wants_overlayfs(android::fs_mgr::FstabEntry* entry);
+android::fs_mgr::Fstab fs_mgr_overlayfs_candidate_list(const android::fs_mgr::Fstab& fstab);
diff --git a/fs_mgr/fs_mgr_priv.h b/fs_mgr/fs_mgr_priv.h
index 46cdb62..c3b18c8 100644
--- a/fs_mgr/fs_mgr_priv.h
+++ b/fs_mgr/fs_mgr_priv.h
@@ -97,8 +97,6 @@
 bool fs_mgr_is_ext4(const std::string& blk_device);
 bool fs_mgr_is_f2fs(const std::string& blk_device);
 
-bool fs_mgr_teardown_verity(android::fs_mgr::FstabEntry* fstab);
-
 bool fs_mgr_filesystem_available(const std::string& filesystem);
 std::string fs_mgr_get_context(const std::string& mount_point);
 
diff --git a/fs_mgr/include_fstab/fstab/fstab.h b/fs_mgr/include_fstab/fstab/fstab.h
index 9cb1546..80b45ba 100644
--- a/fs_mgr/include_fstab/fstab/fstab.h
+++ b/fs_mgr/include_fstab/fstab/fstab.h
@@ -131,5 +131,7 @@
 // expected name.
 std::string GetVerityDeviceName(const FstabEntry& entry);
 
+bool InRecovery();
+
 }  // namespace fs_mgr
 }  // namespace android
diff --git a/fs_mgr/libsnapshot/snapuserd/include/snapuserd/snapuserd_buffer.h b/fs_mgr/libsnapshot/snapuserd/include/snapuserd/snapuserd_buffer.h
index a6b6a7f..c5ca2b1 100644
--- a/fs_mgr/libsnapshot/snapuserd/include/snapuserd/snapuserd_buffer.h
+++ b/fs_mgr/libsnapshot/snapuserd/include/snapuserd/snapuserd_buffer.h
@@ -36,6 +36,21 @@
     struct dm_user_header* GetHeaderPtr();
     void ResetBufferOffset() { buffer_offset_ = 0; }
     void* GetPayloadBufPtr();
+    loff_t GetPayloadBytesWritten() { return buffer_offset_; }
+
+    // Same as calling GetPayloadBuffer and then UpdateBufferOffset.
+    //
+    // This is preferred over GetPayloadBuffer as it does not require a
+    // separate call to UpdateBufferOffset.
+    void* AcquireBuffer(size_t size) { return AcquireBuffer(size, size); }
+
+    // Same as AcquireBuffer, but separates the requested size from the buffer
+    // offset. This is useful for a situation where a full run of data will be
+    // read, but only a partial amount will be returned.
+    //
+    // If size != to_write, the excess bytes may be reallocated by the next
+    // call to AcquireBuffer.
+    void* AcquireBuffer(size_t size, size_t to_write);
 
   private:
     std::unique_ptr<uint8_t[]> buffer_;
@@ -43,19 +58,5 @@
     size_t buffer_size_;
 };
 
-class XorSink final {
-  public:
-    void Initialize(BufferSink* sink, size_t size);
-    void Reset();
-    void* GetBuffer(size_t requested, size_t* actual);
-    bool ReturnData(void* buffer, size_t len);
-
-  private:
-    BufferSink* bufsink_;
-    std::unique_ptr<uint8_t[]> buffer_;
-    size_t buffer_size_;
-    size_t returned_;
-};
-
 }  // namespace snapshot
 }  // namespace android
diff --git a/fs_mgr/libsnapshot/snapuserd/snapuserd_buffer.cpp b/fs_mgr/libsnapshot/snapuserd/snapuserd_buffer.cpp
index ab763ab..35065e6 100644
--- a/fs_mgr/libsnapshot/snapuserd/snapuserd_buffer.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/snapuserd_buffer.cpp
@@ -15,6 +15,8 @@
  */
 
 #include <snapuserd/snapuserd_buffer.h>
+
+#include <android-base/logging.h>
 #include <snapuserd/snapuserd_kernel.h>
 
 namespace android {
@@ -26,11 +28,23 @@
     buffer_ = std::make_unique<uint8_t[]>(size);
 }
 
-void* BufferSink::GetPayloadBuffer(size_t size) {
-    if ((buffer_size_ - buffer_offset_) < size) return nullptr;
+void* BufferSink::AcquireBuffer(size_t size, size_t to_write) {
+    CHECK(to_write <= size);
 
+    void* ptr = GetPayloadBuffer(size);
+    if (!ptr) {
+        return nullptr;
+    }
+    UpdateBufferOffset(to_write);
+    return ptr;
+}
+
+void* BufferSink::GetPayloadBuffer(size_t size) {
     char* buffer = reinterpret_cast<char*>(GetBufPtr());
     struct dm_user_message* msg = (struct dm_user_message*)(&(buffer[0]));
+    if ((buffer_size_ - buffer_offset_ - sizeof(msg->header)) < size) {
+        return nullptr;
+    }
     return (char*)msg->payload.buf + buffer_offset_;
 }
 
@@ -59,38 +73,5 @@
     return msg->payload.buf;
 }
 
-void XorSink::Initialize(BufferSink* sink, size_t size) {
-    bufsink_ = sink;
-    buffer_size_ = size;
-    returned_ = 0;
-    buffer_ = std::make_unique<uint8_t[]>(size);
-}
-
-void XorSink::Reset() {
-    returned_ = 0;
-}
-
-void* XorSink::GetBuffer(size_t requested, size_t* actual) {
-    if (requested > buffer_size_) {
-        *actual = buffer_size_;
-    } else {
-        *actual = requested;
-    }
-    return buffer_.get();
-}
-
-bool XorSink::ReturnData(void* buffer, size_t len) {
-    uint8_t* xor_data = reinterpret_cast<uint8_t*>(buffer);
-    uint8_t* buff = reinterpret_cast<uint8_t*>(bufsink_->GetPayloadBuffer(len + returned_));
-    if (buff == nullptr) {
-        return false;
-    }
-    for (size_t i = 0; i < len; i++) {
-        buff[returned_ + i] ^= xor_data[i];
-    }
-    returned_ += len;
-    return true;
-}
-
 }  // namespace snapshot
 }  // namespace android
diff --git a/fs_mgr/libsnapshot/snapuserd/user-space-merge/read_worker.cpp b/fs_mgr/libsnapshot/snapuserd/user-space-merge/read_worker.cpp
index dd2996b..7d2e3a6 100644
--- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/read_worker.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/read_worker.cpp
@@ -42,12 +42,7 @@
 // Start the replace operation. This will read the
 // internal COW format and if the block is compressed,
 // it will be de-compressed.
-bool ReadWorker::ProcessReplaceOp(const CowOperation* cow_op) {
-    void* buffer = bufsink_.GetPayloadBuffer(BLOCK_SZ);
-    if (!buffer) {
-        SNAP_LOG(ERROR) << "ProcessReplaceOp failed to allocate buffer";
-        return false;
-    }
+bool ReadWorker::ProcessReplaceOp(const CowOperation* cow_op, void* buffer) {
     if (!reader_->ReadData(cow_op, buffer, BLOCK_SZ)) {
         SNAP_LOG(ERROR) << "ProcessReplaceOp failed for block " << cow_op->new_block;
         return false;
@@ -55,12 +50,7 @@
     return true;
 }
 
-bool ReadWorker::ReadFromSourceDevice(const CowOperation* cow_op) {
-    void* buffer = bufsink_.GetPayloadBuffer(BLOCK_SZ);
-    if (buffer == nullptr) {
-        SNAP_LOG(ERROR) << "ReadFromBaseDevice: Failed to get payload buffer";
-        return false;
-    }
+bool ReadWorker::ReadFromSourceDevice(const CowOperation* cow_op, void* buffer) {
     uint64_t offset;
     if (!reader_->GetSourceOffset(cow_op, &offset)) {
         SNAP_LOG(ERROR) << "ReadFromSourceDevice: Failed to get source offset";
@@ -85,60 +75,43 @@
 
 // Start the copy operation. This will read the backing
 // block device which is represented by cow_op->source.
-bool ReadWorker::ProcessCopyOp(const CowOperation* cow_op) {
-    if (!ReadFromSourceDevice(cow_op)) {
+bool ReadWorker::ProcessCopyOp(const CowOperation* cow_op, void* buffer) {
+    if (!ReadFromSourceDevice(cow_op, buffer)) {
         return false;
     }
-
     return true;
 }
 
-bool ReadWorker::ProcessXorOp(const CowOperation* cow_op) {
-    if (!ReadFromSourceDevice(cow_op)) {
+bool ReadWorker::ProcessXorOp(const CowOperation* cow_op, void* buffer) {
+    if (!ReadFromSourceDevice(cow_op, buffer)) {
         return false;
     }
 
-    xorsink_.Reset();
-
-    size_t actual = 0;
-    void* buffer = xorsink_.GetBuffer(BLOCK_SZ, &actual);
-    if (!buffer || actual < BLOCK_SZ) {
-        SNAP_LOG(ERROR) << "ProcessXorOp failed to get buffer of " << BLOCK_SZ << " size, got "
-                        << actual;
-        return false;
+    if (xor_buffer_.empty()) {
+        xor_buffer_.resize(BLOCK_SZ);
     }
-    ssize_t size = reader_->ReadData(cow_op, buffer, BLOCK_SZ);
+    CHECK(xor_buffer_.size() == BLOCK_SZ);
+
+    ssize_t size = reader_->ReadData(cow_op, xor_buffer_.data(), xor_buffer_.size());
     if (size != BLOCK_SZ) {
         SNAP_LOG(ERROR) << "ProcessXorOp failed for block " << cow_op->new_block
                         << ", return value: " << size;
         return false;
     }
-    if (!xorsink_.ReturnData(buffer, size)) {
-        SNAP_LOG(ERROR) << "ProcessXorOp failed to return data";
-        return false;
+
+    auto xor_out = reinterpret_cast<uint8_t*>(buffer);
+    for (size_t i = 0; i < BLOCK_SZ; i++) {
+        xor_out[i] ^= xor_buffer_[i];
     }
     return true;
 }
 
-bool ReadWorker::ProcessZeroOp() {
-    // Zero out the entire block
-    void* buffer = bufsink_.GetPayloadBuffer(BLOCK_SZ);
-    if (buffer == nullptr) {
-        SNAP_LOG(ERROR) << "ProcessZeroOp: Failed to get payload buffer";
-        return false;
-    }
-
+bool ReadWorker::ProcessZeroOp(void* buffer) {
     memset(buffer, 0, BLOCK_SZ);
     return true;
 }
 
-bool ReadWorker::ProcessOrderedOp(const CowOperation* cow_op) {
-    void* buffer = bufsink_.GetPayloadBuffer(BLOCK_SZ);
-    if (buffer == nullptr) {
-        SNAP_LOG(ERROR) << "ProcessOrderedOp: Failed to get payload buffer";
-        return false;
-    }
-
+bool ReadWorker::ProcessOrderedOp(const CowOperation* cow_op, void* buffer) {
     MERGE_GROUP_STATE state = snapuserd_->ProcessMergingBlock(cow_op->new_block, buffer);
 
     switch (state) {
@@ -148,7 +121,7 @@
             SNAP_LOG(DEBUG) << "Merge-completed: Reading from base device sector: "
                             << (cow_op->new_block >> SECTOR_SHIFT)
                             << " Block-number: " << cow_op->new_block;
-            if (!ReadDataFromBaseDevice(ChunkToSector(cow_op->new_block), BLOCK_SZ)) {
+            if (!ReadDataFromBaseDevice(ChunkToSector(cow_op->new_block), buffer, BLOCK_SZ)) {
                 SNAP_LOG(ERROR) << "ReadDataFromBaseDevice at sector: "
                                 << (cow_op->new_block >> SECTOR_SHIFT) << " after merge-complete.";
                 return false;
@@ -158,9 +131,9 @@
         case MERGE_GROUP_STATE::GROUP_MERGE_PENDING: {
             bool ret;
             if (cow_op->type == kCowCopyOp) {
-                ret = ProcessCopyOp(cow_op);
+                ret = ProcessCopyOp(cow_op, buffer);
             } else {
-                ret = ProcessXorOp(cow_op);
+                ret = ProcessXorOp(cow_op, buffer);
             }
 
             // I/O is complete - decrement the refcount irrespective of the return
@@ -185,7 +158,7 @@
     return false;
 }
 
-bool ReadWorker::ProcessCowOp(const CowOperation* cow_op) {
+bool ReadWorker::ProcessCowOp(const CowOperation* cow_op, void* buffer) {
     if (cow_op == nullptr) {
         SNAP_LOG(ERROR) << "ProcessCowOp: Invalid cow_op";
         return false;
@@ -193,17 +166,17 @@
 
     switch (cow_op->type) {
         case kCowReplaceOp: {
-            return ProcessReplaceOp(cow_op);
+            return ProcessReplaceOp(cow_op, buffer);
         }
 
         case kCowZeroOp: {
-            return ProcessZeroOp();
+            return ProcessZeroOp(buffer);
         }
 
         case kCowCopyOp:
             [[fallthrough]];
         case kCowXorOp: {
-            return ProcessOrderedOp(cow_op);
+            return ProcessOrderedOp(cow_op, buffer);
         }
 
         default: {
@@ -229,8 +202,6 @@
         SNAP_PLOG(ERROR) << "Unable to open " << control_device_;
         return false;
     }
-
-    xorsink_.Initialize(&bufsink_, BLOCK_SZ);
     return true;
 }
 
@@ -271,18 +242,15 @@
     // After the first header is sent in response to a request, we cannot
     // send any additional headers.
     header_response_ = false;
+
+    // Reset the buffer for use by the next request.
+    bufsink_.ResetBufferOffset();
     return true;
 }
 
-bool ReadWorker::ReadDataFromBaseDevice(sector_t sector, size_t read_size) {
+bool ReadWorker::ReadDataFromBaseDevice(sector_t sector, void* buffer, size_t read_size) {
     CHECK(read_size <= BLOCK_SZ);
 
-    void* buffer = bufsink_.GetPayloadBuffer(BLOCK_SZ);
-    if (buffer == nullptr) {
-        SNAP_LOG(ERROR) << "ReadFromBaseDevice: Failed to get payload buffer";
-        return false;
-    }
-
     loff_t offset = sector << SECTOR_SHIFT;
     if (!android::base::ReadFullyAtOffset(base_path_merge_fd_, buffer, read_size, offset)) {
         SNAP_PLOG(ERROR) << "ReadDataFromBaseDevice failed. fd: " << base_path_merge_fd_
@@ -303,7 +271,6 @@
         size_t read_size = std::min(PAYLOAD_BUFFER_SZ, remaining_size);
 
         size_t total_bytes_read = 0;
-        bufsink_.ResetBufferOffset();
 
         while (read_size) {
             // We need to check every 4k block to verify if it is
@@ -314,11 +281,17 @@
                                        std::make_pair(sector, nullptr), SnapshotHandler::compare);
             bool not_found = (it == chunk_vec.end() || it->first != sector);
 
+            void* buffer = bufsink_.AcquireBuffer(BLOCK_SZ, size);
+            if (!buffer) {
+                SNAP_LOG(ERROR) << "AcquireBuffer failed in ReadAlignedSector";
+                return false;
+            }
+
             if (not_found) {
                 // Block not found in map - which means this block was not
                 // changed as per the OTA. Just route the I/O to the base
                 // device.
-                if (!ReadDataFromBaseDevice(sector, size)) {
+                if (!ReadDataFromBaseDevice(sector, buffer, size)) {
                     SNAP_LOG(ERROR) << "ReadDataFromBaseDevice failed";
                     return false;
                 }
@@ -327,25 +300,24 @@
             } else {
                 // We found the sector in mapping. Check the type of COW OP and
                 // process it.
-                if (!ProcessCowOp(it->second)) {
+                if (!ProcessCowOp(it->second, buffer)) {
                     SNAP_LOG(ERROR) << "ProcessCowOp failed";
                     return false;
                 }
 
-                ret = BLOCK_SZ;
+                ret = std::min(BLOCK_SZ, read_size);
             }
 
             read_size -= ret;
             total_bytes_read += ret;
             sector += (ret >> SECTOR_SHIFT);
-            bufsink_.UpdateBufferOffset(ret);
         }
 
-        if (!WriteDmUserPayload(total_bytes_read)) {
+        if (!SendBufferedIo()) {
             return false;
         }
 
-        SNAP_LOG(DEBUG) << "WriteDmUserPayload success total_bytes_read: " << total_bytes_read
+        SNAP_LOG(DEBUG) << "SendBufferedIo success total_bytes_read: " << total_bytes_read
                         << " remaining_size: " << remaining_size;
         remaining_size -= total_bytes_read;
     } while (remaining_size > 0);
@@ -356,40 +328,36 @@
 int ReadWorker::ReadUnalignedSector(
         sector_t sector, size_t size,
         std::vector<std::pair<sector_t, const CowOperation*>>::iterator& it) {
-    size_t skip_sector_size = 0;
-
     SNAP_LOG(DEBUG) << "ReadUnalignedSector: sector " << sector << " size: " << size
                     << " Aligned sector: " << it->first;
 
-    if (!ProcessCowOp(it->second)) {
+    int num_sectors_skip = sector - it->first;
+    size_t skip_size = num_sectors_skip << SECTOR_SHIFT;
+    size_t write_size = std::min(size, BLOCK_SZ - skip_size);
+    auto buffer = reinterpret_cast<uint8_t*>(bufsink_.AcquireBuffer(BLOCK_SZ, write_size));
+    if (!buffer) {
+        SNAP_LOG(ERROR) << "ProcessCowOp failed to allocate buffer";
+        return -1;
+    }
+
+    if (!ProcessCowOp(it->second, buffer)) {
         SNAP_LOG(ERROR) << "ReadUnalignedSector: " << sector << " failed of size: " << size
                         << " Aligned sector: " << it->first;
         return -1;
     }
 
-    int num_sectors_skip = sector - it->first;
-
-    if (num_sectors_skip > 0) {
-        skip_sector_size = num_sectors_skip << SECTOR_SHIFT;
-        char* buffer = reinterpret_cast<char*>(bufsink_.GetBufPtr());
-        struct dm_user_message* msg = (struct dm_user_message*)(&(buffer[0]));
-
-        if (skip_sector_size == BLOCK_SZ) {
+    if (skip_size) {
+        if (skip_size == BLOCK_SZ) {
             SNAP_LOG(ERROR) << "Invalid un-aligned IO request at sector: " << sector
                             << " Base-sector: " << it->first;
             return -1;
         }
-
-        memmove(msg->payload.buf, (char*)msg->payload.buf + skip_sector_size,
-                (BLOCK_SZ - skip_sector_size));
+        memmove(buffer, buffer + skip_size, write_size);
     }
-
-    bufsink_.ResetBufferOffset();
-    return std::min(size, (BLOCK_SZ - skip_sector_size));
+    return write_size;
 }
 
 bool ReadWorker::ReadUnalignedSector(sector_t sector, size_t size) {
-    bufsink_.ResetBufferOffset();
     std::vector<std::pair<sector_t, const CowOperation*>>& chunk_vec = snapuserd_->GetChunkVec();
 
     auto it = std::lower_bound(chunk_vec.begin(), chunk_vec.end(), std::make_pair(sector, nullptr),
@@ -458,7 +426,6 @@
     // requested offset in this case is beyond the last mapped COW op size (which
     // is block 1 in this case).
 
-    size_t total_bytes_read = 0;
     size_t remaining_size = size;
     int ret = 0;
     if (!merge_complete && (requested_offset >= final_offset) &&
@@ -472,11 +439,10 @@
         }
 
         remaining_size -= ret;
-        total_bytes_read += ret;
         sector += (ret >> SECTOR_SHIFT);
 
         // Send the data back
-        if (!WriteDmUserPayload(total_bytes_read)) {
+        if (!SendBufferedIo()) {
             return false;
         }
 
@@ -494,26 +460,21 @@
         // Find the diff of the aligned offset
         size_t diff_size = aligned_offset - requested_offset;
         CHECK(diff_size <= BLOCK_SZ);
-        if (remaining_size < diff_size) {
-            if (!ReadDataFromBaseDevice(sector, remaining_size)) {
-                return false;
-            }
-            total_bytes_read += remaining_size;
 
-            if (!WriteDmUserPayload(total_bytes_read)) {
-                return false;
-            }
-        } else {
-            if (!ReadDataFromBaseDevice(sector, diff_size)) {
-                return false;
-            }
+        size_t read_size = std::min(remaining_size, diff_size);
+        void* buffer = bufsink_.AcquireBuffer(BLOCK_SZ, read_size);
+        if (!buffer) {
+            SNAP_LOG(ERROR) << "AcquireBuffer failed in ReadUnalignedSector";
+            return false;
+        }
+        if (!ReadDataFromBaseDevice(sector, buffer, read_size)) {
+            return false;
+        }
+        if (!SendBufferedIo()) {
+            return false;
+        }
 
-            total_bytes_read += diff_size;
-
-            if (!WriteDmUserPayload(total_bytes_read)) {
-                return false;
-            }
-
+        if (remaining_size >= diff_size) {
             remaining_size -= diff_size;
             size_t num_sectors_read = (diff_size >> SECTOR_SHIFT);
             sector += num_sectors_read;
@@ -555,6 +516,10 @@
     return ReadAlignedSector(header->sector, header->len);
 }
 
+bool ReadWorker::SendBufferedIo() {
+    return WriteDmUserPayload(bufsink_.GetPayloadBytesWritten());
+}
+
 bool ReadWorker::ProcessIORequest() {
     // Read Header from dm-user misc device. This gives
     // us the sector number for which IO is issued by dm-snapshot device
@@ -579,6 +544,9 @@
     header->type = DM_USER_RESP_SUCCESS;
     header_response_ = true;
 
+    // Reset the output buffer.
+    bufsink_.ResetBufferOffset();
+
     bool ok;
     switch (request_type) {
         case DM_USER_REQ_MAP_READ:
diff --git a/fs_mgr/libsnapshot/snapuserd/user-space-merge/read_worker.h b/fs_mgr/libsnapshot/snapuserd/user-space-merge/read_worker.h
index c3a4c34..8d6f663 100644
--- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/read_worker.h
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/read_worker.h
@@ -37,21 +37,22 @@
     bool ProcessIORequest();
     bool WriteDmUserPayload(size_t size);
     bool DmuserReadRequest();
+    bool SendBufferedIo();
     void RespondIOError();
 
-    bool ProcessCowOp(const CowOperation* cow_op);
-    bool ProcessXorOp(const CowOperation* cow_op);
-    bool ProcessOrderedOp(const CowOperation* cow_op);
-    bool ProcessCopyOp(const CowOperation* cow_op);
-    bool ProcessReplaceOp(const CowOperation* cow_op);
-    bool ProcessZeroOp();
+    bool ProcessCowOp(const CowOperation* cow_op, void* buffer);
+    bool ProcessXorOp(const CowOperation* cow_op, void* buffer);
+    bool ProcessOrderedOp(const CowOperation* cow_op, void* buffer);
+    bool ProcessCopyOp(const CowOperation* cow_op, void* buffer);
+    bool ProcessReplaceOp(const CowOperation* cow_op, void* buffer);
+    bool ProcessZeroOp(void* buffer);
 
     bool ReadAlignedSector(sector_t sector, size_t sz);
     bool ReadUnalignedSector(sector_t sector, size_t size);
     int ReadUnalignedSector(sector_t sector, size_t size,
                             std::vector<std::pair<sector_t, const CowOperation*>>::iterator& it);
-    bool ReadFromSourceDevice(const CowOperation* cow_op);
-    bool ReadDataFromBaseDevice(sector_t sector, size_t read_size);
+    bool ReadFromSourceDevice(const CowOperation* cow_op, void* buffer);
+    bool ReadDataFromBaseDevice(sector_t sector, void* buffer, size_t read_size);
 
     constexpr bool IsBlockAligned(size_t size) { return ((size & (BLOCK_SZ - 1)) == 0); }
     constexpr sector_t ChunkToSector(chunk_t chunk) { return chunk << CHUNK_SHIFT; }
@@ -62,8 +63,8 @@
     std::string control_device_;
     unique_fd ctrl_fd_;
 
-    XorSink xorsink_;
     bool header_response_ = false;
+    std::basic_string<uint8_t> xor_buffer_;
 };
 
 }  // namespace snapshot
diff --git a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_core.cpp b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_core.cpp
index e52d752..2dd2ec0 100644
--- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_core.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_core.cpp
@@ -308,7 +308,7 @@
         ra_thread_status =
                 std::async(std::launch::async, &ReadAhead::RunThread, read_ahead_thread_.get());
 
-        SNAP_LOG(INFO) << "Read-ahead thread started...";
+        SNAP_LOG(INFO) << "Read-ahead thread started";
     }
 
     // Launch worker threads
diff --git a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_merge.cpp b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_merge.cpp
index 563f6ad..517148d 100644
--- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_merge.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_merge.cpp
@@ -106,9 +106,9 @@
         for (size_t i = 0; i < replace_zero_vec.size(); i++) {
             const CowOperation* cow_op = replace_zero_vec[i];
 
-            void* buffer = bufsink_.GetPayloadBuffer(BLOCK_SZ);
+            void* buffer = bufsink_.AcquireBuffer(BLOCK_SZ);
             if (!buffer) {
-                SNAP_LOG(ERROR) << "Failed to acquire buffer in merge";
+                SNAP_LOG(ERROR) << "AcquireBuffer failed in MergeReplaceOps";
                 return false;
             }
             if (cow_op->type == kCowReplaceOp) {
@@ -120,8 +120,6 @@
                 CHECK(cow_op->type == kCowZeroOp);
                 memset(buffer, 0, BLOCK_SZ);
             }
-
-            bufsink_.UpdateBufferOffset(BLOCK_SZ);
         }
 
         size_t io_size = linear_blocks * BLOCK_SZ;
diff --git a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_readahead.cpp b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_readahead.cpp
index 399f7b8..8755820 100644
--- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_readahead.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_readahead.cpp
@@ -492,7 +492,7 @@
         if (xor_op_index < xor_op_vec.size()) {
             const CowOperation* xor_op = xor_op_vec[xor_op_index];
             if (xor_op->new_block == new_block) {
-                void* buffer = bufsink_.GetPayloadBuffer(BLOCK_SZ);
+                void* buffer = bufsink_.AcquireBuffer(BLOCK_SZ);
                 if (!buffer) {
                     SNAP_LOG(ERROR) << "ReadAhead - failed to allocate buffer for block: "
                                     << xor_op->new_block;
@@ -506,7 +506,6 @@
                 }
 
                 xor_op_index += 1;
-                bufsink_.UpdateBufferOffset(BLOCK_SZ);
             }
         }
         block_index += 1;
@@ -593,7 +592,7 @@
             // Check if this block is an XOR op
             if (xor_op->new_block == new_block) {
                 // Read the xor'ed data from COW
-                void* buffer = bufsink_.GetPayloadBuffer(BLOCK_SZ);
+                void* buffer = bufsink.GetPayloadBuffer(BLOCK_SZ);
                 if (!buffer) {
                     SNAP_LOG(ERROR) << "ReadAhead - failed to allocate buffer";
                     return false;
diff --git a/init/Android.bp b/init/Android.bp
index 4416b9d..ee34215 100644
--- a/init/Android.bp
+++ b/init/Android.bp
@@ -392,6 +392,10 @@
     ],
 
     static_executable: true,
+    lto: {
+        // b/169004486 ThinLTO breaks x86 static executables.
+        never: true,
+    },
     system_shared_libs: [],
 
     cflags: [
diff --git a/init/devices.cpp b/init/devices.cpp
index d29ffd6..7c23492 100644
--- a/init/devices.cpp
+++ b/init/devices.cpp
@@ -568,6 +568,8 @@
         return;
     } else if (uevent.subsystem == "misc" && StartsWith(uevent.device_name, "dm-user/")) {
         devpath = "/dev/dm-user/" + uevent.device_name.substr(8);
+    } else if (uevent.subsystem == "misc" && uevent.device_name == "vfio/vfio") {
+        devpath = "/dev/" + uevent.device_name;
     } else {
         devpath = "/dev/" + Basename(uevent.path);
     }
diff --git a/init/selinux.cpp b/init/selinux.cpp
index b6d483a..51093d8 100644
--- a/init/selinux.cpp
+++ b/init/selinux.cpp
@@ -667,10 +667,16 @@
 //
 void PrepareApexSepolicy() {
     // If apex sepolicy zip exists in /metadata/sepolicy, use that, otherwise use version on
-    // /system.
-    auto dir = (access((kSepolicyApexMetadataDir + kSepolicyZip).c_str(), F_OK) == 0)
-                       ? kSepolicyApexMetadataDir
-                       : kSepolicyApexSystemDir;
+    // /system. If neither exists, do nothing.
+    std::string dir;
+    if (access((kSepolicyApexMetadataDir + kSepolicyZip).c_str(), F_OK) == 0) {
+        dir = kSepolicyApexMetadataDir;
+    } else if (access((kSepolicyApexSystemDir + kSepolicyZip).c_str(), F_OK) == 0) {
+        dir = kSepolicyApexSystemDir;
+    } else {
+        LOG(INFO) << "APEX Sepolicy not found";
+        return;
+    }
 
     auto sepolicyVerify = SepolicyVerify(dir);
     if (!sepolicyVerify.ok()) {
diff --git a/libcutils/include/cutils/threads.h b/libcutils/include/cutils/threads.h
index 0082c6c..1886184 100644
--- a/libcutils/include/cutils/threads.h
+++ b/libcutils/include/cutils/threads.h
@@ -31,7 +31,7 @@
 //
 // Deprecated: use android::base::GetThreadId instead, which doesn't truncate on Mac/Windows.
 //
-#if !defined(__GLIBC__) || __GLIBC__ >= 2 && __GLIBC_MINOR__ < 32
+#if !defined(__GLIBC__) || __GLIBC__ >= 2 && __GLIBC_MINOR__ < 30
 extern pid_t gettid();
 #endif
 
diff --git a/libcutils/threads.cpp b/libcutils/threads.cpp
index 6ece7a3..2638720 100644
--- a/libcutils/threads.cpp
+++ b/libcutils/threads.cpp
@@ -25,9 +25,9 @@
 #include <windows.h>
 #endif
 
-#if defined(__BIONIC__) || defined(__GLIBC__) && __GLIBC_MINOR__ >= 32
+#if defined(__BIONIC__) || defined(__GLIBC__) && __GLIBC_MINOR__ >= 30
 // No definition needed for Android because we'll just pick up bionic's copy.
-// No definition needed for Glibc >= 2.32 because it exposes its own copy.
+// No definition needed for Glibc >= 2.30 because it exposes its own copy.
 #else
 pid_t gettid() {
 #if defined(__APPLE__)
diff --git a/libprocessgroup/task_profiles.cpp b/libprocessgroup/task_profiles.cpp
index 44dba2a..1276a72 100644
--- a/libprocessgroup/task_profiles.cpp
+++ b/libprocessgroup/task_profiles.cpp
@@ -146,7 +146,7 @@
 
     const std::string& file_name =
             controller()->version() == 2 && !file_v2_name_.empty() ? file_v2_name_ : file_name_;
-    *path = StringPrintf("%s/uid_%d/%s", controller()->path(), uid, file_name.c_str());
+    *path = StringPrintf("%s/uid_%u/%s", controller()->path(), uid, file_name.c_str());
     return true;
 }
 
diff --git a/libutils/String8.cpp b/libutils/String8.cpp
index 82f5cb6..8d312b5 100644
--- a/libutils/String8.cpp
+++ b/libutils/String8.cpp
@@ -393,6 +393,11 @@
 }
 
 bool String8::removeAll(const char* other) {
+    ALOG_ASSERT(other, "String8::removeAll() requires a non-NULL string");
+
+    if (*other == '\0')
+        return true;
+
     ssize_t index = find(other);
     if (index < 0) return false;
 
diff --git a/libutils/String8_fuzz.cpp b/libutils/String8_fuzz.cpp
index faf49b6..e5dcd31 100644
--- a/libutils/String8_fuzz.cpp
+++ b/libutils/String8_fuzz.cpp
@@ -45,6 +45,8 @@
                     str1->toLower();
                 },
                 [](FuzzedDataProvider*, android::String8* str1, android::String8* str2) -> void {
+                    if (str2->size() == 0) return;
+
                     str1->removeAll(str2->c_str());
                 },
                 [](FuzzedDataProvider*, android::String8* str1, android::String8* str2) -> void {
diff --git a/libutils/String8_test.cpp b/libutils/String8_test.cpp
index 1356cd0..35fd512 100644
--- a/libutils/String8_test.cpp
+++ b/libutils/String8_test.cpp
@@ -114,3 +114,21 @@
     EXPECT_EQ(NO_MEMORY, s.append("baz", SIZE_MAX));
     EXPECT_STREQ("foobar", s);
 }
+
+TEST_F(String8Test, removeAll) {
+    String8 s("Hello, world!");
+
+    // NULL input should cause an assertion failure and error message in logcat
+    EXPECT_DEATH(s.removeAll(NULL), "");
+
+    // expect to return true and string content should remain unchanged
+    EXPECT_TRUE(s.removeAll(""));
+    EXPECT_STREQ("Hello, world!", s);
+
+    // expect to return false
+    EXPECT_FALSE(s.removeAll("x"));
+    EXPECT_STREQ("Hello, world!", s);
+
+    EXPECT_TRUE(s.removeAll("o"));
+    EXPECT_STREQ("Hell, wrld!", s);
+}
diff --git a/rootdir/ueventd.rc b/rootdir/ueventd.rc
index 0b7ffb8..60dcc2a 100644
--- a/rootdir/ueventd.rc
+++ b/rootdir/ueventd.rc
@@ -23,6 +23,11 @@
 subsystem dma_heap
    devname uevent_devpath
    dirname /dev/dma_heap
+
+subsystem vfio
+    devname uevent_devpath
+    dirname /dev/vfio
+
 # ueventd can only set permissions on device nodes and their associated
 # sysfs attributes, not on arbitrary paths.
 #
@@ -43,6 +48,7 @@
 /dev/binder               0666   root       root
 /dev/hwbinder             0666   root       root
 /dev/vndbinder            0666   root       root
+/dev/vfio/*               0666   root       root
 
 /dev/pmsg0                0222   root       log
 /dev/dma_heap/system      0444   system     system
diff --git a/trusty/storage/proxy/Android.bp b/trusty/storage/proxy/Android.bp
index 2e97ee0..e362b8b 100644
--- a/trusty/storage/proxy/Android.bp
+++ b/trusty/storage/proxy/Android.bp
@@ -33,6 +33,7 @@
 
     shared_libs: [
         "libbase",
+        "libbinder_ndk",
         "libcutils",
         "liblog",
         "libhardware_legacy",
diff --git a/trusty/storage/proxy/proxy.c b/trusty/storage/proxy/proxy.c
index c89c5b6..67e935e 100644
--- a/trusty/storage/proxy/proxy.c
+++ b/trusty/storage/proxy/proxy.c
@@ -24,6 +24,7 @@
 #include <sys/stat.h>
 #include <unistd.h>
 
+#include <android/binder_process.h>
 #include <cutils/android_filesystem_config.h>
 
 #include "checkpoint_handling.h"
@@ -238,6 +239,18 @@
     /* parse arguments */
     parse_args(argc, argv);
 
+    /*
+     * Start binder threadpool. At least one extra binder thread is needed to
+     * connect to the wakelock service without relying on polling. If we poll on
+     * the main thread we end up pausing for at least 1s even if the service
+     * starts faster. We set the max thread count to 0 because startThreadPool
+     * "Starts one thread, PLUS those requested in setThreadPoolMaxThreadCount,
+     * PLUS those manually requested in joinThreadPool." We only need a single
+     * binder thread to receive notifications on.
+     */
+    ABinderProcess_setThreadPoolMaxThreadCount(0);
+    ABinderProcess_startThreadPool();
+
     /* initialize secure storage directory */
     rc = storage_init(ss_data_root);
     if (rc < 0) return EXIT_FAILURE;
diff --git a/trusty/storage/proxy/rpmb.c b/trusty/storage/proxy/rpmb.c
index 22a85a7..1f5d107 100644
--- a/trusty/storage/proxy/rpmb.c
+++ b/trusty/storage/proxy/rpmb.c
@@ -399,6 +399,14 @@
 
     bool is_request_write = req->reliable_write_size > 0;
 
+    /*
+     * Internally this call connects to the suspend service, which will cause
+     * this service to start if not already running. If the binder thread pool
+     * has not been started at this point, this call will block and poll for the
+     * service every 1s. We need to make sure the thread pool is started to
+     * receive an async notification that the service is started to avoid
+     * blocking (see main).
+     */
     wl_rc = acquire_wake_lock(PARTIAL_WAKE_LOCK, UFS_WAKE_LOCK_NAME);
     if (wl_rc < 0) {
         ALOGE("%s: failed to acquire wakelock: %d, %s\n", __func__, wl_rc, strerror(errno));