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 <algorithm>
18 #include <memory>
19 #include <string>
20 #include <utility>
21
22 #include <fcntl.h>
23 #include <sys/sendfile.h>
24 #include <unistd.h>
25
26 #include <android-base/strings.h>
27 #include <brillo/data_encoding.h>
28 #include <brillo/message_loops/fake_message_loop.h>
29 #include <bsdiff/bsdiff.h>
30 #include <gtest/gtest.h>
31 #include <liblp/builder.h>
32 #include <fs_mgr.h>
33 #include <liblp/liblp.h>
34
35 #include "update_engine/aosp/boot_control_android.h"
36 #include "update_engine/aosp/daemon_state_android.h"
37 #include "update_engine/aosp/update_attempter_android.h"
38 #include "update_engine/common/constants.h"
39 #include "update_engine/common/fake_boot_control.h"
40 #include "update_engine/common/fake_hardware.h"
41 #include "update_engine/common/hash_calculator.h"
42 #include "update_engine/common/prefs.h"
43 #include "update_engine/common/test_utils.h"
44 #include "update_engine/common/testing_constants.h"
45 #include "update_engine/common/utils.h"
46 #include "update_engine/payload_consumer/file_descriptor.h"
47 #include "update_engine/payload_consumer/install_plan.h"
48 #include "update_engine/payload_consumer/payload_constants.h"
49 #include "update_engine/payload_generator/delta_diff_generator.h"
50 #include "update_engine/payload_generator/extent_ranges.h"
51 #include "update_engine/payload_generator/payload_file.h"
52 #include "update_engine/payload_generator/payload_signer.h"
53 #include "update_engine/update_metadata.pb.h"
54 #include "update_engine/update_status_utils.h"
55
56 namespace chromeos_update_engine {
57
58 class UpdateAttempterAndroidIntegrationTest : public ::testing::Test,
59 public ServiceObserverInterface {
AddFakePartitionGroup()60 void AddFakePartitionGroup() {
61 dynamic_control_ = boot_control_.dynamic_control_.get();
62 auto super_device = dynamic_control_->GetSuperDevice();
63 ASSERT_TRUE(super_device.has_value());
64 super_device_ = super_device->value();
65 builder_ = android::fs_mgr::MetadataBuilder::New(
66 super_device->value(), boot_control_.GetCurrentSlot());
67 ASSERT_NE(builder_, nullptr);
68
69 // Remove dangling fake partitions, if test crashed before they might not
70 // get cleaned up properly.
71 RemoveFakePartitionGroup();
72 ASSERT_TRUE(builder_->AddGroup("fake_group", kFakePartitionSize * 2));
73 ExportPartitionTable();
74 }
RemoveFakePartitionGroup()75 void RemoveFakePartitionGroup() {
76 builder_->RemovePartition("fake_a");
77 builder_->RemovePartition("fake_b");
78 builder_->RemoveGroupAndPartitions("fake_group");
79 }
80
81 // Fill partition |path| with arbitrary data.
FillPartition(const std::string & path,const bool is_source)82 void FillPartition(const std::string& path, const bool is_source) {
83 std::array<uint8_t, kBlockSize> data;
84 EintrSafeFileDescriptor fd;
85 fd.Open(path.c_str(), O_RDWR);
86 for (size_t i = 0; i < kFakePartitionSize / kBlockSize; i++) {
87 if (is_source) {
88 std::fill(data.begin(), data.end(), i);
89 } else {
90 std::fill(
91 data.begin(), data.end(), kFakePartitionSize / kBlockSize - i - 1);
92 }
93 fd.Write(data.data(), kBlockSize);
94 }
95 }
96
SetUp()97 void SetUp() override {
98 FillPartition(old_part_.path(), true);
99 FillPartition(new_part_.path(), false);
100 ASSERT_TRUE(boot_control_.Init());
101 if (!DynamicPartitionEnabled()) {
102 return;
103 }
104 ASSERT_NO_FATAL_FAILURE(AddFakePartitionGroup());
105 message_loop_.SetAsCurrent();
106 // Set official build to false so that hash checks are non-mandatory
107 hardware_.SetIsOfficialBuild(false);
108 truncate64(blob_file_.path().c_str(), 0);
109
110 update_attempter_android_.set_update_certificates_path(
111 test_utils::GetBuildArtifactsPath(kUnittestOTACertsPath));
112
113 // Basic setup to create VABC OTA
114 manifest_.set_partial_update(true);
115 manifest_.set_minor_version(kPartialUpdateMinorPayloadVersion);
116 auto dap_group =
117 manifest_.mutable_dynamic_partition_metadata()->add_groups();
118 dap_group->set_name("fake_group");
119 dap_group->add_partition_names("fake");
120
121 manifest_.mutable_dynamic_partition_metadata()->set_snapshot_enabled(true);
122 manifest_.mutable_dynamic_partition_metadata()->set_vabc_enabled(true);
123 manifest_.mutable_dynamic_partition_metadata()->set_cow_version(
124 android::snapshot::kCowVersionMajor);
125 }
126
TearDown()127 void TearDown() override {
128 if (!builder_ || !DynamicPartitionEnabled()) {
129 return;
130 }
131 auto dynamic_control = boot_control_.GetDynamicPartitionControl();
132 if (dynamic_control) {
133 dynamic_control->UnmapAllPartitions();
134 dynamic_control->ResetUpdate(&prefs_);
135 }
136 RemoveFakePartitionGroup();
137 ExportPartitionTable();
138 }
139
CreateFakePartition()140 void CreateFakePartition() {
141 // Create a fake partition for testing purposes
142 auto partition_a = builder_->AddPartition(
143 boot_control_.GetCurrentSlot() == 0 ? "fake_a" : "fake_b",
144 "fake_group",
145 0);
146 ASSERT_NE(partition_a, nullptr);
147 ASSERT_TRUE(builder_->ResizePartition(partition_a, kFakePartitionSize));
148 ExportPartitionTable();
149 std::string source_part;
150 ASSERT_TRUE(
151 dynamic_control_->GetPartitionDevice("fake",
152 boot_control_.GetCurrentSlot(),
153 boot_control_.GetCurrentSlot(),
154 &source_part));
155 int out_fd = open(source_part.c_str(), O_RDWR);
156 ScopedFdCloser closer{&out_fd};
157 ASSERT_GE(out_fd, 0) << android::base::ErrnoNumberAsString(errno);
158 ASSERT_TRUE(utils::SendFile(out_fd, old_part_.fd(), kFakePartitionSize));
159 }
160
SendStatusUpdate(const update_engine::UpdateEngineStatus & update_engine_status)161 void SendStatusUpdate(
162 const update_engine::UpdateEngineStatus& update_engine_status) override {
163 LOG(INFO) << UpdateStatusToString(update_engine_status.status) << ", "
164 << update_engine_status.progress;
165 }
166
167 // Called whenever an update attempt is completed.
SendPayloadApplicationComplete(ErrorCode error_code)168 void SendPayloadApplicationComplete(ErrorCode error_code) override {
169 completion_code_ = error_code;
170 }
171
ExportPartitionTable()172 void ExportPartitionTable() {
173 auto metadata = builder_->Export();
174 ASSERT_NE(metadata, nullptr);
175 android::fs_mgr::UpdatePartitionTable(
176 super_device_, *metadata, boot_control_.GetCurrentSlot());
177 }
178
179 public:
DynamicPartitionEnabled()180 bool DynamicPartitionEnabled() {
181 auto dynamic_control = boot_control_.GetDynamicPartitionControl();
182 return dynamic_control &&
183 dynamic_control->GetDynamicPartitionsFeatureFlag().IsEnabled();
184 }
AddSignatureInfoToPayload(DeltaArchiveManifest * manifest,const std::string & private_key_path)185 void AddSignatureInfoToPayload(DeltaArchiveManifest* manifest,
186 const std::string& private_key_path) {
187 size_t total_blob_size = 0;
188 for (const auto& part : manifest->partitions()) {
189 for (const auto& op : part.operations()) {
190 if (!op.has_data_offset())
191 continue;
192 ASSERT_EQ(total_blob_size, op.data_offset())
193 << "Ops not ordered by blob isze";
194 total_blob_size += op.data_length();
195 }
196 }
197 // Signatures appear at the end of the blobs. Note the offset in the
198 // |manifest_|.
199 uint64_t signature_blob_length = 0;
200 if (!private_key_path.empty()) {
201 ASSERT_TRUE(PayloadSigner::SignatureBlobLength({private_key_path},
202 &signature_blob_length));
203 PayloadSigner::AddSignatureToManifest(
204 total_blob_size, signature_blob_length, manifest);
205 }
206 }
207
208 // Generate blob data according to ops specified in the manifest.
209 // Also update |new_part_|'s content to match expectation of ops.
HydratePayload(DeltaArchiveManifest * manifest)210 void HydratePayload(DeltaArchiveManifest* manifest) {
211 for (auto& partition : *manifest->mutable_partitions()) {
212 for (auto& op : *partition.mutable_operations()) {
213 if (op.type() == InstallOperation::REPLACE) {
214 ASSERT_GE(lseek64(blob_file_.fd(), op.data_offset(), SEEK_SET), 0);
215 ASSERT_TRUE(utils::SendFile(
216 new_part_.fd(), blob_file_.fd(), op.data_length()));
217 } else if (op.type() == InstallOperation::BROTLI_BSDIFF) {
218 brillo::Blob old_data;
219 ASSERT_TRUE(utils::ReadExtents(
220 old_part_.path(), op.src_extents(), &old_data, kBlockSize))
221 << "Failed to read source data: "
222 << android::base::ErrnoNumberAsString(errno);
223 brillo::Blob new_data;
224 ASSERT_TRUE(utils::ReadExtents(
225 new_part_.path(), op.dst_extents(), &new_data, kBlockSize))
226 << "Failed to read target data: "
227 << android::base::ErrnoNumberAsString(errno);
228 ScopedTempFile patch_file{"bspatch.XXXXXX", true};
229 ASSERT_EQ(bsdiff::bsdiff(old_data.data(),
230 old_data.size(),
231 new_data.data(),
232 new_data.size(),
233 patch_file.path().c_str(),
234 nullptr),
235 0);
236 op.set_data_length(utils::FileSize(patch_file.fd()));
237 const auto offset = lseek64(blob_file_.fd(), 0, SEEK_CUR);
238 ASSERT_GE(offset, 0);
239 op.set_data_offset(offset);
240 brillo::Blob src_data_hash;
241 HashCalculator::RawHashOfData(old_data, &src_data_hash);
242 op.set_src_sha256_hash(src_data_hash.data(), src_data_hash.size());
243 utils::SendFile(blob_file_.fd(), patch_file.fd(), op.data_length());
244
245 } else if (op.type() == InstallOperation::ZERO) {
246 auto zero = utils::GetReadonlyZeroString(
247 utils::BlocksInExtents(op.dst_extents()) * kBlockSize);
248 for (const auto& ext : op.dst_extents()) {
249 utils::PWriteAll(new_part_.fd(),
250 zero.data(),
251 ext.num_blocks() * kBlockSize,
252 ext.start_block() * kBlockSize);
253 }
254 } else if (op.type() == InstallOperation::SOURCE_COPY) {
255 brillo::Blob data;
256 ASSERT_TRUE(utils::ReadExtents(
257 old_part_.path(), op.src_extents(), &data, kBlockSize));
258 ASSERT_TRUE(utils::WriteExtents(
259 new_part_.path(), op.dst_extents(), data, kBlockSize));
260 } else {
261 FAIL() << "Unsupported install op type: " << op.type();
262 }
263 }
264 }
265 }
266
ApplyPayload(DeltaArchiveManifest * manifest)267 void ApplyPayload(DeltaArchiveManifest* manifest) {
268 ASSERT_FALSE(manifest->partitions().empty());
269 ASSERT_NO_FATAL_FAILURE(HydratePayload(manifest));
270 const auto private_key_path =
271 test_utils::GetBuildArtifactsPath(kUnittestPrivateKeyPath);
272 ASSERT_NO_FATAL_FAILURE(
273 AddSignatureInfoToPayload(manifest, private_key_path));
274
275 brillo::Blob hash;
276 HashCalculator::RawHashOfFile(new_part_.path(), &hash);
277 auto partition = &manifest->mutable_partitions()->at(0);
278 partition->mutable_new_partition_info()->set_size(kFakePartitionSize);
279 partition->mutable_new_partition_info()->set_hash(hash.data(), hash.size());
280 const bool source_exist =
281 std::any_of(partition->operations().begin(),
282 partition->operations().end(),
283 [](const auto& op) { return op.src_extents_size() > 0; });
284 if (source_exist) {
285 HashCalculator::RawHashOfFile(old_part_.path(), &hash);
286 partition->mutable_old_partition_info()->set_size(kFakePartitionSize);
287 partition->mutable_old_partition_info()->set_hash(hash.data(),
288 hash.size());
289 // Only create fake partition if the update is incremental
290 LOG(INFO) << "Creating fake partition";
291 ASSERT_NO_FATAL_FAILURE(CreateFakePartition());
292 }
293 uint64_t metadata_size = 0;
294 ASSERT_TRUE(PayloadFile::WritePayload(payload_file_.path(),
295 blob_file_.path(),
296 private_key_path,
297 kBrilloMajorPayloadVersion,
298 *manifest,
299 &metadata_size));
300 LOG(INFO) << "Signature offset: " << manifest->signatures_offset()
301 << ", Signature size: " << manifest->signatures_size();
302 brillo::ErrorPtr error;
303 HashCalculator::RawHashOfFile(payload_file_.path(), &hash);
304 daemon_state_.AddObserver(this);
305 ASSERT_TRUE(update_attempter_android_.ApplyPayload(
306 "file://" + payload_file_.path(),
307 0,
308 utils::FileSize(payload_file_.path()),
309 {kPayloadPropertyMetadataSize + ("=" + std::to_string(metadata_size)),
310 kPayloadPropertyFileHash +
311 ("=" + brillo::data_encoding::Base64Encode(hash))},
312 &error));
313 brillo::MessageLoop::current()->Run();
314 if (error) {
315 LOG(ERROR) << error->GetMessage();
316 }
317 ASSERT_EQ(error, nullptr);
318 ASSERT_EQ(completion_code_, ErrorCode::kSuccess);
319 }
320
321 // Compare contents of fake_b partition to |new_part_| and print difference
DumpTargetPartitionDiff()322 void DumpTargetPartitionDiff() {
323 dynamic_control_->MapAllPartitions();
324 auto partition_device =
325 dynamic_control_->GetPartitionDevice("fake",
326 1 - boot_control_.GetCurrentSlot(),
327 boot_control_.GetCurrentSlot(),
328 false);
329 if (!partition_device.has_value()) {
330 LOG(INFO) << "Failed to get target fake partition, skip diff report";
331 return;
332 }
333
334 EintrSafeFileDescriptor actual_part;
335 CHECK(actual_part.Open(partition_device->readonly_device_path.c_str(),
336 O_RDONLY));
337 EintrSafeFileDescriptor expected_part;
338 CHECK(expected_part.Open(new_part_.path().c_str(), O_RDONLY));
339
340 std::array<uint8_t, kBlockSize> actual_block;
341 std::array<uint8_t, kBlockSize> expected_block;
342 for (size_t i = 0; i < kFakePartitionSize / kBlockSize; i++) {
343 actual_part.Read(actual_block.data(), actual_block.size());
344 expected_part.Read(expected_block.data(), expected_block.size());
345 if (actual_block != expected_block) {
346 LOG(ERROR) << "Block " << i << " differs.";
347 }
348 }
349 }
350 // use 25MB max to avoid super not having enough space
351 static constexpr size_t kFakePartitionSize = 1024 * 1024 * 25;
352 static_assert(kFakePartitionSize % kBlockSize == 0);
353 BootControlAndroid boot_control_;
354
355 std::unique_ptr<android::fs_mgr::MetadataBuilder> builder_;
356 std::string super_device_;
357 FakeHardware hardware_;
358 ScopedTempFile payload_file_;
359 ScopedTempFile blob_file_{"blob_file.XXXXXX", true};
360 // Contains expected data for old partition. Will be copied to fake_a on test
361 // start.
362 ScopedTempFile old_part_{"old_part.XXXXXX", true, kFakePartitionSize};
363 // Expected data for new partition, will be compared against actual data in
364 // fake_b once test finishes.
365 ScopedTempFile new_part_{"new_part.XXXXXX", true, kFakePartitionSize};
366 DaemonStateAndroid daemon_state_;
367 MemoryPrefs prefs_;
368 ErrorCode completion_code_;
369 DynamicPartitionControlAndroid* dynamic_control_{nullptr};
370 brillo::FakeMessageLoop message_loop_{nullptr};
371
372 DeltaArchiveManifest manifest_;
373 UpdateAttempterAndroid update_attempter_android_{
374 &daemon_state_, &prefs_, &boot_control_, &hardware_, nullptr};
375 };
376
377 namespace {
378
TEST_F(UpdateAttempterAndroidIntegrationTest,NewPartitionTest)379 TEST_F(UpdateAttempterAndroidIntegrationTest, NewPartitionTest) {
380 if (!DynamicPartitionEnabled()) {
381 return;
382 }
383 auto partition = manifest_.add_partitions();
384 partition->set_partition_name("fake");
385 partition->set_estimate_cow_size(kFakePartitionSize);
386 {
387 auto op = partition->add_operations();
388 op->set_type(InstallOperation::REPLACE);
389 *op->add_dst_extents() = ExtentForRange(0, 1);
390 op->set_data_offset(0);
391 op->set_data_length(kBlockSize);
392 truncate(blob_file_.path().c_str(), kBlockSize);
393 }
394 {
395 auto op = partition->add_operations();
396 op->set_type(InstallOperation::ZERO);
397 *op->add_dst_extents() =
398 ExtentForRange(1, kFakePartitionSize / kBlockSize - 1);
399 }
400
401 ApplyPayload(&manifest_);
402 if (completion_code_ == ErrorCode::kNewRootfsVerificationError) {
403 DumpTargetPartitionDiff();
404 }
405 }
406
TEST_F(UpdateAttempterAndroidIntegrationTest,XorOpsTest)407 TEST_F(UpdateAttempterAndroidIntegrationTest, XorOpsTest) {
408 if (!DynamicPartitionEnabled()) {
409 return;
410 }
411 auto partition = manifest_.add_partitions();
412 partition->set_partition_name("fake");
413 partition->set_estimate_cow_size(kFakePartitionSize);
414 {
415 auto op = partition->add_operations();
416 op->set_type(InstallOperation::BROTLI_BSDIFF);
417 *op->add_src_extents() = ExtentForRange(0, 10);
418 *op->add_dst_extents() = ExtentForRange(0, 10);
419 }
420 {
421 auto op = partition->add_operations();
422 op->set_type(InstallOperation::BROTLI_BSDIFF);
423 *op->add_src_extents() = ExtentForRange(10, 10);
424 *op->add_dst_extents() = ExtentForRange(10, 10);
425 }
426 {
427 auto op = partition->add_operations();
428 op->set_type(InstallOperation::SOURCE_COPY);
429 *op->add_src_extents() =
430 ExtentForRange(20, kFakePartitionSize / kBlockSize - 20);
431 *op->add_dst_extents() =
432 ExtentForRange(20, kFakePartitionSize / kBlockSize - 20);
433 }
434 {
435 auto op = partition->add_merge_operations();
436 op->set_type(CowMergeOperation::COW_XOR);
437 op->set_src_offset(123);
438 *op->mutable_src_extent() = ExtentForRange(2, 8);
439 *op->mutable_dst_extent() = ExtentForRange(0, 8);
440 }
441 {
442 auto op = partition->add_merge_operations();
443 op->set_type(CowMergeOperation::COW_XOR);
444 op->set_src_offset(456);
445 *op->mutable_src_extent() = ExtentForRange(10, 8);
446 *op->mutable_dst_extent() = ExtentForRange(12, 8);
447 }
448
449 ApplyPayload(&manifest_);
450 if (completion_code_ == ErrorCode::kNewRootfsVerificationError) {
451 DumpTargetPartitionDiff();
452 }
453 }
454
455 } // namespace
456
457 } // namespace chromeos_update_engine
458