blob: 6570cfffde63bb61cc1bc0f19731bb6609c6e8d0 [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;
Yifan Hongbc7e1db2020-04-28 13:26:33 -070044using android::fs_mgr::SlotNumberForSlotSuffix;
Tianjie Xud1188332019-05-24 16:08:45 -070045
46static constexpr std::chrono::milliseconds kMapTimeout{ 1000 };
47
48static std::string GetSuperDevice() {
49 return "/dev/block/by-name/" + fs_mgr_get_super_partition_name();
50}
51
Yifan Hongbc7e1db2020-04-28 13:26:33 -070052static std::string AddSlotSuffix(const std::string& partition_name) {
53 return partition_name + fs_mgr_get_slot_suffix();
54}
55
56static bool UnmapPartitionWithSuffixOnDeviceMapper(const std::string& partition_name_suffix) {
57 auto state = DeviceMapper::Instance().GetState(partition_name_suffix);
Tianjie Xud1188332019-05-24 16:08:45 -070058 if (state == DmDeviceState::INVALID) {
59 return true;
60 }
61 if (state == DmDeviceState::ACTIVE) {
Yifan Hongbc7e1db2020-04-28 13:26:33 -070062 return DestroyLogicalPartition(partition_name_suffix);
Tianjie Xud1188332019-05-24 16:08:45 -070063 }
64 LOG(ERROR) << "Unknown device mapper state: "
65 << static_cast<std::underlying_type_t<DmDeviceState>>(state);
66 return false;
67}
68
69bool UpdaterRuntime::MapPartitionOnDeviceMapper(const std::string& partition_name,
70 std::string* path) {
Yifan Hongbc7e1db2020-04-28 13:26:33 -070071 auto partition_name_suffix = AddSlotSuffix(partition_name);
72 auto state = DeviceMapper::Instance().GetState(partition_name_suffix);
Tianjie Xud1188332019-05-24 16:08:45 -070073 if (state == DmDeviceState::INVALID) {
David Anderson3cbd7ae2019-08-14 12:31:58 -070074 CreateLogicalPartitionParams params = {
75 .block_device = GetSuperDevice(),
Yifan Hongbc7e1db2020-04-28 13:26:33 -070076 // If device supports A/B, apply non-A/B update to the partition at current slot. Otherwise,
77 // SlotNumberForSlotSuffix("") returns 0.
78 .metadata_slot = SlotNumberForSlotSuffix(fs_mgr_get_slot_suffix()),
79 // If device supports A/B, apply non-A/B update to the partition at current slot. Otherwise,
80 // fs_mgr_get_slot_suffix() returns empty string.
81 .partition_name = partition_name_suffix,
David Anderson3cbd7ae2019-08-14 12:31:58 -070082 .force_writable = true,
83 .timeout_ms = kMapTimeout,
84 };
85 return CreateLogicalPartition(params, path);
Tianjie Xud1188332019-05-24 16:08:45 -070086 }
87
88 if (state == DmDeviceState::ACTIVE) {
Yifan Hongbc7e1db2020-04-28 13:26:33 -070089 return DeviceMapper::Instance().GetDmDevicePathByName(partition_name_suffix, path);
Tianjie Xud1188332019-05-24 16:08:45 -070090 }
91 LOG(ERROR) << "Unknown device mapper state: "
92 << static_cast<std::underlying_type_t<DmDeviceState>>(state);
93 return false;
94}
95
96bool UpdaterRuntime::UnmapPartitionOnDeviceMapper(const std::string& partition_name) {
Yifan Hongbc7e1db2020-04-28 13:26:33 -070097 return ::UnmapPartitionWithSuffixOnDeviceMapper(AddSlotSuffix(partition_name));
Tianjie Xud1188332019-05-24 16:08:45 -070098}
99
100namespace { // Ops
101
102struct OpParameters {
103 std::vector<std::string> tokens;
104 MetadataBuilder* builder;
105
106 bool ExpectArgSize(size_t size) const {
107 CHECK(!tokens.empty());
108 auto actual = tokens.size() - 1;
109 if (actual != size) {
110 LOG(ERROR) << "Op " << op() << " expects " << size << " args, got " << actual;
111 return false;
112 }
113 return true;
114 }
115 const std::string& op() const {
116 CHECK(!tokens.empty());
117 return tokens[0];
118 }
119 const std::string& arg(size_t pos) const {
120 CHECK_LE(pos + 1, tokens.size());
121 return tokens[pos + 1];
122 }
123 std::optional<uint64_t> uint_arg(size_t pos, const std::string& name) const {
124 auto str = arg(pos);
125 uint64_t ret;
126 if (!android::base::ParseUint(str, &ret)) {
127 LOG(ERROR) << "Op " << op() << " expects uint64 for argument " << name << ", got " << str;
128 return std::nullopt;
129 }
130 return ret;
131 }
132};
133
134using OpFunction = std::function<bool(const OpParameters&)>;
135using OpMap = std::map<std::string, OpFunction>;
136
137bool PerformOpResize(const OpParameters& params) {
138 if (!params.ExpectArgSize(2)) return false;
Yifan Hongbc7e1db2020-04-28 13:26:33 -0700139 const auto& partition_name_suffix = AddSlotSuffix(params.arg(0));
Tianjie Xud1188332019-05-24 16:08:45 -0700140 auto size = params.uint_arg(1, "size");
141 if (!size.has_value()) return false;
142
Yifan Hongbc7e1db2020-04-28 13:26:33 -0700143 auto partition = params.builder->FindPartition(partition_name_suffix);
Tianjie Xud1188332019-05-24 16:08:45 -0700144 if (partition == nullptr) {
Yifan Hongbc7e1db2020-04-28 13:26:33 -0700145 LOG(ERROR) << "Failed to find partition " << partition_name_suffix
Tianjie Xud1188332019-05-24 16:08:45 -0700146 << " in dynamic partition metadata.";
147 return false;
148 }
Yifan Hongbc7e1db2020-04-28 13:26:33 -0700149 if (!UnmapPartitionWithSuffixOnDeviceMapper(partition_name_suffix)) {
150 LOG(ERROR) << "Cannot unmap " << partition_name_suffix << " before resizing.";
Tianjie Xud1188332019-05-24 16:08:45 -0700151 return false;
152 }
153 if (!params.builder->ResizePartition(partition, size.value())) {
Yifan Hongbc7e1db2020-04-28 13:26:33 -0700154 LOG(ERROR) << "Failed to resize partition " << partition_name_suffix << " to size " << *size
155 << ".";
Tianjie Xud1188332019-05-24 16:08:45 -0700156 return false;
157 }
158 return true;
159}
160
161bool PerformOpRemove(const OpParameters& params) {
162 if (!params.ExpectArgSize(1)) return false;
Yifan Hongbc7e1db2020-04-28 13:26:33 -0700163 const auto& partition_name_suffix = AddSlotSuffix(params.arg(0));
Tianjie Xud1188332019-05-24 16:08:45 -0700164
Yifan Hongbc7e1db2020-04-28 13:26:33 -0700165 if (!UnmapPartitionWithSuffixOnDeviceMapper(partition_name_suffix)) {
166 LOG(ERROR) << "Cannot unmap " << partition_name_suffix << " before removing.";
Tianjie Xud1188332019-05-24 16:08:45 -0700167 return false;
168 }
Yifan Hongbc7e1db2020-04-28 13:26:33 -0700169 params.builder->RemovePartition(partition_name_suffix);
Tianjie Xud1188332019-05-24 16:08:45 -0700170 return true;
171}
172
173bool PerformOpAdd(const OpParameters& params) {
174 if (!params.ExpectArgSize(2)) return false;
Yifan Hongbc7e1db2020-04-28 13:26:33 -0700175 const auto& partition_name_suffix = AddSlotSuffix(params.arg(0));
176 const auto& group_name_suffix = AddSlotSuffix(params.arg(1));
Tianjie Xud1188332019-05-24 16:08:45 -0700177
Yifan Hongbc7e1db2020-04-28 13:26:33 -0700178 if (params.builder->AddPartition(partition_name_suffix, group_name_suffix,
179 LP_PARTITION_ATTR_READONLY) == nullptr) {
180 LOG(ERROR) << "Failed to add partition " << partition_name_suffix << " to group "
181 << group_name_suffix << ".";
Tianjie Xud1188332019-05-24 16:08:45 -0700182 return false;
183 }
184 return true;
185}
186
187bool PerformOpMove(const OpParameters& params) {
188 if (!params.ExpectArgSize(2)) return false;
Yifan Hongbc7e1db2020-04-28 13:26:33 -0700189 const auto& partition_name_suffix = AddSlotSuffix(params.arg(0));
190 const auto& new_group_name_suffix = AddSlotSuffix(params.arg(1));
Tianjie Xud1188332019-05-24 16:08:45 -0700191
Yifan Hongbc7e1db2020-04-28 13:26:33 -0700192 auto partition = params.builder->FindPartition(partition_name_suffix);
Tianjie Xud1188332019-05-24 16:08:45 -0700193 if (partition == nullptr) {
Yifan Hongbc7e1db2020-04-28 13:26:33 -0700194 LOG(ERROR) << "Cannot move partition " << partition_name_suffix << " to group "
195 << new_group_name_suffix << " because it is not found.";
Tianjie Xud1188332019-05-24 16:08:45 -0700196 return false;
197 }
198
Yifan Hongbc7e1db2020-04-28 13:26:33 -0700199 auto old_group_name_suffix = partition->group_name();
200 if (old_group_name_suffix != new_group_name_suffix) {
201 if (!params.builder->ChangePartitionGroup(partition, new_group_name_suffix)) {
202 LOG(ERROR) << "Cannot move partition " << partition_name_suffix << " from group "
203 << old_group_name_suffix << " to group " << new_group_name_suffix << ".";
Tianjie Xud1188332019-05-24 16:08:45 -0700204 return false;
205 }
206 }
207 return true;
208}
209
210bool PerformOpAddGroup(const OpParameters& params) {
211 if (!params.ExpectArgSize(2)) return false;
Yifan Hongbc7e1db2020-04-28 13:26:33 -0700212 const auto& group_name_suffix = AddSlotSuffix(params.arg(0));
Tianjie Xud1188332019-05-24 16:08:45 -0700213 auto maximum_size = params.uint_arg(1, "maximum_size");
214 if (!maximum_size.has_value()) return false;
215
Yifan Hongbc7e1db2020-04-28 13:26:33 -0700216 auto group = params.builder->FindGroup(group_name_suffix);
Tianjie Xud1188332019-05-24 16:08:45 -0700217 if (group != nullptr) {
Yifan Hongbc7e1db2020-04-28 13:26:33 -0700218 LOG(ERROR) << "Cannot add group " << group_name_suffix << " because it already exists.";
Tianjie Xud1188332019-05-24 16:08:45 -0700219 return false;
220 }
221
222 if (maximum_size.value() == 0) {
Yifan Hongbc7e1db2020-04-28 13:26:33 -0700223 LOG(WARNING) << "Adding group " << group_name_suffix << " with no size limits.";
Tianjie Xud1188332019-05-24 16:08:45 -0700224 }
225
Yifan Hongbc7e1db2020-04-28 13:26:33 -0700226 if (!params.builder->AddGroup(group_name_suffix, maximum_size.value())) {
227 LOG(ERROR) << "Failed to add group " << group_name_suffix << " with maximum size "
Tianjie Xud1188332019-05-24 16:08:45 -0700228 << maximum_size.value() << ".";
229 return false;
230 }
231 return true;
232}
233
234bool PerformOpResizeGroup(const OpParameters& params) {
235 if (!params.ExpectArgSize(2)) return false;
Yifan Hongbc7e1db2020-04-28 13:26:33 -0700236 const auto& group_name_suffix = AddSlotSuffix(params.arg(0));
Tianjie Xud1188332019-05-24 16:08:45 -0700237 auto new_size = params.uint_arg(1, "maximum_size");
238 if (!new_size.has_value()) return false;
239
Yifan Hongbc7e1db2020-04-28 13:26:33 -0700240 auto group = params.builder->FindGroup(group_name_suffix);
Tianjie Xud1188332019-05-24 16:08:45 -0700241 if (group == nullptr) {
Yifan Hongbc7e1db2020-04-28 13:26:33 -0700242 LOG(ERROR) << "Cannot resize group " << group_name_suffix << " because it is not found.";
Tianjie Xud1188332019-05-24 16:08:45 -0700243 return false;
244 }
245
246 auto old_size = group->maximum_size();
247 if (old_size != new_size.value()) {
Yifan Hongbc7e1db2020-04-28 13:26:33 -0700248 if (!params.builder->ChangeGroupSize(group_name_suffix, new_size.value())) {
249 LOG(ERROR) << "Cannot resize group " << group_name_suffix << " from " << old_size << " to "
Tianjie Xud1188332019-05-24 16:08:45 -0700250 << new_size.value() << ".";
251 return false;
252 }
253 }
254 return true;
255}
256
257std::vector<std::string> ListPartitionNamesInGroup(MetadataBuilder* builder,
Yifan Hongbc7e1db2020-04-28 13:26:33 -0700258 const std::string& group_name_suffix) {
259 auto partitions = builder->ListPartitionsInGroup(group_name_suffix);
Tianjie Xud1188332019-05-24 16:08:45 -0700260 std::vector<std::string> partition_names;
261 std::transform(partitions.begin(), partitions.end(), std::back_inserter(partition_names),
262 [](Partition* partition) { return partition->name(); });
263 return partition_names;
264}
265
266bool PerformOpRemoveGroup(const OpParameters& params) {
267 if (!params.ExpectArgSize(1)) return false;
Yifan Hongbc7e1db2020-04-28 13:26:33 -0700268 const auto& group_name_suffix = AddSlotSuffix(params.arg(0));
Tianjie Xud1188332019-05-24 16:08:45 -0700269
Yifan Hongbc7e1db2020-04-28 13:26:33 -0700270 auto partition_names = ListPartitionNamesInGroup(params.builder, group_name_suffix);
Tianjie Xud1188332019-05-24 16:08:45 -0700271 if (!partition_names.empty()) {
Yifan Hongbc7e1db2020-04-28 13:26:33 -0700272 LOG(ERROR) << "Cannot remove group " << group_name_suffix
273 << " because it still contains partitions ["
Tianjie Xud1188332019-05-24 16:08:45 -0700274 << android::base::Join(partition_names, ", ") << "]";
275 return false;
276 }
Yifan Hongbc7e1db2020-04-28 13:26:33 -0700277 params.builder->RemoveGroupAndPartitions(group_name_suffix);
Tianjie Xud1188332019-05-24 16:08:45 -0700278 return true;
279}
280
281bool PerformOpRemoveAllGroups(const OpParameters& params) {
282 if (!params.ExpectArgSize(0)) return false;
283
284 auto group_names = params.builder->ListGroups();
Yifan Hongbc7e1db2020-04-28 13:26:33 -0700285 for (const auto& group_name_suffix : group_names) {
286 auto partition_names = ListPartitionNamesInGroup(params.builder, group_name_suffix);
287 for (const auto& partition_name_suffix : partition_names) {
288 if (!UnmapPartitionWithSuffixOnDeviceMapper(partition_name_suffix)) {
289 LOG(ERROR) << "Cannot unmap " << partition_name_suffix << " before removing group "
290 << group_name_suffix << ".";
Tianjie Xud1188332019-05-24 16:08:45 -0700291 return false;
292 }
293 }
Yifan Hongbc7e1db2020-04-28 13:26:33 -0700294 params.builder->RemoveGroupAndPartitions(group_name_suffix);
Tianjie Xud1188332019-05-24 16:08:45 -0700295 }
296 return true;
297}
298
299} // namespace
300
301bool UpdaterRuntime::UpdateDynamicPartitions(const std::string_view op_list_value) {
302 auto super_device = GetSuperDevice();
303 auto builder = MetadataBuilder::New(PartitionOpener(), super_device, 0);
304 if (builder == nullptr) {
305 LOG(ERROR) << "Failed to load dynamic partition metadata.";
306 return false;
307 }
308
309 static const OpMap op_map{
310 // clang-format off
311 {"resize", PerformOpResize},
312 {"remove", PerformOpRemove},
313 {"add", PerformOpAdd},
314 {"move", PerformOpMove},
315 {"add_group", PerformOpAddGroup},
316 {"resize_group", PerformOpResizeGroup},
317 {"remove_group", PerformOpRemoveGroup},
318 {"remove_all_groups", PerformOpRemoveAllGroups},
319 // clang-format on
320 };
321
322 std::vector<std::string> lines = android::base::Split(std::string(op_list_value), "\n");
323 for (const auto& line : lines) {
324 auto comment_idx = line.find('#');
325 auto op_and_args = comment_idx == std::string::npos ? line : line.substr(0, comment_idx);
326 op_and_args = android::base::Trim(op_and_args);
327 if (op_and_args.empty()) continue;
328
329 auto tokens = android::base::Split(op_and_args, " ");
330 const auto& op = tokens[0];
331 auto it = op_map.find(op);
332 if (it == op_map.end()) {
333 LOG(ERROR) << "Unknown operation in op_list: " << op;
334 return false;
335 }
336 OpParameters params;
337 params.tokens = tokens;
338 params.builder = builder.get();
339 if (!it->second(params)) {
340 return false;
341 }
342 }
343
344 auto metadata = builder->Export();
345 if (metadata == nullptr) {
346 LOG(ERROR) << "Failed to export metadata.";
347 return false;
348 }
349
350 if (!UpdatePartitionTable(super_device, *metadata, 0)) {
351 LOG(ERROR) << "Failed to write metadata.";
352 return false;
353 }
354
355 return true;
356}