Merge "V3 writer header" into main
diff --git a/fs_mgr/libsnapshot/libsnapshot_cow/writer_v3.cpp b/fs_mgr/libsnapshot/libsnapshot_cow/writer_v3.cpp
index 2b9867e..5ae5f19 100644
--- a/fs_mgr/libsnapshot/libsnapshot_cow/writer_v3.cpp
+++ b/fs_mgr/libsnapshot/libsnapshot_cow/writer_v3.cpp
@@ -54,14 +54,116 @@
 using android::base::unique_fd;
 
 CowWriterV3::CowWriterV3(const CowOptions& options, unique_fd&& fd)
-    : CowWriterBase(options, std::move(fd)) {}
+    : CowWriterBase(options, std::move(fd)) {
+    SetupHeaders();
+}
+
+void CowWriterV3::SetupHeaders() {
+    header_ = {};
+    header_.prefix.magic = kCowMagicNumber;
+    header_.prefix.major_version = 3;
+    header_.prefix.minor_version = 0;
+    header_.prefix.header_size = sizeof(CowHeaderV3);
+    header_.footer_size = 0;
+    header_.op_size = sizeof(CowOperationV3);
+    header_.block_size = options_.block_size;
+    header_.num_merge_ops = options_.num_merge_ops;
+    header_.cluster_ops = 0;
+    if (options_.scratch_space) {
+        header_.buffer_size = BUFFER_REGION_DEFAULT_SIZE;
+    }
+
+    // v3 specific fields
+    // WIP: not quite sure how some of these are calculated yet, assuming buffer_size is determined
+    // during COW size estimation
+    header_.sequence_buffer_offset = 0;
+    header_.resume_buffer_size = 0;
+    header_.op_buffer_size = 0;
+    header_.compression_algorithm = kCowCompressNone;
+    return;
+}
+
+bool CowWriterV3::ParseOptions() {
+    num_compress_threads_ = std::max(options_.num_compress_threads, 1);
+    auto parts = android::base::Split(options_.compression, ",");
+    if (parts.size() > 2) {
+        LOG(ERROR) << "failed to parse compression parameters: invalid argument count: "
+                   << parts.size() << " " << options_.compression;
+        return false;
+    }
+    auto algorithm = CompressionAlgorithmFromString(parts[0]);
+    if (!algorithm) {
+        LOG(ERROR) << "unrecognized compression: " << options_.compression;
+        return false;
+    }
+    if (parts.size() > 1) {
+        if (!android::base::ParseUint(parts[1], &compression_.compression_level)) {
+            LOG(ERROR) << "failed to parse compression level invalid type: " << parts[1];
+            return false;
+        }
+    } else {
+        compression_.compression_level =
+                CompressWorker::GetDefaultCompressionLevel(algorithm.value());
+    }
+
+    compression_.algorithm = *algorithm;
+    return true;
+}
 
 CowWriterV3::~CowWriterV3() {}
 
 bool CowWriterV3::Initialize(std::optional<uint64_t> label) {
-    LOG(ERROR) << __LINE__ << " " << __FILE__ << " <- function here should never be called";
-    if (label) return false;
-    return false;
+    if (!InitFd() || !ParseOptions()) {
+        return false;
+    }
+
+    CHECK(!label.has_value());
+
+    if (!OpenForWrite()) {
+        return false;
+    }
+
+    return true;
+}
+
+bool CowWriterV3::OpenForWrite() {
+    // This limitation is tied to the data field size in CowOperationV2.
+    // Keeping this for V3 writer <- although we
+    if (header_.block_size > std::numeric_limits<uint16_t>::max()) {
+        LOG(ERROR) << "Block size is too large";
+        return false;
+    }
+
+    if (lseek(fd_.get(), 0, SEEK_SET) < 0) {
+        PLOG(ERROR) << "lseek failed";
+        return false;
+    }
+
+    // Headers are not complete, but this ensures the file is at the right
+    // position.
+    if (!android::base::WriteFully(fd_, &header_, sizeof(header_))) {
+        PLOG(ERROR) << "write failed";
+        return false;
+    }
+
+    if (options_.scratch_space) {
+        // Initialize the scratch space
+        std::string data(header_.buffer_size, 0);
+        if (!android::base::WriteFully(fd_, data.data(), header_.buffer_size)) {
+            PLOG(ERROR) << "writing scratch space failed";
+            return false;
+        }
+    }
+
+    if (!Sync()) {
+        LOG(ERROR) << "Header sync failed";
+        return false;
+    }
+
+    next_op_pos_ = 0;
+    next_data_pos_ = 0;
+
+    return true;
 }
 
 bool CowWriterV3::EmitCopy(uint64_t new_block, uint64_t old_block, uint64_t num_blocks) {
diff --git a/fs_mgr/libsnapshot/libsnapshot_cow/writer_v3.h b/fs_mgr/libsnapshot/libsnapshot_cow/writer_v3.h
index ddd7287..2a88a12 100644
--- a/fs_mgr/libsnapshot/libsnapshot_cow/writer_v3.h
+++ b/fs_mgr/libsnapshot/libsnapshot_cow/writer_v3.h
@@ -37,6 +37,22 @@
     virtual bool EmitZeroBlocks(uint64_t new_block_start, uint64_t num_blocks) override;
     virtual bool EmitLabel(uint64_t label) override;
     virtual bool EmitSequenceData(size_t num_ops, const uint32_t* data) override;
+
+  private:
+    void SetupHeaders();
+    bool ParseOptions();
+    bool OpenForWrite();
+
+  private:
+    CowHeaderV3 header_{};
+    CowCompression compression_;
+    // in the case that we are using one thread for compression, we can store and re-use the same
+    // compressor
+
+    uint64_t next_op_pos_ = 0;
+    uint64_t next_data_pos_ = 0;
+
+    int num_compress_threads_ = 1;
 };
 
 }  // namespace snapshot