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 #include "misc_info.h"
17
18 #include <algorithm>
19 #include <array>
20 #include <memory>
21 #include <set>
22 #include <string>
23 #include <string_view>
24 #include <unordered_set>
25 #include <vector>
26
27 #include <android-base/logging.h>
28 #include <android-base/parseint.h>
29 #include <android-base/strings.h>
30 #include <fmt/format.h>
31
32 #include "common/libs/fs/shared_buf.h"
33 #include "common/libs/fs/shared_fd.h"
34 #include "common/libs/utils/contains.h"
35 #include "common/libs/utils/result.h"
36 #include "host/libs/avb/avb.h"
37 #include "host/libs/config/known_paths.h"
38
39 namespace cuttlefish {
40 namespace {
41
42 constexpr char kAvbVbmetaAlgorithm[] = "avb_vbmeta_algorithm";
43 constexpr char kAvbVbmetaArgs[] = "avb_vbmeta_args";
44 constexpr char kAvbVbmetaKeyPath[] = "avb_vbmeta_key_path";
45 constexpr char kDynamicPartitions[] = "dynamic_partition_list";
46 constexpr char kGoogleDynamicPartitions[] = "google_dynamic_partitions";
47 constexpr char kRollbackIndexSuffix[] = "_rollback_index_location";
48 constexpr char kSuperBlockDevices[] = "super_block_devices";
49 constexpr char kSuperPartitionGroups[] = "super_partition_groups";
50 constexpr char kUseDynamicPartitions[] = "use_dynamic_partitions";
51 constexpr char kRsa2048Algorithm[] = "SHA256_RSA2048";
52 constexpr char kRsa4096Algorithm[] = "SHA256_RSA4096";
53 constexpr std::array kNonPartitionKeysToMerge = {
54 "ab_update", "default_system_dev_certificate"};
55 // based on build/make/tools/releasetools/common.py:AVB_PARTITIONS
56 constexpr std::array kVbmetaPartitions = {"boot",
57 "init_boot",
58 "odm",
59 "odm_dlkm",
60 "vbmeta_system",
61 "vbmeta_system_dlkm",
62 "vbmeta_vendor_dlkm",
63 "vendor",
64 "vendor_boot"};
65
GetExpected(const MiscInfo & misc_info,const std::string & key)66 Result<std::string> GetExpected(const MiscInfo& misc_info,
67 const std::string& key) {
68 auto lookup = misc_info.find(key);
69 CF_EXPECTF(lookup != misc_info.end(),
70 "Unable to retrieve expected value from key: {}", key);
71 return lookup->second;
72 }
73
MergePartitionLists(const std::string & vendor,const std::string & system,const std::set<std::string> & extracted_images)74 std::string MergePartitionLists(const std::string& vendor,
75 const std::string& system,
76 const std::set<std::string>& extracted_images) {
77 const std::string full_string = fmt::format("{} {}", vendor, system);
78 const std::vector<std::string> full_list =
79 android::base::Tokenize(full_string, " ");
80 // std::set removes duplicates and orders the elements, which we want
81 const std::set<std::string> full_set(full_list.begin(), full_list.end());
82 std::set<std::string> filtered_set;
83 std::set_intersection(full_set.cbegin(), full_set.cend(),
84 extracted_images.cbegin(), extracted_images.cend(),
85 std::inserter(filtered_set, filtered_set.begin()));
86 return android::base::Join(filtered_set, " ");
87 }
88
GetPartitionList(const MiscInfo & vendor_info,const MiscInfo & system_info,const std::string & key,const std::set<std::string> & extracted_images)89 std::string GetPartitionList(const MiscInfo& vendor_info,
90 const MiscInfo& system_info,
91 const std::string& key,
92 const std::set<std::string>& extracted_images) {
93 std::string vendor_list = GetExpected(vendor_info, key).value_or("");
94 std::string system_list = GetExpected(system_info, key).value_or("");
95 return MergePartitionLists(vendor_list, system_list, extracted_images);
96 }
97
GeneratePartitionKeys(const std::string & name)98 std::vector<std::string> GeneratePartitionKeys(const std::string& name) {
99 std::vector<std::string> result;
100 result.emplace_back("avb_" + name);
101 result.emplace_back("avb_" + name + "_algorithm");
102 result.emplace_back("avb_" + name + "_key_path");
103 result.emplace_back("avb_" + name + kRollbackIndexSuffix);
104 result.emplace_back("avb_" + name + "_hashtree_enable");
105 result.emplace_back("avb_" + name + "_add_hashtree_footer_args");
106 result.emplace_back(name + "_disable_sparse");
107 result.emplace_back("building_" + name + "_image");
108 auto fs_type_key = name + "_fs_type";
109 if (name == "system") {
110 fs_type_key = "fs_type";
111 }
112 result.emplace_back(fs_type_key);
113 return result;
114 }
115
ResolveRollbackIndexConflicts(const std::string & index_string,const std::unordered_set<int> used_indices)116 Result<int> ResolveRollbackIndexConflicts(
117 const std::string& index_string,
118 const std::unordered_set<int> used_indices) {
119 int index;
120 CF_EXPECTF(android::base::ParseInt(index_string, &index),
121 "Unable to parse value {} to string. Maybe a wrong or bad value "
122 "read for the rollback index?",
123 index_string);
124 while (Contains(used_indices, index)) {
125 ++index;
126 }
127 return index;
128 }
129
GetKeyPath(const std::string_view algorithm)130 Result<std::string> GetKeyPath(const std::string_view algorithm) {
131 if (algorithm == kRsa4096Algorithm) {
132 return TestKeyRsa4096();
133 } else if (algorithm == kRsa2048Algorithm) {
134 return TestKeyRsa2048();
135 } else {
136 return CF_ERR("Unexpected algorithm. No key available.");
137 }
138 }
139
GetPubKeyPath(const std::string_view algorithm)140 Result<std::string> GetPubKeyPath(const std::string_view algorithm) {
141 if (algorithm == kRsa4096Algorithm) {
142 return TestPubKeyRsa4096();
143 } else if (algorithm == kRsa2048Algorithm) {
144 return TestPubKeyRsa2048();
145 } else {
146 return CF_ERR("Unexpected algorithm. No key available.");
147 }
148 }
149
150 } // namespace
151
ParseMiscInfo(const std::string & misc_info_contents)152 Result<MiscInfo> ParseMiscInfo(const std::string& misc_info_contents) {
153 auto lines = android::base::Split(misc_info_contents, "\n");
154 MiscInfo misc_info;
155 for (auto& line : lines) {
156 line = android::base::Trim(line);
157 if (line.size() == 0) {
158 continue;
159 }
160 auto eq_pos = line.find('=');
161 if (eq_pos == std::string::npos) {
162 LOG(WARNING) << "Line in unknown format: \"" << line << "\"";
163 continue;
164 }
165 // Not using android::base::Split here to only capture the first =
166 const auto key = android::base::Trim(line.substr(0, eq_pos));
167 const auto value = android::base::Trim(line.substr(eq_pos + 1));
168 const bool duplicate = Contains(misc_info, key) && misc_info[key] != value;
169 CF_EXPECTF(!duplicate,
170 "Duplicate key with different value. key:\"{}\", previous "
171 "value:\"{}\", this value:\"{}\"",
172 key, misc_info[key], value);
173 misc_info[key] = value;
174 }
175 return misc_info;
176 }
177
WriteMiscInfo(const MiscInfo & misc_info,const std::string & output_path)178 Result<void> WriteMiscInfo(const MiscInfo& misc_info,
179 const std::string& output_path) {
180 std::stringstream file_content;
181 for (const auto& entry : misc_info) {
182 file_content << entry.first << "=" << entry.second << "\n";
183 }
184
185 SharedFD output_file = SharedFD::Creat(output_path.c_str(), 0644);
186 CF_EXPECT(output_file->IsOpen(),
187 "Failed to open output misc file: " << output_file->StrError());
188
189 CF_EXPECT(
190 WriteAll(output_file, file_content.str()) >= 0,
191 "Failed to write output misc file contents: " << output_file->StrError());
192 return {};
193 }
194
195 // based on build/make/tools/releasetools/merge/merge_target_files.py
GetCombinedDynamicPartitions(const MiscInfo & vendor_info,const MiscInfo & system_info,const std::set<std::string> & extracted_images)196 Result<MiscInfo> GetCombinedDynamicPartitions(
197 const MiscInfo& vendor_info, const MiscInfo& system_info,
198 const std::set<std::string>& extracted_images) {
199 auto vendor_use_dp =
200 CF_EXPECT(GetExpected(vendor_info, kUseDynamicPartitions));
201 CF_EXPECTF(vendor_use_dp == "true", "Vendor build must have {}=true",
202 kUseDynamicPartitions);
203 auto system_use_dp =
204 CF_EXPECT(GetExpected(system_info, kUseDynamicPartitions));
205 CF_EXPECTF(system_use_dp == "true", "System build must have {}=true",
206 kUseDynamicPartitions);
207 MiscInfo result;
208 // copy where both files are equal
209 for (const auto& key_val : vendor_info) {
210 const auto value_result = GetExpected(system_info, key_val.first);
211 if (value_result.ok() && *value_result == key_val.second) {
212 result[key_val.first] = key_val.second;
213 }
214 }
215
216 result[kDynamicPartitions] = GetPartitionList(
217 vendor_info, system_info, kDynamicPartitions, extracted_images);
218
219 const auto block_devices_result =
220 GetExpected(vendor_info, kSuperBlockDevices);
221 if (block_devices_result.ok()) {
222 result[kSuperBlockDevices] = *block_devices_result;
223 for (const auto& block_device :
224 android::base::Tokenize(result[kSuperBlockDevices], " ")) {
225 const auto key = "super_" + block_device + "_device_size";
226 result[key] = CF_EXPECT(GetExpected(vendor_info, key));
227 }
228 }
229
230 result[kSuperPartitionGroups] =
231 CF_EXPECT(GetExpected(vendor_info, kSuperPartitionGroups));
232 for (const auto& group :
233 android::base::Tokenize(result[kSuperPartitionGroups], " ")) {
234 const auto group_size_key = "super_" + group + "_group_size";
235 result[group_size_key] =
236 CF_EXPECT(GetExpected(vendor_info, group_size_key));
237
238 const auto partition_list_key = "super_" + group + "_partition_list";
239 result[partition_list_key] = GetPartitionList(
240 vendor_info, system_info, partition_list_key, extracted_images);
241 }
242
243 // TODO(chadreynolds): add vabc_cow_version logic if we need to support older
244 // builds
245 for (const auto& key :
246 {"virtual_ab", "virtual_ab_retrofit", "lpmake", "super_metadata_device",
247 "super_partition_error_limit", "super_partition_size"}) {
248 const auto value_result = GetExpected(vendor_info, key);
249 if (value_result.ok()) {
250 result[key] = *value_result;
251 }
252 }
253 return std::move(result);
254 }
255
MergeMiscInfos(const MiscInfo & vendor_info,const MiscInfo & system_info,const MiscInfo & combined_dp_info,const std::vector<std::string> & system_partitions)256 Result<MiscInfo> MergeMiscInfos(
257 const MiscInfo& vendor_info, const MiscInfo& system_info,
258 const MiscInfo& combined_dp_info,
259 const std::vector<std::string>& system_partitions) {
260 // the combined misc info uses the vendor values as defaults
261 MiscInfo result = vendor_info;
262 std::unordered_set<int> used_indices;
263 for (const auto& partition : system_partitions) {
264 for (const auto& key : GeneratePartitionKeys(partition)) {
265 if (!Contains(system_info, key)) {
266 continue;
267 }
268 auto system_value = system_info.find(key)->second;
269 // avb_<partition>_rollback_index_location values can conflict across
270 // different builds
271 if (android::base::EndsWith(key, kRollbackIndexSuffix)) {
272 const auto index = CF_EXPECT(
273 ResolveRollbackIndexConflicts(system_value, used_indices));
274 used_indices.insert(index);
275 system_value = std::to_string(index);
276 }
277 result[key] = system_value;
278 }
279 }
280 for (const auto& key : kNonPartitionKeysToMerge) {
281 const auto value_result = GetExpected(system_info, key);
282 if (value_result.ok()) {
283 result[key] = *value_result;
284 }
285 }
286 for (const auto& key_val : combined_dp_info) {
287 result[key_val.first] = key_val.second;
288 }
289 return std::move(result);
290 }
291
GetVbmetaArgs(const MiscInfo & misc_info,const std::string & image_path)292 Result<VbmetaArgs> GetVbmetaArgs(const MiscInfo& misc_info,
293 const std::string& image_path) {
294 // The key_path value should exist, but it is a build system path
295 // We use a host artifacts relative path instead
296 CF_EXPECT(Contains(misc_info, kAvbVbmetaKeyPath));
297 const auto algorithm = CF_EXPECT(GetExpected(misc_info, kAvbVbmetaAlgorithm));
298 auto result = VbmetaArgs{
299 .algorithm = algorithm,
300 .key_path = CF_EXPECT(GetKeyPath(algorithm)),
301 };
302 // must split and add --<flag> <arg> arguments(non-equals format) separately
303 // due to how Command.AddParameter handles each argument
304 const auto extra_args_result = GetExpected(misc_info, kAvbVbmetaArgs);
305 if (extra_args_result.ok()) {
306 for (const auto& arg : android::base::Tokenize(*extra_args_result, " ")) {
307 result.extra_arguments.emplace_back(arg);
308 }
309 }
310
311 for (const auto& partition : kVbmetaPartitions) {
312 // The key_path value should exist, but it is a build system path
313 // We use a host artifacts relative path instead
314 if (Contains(misc_info, fmt::format("avb_{}_key_path", partition))) {
315 const auto partition_algorithm = CF_EXPECT(
316 GetExpected(misc_info, fmt::format("avb_{}_algorithm", partition)));
317 result.chained_partitions.emplace_back(ChainPartition{
318 .name = partition,
319 .rollback_index = CF_EXPECT(GetExpected(
320 misc_info,
321 fmt::format("avb_{}_rollback_index_location", partition))),
322 .key_path = CF_EXPECT(GetPubKeyPath(partition_algorithm)),
323 });
324 } else {
325 result.included_partitions.emplace_back(
326 fmt::format("{}/IMAGES/{}.img", image_path, partition));
327 }
328 }
329 return result;
330 }
331
332 } // namespace cuttlefish
333