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