blob: e671a30b1278793fd72dc4787feb22680b605992 [file] [log] [blame]
Tianjie Xud1188332019-05-24 16:08:45 -07001/*
2 * Copyright (C) 2019 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 "updater/updater_runtime.h"
18
19#include <algorithm>
20#include <chrono>
21#include <iterator>
22#include <optional>
23#include <string>
24#include <type_traits>
25#include <vector>
26
27#include <android-base/logging.h>
28#include <android-base/parseint.h>
29#include <android-base/strings.h>
30#include <fs_mgr.h>
31#include <fs_mgr_dm_linear.h>
32#include <libdm/dm.h>
33#include <liblp/builder.h>
34
35using android::dm::DeviceMapper;
36using android::dm::DmDeviceState;
37using android::fs_mgr::CreateLogicalPartition;
David Anderson3cbd7ae2019-08-14 12:31:58 -070038using android::fs_mgr::CreateLogicalPartitionParams;
Tianjie Xud1188332019-05-24 16:08:45 -070039using android::fs_mgr::DestroyLogicalPartition;
40using android::fs_mgr::LpMetadata;
41using android::fs_mgr::MetadataBuilder;
42using android::fs_mgr::Partition;
43using android::fs_mgr::PartitionOpener;
Yumi Yukimura4c1835f2024-03-24 01:39:24 +080044using android::fs_mgr::ReadFromImageBlob;
Yifan Hongbc7e1db2020-04-28 13:26:33 -070045using android::fs_mgr::SlotNumberForSlotSuffix;
Tianjie Xud1188332019-05-24 16:08:45 -070046
47static constexpr std::chrono::milliseconds kMapTimeout{ 1000 };
48
49static std::string GetSuperDevice() {
50 return "/dev/block/by-name/" + fs_mgr_get_super_partition_name();
51}
52
Yifan Hongbc7e1db2020-04-28 13:26:33 -070053static std::string AddSlotSuffix(const std::string& partition_name) {
54 return partition_name + fs_mgr_get_slot_suffix();
55}
56
57static bool UnmapPartitionWithSuffixOnDeviceMapper(const std::string& partition_name_suffix) {
58 auto state = DeviceMapper::Instance().GetState(partition_name_suffix);
Tianjie Xud1188332019-05-24 16:08:45 -070059 if (state == DmDeviceState::INVALID) {
60 return true;
61 }
62 if (state == DmDeviceState::ACTIVE) {
Yifan Hongbc7e1db2020-04-28 13:26:33 -070063 return DestroyLogicalPartition(partition_name_suffix);
Tianjie Xud1188332019-05-24 16:08:45 -070064 }
65 LOG(ERROR) << "Unknown device mapper state: "
66 << static_cast<std::underlying_type_t<DmDeviceState>>(state);
67 return false;
68}
69
70bool UpdaterRuntime::MapPartitionOnDeviceMapper(const std::string& partition_name,
71 std::string* path) {
Yifan Hongbc7e1db2020-04-28 13:26:33 -070072 auto partition_name_suffix = AddSlotSuffix(partition_name);
73 auto state = DeviceMapper::Instance().GetState(partition_name_suffix);
Tianjie Xud1188332019-05-24 16:08:45 -070074 if (state == DmDeviceState::INVALID) {
David Anderson3cbd7ae2019-08-14 12:31:58 -070075 CreateLogicalPartitionParams params = {
76 .block_device = GetSuperDevice(),
Yifan Hongbc7e1db2020-04-28 13:26:33 -070077 // If device supports A/B, apply non-A/B update to the partition at current slot. Otherwise,
78 // SlotNumberForSlotSuffix("") returns 0.
79 .metadata_slot = SlotNumberForSlotSuffix(fs_mgr_get_slot_suffix()),
80 // If device supports A/B, apply non-A/B update to the partition at current slot. Otherwise,
81 // fs_mgr_get_slot_suffix() returns empty string.
82 .partition_name = partition_name_suffix,
David Anderson3cbd7ae2019-08-14 12:31:58 -070083 .force_writable = true,
84 .timeout_ms = kMapTimeout,
85 };
86 return CreateLogicalPartition(params, path);
Tianjie Xud1188332019-05-24 16:08:45 -070087 }
88
89 if (state == DmDeviceState::ACTIVE) {
Yifan Hongbc7e1db2020-04-28 13:26:33 -070090 return DeviceMapper::Instance().GetDmDevicePathByName(partition_name_suffix, path);
Tianjie Xud1188332019-05-24 16:08:45 -070091 }
92 LOG(ERROR) << "Unknown device mapper state: "
93 << static_cast<std::underlying_type_t<DmDeviceState>>(state);
94 return false;
95}
96
97bool UpdaterRuntime::UnmapPartitionOnDeviceMapper(const std::string& partition_name) {
Yifan Hongbc7e1db2020-04-28 13:26:33 -070098 return ::UnmapPartitionWithSuffixOnDeviceMapper(AddSlotSuffix(partition_name));
Tianjie Xud1188332019-05-24 16:08:45 -070099}
100
101namespace { // Ops
102
103struct OpParameters {
104 std::vector<std::string> tokens;
105 MetadataBuilder* builder;
106
107 bool ExpectArgSize(size_t size) const {
108 CHECK(!tokens.empty());
109 auto actual = tokens.size() - 1;
110 if (actual != size) {
111 LOG(ERROR) << "Op " << op() << " expects " << size << " args, got " << actual;
112 return false;
113 }
114 return true;
115 }
116 const std::string& op() const {
117 CHECK(!tokens.empty());
118 return tokens[0];
119 }
120 const std::string& arg(size_t pos) const {
121 CHECK_LE(pos + 1, tokens.size());
122 return tokens[pos + 1];
123 }
124 std::optional<uint64_t> uint_arg(size_t pos, const std::string& name) const {
125 auto str = arg(pos);
126 uint64_t ret;
127 if (!android::base::ParseUint(str, &ret)) {
128 LOG(ERROR) << "Op " << op() << " expects uint64 for argument " << name << ", got " << str;
129 return std::nullopt;
130 }
131 return ret;
132 }
133};
134
135using OpFunction = std::function<bool(const OpParameters&)>;
136using OpMap = std::map<std::string, OpFunction>;
137
138bool PerformOpResize(const OpParameters& params) {
139 if (!params.ExpectArgSize(2)) return false;
Yifan Hongbc7e1db2020-04-28 13:26:33 -0700140 const auto& partition_name_suffix = AddSlotSuffix(params.arg(0));
Tianjie Xud1188332019-05-24 16:08:45 -0700141 auto size = params.uint_arg(1, "size");
142 if (!size.has_value()) return false;
143
Yifan Hongbc7e1db2020-04-28 13:26:33 -0700144 auto partition = params.builder->FindPartition(partition_name_suffix);
Tianjie Xud1188332019-05-24 16:08:45 -0700145 if (partition == nullptr) {
Yifan Hongbc7e1db2020-04-28 13:26:33 -0700146 LOG(ERROR) << "Failed to find partition " << partition_name_suffix
Tianjie Xud1188332019-05-24 16:08:45 -0700147 << " in dynamic partition metadata.";
148 return false;
149 }
Yifan Hongbc7e1db2020-04-28 13:26:33 -0700150 if (!UnmapPartitionWithSuffixOnDeviceMapper(partition_name_suffix)) {
151 LOG(ERROR) << "Cannot unmap " << partition_name_suffix << " before resizing.";
Tianjie Xud1188332019-05-24 16:08:45 -0700152 return false;
153 }
154 if (!params.builder->ResizePartition(partition, size.value())) {
Yifan Hongbc7e1db2020-04-28 13:26:33 -0700155 LOG(ERROR) << "Failed to resize partition " << partition_name_suffix << " to size " << *size
156 << ".";
Tianjie Xud1188332019-05-24 16:08:45 -0700157 return false;
158 }
159 return true;
160}
161
162bool PerformOpRemove(const OpParameters& params) {
163 if (!params.ExpectArgSize(1)) return false;
Yifan Hongbc7e1db2020-04-28 13:26:33 -0700164 const auto& partition_name_suffix = AddSlotSuffix(params.arg(0));
Tianjie Xud1188332019-05-24 16:08:45 -0700165
Yifan Hongbc7e1db2020-04-28 13:26:33 -0700166 if (!UnmapPartitionWithSuffixOnDeviceMapper(partition_name_suffix)) {
167 LOG(ERROR) << "Cannot unmap " << partition_name_suffix << " before removing.";
Tianjie Xud1188332019-05-24 16:08:45 -0700168 return false;
169 }
Yifan Hongbc7e1db2020-04-28 13:26:33 -0700170 params.builder->RemovePartition(partition_name_suffix);
Tianjie Xud1188332019-05-24 16:08:45 -0700171 return true;
172}
173
174bool PerformOpAdd(const OpParameters& params) {
175 if (!params.ExpectArgSize(2)) return false;
Yifan Hongbc7e1db2020-04-28 13:26:33 -0700176 const auto& partition_name_suffix = AddSlotSuffix(params.arg(0));
177 const auto& group_name_suffix = AddSlotSuffix(params.arg(1));
Tianjie Xud1188332019-05-24 16:08:45 -0700178
Yifan Hongbc7e1db2020-04-28 13:26:33 -0700179 if (params.builder->AddPartition(partition_name_suffix, group_name_suffix,
180 LP_PARTITION_ATTR_READONLY) == nullptr) {
181 LOG(ERROR) << "Failed to add partition " << partition_name_suffix << " to group "
182 << group_name_suffix << ".";
Tianjie Xud1188332019-05-24 16:08:45 -0700183 return false;
184 }
185 return true;
186}
187
188bool PerformOpMove(const OpParameters& params) {
189 if (!params.ExpectArgSize(2)) return false;
Yifan Hongbc7e1db2020-04-28 13:26:33 -0700190 const auto& partition_name_suffix = AddSlotSuffix(params.arg(0));
191 const auto& new_group_name_suffix = AddSlotSuffix(params.arg(1));
Tianjie Xud1188332019-05-24 16:08:45 -0700192
Yifan Hongbc7e1db2020-04-28 13:26:33 -0700193 auto partition = params.builder->FindPartition(partition_name_suffix);
Tianjie Xud1188332019-05-24 16:08:45 -0700194 if (partition == nullptr) {
Yifan Hongbc7e1db2020-04-28 13:26:33 -0700195 LOG(ERROR) << "Cannot move partition " << partition_name_suffix << " to group "
196 << new_group_name_suffix << " because it is not found.";
Tianjie Xud1188332019-05-24 16:08:45 -0700197 return false;
198 }
199
Yifan Hongbc7e1db2020-04-28 13:26:33 -0700200 auto old_group_name_suffix = partition->group_name();
201 if (old_group_name_suffix != new_group_name_suffix) {
202 if (!params.builder->ChangePartitionGroup(partition, new_group_name_suffix)) {
203 LOG(ERROR) << "Cannot move partition " << partition_name_suffix << " from group "
204 << old_group_name_suffix << " to group " << new_group_name_suffix << ".";
Tianjie Xud1188332019-05-24 16:08:45 -0700205 return false;
206 }
207 }
208 return true;
209}
210
211bool PerformOpAddGroup(const OpParameters& params) {
212 if (!params.ExpectArgSize(2)) return false;
Yifan Hongbc7e1db2020-04-28 13:26:33 -0700213 const auto& group_name_suffix = AddSlotSuffix(params.arg(0));
Tianjie Xud1188332019-05-24 16:08:45 -0700214 auto maximum_size = params.uint_arg(1, "maximum_size");
215 if (!maximum_size.has_value()) return false;
216
Yifan Hongbc7e1db2020-04-28 13:26:33 -0700217 auto group = params.builder->FindGroup(group_name_suffix);
Tianjie Xud1188332019-05-24 16:08:45 -0700218 if (group != nullptr) {
Yifan Hongbc7e1db2020-04-28 13:26:33 -0700219 LOG(ERROR) << "Cannot add group " << group_name_suffix << " because it already exists.";
Tianjie Xud1188332019-05-24 16:08:45 -0700220 return false;
221 }
222
223 if (maximum_size.value() == 0) {
Yifan Hongbc7e1db2020-04-28 13:26:33 -0700224 LOG(WARNING) << "Adding group " << group_name_suffix << " with no size limits.";
Tianjie Xud1188332019-05-24 16:08:45 -0700225 }
226
Yifan Hongbc7e1db2020-04-28 13:26:33 -0700227 if (!params.builder->AddGroup(group_name_suffix, maximum_size.value())) {
228 LOG(ERROR) << "Failed to add group " << group_name_suffix << " with maximum size "
Tianjie Xud1188332019-05-24 16:08:45 -0700229 << maximum_size.value() << ".";
230 return false;
231 }
232 return true;
233}
234
235bool PerformOpResizeGroup(const OpParameters& params) {
236 if (!params.ExpectArgSize(2)) return false;
Yifan Hongbc7e1db2020-04-28 13:26:33 -0700237 const auto& group_name_suffix = AddSlotSuffix(params.arg(0));
Tianjie Xud1188332019-05-24 16:08:45 -0700238 auto new_size = params.uint_arg(1, "maximum_size");
239 if (!new_size.has_value()) return false;
240
Yifan Hongbc7e1db2020-04-28 13:26:33 -0700241 auto group = params.builder->FindGroup(group_name_suffix);
Tianjie Xud1188332019-05-24 16:08:45 -0700242 if (group == nullptr) {
Yifan Hongbc7e1db2020-04-28 13:26:33 -0700243 LOG(ERROR) << "Cannot resize group " << group_name_suffix << " because it is not found.";
Tianjie Xud1188332019-05-24 16:08:45 -0700244 return false;
245 }
246
247 auto old_size = group->maximum_size();
248 if (old_size != new_size.value()) {
Yifan Hongbc7e1db2020-04-28 13:26:33 -0700249 if (!params.builder->ChangeGroupSize(group_name_suffix, new_size.value())) {
250 LOG(ERROR) << "Cannot resize group " << group_name_suffix << " from " << old_size << " to "
Tianjie Xud1188332019-05-24 16:08:45 -0700251 << new_size.value() << ".";
252 return false;
253 }
254 }
255 return true;
256}
257
258std::vector<std::string> ListPartitionNamesInGroup(MetadataBuilder* builder,
Yifan Hongbc7e1db2020-04-28 13:26:33 -0700259 const std::string& group_name_suffix) {
260 auto partitions = builder->ListPartitionsInGroup(group_name_suffix);
Tianjie Xud1188332019-05-24 16:08:45 -0700261 std::vector<std::string> partition_names;
262 std::transform(partitions.begin(), partitions.end(), std::back_inserter(partition_names),
263 [](Partition* partition) { return partition->name(); });
264 return partition_names;
265}
266
267bool PerformOpRemoveGroup(const OpParameters& params) {
268 if (!params.ExpectArgSize(1)) return false;
Yifan Hongbc7e1db2020-04-28 13:26:33 -0700269 const auto& group_name_suffix = AddSlotSuffix(params.arg(0));
Tianjie Xud1188332019-05-24 16:08:45 -0700270
Yifan Hongbc7e1db2020-04-28 13:26:33 -0700271 auto partition_names = ListPartitionNamesInGroup(params.builder, group_name_suffix);
Tianjie Xud1188332019-05-24 16:08:45 -0700272 if (!partition_names.empty()) {
Yifan Hongbc7e1db2020-04-28 13:26:33 -0700273 LOG(ERROR) << "Cannot remove group " << group_name_suffix
274 << " because it still contains partitions ["
Tianjie Xud1188332019-05-24 16:08:45 -0700275 << android::base::Join(partition_names, ", ") << "]";
276 return false;
277 }
Yifan Hongbc7e1db2020-04-28 13:26:33 -0700278 params.builder->RemoveGroupAndPartitions(group_name_suffix);
Tianjie Xud1188332019-05-24 16:08:45 -0700279 return true;
280}
281
282bool PerformOpRemoveAllGroups(const OpParameters& params) {
283 if (!params.ExpectArgSize(0)) return false;
284
285 auto group_names = params.builder->ListGroups();
Yifan Hongbc7e1db2020-04-28 13:26:33 -0700286 for (const auto& group_name_suffix : group_names) {
287 auto partition_names = ListPartitionNamesInGroup(params.builder, group_name_suffix);
288 for (const auto& partition_name_suffix : partition_names) {
289 if (!UnmapPartitionWithSuffixOnDeviceMapper(partition_name_suffix)) {
290 LOG(ERROR) << "Cannot unmap " << partition_name_suffix << " before removing group "
291 << group_name_suffix << ".";
Tianjie Xud1188332019-05-24 16:08:45 -0700292 return false;
293 }
294 }
Yifan Hongbc7e1db2020-04-28 13:26:33 -0700295 params.builder->RemoveGroupAndPartitions(group_name_suffix);
Tianjie Xud1188332019-05-24 16:08:45 -0700296 }
297 return true;
298}
299
300} // namespace
301
Yumi Yukimura4c1835f2024-03-24 01:39:24 +0800302bool UpdaterRuntime::UpdateDynamicPartitions(const std::string_view op_list_value,
303 const std::string_view super_empty_value) {
304 bool flash_metadata = false;
305 const auto partition_opener = PartitionOpener();
Tianjie Xud1188332019-05-24 16:08:45 -0700306 auto super_device = GetSuperDevice();
Yumi Yukimura4c1835f2024-03-24 01:39:24 +0800307 auto builder = MetadataBuilder::New(partition_opener, super_device, 0);
Tianjie Xud1188332019-05-24 16:08:45 -0700308 if (builder == nullptr) {
Yumi Yukimura4c1835f2024-03-24 01:39:24 +0800309 LOG(ERROR) << "Failed to load dynamic partition metadata from device.";
310 if (super_empty_value.size()) {
311 LOG(INFO) << "Trying to load dynamic partition metadata from OTA.";
312 const auto metadata = ReadFromImageBlob(super_empty_value.data(), super_empty_value.size());
313 if (metadata == nullptr) {
314 LOG(ERROR) << "Failed to parse dynamic partition metadata from OTA.";
315 return false;
316 }
317 builder = MetadataBuilder::New(*metadata, &partition_opener);
318 if (builder == nullptr) {
319 LOG(ERROR) << "Failed to initialize dynamic partition metadata from OTA.";
320 return false;
321 }
322 flash_metadata = true;
323 } else {
324 return false;
325 }
Tianjie Xud1188332019-05-24 16:08:45 -0700326 }
327
328 static const OpMap op_map{
329 // clang-format off
330 {"resize", PerformOpResize},
331 {"remove", PerformOpRemove},
332 {"add", PerformOpAdd},
333 {"move", PerformOpMove},
334 {"add_group", PerformOpAddGroup},
335 {"resize_group", PerformOpResizeGroup},
336 {"remove_group", PerformOpRemoveGroup},
337 {"remove_all_groups", PerformOpRemoveAllGroups},
338 // clang-format on
339 };
340
341 std::vector<std::string> lines = android::base::Split(std::string(op_list_value), "\n");
342 for (const auto& line : lines) {
343 auto comment_idx = line.find('#');
344 auto op_and_args = comment_idx == std::string::npos ? line : line.substr(0, comment_idx);
345 op_and_args = android::base::Trim(op_and_args);
346 if (op_and_args.empty()) continue;
347
348 auto tokens = android::base::Split(op_and_args, " ");
349 const auto& op = tokens[0];
350 auto it = op_map.find(op);
351 if (it == op_map.end()) {
352 LOG(ERROR) << "Unknown operation in op_list: " << op;
353 return false;
354 }
355 OpParameters params;
356 params.tokens = tokens;
357 params.builder = builder.get();
358 if (!it->second(params)) {
359 return false;
360 }
361 }
362
363 auto metadata = builder->Export();
364 if (metadata == nullptr) {
365 LOG(ERROR) << "Failed to export metadata.";
366 return false;
367 }
368
Yumi Yukimura4c1835f2024-03-24 01:39:24 +0800369 if (flash_metadata) {
370 if (!FlashPartitionTable(super_device, *metadata)) {
371 LOG(ERROR) << "Failed to flash metadata.";
372 return false;
373 }
374 } else {
375 if (!UpdatePartitionTable(super_device, *metadata, 0)) {
376 LOG(ERROR) << "Failed to write metadata.";
377 return false;
378 }
Tianjie Xud1188332019-05-24 16:08:45 -0700379 }
380
381 return true;
382}