Merge "Make libfstab available to APEXes."
diff --git a/fastboot/fastboot.cpp b/fastboot/fastboot.cpp
index 287285b..30eb7b5 100644
--- a/fastboot/fastboot.cpp
+++ b/fastboot/fastboot.cpp
@@ -104,7 +104,6 @@
 // libsparse will support INT_MAX, but this results in large allocations, so
 // let's keep it at 1GB to avoid memory pressure on the host.
 static constexpr int64_t RESPARSE_LIMIT = 1 * 1024 * 1024 * 1024;
-static uint64_t sparse_limit = 0;
 static int64_t target_sparse_limit = -1;
 
 static unsigned g_base_addr = 0x10000000;
@@ -1016,8 +1015,8 @@
     return value;
 }
 
-int64_t get_sparse_limit(int64_t size) {
-    int64_t limit = sparse_limit;
+int64_t get_sparse_limit(int64_t size, const FlashingPlan* fp) {
+    int64_t limit = int64_t(fp->sparse_limit);
     if (limit == 0) {
         // Unlimited, so see what the target device's limit is.
         // TODO: shouldn't we apply this limit even if you've used -S?
@@ -1038,7 +1037,7 @@
     return 0;
 }
 
-static bool load_buf_fd(unique_fd fd, struct fastboot_buffer* buf) {
+static bool load_buf_fd(unique_fd fd, struct fastboot_buffer* buf, const FlashingPlan* fp) {
     int64_t sz = get_file_size(fd);
     if (sz == -1) {
         return false;
@@ -1056,7 +1055,7 @@
     }
 
     lseek(fd.get(), 0, SEEK_SET);
-    int64_t limit = get_sparse_limit(sz);
+    int64_t limit = get_sparse_limit(sz, fp);
     buf->fd = std::move(fd);
     if (limit) {
         buf->files = load_sparse_files(buf->fd.get(), limit);
@@ -1072,13 +1071,11 @@
     return true;
 }
 
-static bool load_buf(const char* fname, struct fastboot_buffer* buf) {
+static bool load_buf(const char* fname, struct fastboot_buffer* buf, const FlashingPlan* fp) {
     unique_fd fd(TEMP_FAILURE_RETRY(open(fname, O_RDONLY | O_BINARY)));
 
     if (fd == -1) {
-        auto path = find_item_given_name(fname);
-        fd = unique_fd(TEMP_FAILURE_RETRY(open(path.c_str(), O_RDONLY | O_BINARY)));
-        if (fd == -1) return false;
+        return false;
     }
 
     struct stat s;
@@ -1090,7 +1087,7 @@
         return false;
     }
 
-    return load_buf_fd(std::move(fd), buf);
+    return load_buf_fd(std::move(fd), buf, fp);
 }
 
 static void rewrite_vbmeta_buffer(struct fastboot_buffer* buf, bool vbmeta_in_boot) {
@@ -1492,12 +1489,19 @@
     verbose("Do flash %s %s", pname, fname);
     struct fastboot_buffer buf;
 
-    if (fp->source) {
+    if (fp && fp->source) {
         unique_fd fd = fp->source->OpenFile(fname);
-        if (fd < 0 || !load_buf_fd(std::move(fd), &buf)) {
+        if (fd < 0 || !load_buf_fd(std::move(fd), &buf, fp)) {
             die("could not load '%s': %s", fname, strerror(errno));
         }
-    } else if (!load_buf(fname, &buf)) {
+        std::vector<char> signature_data;
+        std::string file_string(fname);
+        if (fp->source->ReadFile(file_string.substr(0, file_string.find('.')) + ".sig",
+                                 &signature_data)) {
+            fb->Download("signature", signature_data);
+            fb->RawCommand("signature", "installing signature");
+        }
+    } else if (!load_buf(fname, &buf, fp)) {
         die("cannot load '%s': %s", fname, strerror(errno));
     }
 
@@ -1783,7 +1787,10 @@
 
     CancelSnapshotIfNeeded();
 
-    HardcodedFlash();
+    tasks_ = CollectTasksFromImageList();
+    for (auto& task : tasks_) {
+        task->Run();
+    }
     return;
 }
 
@@ -1835,13 +1842,12 @@
     }
 }
 
-void FlashAllTool::HardcodedFlash() {
+std::vector<std::unique_ptr<Task>> FlashAllTool::CollectTasksFromImageList() {
     CollectImages();
     // First flash boot partitions. We allow this to happen either in userspace
     // or in bootloader fastboot.
-    FlashImages(boot_images_);
-
     std::vector<std::unique_ptr<Task>> tasks;
+    AddFlashTasks(boot_images_, tasks);
 
     if (auto flash_super_task = FlashSuperLayoutTask::Initialize(fp_, os_images_)) {
         tasks.emplace_back(std::move(flash_super_task));
@@ -1865,53 +1871,26 @@
             tasks.emplace_back(std::make_unique<ResizeTask>(fp_, image->part_name, "0", slot));
         }
     }
-    for (auto& i : tasks) {
-        i->Run();
-    }
-    FlashImages(os_images_);
+    AddFlashTasks(os_images_, tasks);
+    return tasks;
 }
 
-void FlashAllTool::FlashImages(const std::vector<std::pair<const Image*, std::string>>& images) {
+void FlashAllTool::AddFlashTasks(const std::vector<std::pair<const Image*, std::string>>& images,
+                                 std::vector<std::unique_ptr<Task>>& tasks) {
     for (const auto& [image, slot] : images) {
         fastboot_buffer buf;
         unique_fd fd = fp_->source->OpenFile(image->img_name);
-        if (fd < 0 || !load_buf_fd(std::move(fd), &buf)) {
+        if (fd < 0 || !load_buf_fd(std::move(fd), &buf, fp_)) {
             if (image->optional_if_no_image) {
                 continue;
             }
             die("could not load '%s': %s", image->img_name.c_str(), strerror(errno));
         }
-        FlashImage(*image, slot, &buf);
+        tasks.emplace_back(std::make_unique<FlashTask>(slot, image->part_name, image->img_name,
+                                                       is_vbmeta_partition(image->part_name), fp_));
     }
 }
 
-void FlashAllTool::FlashImage(const Image& image, const std::string& slot, fastboot_buffer* buf) {
-    auto flash = [&, this](const std::string& partition_name) {
-        std::vector<char> signature_data;
-        if (fp_->source->ReadFile(image.sig_name, &signature_data)) {
-            fb->Download("signature", signature_data);
-            fb->RawCommand("signature", "installing signature");
-        }
-
-        if (is_logical(partition_name)) {
-            fb->ResizePartition(partition_name, std::to_string(buf->image_size));
-        }
-
-        flash_buf(partition_name.c_str(), buf, is_vbmeta_partition(partition_name));
-    };
-    do_for_partitions(image.part_name, slot, flash, false);
-}
-
-class ZipImageSource final : public ImageSource {
-  public:
-    explicit ZipImageSource(ZipArchiveHandle zip) : zip_(zip) {}
-    bool ReadFile(const std::string& name, std::vector<char>* out) const override;
-    unique_fd OpenFile(const std::string& name) const override;
-
-  private:
-    ZipArchiveHandle zip_;
-};
-
 bool ZipImageSource::ReadFile(const std::string& name, std::vector<char>* out) const {
     return UnzipToMemory(zip_, name, out);
 }
@@ -1935,12 +1914,6 @@
     CloseArchive(zip);
 }
 
-class LocalImageSource final : public ImageSource {
-  public:
-    bool ReadFile(const std::string& name, std::vector<char>* out) const override;
-    unique_fd OpenFile(const std::string& name) const override;
-};
-
 bool LocalImageSource::ReadFile(const std::string& name, std::vector<char>* out) const {
     auto path = find_item_given_name(name);
     if (path.empty()) {
@@ -2000,7 +1973,7 @@
 
 void fb_perform_format(const std::string& partition, int skip_if_not_supported,
                        const std::string& type_override, const std::string& size_override,
-                       const unsigned fs_options) {
+                       const unsigned fs_options, const FlashingPlan* fp) {
     std::string partition_type, partition_size;
 
     struct fastboot_buffer buf;
@@ -2013,8 +1986,8 @@
     if (target_sparse_limit > 0 && target_sparse_limit < limit) {
         limit = target_sparse_limit;
     }
-    if (sparse_limit > 0 && sparse_limit < limit) {
-        limit = sparse_limit;
+    if (fp->sparse_limit > 0 && fp->sparse_limit < limit) {
+        limit = fp->sparse_limit;
     }
 
     if (fb->GetVar("partition-type:" + partition, &partition_type) != fastboot::SUCCESS) {
@@ -2069,7 +2042,7 @@
     if (fd == -1) {
         die("Cannot open generated image: %s", strerror(errno));
     }
-    if (!load_buf_fd(std::move(fd), &buf)) {
+    if (!load_buf_fd(std::move(fd), &buf, fp)) {
         die("Cannot read image: %s", strerror(errno));
     }
     flash_buf(partition, &buf, is_vbmeta_partition(partition));
@@ -2209,6 +2182,7 @@
                                       {"cmdline", required_argument, 0, 0},
                                       {"disable-verification", no_argument, 0, 0},
                                       {"disable-verity", no_argument, 0, 0},
+                                      {"disable-super-optimization", no_argument, 0, 0},
                                       {"force", no_argument, 0, 0},
                                       {"fs-options", required_argument, 0, 0},
                                       {"header-version", required_argument, 0, 0},
@@ -2246,6 +2220,8 @@
                 g_disable_verification = true;
             } else if (name == "disable-verity") {
                 g_disable_verity = true;
+            } else if (name == "disable-super-optimization") {
+                fp->should_optimize_flash_super = false;
             } else if (name == "force") {
                 fp->force_flash = true;
             } else if (name == "fs-options") {
@@ -2301,7 +2277,7 @@
                     serial = optarg;
                     break;
                 case 'S':
-                    if (!android::base::ParseByteCount(optarg, &sparse_limit)) {
+                    if (!android::base::ParseByteCount(optarg, &fp->sparse_limit)) {
                         die("invalid sparse limit %s", optarg);
                     }
                     break;
@@ -2419,7 +2395,8 @@
             std::string partition = next_arg(&args);
 
             auto format = [&](const std::string& partition) {
-                fb_perform_format(partition, 0, type_override, size_override, fp->fs_options);
+                fb_perform_format(partition, 0, type_override, size_override, fp->fs_options,
+                                  fp.get());
             };
             do_for_partitions(partition, fp->slot_override, format, true);
         } else if (command == "signature") {
@@ -2513,7 +2490,7 @@
             std::string filename = next_arg(&args);
 
             struct fastboot_buffer buf;
-            if (!load_buf(filename.c_str(), &buf) || buf.type != FB_BUFFER_FD) {
+            if (!load_buf(filename.c_str(), &buf, fp.get()) || buf.type != FB_BUFFER_FD) {
                 die("cannot load '%s'", filename.c_str());
             }
             fb->Download(filename, buf.fd.get(), buf.sz);
diff --git a/fastboot/fastboot.h b/fastboot/fastboot.h
index abdf636..196bd67 100644
--- a/fastboot/fastboot.h
+++ b/fastboot/fastboot.h
@@ -40,6 +40,7 @@
 #include "result.h"
 #include "socket.h"
 #include "util.h"
+#include "ziparchive/zip_archive.h"
 
 class FastBootTool {
   public:
@@ -95,6 +96,8 @@
     bool wants_set_active = false;
     bool skip_secondary = false;
     bool force_flash = false;
+    bool should_optimize_flash_super = true;
+    uint64_t sparse_limit = 0;
 
     std::string slot_override;
     std::string current_slot;
@@ -113,15 +116,33 @@
     void CheckRequirements();
     void DetermineSlot();
     void CollectImages();
-    void FlashImages(const std::vector<std::pair<const Image*, std::string>>& images);
-    void FlashImage(const Image& image, const std::string& slot, fastboot_buffer* buf);
-    void HardcodedFlash();
+    void AddFlashTasks(const std::vector<std::pair<const Image*, std::string>>& images,
+                       std::vector<std::unique_ptr<Task>>& tasks);
+    std::vector<std::unique_ptr<Task>> CollectTasksFromImageList();
 
     std::vector<ImageEntry> boot_images_;
     std::vector<ImageEntry> os_images_;
+    std::vector<std::unique_ptr<Task>> tasks_;
+
     FlashingPlan* fp_;
 };
 
+class ZipImageSource final : public ImageSource {
+  public:
+    explicit ZipImageSource(ZipArchiveHandle zip) : zip_(zip) {}
+    bool ReadFile(const std::string& name, std::vector<char>* out) const override;
+    unique_fd OpenFile(const std::string& name) const override;
+
+  private:
+    ZipArchiveHandle zip_;
+};
+
+class LocalImageSource final : public ImageSource {
+  public:
+    bool ReadFile(const std::string& name, std::vector<char>* out) const override;
+    unique_fd OpenFile(const std::string& name) const override;
+};
+
 bool should_flash_in_userspace(const std::string& partition_name);
 bool is_userspace_fastboot();
 void do_flash(const char* pname, const char* fname, const bool apply_vbmeta,
@@ -158,11 +179,11 @@
 bool supports_AB();
 std::string GetPartitionName(const ImageEntry& entry, const std::string& current_slot_);
 void flash_partition_files(const std::string& partition, const std::vector<SparsePtr>& files);
-int64_t get_sparse_limit(int64_t size);
+int64_t get_sparse_limit(int64_t size, const FlashingPlan* fp);
 std::vector<SparsePtr> resparse_file(sparse_file* s, int64_t max_size);
 
 bool is_retrofit_device();
 bool is_logical(const std::string& partition);
 void fb_perform_format(const std::string& partition, int skip_if_not_supported,
                        const std::string& type_override, const std::string& size_override,
-                       const unsigned fs_options);
+                       const unsigned fs_options, const FlashingPlan* fp);
diff --git a/fastboot/task.cpp b/fastboot/task.cpp
index 03f9b89..c1b9a31 100644
--- a/fastboot/task.cpp
+++ b/fastboot/task.cpp
@@ -98,18 +98,20 @@
 
 FlashSuperLayoutTask::FlashSuperLayoutTask(const std::string& super_name,
                                            std::unique_ptr<SuperFlashHelper> helper,
-                                           SparsePtr sparse_layout, uint64_t super_size)
+                                           SparsePtr sparse_layout, uint64_t super_size,
+                                           const FlashingPlan* fp)
     : super_name_(super_name),
       helper_(std::move(helper)),
       sparse_layout_(std::move(sparse_layout)),
-      super_size_(super_size) {}
+      super_size_(super_size),
+      fp_(fp) {}
 
 void FlashSuperLayoutTask::Run() {
     // Use the reported super partition size as the upper limit, rather than
     // sparse_file_len, which (1) can fail and (2) is kind of expensive, since
     // it will map in all of the embedded fds.
     std::vector<SparsePtr> files;
-    if (int limit = get_sparse_limit(super_size_)) {
+    if (int limit = get_sparse_limit(super_size_, fp_)) {
         files = resparse_file(sparse_layout_.get(), limit);
     } else {
         files.emplace_back(std::move(sparse_layout_));
@@ -124,6 +126,10 @@
 
 std::unique_ptr<FlashSuperLayoutTask> FlashSuperLayoutTask::Initialize(
         const FlashingPlan* fp, std::vector<ImageEntry>& os_images) {
+    if (!fp->should_optimize_flash_super) {
+        LOG(INFO) << "super optimization is disabled";
+        return nullptr;
+    }
     if (!supports_AB()) {
         LOG(VERBOSE) << "Cannot optimize flashing super on non-AB device";
         return nullptr;
@@ -183,11 +189,15 @@
     os_images.erase(std::remove_if(os_images.begin(), os_images.end(), remove_if_callback),
                     os_images.end());
     return std::make_unique<FlashSuperLayoutTask>(super_name, std::move(helper), std::move(s),
-                                                  partition_size);
+                                                  partition_size, fp);
 }
 
 std::unique_ptr<FlashSuperLayoutTask> FlashSuperLayoutTask::InitializeFromTasks(
         const FlashingPlan* fp, std::vector<std::unique_ptr<Task>>& tasks) {
+    if (!fp->should_optimize_flash_super) {
+        LOG(INFO) << "super optimization is disabled";
+        return nullptr;
+    }
     if (!supports_AB()) {
         LOG(VERBOSE) << "Cannot optimize flashing super on non-AB device";
         return nullptr;
@@ -252,7 +262,7 @@
     tasks.erase(std::remove_if(tasks.begin(), tasks.end(), remove_if_callback), tasks.end());
 
     return std::make_unique<FlashSuperLayoutTask>(super_name, std::move(helper), std::move(s),
-                                                  partition_size);
+                                                  partition_size, fp);
 }
 
 UpdateSuperTask::UpdateSuperTask(const FlashingPlan* fp) : fp_(fp) {}
@@ -322,7 +332,7 @@
         LOG(ERROR) << "wipe task erase failed with partition: " << pname_;
         return;
     }
-    fb_perform_format(pname_, 1, partition_type, "", fp_->fs_options);
+    fb_perform_format(pname_, 1, partition_type, "", fp_->fs_options, fp_);
 }
 
 std::string WipeTask::ToString() {
diff --git a/fastboot/task.h b/fastboot/task.h
index 500655d..858f43a 100644
--- a/fastboot/task.h
+++ b/fastboot/task.h
@@ -82,7 +82,7 @@
 class FlashSuperLayoutTask : public Task {
   public:
     FlashSuperLayoutTask(const std::string& super_name, std::unique_ptr<SuperFlashHelper> helper,
-                         SparsePtr sparse_layout, uint64_t super_size);
+                         SparsePtr sparse_layout, uint64_t super_size, const FlashingPlan* fp);
     static std::unique_ptr<FlashSuperLayoutTask> Initialize(const FlashingPlan* fp,
                                                             std::vector<ImageEntry>& os_images);
     static std::unique_ptr<FlashSuperLayoutTask> InitializeFromTasks(
@@ -96,6 +96,7 @@
     std::unique_ptr<SuperFlashHelper> helper_;
     SparsePtr sparse_layout_;
     uint64_t super_size_;
+    const FlashingPlan* fp_;
 };
 
 class UpdateSuperTask : public Task {
diff --git a/fastboot/usb_osx.cpp b/fastboot/usb_osx.cpp
index 5b9e5c8..8b852f5 100644
--- a/fastboot/usb_osx.cpp
+++ b/fastboot/usb_osx.cpp
@@ -436,12 +436,7 @@
 
     for (;;) {
         if (! IOIteratorIsValid(iterator)) {
-            /*
-             * Apple documentation advises resetting the iterator if
-             * it should become invalid during iteration.
-             */
-            IOIteratorReset(iterator);
-            continue;
+            break;
         }
 
         io_service_t device = IOIteratorNext(iterator);
diff --git a/fs_mgr/fs_mgr.cpp b/fs_mgr/fs_mgr.cpp
index 742cdfa..7f4959f 100644
--- a/fs_mgr/fs_mgr.cpp
+++ b/fs_mgr/fs_mgr.cpp
@@ -1096,8 +1096,11 @@
 
 class CheckpointManager {
   public:
-    CheckpointManager(int needs_checkpoint = -1, bool metadata_encrypted = false)
-        : needs_checkpoint_(needs_checkpoint), metadata_encrypted_(metadata_encrypted) {}
+    CheckpointManager(int needs_checkpoint = -1, bool metadata_encrypted = false,
+                      bool needs_encrypt = false)
+        : needs_checkpoint_(needs_checkpoint),
+          metadata_encrypted_(metadata_encrypted),
+          needs_encrypt_(needs_encrypt) {}
 
     bool NeedsCheckpoint() {
         if (needs_checkpoint_ != UNKNOWN) {
@@ -1160,7 +1163,7 @@
             } else {
                 LERROR << entry->fs_type << " does not implement checkpoints.";
             }
-        } else if (entry->fs_mgr_flags.checkpoint_blk) {
+        } else if (entry->fs_mgr_flags.checkpoint_blk && !needs_encrypt_) {
             auto actual_block_device = block_device.empty() ? entry->blk_device : block_device;
             if (fs_mgr_find_bow_device(actual_block_device).empty()) {
                 unique_fd fd(
@@ -1228,6 +1231,7 @@
     enum { UNKNOWN = -1, NO = 0, YES = 1 };
     int needs_checkpoint_;
     bool metadata_encrypted_;
+    bool needs_encrypt_;
     std::map<std::string, std::string> device_map_;
 };
 
@@ -1845,17 +1849,14 @@
     return ret;
 }
 
-// If tmp_mount_point is non-null, mount the filesystem there.  This is for the
-// tmp mount we do to check the user password
 // If multiple fstab entries are to be mounted on "n_name", it will try to mount each one
 // in turn, and stop on 1st success, or no more match.
-static int fs_mgr_do_mount_helper(Fstab* fstab, const std::string& n_name,
-                                  const std::string& n_blk_device, const char* tmp_mount_point,
-                                  int needs_checkpoint, bool metadata_encrypted) {
+int fs_mgr_do_mount(Fstab* fstab, const std::string& n_name, const std::string& n_blk_device,
+                    int needs_checkpoint, bool needs_encrypt) {
     int mount_errors = 0;
     int first_mount_errno = 0;
     std::string mount_point;
-    CheckpointManager checkpoint_manager(needs_checkpoint, metadata_encrypted);
+    CheckpointManager checkpoint_manager(needs_checkpoint, true, needs_encrypt);
     AvbUniquePtr avb_handle(nullptr);
 
     if (!fstab) {
@@ -1897,11 +1898,7 @@
         }
 
         // Now mount it where requested */
-        if (tmp_mount_point) {
-            mount_point = tmp_mount_point;
-        } else {
-            mount_point = fstab_entry.mount_point;
-        }
+        mount_point = fstab_entry.mount_point;
 
         int fs_stat = prepare_fs_for_mount(n_blk_device, fstab_entry, mount_point);
 
@@ -1958,35 +1955,6 @@
     return FS_MGR_DOMNT_FAILED;
 }
 
-int fs_mgr_do_mount(Fstab* fstab, const char* n_name, char* n_blk_device, char* tmp_mount_point) {
-    return fs_mgr_do_mount_helper(fstab, n_name, n_blk_device, tmp_mount_point, -1, false);
-}
-
-int fs_mgr_do_mount(Fstab* fstab, const char* n_name, char* n_blk_device, char* tmp_mount_point,
-                    bool needs_checkpoint, bool metadata_encrypted) {
-    return fs_mgr_do_mount_helper(fstab, n_name, n_blk_device, tmp_mount_point, needs_checkpoint,
-                                  metadata_encrypted);
-}
-
-/*
- * mount a tmpfs filesystem at the given point.
- * return 0 on success, non-zero on failure.
- */
-int fs_mgr_do_tmpfs_mount(const char *n_name)
-{
-    int ret;
-
-    ret = mount("tmpfs", n_name, "tmpfs", MS_NOATIME | MS_NOSUID | MS_NODEV | MS_NOEXEC,
-                CRYPTO_TMPFS_OPTIONS);
-    if (ret < 0) {
-        LERROR << "Cannot mount tmpfs filesystem at " << n_name;
-        return -1;
-    }
-
-    /* Success */
-    return 0;
-}
-
 static bool ConfigureIoScheduler(const std::string& device_path) {
     if (!StartsWith(device_path, "/dev/")) {
         LERROR << __func__ << ": invalid argument " << device_path;
diff --git a/fs_mgr/include/fs_mgr.h b/fs_mgr/include/fs_mgr.h
index 43de6d8..bc4a7a6 100644
--- a/fs_mgr/include/fs_mgr.h
+++ b/fs_mgr/include/fs_mgr.h
@@ -85,13 +85,10 @@
 #define FS_MGR_DOMNT_FAILED (-1)
 #define FS_MGR_DOMNT_BUSY (-2)
 #define FS_MGR_DOMNT_SUCCESS 0
-int fs_mgr_do_mount(android::fs_mgr::Fstab* fstab, const char* n_name, char* n_blk_device,
-                    char* tmp_mount_point);
-int fs_mgr_do_mount(android::fs_mgr::Fstab* fstab, const char* n_name, char* n_blk_device,
-                    char* tmp_mount_point, bool need_cp, bool metadata_encrypted);
+int fs_mgr_do_mount(android::fs_mgr::Fstab* fstab, const std::string& n_name,
+                    const std::string& n_blk_device, int needs_checkpoint, bool needs_encrypt);
 int fs_mgr_do_mount_one(const android::fs_mgr::FstabEntry& entry,
                         const std::string& mount_point = "");
-int fs_mgr_do_tmpfs_mount(const char *n_name);
 bool fs_mgr_load_verity_state(int* mode);
 // Returns true if verity is enabled on this particular FstabEntry.
 bool fs_mgr_is_verity_enabled(const android::fs_mgr::FstabEntry& entry);
diff --git a/fs_mgr/libdm/dm.cpp b/fs_mgr/libdm/dm.cpp
index 1e8c14f..3a9ed9b 100644
--- a/fs_mgr/libdm/dm.cpp
+++ b/fs_mgr/libdm/dm.cpp
@@ -106,6 +106,10 @@
     if (!GetDeviceUniquePath(name, &unique_path)) {
         LOG(ERROR) << "Failed to get unique path for device " << name;
     }
+    // Expect to have uevent generated if the unique path actually exists. This may not exist
+    // if the device was created but has never been activated before it gets deleted.
+    bool need_uevent = !unique_path.empty() && access(unique_path.c_str(), F_OK) == 0;
+
     struct dm_ioctl io;
     InitIo(&io, name);
 
@@ -116,7 +120,7 @@
 
     // Check to make sure appropriate uevent is generated so ueventd will
     // do the right thing and remove the corresponding device node and symlinks.
-    if ((io.flags & DM_UEVENT_GENERATED_FLAG) == 0) {
+    if (need_uevent && (io.flags & DM_UEVENT_GENERATED_FLAG) == 0) {
         LOG(ERROR) << "Didn't generate uevent for [" << name << "] removal";
         return false;
     }
diff --git a/fs_mgr/libsnapshot/Android.bp b/fs_mgr/libsnapshot/Android.bp
index e931bec..9d1ce7d 100644
--- a/fs_mgr/libsnapshot/Android.bp
+++ b/fs_mgr/libsnapshot/Android.bp
@@ -85,11 +85,9 @@
         "android/snapshot/snapshot.proto",
         "device_info.cpp",
         "snapshot.cpp",
-        "snapshot_reader.cpp",
         "snapshot_stats.cpp",
         "snapshot_stub.cpp",
         "snapshot_metadata_updater.cpp",
-        "snapshot_writer.cpp",
         "partition_cow_creator.cpp",
         "return.cpp",
         "utility.cpp",
@@ -165,6 +163,9 @@
         "liblz4",
         "libzstd",
     ],
+    header_libs: [
+        "libupdate_engine_headers",
+    ],
     export_include_dirs: ["include"],
 }
 
@@ -179,6 +180,7 @@
         "libsnapshot_cow/cow_format.cpp",
         "libsnapshot_cow/cow_reader.cpp",
         "libsnapshot_cow/parser_v2.cpp",
+        "libsnapshot_cow/snapshot_reader.cpp",
         "libsnapshot_cow/writer_base.cpp",
         "libsnapshot_cow/writer_v2.cpp",
     ],
@@ -224,9 +226,7 @@
     srcs: [
         "partition_cow_creator_test.cpp",
         "snapshot_metadata_updater_test.cpp",
-        "snapshot_reader_test.cpp",
         "snapshot_test.cpp",
-        "snapshot_writer_test.cpp",
     ],
     shared_libs: [
         "libbinder",
@@ -372,6 +372,7 @@
         "libsnapshot_cow_defaults",
     ],
     srcs: [
+        "libsnapshot_cow/snapshot_reader_test.cpp",
         "libsnapshot_cow/test_v2.cpp",
     ],
     cflags: [
@@ -416,6 +417,7 @@
         "libbrotli",
         "libcrypto_static",
         "liblog",
+        "libgflags",
         "libsnapshot_cow",
         "libz",
     ],
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/cow_reader.h b/fs_mgr/libsnapshot/include/libsnapshot/cow_reader.h
index 95a1270..f599501 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/cow_reader.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/cow_reader.h
@@ -24,6 +24,10 @@
 #include <android-base/unique_fd.h>
 #include <libsnapshot/cow_format.h>
 
+namespace chromeos_update_engine {
+class FileDescriptor;
+}  // namespace chromeos_update_engine
+
 namespace android {
 namespace snapshot {
 
@@ -32,6 +36,8 @@
 // Interface for reading from a snapuserd COW.
 class ICowReader {
   public:
+    using FileDescriptor = chromeos_update_engine::FileDescriptor;
+
     virtual ~ICowReader() {}
 
     // Return the file header.
@@ -109,10 +115,9 @@
     bool Parse(android::base::borrowed_fd fd, std::optional<uint64_t> label = {});
 
     bool InitForMerge(android::base::unique_fd&& fd);
+
     bool VerifyMergeOps() override;
-
     bool GetFooter(CowFooter* footer) override;
-
     bool GetLastLabel(uint64_t* label) override;
 
     // Create a CowOpIter object which contains footer_.num_ops
@@ -128,6 +133,7 @@
 
     CowHeader& GetHeader() override { return header_; }
 
+    bool GetRawBytes(const CowOperation* op, void* buffer, size_t len, size_t* read);
     bool GetRawBytes(uint64_t offset, void* buffer, size_t len, size_t* read);
 
     // Returns the total number of data ops that should be merged. This is the
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/cow_writer.h b/fs_mgr/libsnapshot/include/libsnapshot/cow_writer.h
index af2d3ef..d6194eb 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/cow_writer.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/cow_writer.h
@@ -61,6 +61,8 @@
 // will occur in the sequence they were added to the COW.
 class ICowWriter {
   public:
+    using FileDescriptor = chromeos_update_engine::FileDescriptor;
+
     virtual ~ICowWriter() {}
 
     // Encode an operation that copies the contents of |old_block| to the
@@ -93,6 +95,17 @@
 
     virtual uint32_t GetBlockSize() const = 0;
     virtual std::optional<uint32_t> GetMaxBlocks() const = 0;
+
+    // Open an ICowReader for this writer. The reader will be a snapshot of the
+    // current operations in the writer; new writes after OpenReader() will not
+    // be reflected.
+    virtual std::unique_ptr<ICowReader> OpenReader() = 0;
+
+    // Open a file descriptor. This allows reading and seeing through the cow
+    // as if it were a normal file. The optional source_device must be a valid
+    // path if the CowReader contains any copy or xor operations.
+    virtual std::unique_ptr<FileDescriptor> OpenFileDescriptor(
+            const std::optional<std::string>& source_device) = 0;
 };
 
 class CompressWorker {
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/mock_snapshot_writer.h b/fs_mgr/libsnapshot/include/libsnapshot/mock_cow_writer.h
similarity index 75%
rename from fs_mgr/libsnapshot/include/libsnapshot/mock_snapshot_writer.h
rename to fs_mgr/libsnapshot/include/libsnapshot/mock_cow_writer.h
index 52e3a9c..c58c654 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/mock_snapshot_writer.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/mock_cow_writer.h
@@ -15,17 +15,16 @@
 //
 
 #include <gmock/gmock.h>
-#include <libsnapshot/snapshot_writer.h>
+#include <libsnapshot/cow_writer.h>
 
 namespace android::snapshot {
 
-class MockSnapshotWriter : public ISnapshotWriter {
+class MockCowWriter : public ICowWriter {
   public:
-    using FileDescriptor = ISnapshotWriter::FileDescriptor;
+    using FileDescriptor = chromeos_update_engine::FileDescriptor;
 
     MOCK_METHOD(bool, Finalize, (), (override));
 
-    // Return number of bytes the cow image occupies on disk.
     MOCK_METHOD(uint64_t, GetCowSize, (), (override));
 
     MOCK_METHOD(bool, AddCopy, (uint64_t, uint64_t, uint64_t), (override));
@@ -35,11 +34,12 @@
     MOCK_METHOD(bool, AddZeroBlocks, (uint64_t, uint64_t), (override));
     MOCK_METHOD(bool, AddLabel, (uint64_t), (override));
     MOCK_METHOD(bool, AddSequenceData, (size_t, const uint32_t*), (override));
-    MOCK_METHOD(bool, Initialize, (), (override));
-    MOCK_METHOD(bool, InitializeAppend, (uint64_t), (override));
-    MOCK_METHOD(bool, VerifyMergeOps, (), (override, const, noexcept));
-    MOCK_METHOD(std::unique_ptr<FileDescriptor>, OpenReader, (), (override));
     MOCK_METHOD(uint32_t, GetBlockSize, (), (override, const));
     MOCK_METHOD(std::optional<uint32_t>, GetMaxBlocks, (), (override, const));
+
+    MOCK_METHOD(std::unique_ptr<ICowReader>, OpenReader, (), (override));
+    MOCK_METHOD(std::unique_ptr<FileDescriptor>, OpenFileDescriptor,
+                (const std::optional<std::string>&), (override));
 };
+
 }  // namespace android::snapshot
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/mock_snapshot.h b/fs_mgr/libsnapshot/include/libsnapshot/mock_snapshot.h
index d458b87..ca45d2f 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/mock_snapshot.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/mock_snapshot.h
@@ -42,9 +42,9 @@
                 (const android::fs_mgr::CreateLogicalPartitionParams& params,
                  std::string* snapshot_path),
                 (override));
-    MOCK_METHOD(std::unique_ptr<ISnapshotWriter>, OpenSnapshotWriter,
+    MOCK_METHOD(std::unique_ptr<ICowWriter>, OpenSnapshotWriter,
                 (const android::fs_mgr::CreateLogicalPartitionParams& params,
-                 const std::optional<std::string>&),
+                 std::optional<uint64_t>),
                 (override));
     MOCK_METHOD(bool, UnmapUpdateSnapshot, (const std::string& target_partition_name), (override));
     MOCK_METHOD(bool, NeedSnapshotsInFirstStageMount, (), (override));
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
index ecf1d15..df532ee 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
@@ -33,12 +33,11 @@
 #include <libfiemap/image_manager.h>
 #include <liblp/builder.h>
 #include <liblp/liblp.h>
-#include <update_engine/update_metadata.pb.h>
-
 #include <libsnapshot/auto_device.h>
+#include <libsnapshot/cow_writer.h>
 #include <libsnapshot/return.h>
-#include <libsnapshot/snapshot_writer.h>
 #include <snapuserd/snapuserd_client.h>
+#include <update_engine/update_metadata.pb.h>
 
 #ifndef FRIEND_TEST
 #define FRIEND_TEST(test_set_name, individual_test) \
@@ -211,16 +210,13 @@
     virtual bool MapUpdateSnapshot(const android::fs_mgr::CreateLogicalPartitionParams& params,
                                    std::string* snapshot_path) = 0;
 
-    // Create an ISnapshotWriter to build a snapshot against a target partition. The partition name
+    // Create an ICowWriter to build a snapshot against a target partition. The partition name
     // must be suffixed. If a source partition exists, it must be specified as well. The source
     // partition will only be used if raw bytes are needed. The source partition should be an
     // absolute path to the device, not a partition name.
-    //
-    // After calling OpenSnapshotWriter, the caller must invoke Initialize or InitializeForAppend
-    // before invoking write operations.
-    virtual std::unique_ptr<ISnapshotWriter> OpenSnapshotWriter(
+    virtual std::unique_ptr<ICowWriter> OpenSnapshotWriter(
             const android::fs_mgr::CreateLogicalPartitionParams& params,
-            const std::optional<std::string>& source_device) = 0;
+            std::optional<uint64_t> label = {}) = 0;
 
     // Unmap a snapshot device or CowWriter that was previously opened with MapUpdateSnapshot,
     // OpenSnapshotWriter. All outstanding open descriptors, writers, or
@@ -362,9 +358,9 @@
     Return CreateUpdateSnapshots(const DeltaArchiveManifest& manifest) override;
     bool MapUpdateSnapshot(const CreateLogicalPartitionParams& params,
                            std::string* snapshot_path) override;
-    std::unique_ptr<ISnapshotWriter> OpenSnapshotWriter(
+    std::unique_ptr<ICowWriter> OpenSnapshotWriter(
             const android::fs_mgr::CreateLogicalPartitionParams& params,
-            const std::optional<std::string>& source_device) override;
+            std::optional<uint64_t> label) override;
     bool UnmapUpdateSnapshot(const std::string& target_partition_name) override;
     bool NeedSnapshotsInFirstStageMount() override;
     bool CreateLogicalAndSnapshotPartitions(
@@ -693,10 +689,10 @@
     };
 
     // Helpers for OpenSnapshotWriter.
-    std::unique_ptr<ISnapshotWriter> OpenCompressedSnapshotWriter(
-            LockedFile* lock, const std::optional<std::string>& source_device,
-            const std::string& partition_name, const SnapshotStatus& status,
-            const SnapshotPaths& paths);
+    std::unique_ptr<ICowWriter> OpenCompressedSnapshotWriter(LockedFile* lock,
+                                                             const SnapshotStatus& status,
+                                                             const SnapshotPaths& paths,
+                                                             std::optional<uint64_t> label);
 
     // Map the base device, COW devices, and snapshot device.
     bool MapPartitionWithSnapshot(LockedFile* lock, CreateLogicalPartitionParams params,
@@ -743,7 +739,7 @@
     // Initialize snapshots so that they can be mapped later.
     // Map the COW partition and zero-initialize the header.
     Return InitializeUpdateSnapshots(
-            LockedFile* lock, MetadataBuilder* target_metadata,
+            LockedFile* lock, uint32_t cow_version, MetadataBuilder* target_metadata,
             const LpMetadata* exported_target_metadata, const std::string& target_suffix,
             const std::map<std::string, SnapshotStatus>& all_snapshot_status);
 
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/snapshot_stub.h b/fs_mgr/libsnapshot/include/libsnapshot/snapshot_stub.h
index 171c7c6..1c9b403 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/snapshot_stub.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/snapshot_stub.h
@@ -40,9 +40,9 @@
             const chromeos_update_engine::DeltaArchiveManifest& manifest) override;
     bool MapUpdateSnapshot(const android::fs_mgr::CreateLogicalPartitionParams& params,
                            std::string* snapshot_path) override;
-    std::unique_ptr<ISnapshotWriter> OpenSnapshotWriter(
+    std::unique_ptr<ICowWriter> OpenSnapshotWriter(
             const android::fs_mgr::CreateLogicalPartitionParams& params,
-            const std::optional<std::string>& source_device) override;
+            std::optional<uint64_t> label) override;
     bool UnmapUpdateSnapshot(const std::string& target_partition_name) override;
     bool NeedSnapshotsInFirstStageMount() override;
     bool CreateLogicalAndSnapshotPartitions(
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/snapshot_writer.h b/fs_mgr/libsnapshot/include/libsnapshot/snapshot_writer.h
deleted file mode 100644
index 2653a60..0000000
--- a/fs_mgr/libsnapshot/include/libsnapshot/snapshot_writer.h
+++ /dev/null
@@ -1,93 +0,0 @@
-// Copyright (C) 2020 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.
-
-#pragma once
-
-#include <optional>
-
-#include <android-base/unique_fd.h>
-
-#include <libsnapshot/cow_writer.h>
-
-namespace chromeos_update_engine {
-class FileDescriptor;
-}  // namespace chromeos_update_engine
-
-namespace android {
-namespace snapshot {
-
-class ISnapshotWriter : public ICowWriter {
-  public:
-    using FileDescriptor = chromeos_update_engine::FileDescriptor;
-
-    virtual ~ISnapshotWriter() {}
-
-    // Open the writer in write mode (no append).
-    virtual bool Initialize() = 0;
-
-    // Open the writer in append mode, with the last label to resume
-    // from. See CowWriter::InitializeAppend.
-    virtual bool InitializeAppend(uint64_t label) = 0;
-
-    virtual std::unique_ptr<FileDescriptor> OpenReader() = 0;
-
-    virtual bool VerifyMergeOps() const noexcept = 0;
-};
-
-// Send writes to a COW or a raw device directly, based on a threshold.
-class CompressedSnapshotWriter final : public ISnapshotWriter {
-  public:
-    CompressedSnapshotWriter(const CowOptions& options);
-
-    void SetSourceDevice(const std::string& source_device);
-
-    // Sets the COW device; this is required.
-    bool SetCowDevice(android::base::unique_fd&& cow_device);
-
-    bool Initialize() override;
-    bool InitializeAppend(uint64_t label) override;
-    bool Finalize() override;
-    uint64_t GetCowSize() override;
-    uint32_t GetBlockSize() const override;
-    std::optional<uint32_t> GetMaxBlocks() const override;
-    std::unique_ptr<FileDescriptor> OpenReader() override;
-    bool VerifyMergeOps() const noexcept;
-
-    bool AddCopy(uint64_t new_block, uint64_t old_block, uint64_t num_blocks = 1) override;
-    bool AddRawBlocks(uint64_t new_block_start, const void* data, size_t size) override;
-    bool AddXorBlocks(uint32_t new_block_start, const void* data, size_t size, uint32_t old_block,
-                      uint16_t offset) override;
-    bool AddZeroBlocks(uint64_t new_block_start, uint64_t num_blocks) override;
-    bool AddLabel(uint64_t label) override;
-    bool AddSequenceData(size_t num_ops, const uint32_t* data) override;
-
-  private:
-    std::unique_ptr<CowReader> OpenCowReader() const;
-    android::base::borrowed_fd GetSourceFd();
-
-    CowOptions options_;
-
-    // Set the source device. This is used for AddCopy() operations, if the
-    // underlying writer needs the original bytes (for example if backed by
-    // dm-snapshot or if writing directly to an unsnapshotted region). The
-    // device is only opened on the first operation that requires it.
-    std::optional<std::string> source_device_;
-    android::base::unique_fd source_fd_;
-
-    android::base::unique_fd cow_device_;
-    std::unique_ptr<ICowWriter> cow_;
-};
-
-}  // namespace snapshot
-}  // namespace android
diff --git a/fs_mgr/libsnapshot/include_test/libsnapshot/test_helpers.h b/fs_mgr/libsnapshot/include_test/libsnapshot/test_helpers.h
index f45d4ed..5e9f049 100644
--- a/fs_mgr/libsnapshot/include_test/libsnapshot/test_helpers.h
+++ b/fs_mgr/libsnapshot/include_test/libsnapshot/test_helpers.h
@@ -198,7 +198,7 @@
 // Expect space of |path| is multiple of 4K.
 bool WriteRandomData(const std::string& path, std::optional<size_t> expect_size = std::nullopt,
                      std::string* hash = nullptr);
-std::string HashSnapshot(ISnapshotWriter* writer);
+std::string HashSnapshot(ICowWriter::FileDescriptor* writer);
 
 std::string ToHexString(const uint8_t* buf, size_t len);
 
diff --git a/fs_mgr/libsnapshot/libsnapshot_cow/cow_reader.cpp b/fs_mgr/libsnapshot/libsnapshot_cow/cow_reader.cpp
index c2a7fdb..4ac89ae 100644
--- a/fs_mgr/libsnapshot/libsnapshot_cow/cow_reader.cpp
+++ b/fs_mgr/libsnapshot/libsnapshot_cow/cow_reader.cpp
@@ -230,7 +230,7 @@
             size_t seq_len = current_op.data_length / sizeof(uint32_t);
 
             merge_op_blocks->resize(merge_op_blocks->size() + seq_len);
-            if (!GetRawBytes(current_op.source, &merge_op_blocks->data()[num_seqs],
+            if (!GetRawBytes(&current_op, &merge_op_blocks->data()[num_seqs],
                              current_op.data_length, &read)) {
                 PLOG(ERROR) << "Failed to read sequence op!";
                 return false;
@@ -516,6 +516,18 @@
                                             ignore_progress ? 0 : merge_op_start_);
 }
 
+bool CowReader::GetRawBytes(const CowOperation* op, void* buffer, size_t len, size_t* read) {
+    switch (op->type) {
+        case kCowSequenceOp:
+        case kCowReplaceOp:
+        case kCowXorOp:
+            return GetRawBytes(op->source, buffer, len, read);
+        default:
+            LOG(ERROR) << "Cannot get raw bytes of non-data op: " << *op;
+            return false;
+    }
+}
+
 bool CowReader::GetRawBytes(uint64_t offset, void* buffer, size_t len, size_t* read) {
     // Validate the offset, taking care to acknowledge possible overflow of offset+len.
     if (offset < header_.prefix.header_size || offset >= fd_size_ - sizeof(CowFooter) ||
diff --git a/fs_mgr/libsnapshot/libsnapshot_cow/inspect_cow.cpp b/fs_mgr/libsnapshot/libsnapshot_cow/inspect_cow.cpp
index c2c86ee..a6dee4f 100644
--- a/fs_mgr/libsnapshot/libsnapshot_cow/inspect_cow.cpp
+++ b/fs_mgr/libsnapshot/libsnapshot_cow/inspect_cow.cpp
@@ -24,11 +24,26 @@
 
 #include <android-base/logging.h>
 #include <android-base/unique_fd.h>
+#include <gflags/gflags.h>
 #include <libsnapshot/cow_reader.h>
+#include "parser_v2.h"
+
+DEFINE_bool(silent, false, "Run silently");
+DEFINE_bool(decompress, false, "Attempt to decompress data ops");
+DEFINE_bool(show_bad_data, false, "If an op fails to decompress, show its daw data");
+DEFINE_bool(show_ops, false, "Print all opcode information");
+DEFINE_string(order, "", "If show_ops is true, change the order (either merge or reverse-merge)");
+DEFINE_bool(show_merged, false,
+            "If show_ops is true, and order is merge or reverse-merge, include merged ops");
+DEFINE_bool(verify_merge_sequence, false, "Verify merge order sequencing");
+DEFINE_bool(show_merge_sequence, false, "Show merge order sequence");
+DEFINE_bool(show_raw_ops, false, "Show raw ops directly from the underlying parser");
 
 namespace android {
 namespace snapshot {
 
+using android::base::borrowed_fd;
+
 void MyLogger(android::base::LogId, android::base::LogSeverity severity, const char*, const char*,
               unsigned int, const char* message) {
     if (severity == android::base::ERROR) {
@@ -38,37 +53,11 @@
     }
 }
 
-static void usage(void) {
-    std::cerr << "Usage: inspect_cow [-sd] <COW_FILE>\n";
-    std::cerr << "\t -s Run Silent\n";
-    std::cerr << "\t -d Attempt to decompress\n";
-    std::cerr << "\t -b Show data for failed decompress\n";
-    std::cerr << "\t -l Show ops\n";
-    std::cerr << "\t -m Show ops in reverse merge order\n";
-    std::cerr << "\t -n Show ops in merge order\n";
-    std::cerr << "\t -a Include merged ops in any merge order listing\n";
-    std::cerr << "\t -o Shows sequence op block order\n";
-    std::cerr << "\t -v Verifies merge order has no conflicts\n";
-}
-
-enum OpIter { Normal, RevMerge, Merge };
-
-struct Options {
-    bool silent;
-    bool decompress;
-    bool show_ops;
-    bool show_bad;
-    bool show_seq;
-    bool verify_sequence;
-    OpIter iter_type;
-    bool include_merged;
-};
-
 static void ShowBad(CowReader& reader, const struct CowOperation* op) {
     size_t count;
     auto buffer = std::make_unique<uint8_t[]>(op->data_length);
 
-    if (!reader.GetRawBytes(op->source, buffer.get(), op->data_length, &count)) {
+    if (!reader.GetRawBytes(op, buffer.get(), op->data_length, &count)) {
         std::cerr << "Failed to read at all!\n";
     } else {
         std::cout << "The Block data is:\n";
@@ -82,7 +71,39 @@
     }
 }
 
-static bool Inspect(const std::string& path, Options opt) {
+static bool ShowRawOpStreamV2(borrowed_fd fd, const CowHeader& header) {
+    CowParserV2 parser;
+    if (!parser.Parse(fd, header)) {
+        LOG(ERROR) << "v2 parser failed";
+        return false;
+    }
+    for (const auto& op : *parser.ops()) {
+        std::cout << op << "\n";
+        if (auto iter = parser.data_loc()->find(op.new_block); iter != parser.data_loc()->end()) {
+            std::cout << "    data loc: " << iter->second << "\n";
+        }
+    }
+    return true;
+}
+
+static bool ShowRawOpStream(borrowed_fd fd) {
+    CowHeader header;
+    if (!ReadCowHeader(fd, &header)) {
+        LOG(ERROR) << "parse header failed";
+        return false;
+    }
+
+    switch (header.prefix.major_version) {
+        case 1:
+        case 2:
+            return ShowRawOpStreamV2(fd, header);
+        default:
+            LOG(ERROR) << "unknown COW version: " << header.prefix.major_version;
+            return false;
+    }
+}
+
+static bool Inspect(const std::string& path) {
     android::base::unique_fd fd(open(path.c_str(), O_RDONLY));
     if (fd < 0) {
         PLOG(ERROR) << "open failed: " << path;
@@ -103,7 +124,7 @@
     bool has_footer = false;
     if (reader.GetFooter(&footer)) has_footer = true;
 
-    if (!opt.silent) {
+    if (!FLAGS_silent) {
         std::cout << "Version: " << header.prefix.major_version << "."
                   << header.prefix.minor_version << "\n";
         std::cout << "Header size: " << header.prefix.header_size << "\n";
@@ -119,11 +140,11 @@
         }
     }
 
-    if (!opt.silent) {
+    if (!FLAGS_silent) {
         std::cout << "Parse time: " << (parse_time.count() * 1000) << "ms\n";
     }
 
-    if (opt.verify_sequence) {
+    if (FLAGS_verify_merge_sequence) {
         std::cout << "\n";
         if (reader.VerifyMergeOps()) {
             std::cout << "\nMerge sequence is consistent.\n";
@@ -133,41 +154,56 @@
     }
 
     std::unique_ptr<ICowOpIter> iter;
-    if (opt.iter_type == Normal) {
+    if (FLAGS_order.empty()) {
         iter = reader.GetOpIter();
-    } else if (opt.iter_type == RevMerge) {
-        iter = reader.GetRevMergeOpIter(opt.include_merged);
-    } else if (opt.iter_type == Merge) {
-        iter = reader.GetMergeOpIter(opt.include_merged);
+    } else if (FLAGS_order == "reverse-merge") {
+        iter = reader.GetRevMergeOpIter(FLAGS_show_merged);
+    } else if (FLAGS_order == "merge") {
+        iter = reader.GetMergeOpIter(FLAGS_show_merged);
     }
 
     std::string buffer(header.block_size, '\0');
 
+    if (!FLAGS_silent && FLAGS_show_raw_ops) {
+        std::cout << "\n";
+        std::cout << "Listing raw op stream:\n";
+        std::cout << "----------------------\n";
+        if (!ShowRawOpStream(fd)) {
+            return false;
+        }
+    }
+
+    if (!FLAGS_silent && FLAGS_show_ops) {
+        std::cout << "\n";
+        std::cout << "Listing op stream:\n";
+        std::cout << "------------------\n";
+    }
+
     bool success = true;
     uint64_t xor_ops = 0, copy_ops = 0, replace_ops = 0, zero_ops = 0;
     while (!iter->AtEnd()) {
         const CowOperation* op = iter->Get();
 
-        if (!opt.silent && opt.show_ops) std::cout << *op << "\n";
+        if (!FLAGS_silent && FLAGS_show_ops) std::cout << *op << "\n";
 
-        if (opt.decompress && op->type == kCowReplaceOp && op->compression != kCowCompressNone) {
+        if (FLAGS_decompress && op->type == kCowReplaceOp && op->compression != kCowCompressNone) {
             if (reader.ReadData(op, buffer.data(), buffer.size()) < 0) {
                 std::cerr << "Failed to decompress for :" << *op << "\n";
                 success = false;
-                if (opt.show_bad) ShowBad(reader, op);
+                if (FLAGS_show_bad_data) ShowBad(reader, op);
             }
         }
 
-        if (op->type == kCowSequenceOp && opt.show_seq) {
+        if (op->type == kCowSequenceOp && FLAGS_show_merge_sequence) {
             size_t read;
             std::vector<uint32_t> merge_op_blocks;
             size_t seq_len = op->data_length / sizeof(uint32_t);
             merge_op_blocks.resize(seq_len);
-            if (!reader.GetRawBytes(op->source, merge_op_blocks.data(), op->data_length, &read)) {
+            if (!reader.GetRawBytes(op, merge_op_blocks.data(), op->data_length, &read)) {
                 PLOG(ERROR) << "Failed to read sequence op!";
                 return false;
             }
-            if (!opt.silent) {
+            if (!FLAGS_silent) {
                 std::cout << "Sequence for " << *op << " is :\n";
                 for (size_t i = 0; i < seq_len; i++) {
                     std::cout << std::setfill('0') << std::setw(6) << merge_op_blocks[i] << ", ";
@@ -189,7 +225,7 @@
         iter->Next();
     }
 
-    if (!opt.silent) {
+    if (!FLAGS_silent) {
         auto total_ops = replace_ops + zero_ops + copy_ops + xor_ops;
         std::cout << "Data ops: " << total_ops << "\n";
         std::cout << "Replace ops: " << replace_ops << "\n";
@@ -205,57 +241,20 @@
 }  // namespace android
 
 int main(int argc, char** argv) {
-    int ch;
-    struct android::snapshot::Options opt;
-    opt.silent = false;
-    opt.decompress = false;
-    opt.show_bad = false;
-    opt.iter_type = android::snapshot::Normal;
-    opt.verify_sequence = false;
-    opt.include_merged = false;
-    while ((ch = getopt(argc, argv, "sdbmnolva")) != -1) {
-        switch (ch) {
-            case 's':
-                opt.silent = true;
-                break;
-            case 'd':
-                opt.decompress = true;
-                break;
-            case 'b':
-                opt.show_bad = true;
-                break;
-            case 'm':
-                opt.iter_type = android::snapshot::RevMerge;
-                break;
-            case 'n':
-                opt.iter_type = android::snapshot::Merge;
-                break;
-            case 'o':
-                opt.show_seq = true;
-                break;
-            case 'l':
-                opt.show_ops = true;
-                break;
-            case 'v':
-                opt.verify_sequence = true;
-                break;
-            case 'a':
-                opt.include_merged = true;
-                break;
-            default:
-                android::snapshot::usage();
-                return 1;
-        }
-    }
+    gflags::ParseCommandLineFlags(&argc, &argv, true);
 
-    if (argc < optind + 1) {
-        android::snapshot::usage();
+    if (argc < 2) {
+        gflags::ShowUsageWithFlags(argv[0]);
+        return 1;
+    }
+    if (FLAGS_order != "" && FLAGS_order != "merge" && FLAGS_order != "reverse-merge") {
+        std::cerr << "Order must either be \"merge\" or \"reverse-merge\".\n";
         return 1;
     }
 
     android::base::InitLogging(argv, android::snapshot::MyLogger);
 
-    if (!android::snapshot::Inspect(argv[optind], opt)) {
+    if (!android::snapshot::Inspect(argv[1])) {
         return 1;
     }
     return 0;
diff --git a/fs_mgr/libsnapshot/snapshot_reader.cpp b/fs_mgr/libsnapshot/libsnapshot_cow/snapshot_reader.cpp
similarity index 82%
rename from fs_mgr/libsnapshot/snapshot_reader.cpp
rename to fs_mgr/libsnapshot/libsnapshot_cow/snapshot_reader.cpp
index 4e75ba7..f9cdbc0 100644
--- a/fs_mgr/libsnapshot/snapshot_reader.cpp
+++ b/fs_mgr/libsnapshot/libsnapshot_cow/snapshot_reader.cpp
@@ -18,73 +18,24 @@
 
 #include <android-base/file.h>
 #include <android-base/logging.h>
-#include <ext4_utils/ext4_utils.h>
 
 namespace android {
 namespace snapshot {
 
 using android::base::borrowed_fd;
 
-// Not supported.
-bool ReadOnlyFileDescriptor::Open(const char*, int, mode_t) {
-    errno = EINVAL;
-    return false;
-}
-
-bool ReadOnlyFileDescriptor::Open(const char*, int) {
-    errno = EINVAL;
-    return false;
-}
-
-ssize_t ReadOnlyFileDescriptor::Write(const void*, size_t) {
-    errno = EINVAL;
-    return false;
-}
-
-bool ReadOnlyFileDescriptor::BlkIoctl(int, uint64_t, uint64_t, int*) {
-    errno = EINVAL;
-    return false;
-}
-
-ReadFdFileDescriptor::ReadFdFileDescriptor(android::base::unique_fd&& fd) : fd_(std::move(fd)) {}
-
-ssize_t ReadFdFileDescriptor::Read(void* buf, size_t count) {
-    return read(fd_.get(), buf, count);
-}
-
-off64_t ReadFdFileDescriptor::Seek(off64_t offset, int whence) {
-    return lseek(fd_.get(), offset, whence);
-}
-
-uint64_t ReadFdFileDescriptor::BlockDevSize() {
-    return get_block_device_size(fd_.get());
-}
-
-bool ReadFdFileDescriptor::Close() {
-    fd_ = {};
-    return true;
-}
-
-bool ReadFdFileDescriptor::IsSettingErrno() {
-    return true;
-}
-
-bool ReadFdFileDescriptor::IsOpen() {
-    return fd_ >= 0;
-}
-
-bool ReadFdFileDescriptor::Flush() {
-    return true;
-}
-
-bool CompressedSnapshotReader::SetCow(std::unique_ptr<CowReader>&& cow) {
-    cow_ = std::move(cow);
-
+CompressedSnapshotReader::CompressedSnapshotReader(std::unique_ptr<ICowReader>&& cow,
+                                                   const std::optional<std::string>& source_device,
+                                                   std::optional<uint64_t> block_dev_size)
+    : cow_(std::move(cow)),
+      block_size_(cow_->GetHeader().block_size),
+      source_device_(source_device),
+      block_device_size_(block_dev_size.value_or(0)) {
     const auto& header = cow_->GetHeader();
     block_size_ = header.block_size;
 
     // Populate the operation map.
-    op_iter_ = cow_->GetOpIter();
+    op_iter_ = cow_->GetOpIter(false);
     while (!op_iter_->AtEnd()) {
         const CowOperation* op = op_iter_->Get();
         if (IsMetadataOp(*op)) {
@@ -97,16 +48,27 @@
         ops_[op->new_block] = op;
         op_iter_->Next();
     }
-
-    return true;
 }
 
-void CompressedSnapshotReader::SetSourceDevice(const std::string& source_device) {
-    source_device_ = {source_device};
+// Not supported.
+bool CompressedSnapshotReader::Open(const char*, int, mode_t) {
+    errno = EINVAL;
+    return false;
 }
 
-void CompressedSnapshotReader::SetBlockDeviceSize(uint64_t block_device_size) {
-    block_device_size_ = block_device_size;
+bool CompressedSnapshotReader::Open(const char*, int) {
+    errno = EINVAL;
+    return false;
+}
+
+ssize_t CompressedSnapshotReader::Write(const void*, size_t) {
+    errno = EINVAL;
+    return false;
+}
+
+bool CompressedSnapshotReader::BlkIoctl(int, uint64_t, uint64_t, int*) {
+    errno = EINVAL;
+    return false;
 }
 
 borrowed_fd CompressedSnapshotReader::GetSourceFd() {
diff --git a/fs_mgr/libsnapshot/snapshot_reader.h b/fs_mgr/libsnapshot/libsnapshot_cow/snapshot_reader.h
similarity index 69%
rename from fs_mgr/libsnapshot/snapshot_reader.h
rename to fs_mgr/libsnapshot/libsnapshot_cow/snapshot_reader.h
index 5e19c62..3de63ed 100644
--- a/fs_mgr/libsnapshot/snapshot_reader.h
+++ b/fs_mgr/libsnapshot/libsnapshot_cow/snapshot_reader.h
@@ -26,36 +26,16 @@
 namespace android {
 namespace snapshot {
 
-class ReadOnlyFileDescriptor : public chromeos_update_engine::FileDescriptor {
+class CompressedSnapshotReader : public chromeos_update_engine::FileDescriptor {
   public:
+    CompressedSnapshotReader(std::unique_ptr<ICowReader>&& cow,
+                             const std::optional<std::string>& source_device,
+                             std::optional<uint64_t> block_dev_size);
+
     bool Open(const char* path, int flags, mode_t mode) override;
     bool Open(const char* path, int flags) override;
     ssize_t Write(const void* buf, size_t count) override;
     bool BlkIoctl(int request, uint64_t start, uint64_t length, int* result) override;
-};
-
-class ReadFdFileDescriptor : public ReadOnlyFileDescriptor {
-  public:
-    explicit ReadFdFileDescriptor(android::base::unique_fd&& fd);
-
-    ssize_t Read(void* buf, size_t count) override;
-    off64_t Seek(off64_t offset, int whence) override;
-    uint64_t BlockDevSize() override;
-    bool Close() override;
-    bool IsSettingErrno() override;
-    bool IsOpen() override;
-    bool Flush() override;
-
-  private:
-    android::base::unique_fd fd_;
-};
-
-class CompressedSnapshotReader : public ReadOnlyFileDescriptor {
-  public:
-    bool SetCow(std::unique_ptr<CowReader>&& cow);
-    void SetSourceDevice(const std::string& source_device);
-    void SetBlockDeviceSize(uint64_t block_device_size);
-
     ssize_t Read(void* buf, size_t count) override;
     off64_t Seek(off64_t offset, int whence) override;
     uint64_t BlockDevSize() override;
@@ -68,7 +48,7 @@
     ssize_t ReadBlock(uint64_t chunk, size_t start_offset, void* buffer, size_t size);
     android::base::borrowed_fd GetSourceFd();
 
-    std::unique_ptr<CowReader> cow_;
+    std::unique_ptr<ICowReader> cow_;
     std::unique_ptr<ICowOpIter> op_iter_;
     uint32_t block_size_ = 0;
 
diff --git a/fs_mgr/libsnapshot/snapshot_reader_test.cpp b/fs_mgr/libsnapshot/libsnapshot_cow/snapshot_reader_test.cpp
similarity index 92%
rename from fs_mgr/libsnapshot/snapshot_reader_test.cpp
rename to fs_mgr/libsnapshot/libsnapshot_cow/snapshot_reader_test.cpp
index f25023d..10cb06d 100644
--- a/fs_mgr/libsnapshot/snapshot_reader_test.cpp
+++ b/fs_mgr/libsnapshot/libsnapshot_cow/snapshot_reader_test.cpp
@@ -12,8 +12,6 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include <libsnapshot/snapshot.h>
-
 #include <unordered_set>
 
 #include <android-base/file.h>
@@ -61,7 +59,7 @@
         ASSERT_EQ(fsync(base_->fd), 0);
     }
 
-    void WriteCow(ISnapshotWriter* writer) {
+    void WriteCow(ICowWriter* writer) {
         std::string new_block = MakeNewBlockString();
         std::string xor_block = MakeXorBlockString();
 
@@ -72,8 +70,8 @@
         ASSERT_TRUE(writer->Finalize());
     }
 
-    void TestBlockReads(ISnapshotWriter* writer) {
-        auto reader = writer->OpenReader();
+    void TestBlockReads(ICowWriter* writer) {
+        auto reader = writer->OpenFileDescriptor(base_->path);
         ASSERT_NE(reader, nullptr);
 
         // Test that unchanged blocks are not modified.
@@ -117,8 +115,8 @@
         ASSERT_EQ(two_blocks, zeroes);
     }
 
-    void TestByteReads(ISnapshotWriter* writer) {
-        auto reader = writer->OpenReader();
+    void TestByteReads(ICowWriter* writer) {
+        auto reader = writer->OpenFileDescriptor(base_->path);
         ASSERT_NE(reader, nullptr);
 
         std::string blob(kBlockSize * 3, 'x');
@@ -154,7 +152,7 @@
         ASSERT_EQ(got, expected);
     }
 
-    void TestReads(ISnapshotWriter* writer) {
+    void TestReads(ICowWriter* writer) {
         ASSERT_NO_FATAL_FAILURE(TestBlockReads(writer));
         ASSERT_NO_FATAL_FAILURE(TestByteReads(writer));
     }
@@ -186,10 +184,7 @@
     unique_fd cow_fd(dup(cow_->fd));
     ASSERT_GE(cow_fd, 0);
 
-    auto writer = std::make_unique<CompressedSnapshotWriter>(options);
-    writer->SetSourceDevice(base_->path);
-    ASSERT_TRUE(writer->SetCowDevice(std::move(cow_fd)));
-    ASSERT_TRUE(writer->Initialize());
+    auto writer = CreateCowWriter(kDefaultCowVersion, options, std::move(cow_fd));
     ASSERT_NO_FATAL_FAILURE(WriteCow(writer.get()));
     ASSERT_NO_FATAL_FAILURE(TestReads(writer.get()));
 }
diff --git a/fs_mgr/libsnapshot/libsnapshot_cow/writer_base.cpp b/fs_mgr/libsnapshot/libsnapshot_cow/writer_base.cpp
index 22e63d0..ff34c59 100644
--- a/fs_mgr/libsnapshot/libsnapshot_cow/writer_base.cpp
+++ b/fs_mgr/libsnapshot/libsnapshot_cow/writer_base.cpp
@@ -21,6 +21,7 @@
 #include <unistd.h>
 
 #include <android-base/logging.h>
+#include "snapshot_reader.h"
 
 // The info messages here are spammy, but as useful for update_engine. Disable
 // them when running on the host.
@@ -159,5 +160,36 @@
     return true;
 }
 
+std::unique_ptr<ICowReader> CowWriterBase::OpenReader() {
+    unique_fd cow_fd(fcntl(fd_.get(), F_DUPFD | F_DUPFD_CLOEXEC, 0));
+    if (cow_fd < 0) {
+        PLOG(ERROR) << "CowWriterV2::OpenReander: dup COW device";
+        return nullptr;
+    }
+
+    auto cow = std::make_unique<CowReader>();
+    if (!cow->Parse(std::move(cow_fd))) {
+        LOG(ERROR) << "CowWriterV2::OpenReader: unable to read COW";
+        return nullptr;
+    }
+    return cow;
+}
+
+std::unique_ptr<chromeos_update_engine::FileDescriptor> CowWriterBase::OpenFileDescriptor(
+        const std::optional<std::string>& source_device) {
+    auto reader = OpenReader();
+    if (!reader) {
+        return nullptr;
+    }
+
+    std::optional<uint64_t> block_dev_size;
+    if (options_.max_blocks) {
+        block_dev_size = {*options_.max_blocks * options_.block_size};
+    }
+
+    return std::make_unique<CompressedSnapshotReader>(std::move(reader), source_device,
+                                                      block_dev_size);
+}
+
 }  // namespace snapshot
 }  // namespace android
diff --git a/fs_mgr/libsnapshot/libsnapshot_cow/writer_base.h b/fs_mgr/libsnapshot/libsnapshot_cow/writer_base.h
index 8fa9065..c8b4772 100644
--- a/fs_mgr/libsnapshot/libsnapshot_cow/writer_base.h
+++ b/fs_mgr/libsnapshot/libsnapshot_cow/writer_base.h
@@ -43,6 +43,9 @@
     bool AddSequenceData(size_t num_ops, const uint32_t* data) override;
     uint32_t GetBlockSize() const override { return options_.block_size; }
     std::optional<uint32_t> GetMaxBlocks() const override { return options_.max_blocks; }
+    std::unique_ptr<ICowReader> OpenReader() override;
+    std::unique_ptr<FileDescriptor> OpenFileDescriptor(
+            const std::optional<std::string>& source_device) override;
 
     const CowOptions& options() const { return options_; }
 
diff --git a/fs_mgr/libsnapshot/libsnapshot_cow/writer_v2.cpp b/fs_mgr/libsnapshot/libsnapshot_cow/writer_v2.cpp
index b6603da..c549969 100644
--- a/fs_mgr/libsnapshot/libsnapshot_cow/writer_v2.cpp
+++ b/fs_mgr/libsnapshot/libsnapshot_cow/writer_v2.cpp
@@ -14,6 +14,8 @@
 // limitations under the License.
 //
 
+#include "writer_v2.h"
+
 #include <sys/types.h>
 #include <sys/uio.h>
 #include <unistd.h>
@@ -37,7 +39,7 @@
 #include <sys/ioctl.h>
 #include <unistd.h>
 
-#include "writer_v2.h"
+#include "parser_v2.h"
 
 // The info messages here are spammy, but as useful for update_engine. Disable
 // them when running on the host.
@@ -252,14 +254,20 @@
 }
 
 bool CowWriterV2::OpenForAppend(uint64_t label) {
-    auto reader = std::make_unique<CowReader>();
-    std::queue<CowOperation> toAdd;
-
-    if (!reader->Parse(fd_, {label})) {
+    if (!ReadCowHeader(fd_, &header_)) {
         return false;
     }
 
-    header_ = reader->GetHeader();
+    CowParserV2 parser;
+    if (!parser.Parse(fd_, header_, {label})) {
+        return false;
+    }
+    if (header_.prefix.major_version > 2) {
+        LOG(ERROR) << "CowWriterV2 tried to open incompatible version "
+                   << header_.prefix.major_version;
+        return false;
+    }
+
     options_.block_size = header_.block_size;
     options_.cluster_ops = header_.cluster_ops;
 
@@ -267,16 +275,10 @@
     footer_.op.num_ops = 0;
     InitPos();
 
-    auto iter = reader->GetOpIter();
-
-    while (!iter->AtEnd()) {
-        AddOperation(*iter->Get());
-        iter->Next();
+    for (const auto& op : *parser.ops()) {
+        AddOperation(op);
     }
 
-    // Free reader so we own the descriptor position again.
-    reader = nullptr;
-
     if (lseek(fd_.get(), next_op_pos_, SEEK_SET) < 0) {
         PLOG(ERROR) << "lseek failed";
         return false;
diff --git a/fs_mgr/libsnapshot/snapshot.cpp b/fs_mgr/libsnapshot/snapshot.cpp
index 5920bc2..fbea79b 100644
--- a/fs_mgr/libsnapshot/snapshot.cpp
+++ b/fs_mgr/libsnapshot/snapshot.cpp
@@ -45,10 +45,9 @@
 #include <android/snapshot/snapshot.pb.h>
 #include <libsnapshot/snapshot_stats.h>
 #include "device_info.h"
-#include "libsnapshot_cow/writer_v2.h"
+#include "libsnapshot_cow/parser_v2.h"
 #include "partition_cow_creator.h"
 #include "snapshot_metadata_updater.h"
-#include "snapshot_reader.h"
 #include "utility.h"
 
 namespace android {
@@ -3288,7 +3287,7 @@
         return Return::Error();
     }
 
-    ret = InitializeUpdateSnapshots(lock.get(), target_metadata.get(),
+    ret = InitializeUpdateSnapshots(lock.get(), dap_metadata.cow_version(), target_metadata.get(),
                                     exported_target_metadata.get(), target_suffix,
                                     all_snapshot_status);
     if (!ret.is_ok()) return ret;
@@ -3510,7 +3509,7 @@
 }
 
 Return SnapshotManager::InitializeUpdateSnapshots(
-        LockedFile* lock, MetadataBuilder* target_metadata,
+        LockedFile* lock, uint32_t cow_version, MetadataBuilder* target_metadata,
         const LpMetadata* exported_target_metadata, const std::string& target_suffix,
         const std::map<std::string, SnapshotStatus>& all_snapshot_status) {
     CHECK(lock);
@@ -3558,8 +3557,8 @@
             }
             options.compression = it->second.compression_algorithm();
 
-            CowWriterV2 writer(options, std::move(fd));
-            if (!writer.Initialize(std::nullopt) || !writer.Finalize()) {
+            auto writer = CreateCowWriter(cow_version, options, std::move(fd));
+            if (!writer->Finalize()) {
                 LOG(ERROR) << "Could not initialize COW device for " << target_partition->name();
                 return Return::Error();
             }
@@ -3609,12 +3608,12 @@
     return true;
 }
 
-std::unique_ptr<ISnapshotWriter> SnapshotManager::OpenSnapshotWriter(
+std::unique_ptr<ICowWriter> SnapshotManager::OpenSnapshotWriter(
         const android::fs_mgr::CreateLogicalPartitionParams& params,
-        const std::optional<std::string>& source_device) {
+        std::optional<uint64_t> label) {
 #if defined(LIBSNAPSHOT_NO_COW_WRITE)
     (void)params;
-    (void)source_device;
+    (void)label;
 
     LOG(ERROR) << "Snapshots cannot be written in first-stage init or recovery";
     return nullptr;
@@ -3653,16 +3652,14 @@
         return nullptr;
     }
 
-    return OpenCompressedSnapshotWriter(lock.get(), source_device, params.GetPartitionName(),
-                                        status, paths);
+    return OpenCompressedSnapshotWriter(lock.get(), status, paths, label);
 #endif
 }
 
 #if !defined(LIBSNAPSHOT_NO_COW_WRITE)
-std::unique_ptr<ISnapshotWriter> SnapshotManager::OpenCompressedSnapshotWriter(
-        LockedFile* lock, const std::optional<std::string>& source_device,
-        [[maybe_unused]] const std::string& partition_name, const SnapshotStatus& status,
-        const SnapshotPaths& paths) {
+std::unique_ptr<ICowWriter> SnapshotManager::OpenCompressedSnapshotWriter(
+        LockedFile* lock, const SnapshotStatus& status, const SnapshotPaths& paths,
+        std::optional<uint64_t> label) {
     CHECK(lock);
 
     CowOptions cow_options;
@@ -3679,11 +3676,6 @@
     // never creates this scenario.
     CHECK(status.snapshot_size() == status.device_size());
 
-    auto writer = std::make_unique<CompressedSnapshotWriter>(cow_options);
-    if (source_device) {
-        writer->SetSourceDevice(*source_device);
-    }
-
     std::string cow_path;
     if (!GetMappedImageDevicePath(paths.cow_device_name, &cow_path)) {
         LOG(ERROR) << "Could not determine path for " << paths.cow_device_name;
@@ -3695,12 +3687,14 @@
         PLOG(ERROR) << "OpenCompressedSnapshotWriter: open " << cow_path;
         return nullptr;
     }
-    if (!writer->SetCowDevice(std::move(cow_fd))) {
-        LOG(ERROR) << "Could not create COW writer from " << cow_path;
+
+    CowHeader header;
+    if (!ReadCowHeader(cow_fd, &header)) {
+        LOG(ERROR) << "OpenCompressedSnapshotWriter: read header failed";
         return nullptr;
     }
 
-    return writer;
+    return CreateCowWriter(header.prefix.major_version, cow_options, std::move(cow_fd), label);
 }
 #endif  // !defined(LIBSNAPSHOT_NO_COW_WRITE)
 
diff --git a/fs_mgr/libsnapshot/snapshot_stub.cpp b/fs_mgr/libsnapshot/snapshot_stub.cpp
index 84e2226..9354102 100644
--- a/fs_mgr/libsnapshot/snapshot_stub.cpp
+++ b/fs_mgr/libsnapshot/snapshot_stub.cpp
@@ -154,8 +154,8 @@
     return &snapshot_merge_stats;
 }
 
-std::unique_ptr<ISnapshotWriter> SnapshotManagerStub::OpenSnapshotWriter(
-        const CreateLogicalPartitionParams&, const std::optional<std::string>&) {
+std::unique_ptr<ICowWriter> SnapshotManagerStub::OpenSnapshotWriter(
+        const CreateLogicalPartitionParams&, std::optional<uint64_t>) {
     LOG(ERROR) << __FUNCTION__ << " should never be called.";
     return nullptr;
 }
diff --git a/fs_mgr/libsnapshot/snapshot_test.cpp b/fs_mgr/libsnapshot/snapshot_test.cpp
index dac1b77..0a85489 100644
--- a/fs_mgr/libsnapshot/snapshot_test.cpp
+++ b/fs_mgr/libsnapshot/snapshot_test.cpp
@@ -345,7 +345,7 @@
     }
 
     AssertionResult MapUpdateSnapshot(const std::string& name,
-                                      std::unique_ptr<ISnapshotWriter>* writer) {
+                                      std::unique_ptr<ICowWriter>* writer) {
         TestPartitionOpener opener(fake_super);
         CreateLogicalPartitionParams params{
                 .block_device = fake_super,
@@ -355,14 +355,10 @@
                 .partition_opener = &opener,
         };
 
-        auto old_partition = "/dev/block/mapper/" + GetOtherPartitionName(name);
-        auto result = sm->OpenSnapshotWriter(params, {old_partition});
+        auto result = sm->OpenSnapshotWriter(params, {});
         if (!result) {
             return AssertionFailure() << "Cannot open snapshot for writing: " << name;
         }
-        if (!result->Initialize()) {
-            return AssertionFailure() << "Cannot initialize snapshot for writing: " << name;
-        }
 
         if (writer) {
             *writer = std::move(result);
@@ -440,7 +436,7 @@
 
     // Prepare A/B slot for a partition named "test_partition".
     AssertionResult PrepareOneSnapshot(uint64_t device_size,
-                                       std::unique_ptr<ISnapshotWriter>* writer = nullptr) {
+                                       std::unique_ptr<ICowWriter>* writer = nullptr) {
         lock_ = nullptr;
 
         DeltaArchiveManifest manifest;
@@ -651,7 +647,7 @@
 
     bool userspace_snapshots = false;
     if (snapuserd_required_) {
-        std::unique_ptr<ISnapshotWriter> writer;
+        std::unique_ptr<ICowWriter> writer;
         ASSERT_TRUE(PrepareOneSnapshot(kDeviceSize, &writer));
 
         userspace_snapshots = sm->UpdateUsesUserSnapshots(lock_.get());
@@ -1160,7 +1156,7 @@
 
     AssertionResult MapOneUpdateSnapshot(const std::string& name) {
         if (snapuserd_required_) {
-            std::unique_ptr<ISnapshotWriter> writer;
+            std::unique_ptr<ICowWriter> writer;
             return MapUpdateSnapshot(name, &writer);
         } else {
             std::string path;
@@ -1181,7 +1177,7 @@
     AssertionResult WriteSnapshotAndHash(PartitionUpdate* partition) {
         std::string name = partition->partition_name() + "_b";
         if (snapuserd_required_) {
-            std::unique_ptr<ISnapshotWriter> writer;
+            std::unique_ptr<ICowWriter> writer;
             auto res = MapUpdateSnapshot(name, &writer);
             if (!res) {
                 return res;
@@ -1250,7 +1246,7 @@
     // It doesn't really matter the order, we just want copies that reference
     // blocks that won't exist if the partition shrinks.
     AssertionResult ShiftAllSnapshotBlocks(const std::string& name, uint64_t old_size) {
-        std::unique_ptr<ISnapshotWriter> writer;
+        std::unique_ptr<ICowWriter> writer;
         if (auto res = MapUpdateSnapshot(name, &writer); !res) {
             return res;
         }
@@ -1273,7 +1269,13 @@
             return AssertionFailure() << "Unable to finalize writer for " << name;
         }
 
-        auto hash = HashSnapshot(writer.get());
+        auto old_partition = "/dev/block/mapper/" + GetOtherPartitionName(name);
+        auto reader = writer->OpenFileDescriptor(old_partition);
+        if (!reader) {
+            return AssertionFailure() << "Could not open file descriptor for " << name;
+        }
+
+        auto hash = HashSnapshot(reader.get());
         if (hash.empty()) {
             return AssertionFailure() << "Unable to hash snapshot writer for " << name;
         }
@@ -1428,7 +1430,7 @@
     for (auto* partition : partitions) {
         AddOperation(partition);
 
-        std::unique_ptr<ISnapshotWriter> writer;
+        std::unique_ptr<ICowWriter> writer;
         auto res = MapUpdateSnapshot(partition->partition_name() + "_b", &writer);
         ASSERT_TRUE(res);
         ASSERT_TRUE(writer->AddZeroBlocks(0, 1));
diff --git a/fs_mgr/libsnapshot/snapshot_writer.cpp b/fs_mgr/libsnapshot/snapshot_writer.cpp
deleted file mode 100644
index 0ea424c..0000000
--- a/fs_mgr/libsnapshot/snapshot_writer.cpp
+++ /dev/null
@@ -1,179 +0,0 @@
-//
-// Copyright (C) 2020 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 <libsnapshot/snapshot_writer.h>
-
-#include <android-base/file.h>
-#include <android-base/logging.h>
-#include <payload_consumer/file_descriptor.h>
-#include "libsnapshot_cow/writer_v2.h"
-#include "snapshot_reader.h"
-
-namespace android {
-namespace snapshot {
-
-using android::base::borrowed_fd;
-using android::base::unique_fd;
-using chromeos_update_engine::FileDescriptor;
-
-void CompressedSnapshotWriter::SetSourceDevice(const std::string& source_device) {
-    source_device_ = {source_device};
-}
-
-borrowed_fd CompressedSnapshotWriter::GetSourceFd() {
-    if (!source_device_) {
-        LOG(ERROR) << "Attempted to read from source device but none was set";
-        return borrowed_fd{-1};
-    }
-
-    if (source_fd_ < 0) {
-        source_fd_.reset(open(source_device_->c_str(), O_RDONLY | O_CLOEXEC));
-        if (source_fd_ < 0) {
-            PLOG(ERROR) << "open " << *source_device_;
-            return borrowed_fd{-1};
-        }
-    }
-    return source_fd_;
-}
-
-CompressedSnapshotWriter::CompressedSnapshotWriter(const CowOptions& options) : options_(options) {}
-
-bool CompressedSnapshotWriter::SetCowDevice(android::base::unique_fd&& cow_device) {
-    cow_device_ = std::move(cow_device);
-    return true;
-}
-
-bool CompressedSnapshotWriter::Finalize() {
-    return cow_->Finalize();
-}
-
-uint64_t CompressedSnapshotWriter::GetCowSize() {
-    return cow_->GetCowSize();
-}
-
-std::unique_ptr<CowReader> CompressedSnapshotWriter::OpenCowReader() const {
-    unique_fd cow_fd(dup(cow_device_.get()));
-    if (cow_fd < 0) {
-        PLOG(ERROR) << "dup COW device";
-        return nullptr;
-    }
-
-    auto cow = std::make_unique<CowReader>();
-    if (!cow->Parse(std::move(cow_fd))) {
-        LOG(ERROR) << "Unable to read COW";
-        return nullptr;
-    }
-    return cow;
-}
-
-bool CompressedSnapshotWriter::VerifyMergeOps() const noexcept {
-    auto cow_reader = OpenCowReader();
-    if (cow_reader == nullptr) {
-        LOG(ERROR) << "Couldn't open CowReader";
-        return false;
-    }
-    return cow_reader->VerifyMergeOps();
-}
-
-std::unique_ptr<FileDescriptor> CompressedSnapshotWriter::OpenReader() {
-    auto cow = OpenCowReader();
-    if (cow == nullptr) {
-        return nullptr;
-    }
-
-    auto reader = std::make_unique<CompressedSnapshotReader>();
-    if (!reader->SetCow(std::move(cow))) {
-        LOG(ERROR) << "Unable to initialize COW reader";
-        return nullptr;
-    }
-    if (source_device_) {
-        reader->SetSourceDevice(*source_device_);
-    }
-
-    if (options_.max_blocks) {
-        reader->SetBlockDeviceSize(*options_.max_blocks * options_.block_size);
-    }
-
-    return reader;
-}
-
-bool CompressedSnapshotWriter::AddCopy(uint64_t new_block, uint64_t old_block,
-                                       uint64_t num_blocks) {
-    return cow_->AddCopy(new_block, old_block, num_blocks);
-}
-
-bool CompressedSnapshotWriter::AddRawBlocks(uint64_t new_block_start, const void* data,
-                                            size_t size) {
-    return cow_->AddRawBlocks(new_block_start, data, size);
-}
-
-bool CompressedSnapshotWriter::AddXorBlocks(uint32_t new_block_start, const void* data, size_t size,
-                                            uint32_t old_block, uint16_t offset) {
-    return cow_->AddXorBlocks(new_block_start, data, size, old_block, offset);
-}
-
-bool CompressedSnapshotWriter::AddZeroBlocks(uint64_t new_block_start, uint64_t num_blocks) {
-    return cow_->AddZeroBlocks(new_block_start, num_blocks);
-}
-
-bool CompressedSnapshotWriter::AddLabel(uint64_t label) {
-    return cow_->AddLabel(label);
-}
-
-bool CompressedSnapshotWriter::AddSequenceData(size_t num_ops, const uint32_t* data) {
-    return cow_->AddSequenceData(num_ops, data);
-}
-
-bool CompressedSnapshotWriter::Initialize() {
-    unique_fd cow_fd(dup(cow_device_.get()));
-    if (cow_fd < 0) {
-        PLOG(ERROR) << "dup COW device";
-        return false;
-    }
-
-    auto cow = std::make_unique<CowWriterV2>(options_, std::move(cow_fd));
-    if (!cow->Initialize(std::nullopt)) {
-        return false;
-    }
-    cow_ = std::move(cow);
-    return true;
-}
-
-bool CompressedSnapshotWriter::InitializeAppend(uint64_t label) {
-    unique_fd cow_fd(dup(cow_device_.get()));
-    if (cow_fd < 0) {
-        PLOG(ERROR) << "dup COW device";
-        return false;
-    }
-
-    auto cow = std::make_unique<CowWriterV2>(options_, std::move(cow_fd));
-    if (!cow->Initialize(label)) {
-        return false;
-    }
-    cow_ = std::move(cow);
-    return true;
-}
-
-uint32_t CompressedSnapshotWriter::GetBlockSize() const {
-    return cow_->GetBlockSize();
-}
-
-std::optional<uint32_t> CompressedSnapshotWriter::GetMaxBlocks() const {
-    return cow_->GetMaxBlocks();
-}
-
-}  // namespace snapshot
-}  // namespace android
diff --git a/fs_mgr/libsnapshot/snapshot_writer_test.cpp b/fs_mgr/libsnapshot/snapshot_writer_test.cpp
deleted file mode 100644
index a03632b..0000000
--- a/fs_mgr/libsnapshot/snapshot_writer_test.cpp
+++ /dev/null
@@ -1,62 +0,0 @@
-//
-// 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 <libsnapshot/snapshot.h>
-
-#include <unordered_set>
-
-#include <android-base/file.h>
-#include <gtest/gtest.h>
-#include <libsnapshot/snapshot_writer.h>
-#include <payload_consumer/file_descriptor.h>
-
-namespace android::snapshot {
-class CompressedSnapshotWriterTest : public ::testing::Test {
-  public:
-    static constexpr size_t BLOCK_SIZE = 4096;
-};
-
-TEST_F(CompressedSnapshotWriterTest, ReadAfterWrite) {
-    TemporaryFile cow_device_file{};
-    android::snapshot::CowOptions options{.block_size = BLOCK_SIZE};
-    android::snapshot::CompressedSnapshotWriter snapshot_writer{options};
-    ASSERT_TRUE(snapshot_writer.SetCowDevice(android::base::unique_fd{cow_device_file.fd}));
-    ASSERT_TRUE(snapshot_writer.Initialize());
-    std::vector<unsigned char> buffer;
-    buffer.resize(BLOCK_SIZE);
-    std::fill(buffer.begin(), buffer.end(), 123);
-
-    ASSERT_TRUE(snapshot_writer.AddRawBlocks(0, buffer.data(), buffer.size()));
-    ASSERT_TRUE(snapshot_writer.Finalize());
-    auto cow_reader = snapshot_writer.OpenReader();
-    ASSERT_NE(cow_reader, nullptr);
-    ASSERT_TRUE(snapshot_writer.AddRawBlocks(1, buffer.data(), buffer.size()));
-    ASSERT_TRUE(snapshot_writer.AddRawBlocks(2, buffer.data(), buffer.size()));
-    ASSERT_TRUE(snapshot_writer.Finalize());
-    // After wrigin some data, if we call OpenReader() again, writes should
-    // be visible to the newly opened reader. update_engine relies on this
-    // behavior for verity writes.
-    cow_reader = snapshot_writer.OpenReader();
-    ASSERT_NE(cow_reader, nullptr);
-    std::vector<unsigned char> read_back;
-    read_back.resize(buffer.size());
-    cow_reader->Seek(BLOCK_SIZE, SEEK_SET);
-    const auto bytes_read = cow_reader->Read(read_back.data(), read_back.size());
-    ASSERT_EQ((size_t)(bytes_read), BLOCK_SIZE);
-    ASSERT_EQ(read_back, buffer);
-}
-
-}  // namespace android::snapshot
diff --git a/fs_mgr/libsnapshot/snapshotctl.cpp b/fs_mgr/libsnapshot/snapshotctl.cpp
index ad3f83c..38eb719 100644
--- a/fs_mgr/libsnapshot/snapshotctl.cpp
+++ b/fs_mgr/libsnapshot/snapshotctl.cpp
@@ -133,7 +133,6 @@
 
     // Write the "new" system partition.
     auto system_target_name = "system" + target_slot;
-    auto source_device = "/dev/block/mapper/" + system_source_name;
     CreateLogicalPartitionParams clpp = {
             .block_device = fs_mgr_get_super_partition_name(target_slot_number),
             .metadata_slot = {target_slot_number},
@@ -141,15 +140,11 @@
             .partition_opener = &opener,
             .timeout_ms = 10s,
     };
-    auto writer = sm->OpenSnapshotWriter(clpp, {source_device});
+    auto writer = sm->OpenSnapshotWriter(clpp, std::nullopt);
     if (!writer) {
         std::cerr << "Could not open snapshot writer.\n";
         return false;
     }
-    if (!writer->Initialize()) {
-        std::cerr << "Could not initialize snapshot for writing.\n";
-        return false;
-    }
 
     for (uint64_t block = 0; block < system_source_size / 4096; block++) {
         if (!writer->AddCopy(block, block)) {
diff --git a/fs_mgr/libsnapshot/test_helpers.cpp b/fs_mgr/libsnapshot/test_helpers.cpp
index a224f6b..2eac347 100644
--- a/fs_mgr/libsnapshot/test_helpers.cpp
+++ b/fs_mgr/libsnapshot/test_helpers.cpp
@@ -130,12 +130,7 @@
     return true;
 }
 
-std::string HashSnapshot(ISnapshotWriter* writer) {
-    auto reader = writer->OpenReader();
-    if (!reader) {
-        return {};
-    }
-
+std::string HashSnapshot(ICowWriter::FileDescriptor* reader) {
     SHA256_CTX ctx;
     SHA256_Init(&ctx);
 
diff --git a/init/Android.bp b/init/Android.bp
index 41c7a95..f62d7b7 100644
--- a/init/Android.bp
+++ b/init/Android.bp
@@ -265,8 +265,8 @@
     ],
 }
 
-cc_binary {
-    name: "init_second_stage",
+cc_defaults {
+    name: "init_second_stage_defaults",
     recovery_available: true,
     stem: "init",
     defaults: ["init_defaults"],
@@ -304,9 +304,22 @@
             ],
         },
     },
+}
+
+cc_binary {
+    name: "init_second_stage",
+    defaults: ["init_second_stage_defaults"],
+}
+
+cc_binary {
+    name: "init_second_stage.microdroid",
+    defaults: ["init_second_stage_defaults"],
+    cflags: ["-DMICRODROID"],
+    installable: false,
     visibility: ["//packages/modules/Virtualization/microdroid"],
 }
 
+
 soong_config_module_type {
     name: "init_first_stage_cc_defaults",
     module_type: "cc_defaults",
@@ -324,12 +337,8 @@
             installable: false,
         },
     },
-}
 
-cc_binary {
-    name: "init_first_stage",
     stem: "init",
-    defaults: ["init_first_stage_defaults"],
 
     srcs: [
         "block_dev_initializer.cpp",
@@ -443,6 +452,18 @@
     install_in_root: true,
 }
 
+cc_binary {
+    name: "init_first_stage",
+    defaults: ["init_first_stage_defaults"],
+}
+
+cc_binary {
+    name: "init_first_stage.microdroid",
+    defaults: ["init_first_stage_defaults"],
+    cflags: ["-DMICRODROID"],
+    installable: false,
+}
+
 phony {
     name: "init_system",
     required: ["init_second_stage"],
diff --git a/init/test_kill_services/init_kill_services_test.cpp b/init/test_kill_services/init_kill_services_test.cpp
index dd46064..510ad8a 100644
--- a/init/test_kill_services/init_kill_services_test.cpp
+++ b/init/test_kill_services/init_kill_services_test.cpp
@@ -32,7 +32,7 @@
     // b/280514080 - servicemanager will restart apexd, and apexd will restart the
     // system when crashed. This is fine as the device recovers, but it causes
     // flakes in this test.
-    ASSERT_TRUE(WaitForProperty("init.svc.apexd", "stopped", 60s))
+    ASSERT_TRUE(WaitForProperty("init.svc.apexd", "stopped", 120s))
             << (system("cat /dev/binderfs/binder_logs/state"), "apexd won't stop");
 
     LOG(INFO) << "hello " << service_name << "!";
diff --git a/libsparse/Android.bp b/libsparse/Android.bp
index 8e83e16..5a7d0fc 100644
--- a/libsparse/Android.bp
+++ b/libsparse/Android.bp
@@ -28,6 +28,9 @@
         "libbase",
     ],
     target: {
+        darwin: {
+            enabled: true,
+        },
         windows: {
             enabled: true,
         },
@@ -52,6 +55,11 @@
     ],
 
     cflags: ["-Werror"],
+    target: {
+        darwin: {
+            enabled: true,
+        },
+    },
 }
 
 cc_binary {
diff --git a/rootdir/init.rc b/rootdir/init.rc
index 0ee85c7..5344368 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -919,15 +919,22 @@
     # encryption policies apply recursively.  These directories should never
     # contain any subdirectories other than the per-user ones.  /data/media/obb
     # is an exception that exists for legacy reasons.
-    mkdir /data/media 0770 media_rw media_rw encryption=None
-    mkdir /data/misc_ce 01771 system misc encryption=None
-    mkdir /data/misc_de 01771 system misc encryption=None
-    mkdir /data/system_ce 0770 system system encryption=None
-    mkdir /data/system_de 0770 system system encryption=None
-    mkdir /data/user 0711 system system encryption=None
-    mkdir /data/user_de 0711 system system encryption=None
-    mkdir /data/vendor_ce 0771 root root encryption=None
-    mkdir /data/vendor_de 0771 root root encryption=None
+    #
+    # Don't use any write mode bits (0222) for any of these directories, since
+    # the only process that should write to them directly is vold (since it
+    # needs to set up file-based encryption on the subdirectories), which runs
+    # as root with CAP_DAC_OVERRIDE.  This is also fully enforced via the
+    # SELinux policy.  But we also set the DAC file modes accordingly, to try to
+    # minimize differences in behavior if SELinux is set to permissive mode.
+    mkdir /data/media 0550 media_rw media_rw encryption=None
+    mkdir /data/misc_ce 0551 system misc encryption=None
+    mkdir /data/misc_de 0551 system misc encryption=None
+    mkdir /data/system_ce 0550 system system encryption=None
+    mkdir /data/system_de 0550 system system encryption=None
+    mkdir /data/user 0511 system system encryption=None
+    mkdir /data/user_de 0511 system system encryption=None
+    mkdir /data/vendor_ce 0551 root root encryption=None
+    mkdir /data/vendor_de 0551 root root encryption=None
 
     # Set the casefold flag on /data/media.  For upgrades, a restorecon can be
     # needed first to relabel the directory from media_rw_data_file.