• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //
2 // Copyright (C) 2021 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 "update_engine/payload_consumer/install_operation_executor.h"
18 
19 #include <fcntl.h>
20 #include <unistd.h>
21 
22 #include <algorithm>
23 #include <array>
24 #include <cstring>
25 #include <limits>
26 #include <memory>
27 #include <ostream>
28 #include <utility>
29 #include <vector>
30 
31 #include <brillo/secure_blob.h>
32 #include <gtest/gtest.h>
33 #include <update_engine/update_metadata.pb.h>
34 #include <zucchini/buffer_view.h>
35 #include <zucchini/patch_writer.h>
36 #include <zucchini/zucchini.h>
37 #include <puffin/brotli_util.h>
38 
39 #include "update_engine/common/utils.h"
40 #include "update_engine/payload_consumer/extent_writer.h"
41 #include "update_engine/payload_consumer/fake_extent_writer.h"
42 #include "update_engine/payload_consumer/file_descriptor.h"
43 #include "update_engine/payload_consumer/payload_constants.h"
44 #include "update_engine/payload_generator/delta_diff_utils.h"
45 #include "update_engine/payload_generator/extent_ranges.h"
46 #include "update_engine/payload_generator/extent_utils.h"
47 
48 namespace chromeos_update_engine {
49 
operator <<(std::ostream & out,const chromeos_update_engine::InstallOperation & op)50 std::ostream& operator<<(std::ostream& out,
51                          const chromeos_update_engine::InstallOperation& op) {
52   out << InstallOperationTypeName(op.type())
53       << " SRC: " << ExtentsToString(op.src_extents())
54       << " DST: " << ExtentsToString(op.dst_extents());
55   return out;
56 }
57 
58 namespace {}  // namespace
59 
60 class InstallOperationExecutorTest : public ::testing::Test {
61  public:
62   static constexpr size_t NUM_BLOCKS = 10;
63   static constexpr size_t BLOCK_SIZE = 4096;
SetUp()64   void SetUp() override {
65     // Fill source partition with arbitrary data.
66     source_data_.resize(NUM_BLOCKS * BLOCK_SIZE);
67     target_data_.resize(NUM_BLOCKS * BLOCK_SIZE);
68     for (size_t i = 0; i < NUM_BLOCKS; i++) {
69       // Fill block with arbitrary data. We don't care about what data is being
70       // written to source partition, so as long as each block is slightly
71       // different.
72       uint32_t offset = i * BLOCK_SIZE;
73       std::fill(source_data_.begin() + offset,
74                 source_data_.begin() + offset + BLOCK_SIZE,
75                 i);
76       std::fill(target_data_.begin() + offset,
77                 target_data_.begin() + offset + BLOCK_SIZE,
78                 NUM_BLOCKS + i);
79     }
80 
81     ASSERT_TRUE(
82         utils::WriteAll(source_.fd(), source_data_.data(), source_data_.size()))
83         << "Failed to write to source partition file: " << strerror(errno);
84     ASSERT_TRUE(
85         utils::WriteAll(target_.fd(), target_data_.data(), target_data_.size()))
86         << "Failed to write to target partition file: " << strerror(errno);
87     fsync(source_.fd());
88     fsync(target_.fd());
89 
90     // set target partition to have same size as source partition.
91     // update_engine mostly assumes that target partition have the desired
92     // size, so we mock that.
93     ASSERT_GE(ftruncate64(target_.fd(), NUM_BLOCKS * BLOCK_SIZE), 0)
94         << strerror(errno) << " failed to set target partition size to "
95         << NUM_BLOCKS * BLOCK_SIZE;
96 
97     source_fd_->Open(source_.path().c_str(), O_RDONLY);
98     target_fd_->Open(target_.path().c_str(), O_RDWR);
99   }
100 
VerityUntouchedExtents(const InstallOperation & op)101   void VerityUntouchedExtents(const InstallOperation& op) {
102     ExtentRanges extent_set;
103     extent_set.AddExtent(ExtentForRange(0, 10));
104     extent_set.SubtractRepeatedExtents(op.dst_extents());
105     std::vector<Extent> untouched_extents{extent_set.extent_set().begin(),
106                                           extent_set.extent_set().end()};
107     brillo::Blob actual_data;
108     ASSERT_TRUE(utils::ReadExtents(target_.path(),
109                                    untouched_extents,
110                                    &actual_data,
111                                    extent_set.blocks() * BLOCK_SIZE,
112                                    BLOCK_SIZE));
113     const auto untouched_blocks = ExpandExtents(untouched_extents);
114     for (size_t i = 0; i < actual_data.size(); i++) {
115       const auto block_offset = i / BLOCK_SIZE;
116       const auto offset = i % BLOCK_SIZE;
117       ASSERT_EQ(
118           actual_data[i],
119           static_cast<uint8_t>(NUM_BLOCKS + untouched_blocks[block_offset]))
120           << "After performing op " << op << ", offset " << offset
121           << " in block " << GetNthBlock(untouched_extents, block_offset)
122           << " is modified but it shouldn't.";
123     }
124   }
125   ScopedTempFile source_{"source_partition.XXXXXXXX", true};
126   ScopedTempFile target_{"target_partition.XXXXXXXX", true};
127   FileDescriptorPtr source_fd_ = std::make_shared<EintrSafeFileDescriptor>();
128   FileDescriptorPtr target_fd_ = std::make_shared<EintrSafeFileDescriptor>();
129   std::vector<uint8_t> source_data_;
130   std::vector<uint8_t> target_data_;
131 
132   InstallOperationExecutor executor_{BLOCK_SIZE};
133 };
134 
TEST_F(InstallOperationExecutorTest,ReplaceOpTest)135 TEST_F(InstallOperationExecutorTest, ReplaceOpTest) {
136   InstallOperation op;
137   op.set_type(InstallOperation::REPLACE);
138   *op.mutable_dst_extents()->Add() = ExtentForRange(2, 2);
139   *op.mutable_dst_extents()->Add() = ExtentForRange(6, 2);
140   op.set_data_length(BLOCK_SIZE * 4);
141   brillo::Blob expected_data;
142   expected_data.resize(BLOCK_SIZE * 4);
143   // Fill buffer with arbitrary data. Doesn't matter what it is. Each block
144   // needs to be different so that we can ensure the InstallOperationExecutor
145   // is reading data from the correct offset.
146   for (int i = 0; i < 4; i++) {
147     std::fill(&expected_data[i * BLOCK_SIZE],
148               &expected_data[(i + 1) * BLOCK_SIZE],
149               i + 99);
150   }
151   auto writer = std::make_unique<DirectExtentWriter>(target_fd_);
152   ASSERT_TRUE(executor_.ExecuteReplaceOperation(
153       op, std::move(writer), expected_data.data(), expected_data.size()));
154 
155   brillo::Blob actual_data;
156   utils::ReadExtents(
157       target_.path(),
158       std::vector<Extent>{op.dst_extents().begin(), op.dst_extents().end()},
159       &actual_data,
160       BLOCK_SIZE * 4,
161       BLOCK_SIZE);
162   ASSERT_EQ(actual_data, expected_data);
163   VerityUntouchedExtents(op);
164 }
165 
TEST_F(InstallOperationExecutorTest,ZeroOrDiscardeOpTest)166 TEST_F(InstallOperationExecutorTest, ZeroOrDiscardeOpTest) {
167   InstallOperation op;
168   op.set_type(InstallOperation::ZERO);
169   *op.mutable_dst_extents()->Add() = ExtentForRange(2, 2);
170   *op.mutable_dst_extents()->Add() = ExtentForRange(6, 2);
171   auto writer = std::make_unique<DirectExtentWriter>(target_fd_);
172   ASSERT_TRUE(executor_.ExecuteZeroOrDiscardOperation(op, std::move(writer)));
173   brillo::Blob actual_data;
174   utils::ReadExtents(
175       target_.path(),
176       std::vector<Extent>{op.dst_extents().begin(), op.dst_extents().end()},
177       &actual_data,
178       BLOCK_SIZE * 4,
179       BLOCK_SIZE);
180   for (size_t i = 0; i < actual_data.size(); i++) {
181     ASSERT_EQ(actual_data[i], 0U) << "position " << i << " isn't zeroed!";
182   }
183   VerityUntouchedExtents(op);
184 }
185 
TEST_F(InstallOperationExecutorTest,SourceCopyOpTest)186 TEST_F(InstallOperationExecutorTest, SourceCopyOpTest) {
187   InstallOperation op;
188   op.set_type(InstallOperation::SOURCE_COPY);
189   *op.mutable_src_extents()->Add() = ExtentForRange(1, 2);
190   *op.mutable_src_extents()->Add() = ExtentForRange(5, 1);
191   *op.mutable_src_extents()->Add() = ExtentForRange(7, 1);
192 
193   *op.mutable_dst_extents()->Add() = ExtentForRange(2, 2);
194   *op.mutable_dst_extents()->Add() = ExtentForRange(6, 2);
195 
196   auto writer = std::make_unique<DirectExtentWriter>(target_fd_);
197   ASSERT_TRUE(
198       executor_.ExecuteSourceCopyOperation(op, std::move(writer), source_fd_));
199   brillo::Blob actual_data;
200   utils::ReadExtents(
201       target_.path(),
202       std::vector<Extent>{op.dst_extents().begin(), op.dst_extents().end()},
203       &actual_data,
204       BLOCK_SIZE * 4,
205       BLOCK_SIZE);
206   brillo::Blob expected_data;
207   utils::ReadExtents(
208       source_.path(),
209       std::vector<Extent>{op.src_extents().begin(), op.src_extents().end()},
210       &expected_data,
211       BLOCK_SIZE * 4,
212       BLOCK_SIZE);
213 
214   ASSERT_EQ(expected_data.size(), actual_data.size());
215   for (size_t i = 0; i < actual_data.size(); i++) {
216     const auto block_offset = i / BLOCK_SIZE;
217     const auto offset = i % BLOCK_SIZE;
218     ASSERT_EQ(actual_data[i], expected_data[i])
219         << "After performing op " << op << ", offset " << offset << " in  ["
220         << GetNthBlock(op.src_extents(), block_offset) << " -> "
221         << GetNthBlock(op.dst_extents(), block_offset) << "]"
222         << " is not copied correctly";
223   }
224   VerityUntouchedExtents(op);
225 }
226 
TEST_F(InstallOperationExecutorTest,ZucchiniOpTest)227 TEST_F(InstallOperationExecutorTest, ZucchiniOpTest) {
228   InstallOperation op;
229   op.set_type(InstallOperation::ZUCCHINI);
230   *op.mutable_src_extents()->Add() = ExtentForRange(0, NUM_BLOCKS);
231   *op.mutable_dst_extents()->Add() = ExtentForRange(0, NUM_BLOCKS);
232 
233   // Make a zucchini patch
234   std::vector<Extent> src_extents{ExtentForRange(0, NUM_BLOCKS)};
235   std::vector<Extent> dst_extents{ExtentForRange(0, NUM_BLOCKS)};
236   PayloadGenerationConfig config{
237       .version = PayloadVersion(kBrilloMajorPayloadVersion,
238                                 kZucchiniMinorPayloadVersion)};
239   const FilesystemInterface::File empty;
240   diff_utils::BestDiffGenerator best_diff_generator(
241       source_data_, target_data_, src_extents, dst_extents, empty, empty, config);
242   std::vector<uint8_t> patch_data = target_data_;  // Fake the full operation
243   AnnotatedOperation aop;
244   // Zucchini is enabled only on files with certain extensions
245   aop.name = "test.so";
246   ASSERT_TRUE(best_diff_generator.GenerateBestDiffOperation(
247       {{InstallOperation::ZUCCHINI, 1024 * BLOCK_SIZE}}, &aop, &patch_data));
248   ASSERT_EQ(InstallOperation::ZUCCHINI, aop.op.type());
249 
250   // Call the executor
251   ScopedTempFile patched{"patched.XXXXXXXX", true};
252   FileDescriptorPtr patched_fd = std::make_shared<EintrSafeFileDescriptor>();
253   patched_fd->Open(patched.path().c_str(), O_RDWR);
254   std::unique_ptr<ExtentWriter> writer(new DirectExtentWriter(patched_fd));
255   writer->Init(op.dst_extents(), BLOCK_SIZE);
256   ASSERT_TRUE(executor_.ExecuteDiffOperation(
257       op, std::move(writer), source_fd_, patch_data.data(), patch_data.size()));
258 
259   // Compare the result
260   std::vector<uint8_t> patched_data;
261   ASSERT_TRUE(utils::ReadFile(patched.path(), &patched_data));
262   ASSERT_EQ(NUM_BLOCKS * BLOCK_SIZE, patched_data.size());
263   ASSERT_EQ(target_data_, patched_data);
264 }
265 
TEST_F(InstallOperationExecutorTest,GetNthBlockTest)266 TEST_F(InstallOperationExecutorTest, GetNthBlockTest) {
267   std::vector<Extent> extents;
268   extents.emplace_back(ExtentForRange(10, 3));
269   extents.emplace_back(ExtentForRange(20, 2));
270   extents.emplace_back(ExtentForRange(30, 1));
271   extents.emplace_back(ExtentForRange(40, 4));
272 
273   ASSERT_EQ(GetNthBlock(extents, 0), 10U);
274   ASSERT_EQ(GetNthBlock(extents, 2), 12U);
275   ASSERT_EQ(GetNthBlock(extents, 3), 20U);
276   ASSERT_EQ(GetNthBlock(extents, 4), 21U);
277   ASSERT_EQ(GetNthBlock(extents, 5), 30U);
278   ASSERT_EQ(GetNthBlock(extents, 6), 40U);
279   ASSERT_EQ(GetNthBlock(extents, 7), 41U);
280   ASSERT_EQ(GetNthBlock(extents, 8), 42U);
281 }
282 
283 }  // namespace chromeos_update_engine
284