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