Merge "libsnapshot: Implement CowWriterV3:EmitBlocks" into main am: fc94e8a96f
Original change: https://android-review.googlesource.com/c/platform/system/core/+/2809875 Change-Id: I976ab9dcc15a43e70a4293f67d731ba09315fcd7 Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
This commit is contained in:
commit
7c77bfa892
4 changed files with 137 additions and 9 deletions
|
@ -601,8 +601,8 @@ bool CowReader::GetRawBytes(const CowOperation* op, void* buffer, size_t len, si
|
|||
|
||||
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) ||
|
||||
len >= fd_size_ || offset + len > fd_size_ - sizeof(CowFooter)) {
|
||||
if (offset < header_.prefix.header_size || offset >= fd_size_ || offset + len > fd_size_ ||
|
||||
len >= fd_size_) {
|
||||
LOG(ERROR) << "invalid data offset: " << offset << ", " << len << " bytes";
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -51,6 +51,11 @@ class CowTestV3 : public ::testing::Test {
|
|||
std::unique_ptr<TemporaryFile> cow_;
|
||||
};
|
||||
|
||||
// Helper to check read sizes.
|
||||
static inline bool ReadData(CowReader& reader, const CowOperation* op, void* buffer, size_t size) {
|
||||
return reader.ReadData(op, buffer, size) == size;
|
||||
}
|
||||
|
||||
TEST_F(CowTestV3, CowHeaderV2Test) {
|
||||
CowOptions options;
|
||||
options.cluster_ops = 5;
|
||||
|
@ -120,5 +125,86 @@ TEST_F(CowTestV3, ZeroOp) {
|
|||
ASSERT_EQ(op->source_info, 0);
|
||||
}
|
||||
|
||||
TEST_F(CowTestV3, ReplaceOp) {
|
||||
CowOptions options;
|
||||
options.op_count_max = 20;
|
||||
options.scratch_space = false;
|
||||
auto writer = CreateCowWriter(3, options, GetCowFd());
|
||||
std::string data = "This is some data, believe it";
|
||||
data.resize(options.block_size, '\0');
|
||||
|
||||
ASSERT_TRUE(writer->AddRawBlocks(5, data.data(), data.size()));
|
||||
ASSERT_TRUE(writer->Finalize());
|
||||
|
||||
CowReader reader;
|
||||
ASSERT_TRUE(reader.Parse(cow_->fd));
|
||||
|
||||
const auto& header = reader.header_v3();
|
||||
ASSERT_EQ(header.prefix.magic, kCowMagicNumber);
|
||||
ASSERT_EQ(header.prefix.major_version, 3);
|
||||
ASSERT_EQ(header.prefix.minor_version, kCowVersionMinor);
|
||||
ASSERT_EQ(header.block_size, options.block_size);
|
||||
ASSERT_EQ(header.op_count, 1);
|
||||
|
||||
auto iter = reader.GetOpIter();
|
||||
ASSERT_NE(iter, nullptr);
|
||||
ASSERT_FALSE(iter->AtEnd());
|
||||
|
||||
auto op = iter->Get();
|
||||
std::string sink(data.size(), '\0');
|
||||
|
||||
ASSERT_EQ(op->type, kCowReplaceOp);
|
||||
ASSERT_EQ(op->data_length, 4096);
|
||||
ASSERT_EQ(op->new_block, 5);
|
||||
ASSERT_TRUE(ReadData(reader, op, sink.data(), sink.size()));
|
||||
ASSERT_EQ(sink, data);
|
||||
}
|
||||
|
||||
TEST_F(CowTestV3, ConsecutiveReplaceOp) {
|
||||
CowOptions options;
|
||||
options.op_count_max = 20;
|
||||
options.scratch_space = false;
|
||||
auto writer = CreateCowWriter(3, options, GetCowFd());
|
||||
std::string data;
|
||||
data.resize(options.block_size * 5);
|
||||
for (int i = 0; i < data.size(); i++) {
|
||||
data[i] = char(rand() % 256);
|
||||
}
|
||||
|
||||
ASSERT_TRUE(writer->AddRawBlocks(5, data.data(), data.size()));
|
||||
ASSERT_TRUE(writer->Finalize());
|
||||
|
||||
CowReader reader;
|
||||
ASSERT_TRUE(reader.Parse(cow_->fd));
|
||||
|
||||
const auto& header = reader.header_v3();
|
||||
ASSERT_EQ(header.prefix.magic, kCowMagicNumber);
|
||||
ASSERT_EQ(header.prefix.major_version, 3);
|
||||
ASSERT_EQ(header.prefix.minor_version, kCowVersionMinor);
|
||||
ASSERT_EQ(header.block_size, options.block_size);
|
||||
ASSERT_EQ(header.op_count, 5);
|
||||
|
||||
auto iter = reader.GetOpIter();
|
||||
ASSERT_NE(iter, nullptr);
|
||||
ASSERT_FALSE(iter->AtEnd());
|
||||
|
||||
size_t i = 0;
|
||||
std::string sink(data.size(), '\0');
|
||||
|
||||
while (!iter->AtEnd()) {
|
||||
auto op = iter->Get();
|
||||
ASSERT_EQ(op->type, kCowReplaceOp);
|
||||
ASSERT_EQ(op->data_length, options.block_size);
|
||||
ASSERT_EQ(op->new_block, 5 + i);
|
||||
ASSERT_TRUE(
|
||||
ReadData(reader, op, sink.data() + (i * options.block_size), options.block_size));
|
||||
iter->Next();
|
||||
i++;
|
||||
}
|
||||
ASSERT_EQ(sink, data);
|
||||
|
||||
ASSERT_EQ(i, 5);
|
||||
}
|
||||
|
||||
} // namespace snapshot
|
||||
} // namespace android
|
||||
|
|
|
@ -161,6 +161,9 @@ bool CowWriterV3::OpenForWrite() {
|
|||
LOG(ERROR) << "Header sync failed";
|
||||
return false;
|
||||
}
|
||||
next_data_pos_ =
|
||||
sizeof(CowHeaderV3) + header_.buffer_size + header_.op_count_max * sizeof(CowOperation);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -171,10 +174,7 @@ bool CowWriterV3::EmitCopy(uint64_t new_block, uint64_t old_block, uint64_t num_
|
|||
}
|
||||
|
||||
bool CowWriterV3::EmitRawBlocks(uint64_t new_block_start, const void* data, size_t size) {
|
||||
LOG(ERROR) << __LINE__ << " " << __FILE__ << " <- function here should never be called";
|
||||
|
||||
if (new_block_start || data || size) return false;
|
||||
return false;
|
||||
return EmitBlocks(new_block_start, data, size, 0, 0, kCowReplaceOp);
|
||||
}
|
||||
|
||||
bool CowWriterV3::EmitXorBlocks(uint32_t new_block_start, const void* data, size_t size,
|
||||
|
@ -184,6 +184,33 @@ bool CowWriterV3::EmitXorBlocks(uint32_t new_block_start, const void* data, size
|
|||
return false;
|
||||
}
|
||||
|
||||
bool CowWriterV3::EmitBlocks(uint64_t new_block_start, const void* data, size_t size,
|
||||
uint64_t old_block, uint16_t offset, uint8_t type) {
|
||||
const uint8_t* iter = reinterpret_cast<const uint8_t*>(data);
|
||||
|
||||
// Placing here until we support XOR ops
|
||||
CHECK_EQ(old_block, 0);
|
||||
CHECK_EQ(offset, 0);
|
||||
|
||||
const size_t num_blocks = (size / header_.block_size);
|
||||
|
||||
for (size_t i = 0; i < num_blocks; i++) {
|
||||
CowOperation op = {};
|
||||
op.new_block = new_block_start + i;
|
||||
|
||||
op.type = type;
|
||||
op.source_info = next_data_pos_;
|
||||
op.data_length = static_cast<uint16_t>(header_.block_size);
|
||||
if (!WriteOperation(op, iter, header_.block_size)) {
|
||||
LOG(ERROR) << "AddRawBlocks: write failed";
|
||||
return false;
|
||||
}
|
||||
iter += header_.block_size;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CowWriterV3::EmitZeroBlocks(uint64_t new_block_start, uint64_t num_blocks) {
|
||||
for (uint64_t i = 0; i < num_blocks; i++) {
|
||||
CowOperationV3 op;
|
||||
|
@ -210,7 +237,7 @@ bool CowWriterV3::EmitSequenceData(size_t num_ops, const uint32_t* data) {
|
|||
return false;
|
||||
}
|
||||
|
||||
bool CowWriterV3::WriteOperation(const CowOperationV3& op) {
|
||||
bool CowWriterV3::WriteOperation(const CowOperationV3& op, const void* data, size_t size) {
|
||||
if (IsEstimating()) {
|
||||
header_.op_count++;
|
||||
header_.op_count_max++;
|
||||
|
@ -224,10 +251,20 @@ bool CowWriterV3::WriteOperation(const CowOperationV3& op) {
|
|||
|
||||
const off_t offset = GetOpOffset(header_.op_count);
|
||||
if (!android::base::WriteFullyAtOffset(fd_, &op, sizeof(op), offset)) {
|
||||
PLOG(ERROR) << "write failed for " << op << " at " << offset;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (data && size > 0) {
|
||||
if (!android::base::WriteFullyAtOffset(fd_, data, size, next_data_pos_)) {
|
||||
PLOG(ERROR) << "write failed for data of size: " << size
|
||||
<< " at offset: " << next_data_pos_;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
header_.op_count++;
|
||||
next_data_pos_ += op.data_length;
|
||||
next_op_pos_ += sizeof(CowOperationV3);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -43,7 +43,9 @@ class CowWriterV3 : public CowWriterBase {
|
|||
void SetupHeaders();
|
||||
bool ParseOptions();
|
||||
bool OpenForWrite();
|
||||
bool WriteOperation(const CowOperationV3& op);
|
||||
bool WriteOperation(const CowOperationV3& op, const void* data = nullptr, size_t size = 0);
|
||||
bool EmitBlocks(uint64_t new_block_start, const void* data, size_t size, uint64_t old_block,
|
||||
uint16_t offset, uint8_t type);
|
||||
|
||||
off_t GetOpOffset(uint32_t op_index) const {
|
||||
CHECK_LT(op_index, header_.op_count_max);
|
||||
|
@ -55,6 +57,9 @@ class CowWriterV3 : public CowWriterBase {
|
|||
CowHeaderV3 header_{};
|
||||
CowCompression compression_;
|
||||
|
||||
uint64_t next_op_pos_ = 0;
|
||||
uint64_t next_data_pos_ = 0;
|
||||
|
||||
// in the case that we are using one thread for compression, we can store and re-use the same
|
||||
// compressor
|
||||
int num_compress_threads_ = 1;
|
||||
|
|
Loading…
Reference in a new issue