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 "update_engine/payload_consumer/partition_update_generator_android.h"
18
19 #include <filesystem>
20 #include <memory>
21 #include <utility>
22
23 #include <android-base/properties.h>
24 #include <android-base/strings.h>
25 #include <base/logging.h>
26 #include <base/strings/string_split.h>
27
28 #include "update_engine/common/boot_control_interface.h"
29 #include "update_engine/common/hash_calculator.h"
30 #include "update_engine/common/utils.h"
31
32 namespace chromeos_update_engine {
33
PartitionUpdateGeneratorAndroid(BootControlInterface * boot_control,size_t block_size)34 PartitionUpdateGeneratorAndroid::PartitionUpdateGeneratorAndroid(
35 BootControlInterface* boot_control, size_t block_size)
36 : boot_control_(boot_control), block_size_(block_size) {}
37
38 bool PartitionUpdateGeneratorAndroid::
GenerateOperationsForPartitionsNotInPayload(BootControlInterface::Slot source_slot,BootControlInterface::Slot target_slot,const std::set<std::string> & partitions_in_payload,std::vector<PartitionUpdate> * update_list)39 GenerateOperationsForPartitionsNotInPayload(
40 BootControlInterface::Slot source_slot,
41 BootControlInterface::Slot target_slot,
42 const std::set<std::string>& partitions_in_payload,
43 std::vector<PartitionUpdate>* update_list) {
44 #ifndef __ANDROID__
45 // Skip copying partitions for host verification.
46 return true;
47 #endif
48
49 auto ab_partitions = GetAbPartitionsOnDevice();
50 if (ab_partitions.empty()) {
51 LOG(ERROR) << "Failed to load static a/b partitions";
52 return false;
53 }
54
55 std::vector<PartitionUpdate> partition_updates;
56 for (const auto& partition_name : ab_partitions) {
57 if (partitions_in_payload.find(partition_name) !=
58 partitions_in_payload.end()) {
59 LOG(INFO) << partition_name << " has included in payload";
60 continue;
61 }
62 bool is_source_dynamic = false;
63 std::string source_device;
64
65 TEST_AND_RETURN_FALSE(
66 boot_control_->GetPartitionDevice(partition_name,
67 source_slot,
68 true, /* not_in_payload */
69 &source_device,
70 &is_source_dynamic));
71 bool is_target_dynamic = false;
72 std::string target_device;
73 TEST_AND_RETURN_FALSE(boot_control_->GetPartitionDevice(
74 partition_name, target_slot, true, &target_device, &is_target_dynamic));
75
76 if (is_source_dynamic || is_target_dynamic) {
77 if (is_source_dynamic != is_target_dynamic) {
78 LOG(ERROR) << "Partition " << partition_name << " is expected to be a"
79 << " static partition. source slot is "
80 << (is_source_dynamic ? "" : "not")
81 << " dynamic, and target slot " << target_slot << " is "
82 << (is_target_dynamic ? "" : "not") << " dynamic.";
83 return false;
84 } else {
85 continue;
86 }
87 }
88
89 auto source_size = utils::FileSize(source_device);
90 auto target_size = utils::FileSize(target_device);
91 if (source_size == -1 || target_size == -1 || source_size != target_size ||
92 source_size % block_size_ != 0) {
93 LOG(ERROR) << "Invalid partition size. source size " << source_size
94 << ", target size " << target_size;
95 return false;
96 }
97
98 auto partition_update = CreatePartitionUpdate(
99 partition_name, source_device, target_device, source_size);
100 if (!partition_update.has_value()) {
101 LOG(ERROR) << "Failed to create partition update for " << partition_name;
102 return false;
103 }
104 partition_updates.push_back(partition_update.value());
105 }
106 *update_list = std::move(partition_updates);
107 return true;
108 }
109
110 std::vector<std::string>
GetAbPartitionsOnDevice() const111 PartitionUpdateGeneratorAndroid::GetAbPartitionsOnDevice() const {
112 auto partition_list_str =
113 android::base::GetProperty("ro.vendor.build.ab_ota_partitions", "");
114 if (partition_list_str.empty()) {
115 partition_list_str =
116 android::base::GetProperty("ro.product.ab_ota_partitions", "");
117 }
118 return base::SplitString(partition_list_str,
119 ",",
120 base::TRIM_WHITESPACE,
121 base::SPLIT_WANT_NONEMPTY);
122 }
123
124 std::optional<PartitionUpdate>
CreatePartitionUpdate(const std::string & partition_name,const std::string & source_device,const std::string & target_device,int64_t partition_size)125 PartitionUpdateGeneratorAndroid::CreatePartitionUpdate(
126 const std::string& partition_name,
127 const std::string& source_device,
128 const std::string& target_device,
129 int64_t partition_size) {
130 PartitionUpdate partition_update;
131 partition_update.set_partition_name(partition_name);
132 auto old_partition_info = partition_update.mutable_old_partition_info();
133 old_partition_info->set_size(partition_size);
134
135 auto raw_hash = CalculateHashForPartition(source_device, partition_size);
136 if (!raw_hash.has_value()) {
137 LOG(ERROR) << "Failed to calculate hash for partition " << source_device
138 << " size: " << partition_size;
139 return {};
140 }
141 old_partition_info->set_hash(raw_hash->data(), raw_hash->size());
142 auto new_partition_info = partition_update.mutable_new_partition_info();
143 new_partition_info->set_size(partition_size);
144 new_partition_info->set_hash(raw_hash->data(), raw_hash->size());
145
146 auto copy_operation = partition_update.add_operations();
147 copy_operation->set_type(InstallOperation::SOURCE_COPY);
148 Extent copy_extent;
149 copy_extent.set_start_block(0);
150 copy_extent.set_num_blocks(partition_size / block_size_);
151
152 *copy_operation->add_src_extents() = copy_extent;
153 *copy_operation->add_dst_extents() = copy_extent;
154
155 return partition_update;
156 }
157
158 std::optional<brillo::Blob>
CalculateHashForPartition(const std::string & block_device,int64_t partition_size)159 PartitionUpdateGeneratorAndroid::CalculateHashForPartition(
160 const std::string& block_device, int64_t partition_size) {
161 // TODO(xunchang) compute the hash with ecc partitions first, the hashing
162 // behavior should match the one in SOURCE_COPY. Also, we don't have the
163 // correct hash for source partition.
164 // An alternative way is to verify the written bytes match the read bytes
165 // during filesystem verification. This could probably save us a read of
166 // partitions here.
167 brillo::Blob raw_hash;
168 if (HashCalculator::RawHashOfFile(block_device, partition_size, &raw_hash) !=
169 partition_size) {
170 LOG(ERROR) << "Failed to calculate hash for " << block_device;
171 return std::nullopt;
172 }
173
174 return raw_hash;
175 }
176
177 namespace partition_update_generator {
Create(BootControlInterface * boot_control,size_t block_size)178 std::unique_ptr<PartitionUpdateGeneratorInterface> Create(
179 BootControlInterface* boot_control, size_t block_size) {
180 CHECK(boot_control);
181
182 return std::unique_ptr<PartitionUpdateGeneratorInterface>(
183 new PartitionUpdateGeneratorAndroid(boot_control, block_size));
184 }
185 } // namespace partition_update_generator
186
187 } // namespace chromeos_update_engine
188