1 //
2 // Copyright (C) 2020 The Android Open Source Project
3 //
4 // Licensed under the Apache License, Version 2.0 (the "License");
5 // you may not use this file except in compliance with the License.
6 // You may obtain a copy of the License at
7 //
8 //      http://www.apache.org/licenses/LICENSE-2.0
9 //
10 // Unless required by applicable law or agreed to in writing, software
11 // distributed under the License is distributed on an "AS IS" BASIS,
12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 // See the License for the specific language governing permissions and
14 // limitations under the License.
15 //
16 
17 #include <array>
18 #include <cstring>
19 #include <map>
20 #include <numeric>
21 #include <vector>
22 
23 #include <gtest/gtest.h>
24 #include <google/protobuf/message_lite.h>
25 #include <libsnapshot/cow_writer.h>
26 
27 #include "update_engine/payload_consumer/snapshot_extent_writer.h"
28 #include "update_engine/payload_generator/delta_diff_generator.h"
29 #include "update_engine/update_metadata.pb.h"
30 
31 namespace chromeos_update_engine {
32 
33 class FakeCowWriter : public android::snapshot::ICowWriter {
34  public:
35   struct CowOp {
36     enum { COW_COPY, COW_REPLACE, COW_ZERO } type;
37     std::vector<unsigned char> data;
38     union {
39       size_t source_block;
40       size_t num_blocks;
41     };
42   };
43   using ICowWriter::ICowWriter;
44   ~FakeCowWriter() = default;
45 
EmitCopy(uint64_t new_block,uint64_t old_block)46   bool EmitCopy(uint64_t new_block, uint64_t old_block) override {
47     operations_[new_block] = {.type = CowOp::COW_COPY,
48                               .source_block = static_cast<size_t>(old_block)};
49     return true;
50   }
EmitRawBlocks(uint64_t new_block_start,const void * data,size_t size)51   bool EmitRawBlocks(uint64_t new_block_start,
52                      const void* data,
53                      size_t size) override {
54     auto&& op = operations_[new_block_start];
55     const auto uint8_ptr = static_cast<const unsigned char*>(data);
56     op.data.insert(op.data.end(), uint8_ptr, uint8_ptr + size);
57     return true;
58   }
EmitZeroBlocks(uint64_t new_block_start,uint64_t num_blocks)59   bool EmitZeroBlocks(uint64_t new_block_start, uint64_t num_blocks) override {
60     operations_[new_block_start] = {.type = CowOp::COW_ZERO};
61     return true;
62   }
Finalize()63   bool Finalize() override {
64     finalize_called_ = true;
65     return true;
66   }
67 
EmitLabel(uint64_t label)68   bool EmitLabel(uint64_t label) {
69     label_count_++;
70     return true;
71   }
72 
73   // Return number of bytes the cow image occupies on disk.
GetCowSize()74   uint64_t GetCowSize() override {
75     return std::accumulate(
76         operations_.begin(), operations_.end(), 0, [](auto&& acc, auto&& op) {
77           return acc + op.second.data.size();
78         });
79   }
Contains(size_t block)80   bool Contains(size_t block) {
81     return operations_.find(block) != operations_.end();
82   }
83   bool finalize_called_ = true;
84   size_t label_count_ = 0;
85   std::map<size_t, CowOp> operations_;
86 };
87 
88 class SnapshotExtentWriterTest : public ::testing::Test {
89  public:
SetUp()90   void SetUp() override {}
91 
92  protected:
93   android::snapshot::CowOptions options_ = {
94       .block_size = static_cast<uint32_t>(kBlockSize)};
95   FakeCowWriter cow_writer_{options_};
96   SnapshotExtentWriter writer_{&cow_writer_};
97 };
98 
AddExtent(google::protobuf::RepeatedPtrField<Extent> * extents,size_t start_block,size_t num_blocks)99 void AddExtent(google::protobuf::RepeatedPtrField<Extent>* extents,
100                size_t start_block,
101                size_t num_blocks) {
102   auto&& extent = extents->Add();
103   extent->set_start_block(start_block);
104   extent->set_num_blocks(num_blocks);
105 }
106 
TEST_F(SnapshotExtentWriterTest,BufferWrites)107 TEST_F(SnapshotExtentWriterTest, BufferWrites) {
108   google::protobuf::RepeatedPtrField<Extent> extents;
109   AddExtent(&extents, 123, 1);
110   writer_.Init(extents, kBlockSize);
111 
112   std::vector<uint8_t> buf(kBlockSize, 0);
113   buf[123] = 231;
114   buf[231] = 123;
115   buf[buf.size() - 1] = 255;
116 
117   writer_.Write(buf.data(), kBlockSize - 1);
118   ASSERT_TRUE(cow_writer_.operations_.empty())
119       << "Haven't send data of a complete block yet, CowWriter should not be "
120          "invoked.";
121   writer_.Write(buf.data() + kBlockSize - 1, 1);
122   ASSERT_TRUE(cow_writer_.Contains(123))
123       << "Once a block of data is sent to SnapshotExtentWriter, it should "
124          "forward data to cow_writer.";
125   ASSERT_EQ(cow_writer_.operations_.size(), 1U);
126   ASSERT_EQ(buf, cow_writer_.operations_[123].data);
127 }
128 
TEST_F(SnapshotExtentWriterTest,NonBufferedWrites)129 TEST_F(SnapshotExtentWriterTest, NonBufferedWrites) {
130   google::protobuf::RepeatedPtrField<Extent> extents;
131   AddExtent(&extents, 123, 1);
132   AddExtent(&extents, 125, 1);
133   writer_.Init(extents, kBlockSize);
134 
135   std::vector<uint8_t> buf(kBlockSize * 2, 0);
136   buf[123] = 231;
137   buf[231] = 123;
138   buf[buf.size() - 1] = 255;
139 
140   writer_.Write(buf.data(), buf.size());
141   ASSERT_TRUE(cow_writer_.Contains(123));
142   ASSERT_TRUE(cow_writer_.Contains(125));
143 
144   ASSERT_EQ(cow_writer_.operations_.size(), 2U);
145   auto actual_data = cow_writer_.operations_[123].data;
146   actual_data.insert(actual_data.end(),
147                      cow_writer_.operations_[125].data.begin(),
148                      cow_writer_.operations_[125].data.end());
149   ASSERT_EQ(buf, actual_data);
150 }
151 
TEST_F(SnapshotExtentWriterTest,WriteAcrossBlockBoundary)152 TEST_F(SnapshotExtentWriterTest, WriteAcrossBlockBoundary) {
153   google::protobuf::RepeatedPtrField<Extent> extents;
154   AddExtent(&extents, 123, 1);
155   AddExtent(&extents, 125, 2);
156   writer_.Init(extents, kBlockSize);
157 
158   std::vector<uint8_t> buf(kBlockSize * 3);
159   std::memset(buf.data(), 0, buf.size());
160   buf[123] = 231;
161   buf[231] = 123;
162   buf[buf.size() - 1] = 255;
163   buf[kBlockSize - 1] = 254;
164 
165   writer_.Write(buf.data(), kBlockSize - 1);
166   ASSERT_TRUE(cow_writer_.operations_.empty())
167       << "Haven't send data of a complete block yet, CowWriter should not be "
168          "invoked.";
169   writer_.Write(buf.data() + kBlockSize - 1, 1 + kBlockSize * 2);
170   ASSERT_TRUE(cow_writer_.Contains(123));
171   ASSERT_TRUE(cow_writer_.Contains(125));
172 
173   ASSERT_EQ(cow_writer_.operations_.size(), 2U);
174   auto actual_data = cow_writer_.operations_[123].data;
175   actual_data.insert(actual_data.end(),
176                      cow_writer_.operations_[125].data.begin(),
177                      cow_writer_.operations_[125].data.end());
178   ASSERT_EQ(buf, actual_data);
179 }
180 }  // namespace chromeos_update_engine
181