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