blob: be9250a81ecf2808731d35ba952d978d23dc211c [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;
44
45static constexpr std::chrono::milliseconds kMapTimeout{ 1000 };
46
47static std::string GetSuperDevice() {
48 return "/dev/block/by-name/" + fs_mgr_get_super_partition_name();
49}
50
51static bool UnmapPartitionOnDeviceMapper(const std::string& partition_name) {
52 auto state = DeviceMapper::Instance().GetState(partition_name);
53 if (state == DmDeviceState::INVALID) {
54 return true;
55 }
56 if (state == DmDeviceState::ACTIVE) {
David Anderson32148d92019-06-24 13:51:43 -070057 return DestroyLogicalPartition(partition_name);
Tianjie Xud1188332019-05-24 16:08:45 -070058 }
59 LOG(ERROR) << "Unknown device mapper state: "
60 << static_cast<std::underlying_type_t<DmDeviceState>>(state);
61 return false;
62}
63
64bool UpdaterRuntime::MapPartitionOnDeviceMapper(const std::string& partition_name,
65 std::string* path) {
66 auto state = DeviceMapper::Instance().GetState(partition_name);
67 if (state == DmDeviceState::INVALID) {
David Anderson3cbd7ae2019-08-14 12:31:58 -070068 CreateLogicalPartitionParams params = {
69 .block_device = GetSuperDevice(),
70 .metadata_slot = 0,
71 .partition_name = partition_name,
72 .force_writable = true,
73 .timeout_ms = kMapTimeout,
74 };
75 return CreateLogicalPartition(params, path);
Tianjie Xud1188332019-05-24 16:08:45 -070076 }
77
78 if (state == DmDeviceState::ACTIVE) {
79 return DeviceMapper::Instance().GetDmDevicePathByName(partition_name, path);
80 }
81 LOG(ERROR) << "Unknown device mapper state: "
82 << static_cast<std::underlying_type_t<DmDeviceState>>(state);
83 return false;
84}
85
86bool UpdaterRuntime::UnmapPartitionOnDeviceMapper(const std::string& partition_name) {
87 return ::UnmapPartitionOnDeviceMapper(partition_name);
88}
89
90namespace { // Ops
91
92struct OpParameters {
93 std::vector<std::string> tokens;
94 MetadataBuilder* builder;
95
96 bool ExpectArgSize(size_t size) const {
97 CHECK(!tokens.empty());
98 auto actual = tokens.size() - 1;
99 if (actual != size) {
100 LOG(ERROR) << "Op " << op() << " expects " << size << " args, got " << actual;
101 return false;
102 }
103 return true;
104 }
105 const std::string& op() const {
106 CHECK(!tokens.empty());
107 return tokens[0];
108 }
109 const std::string& arg(size_t pos) const {
110 CHECK_LE(pos + 1, tokens.size());
111 return tokens[pos + 1];
112 }
113 std::optional<uint64_t> uint_arg(size_t pos, const std::string& name) const {
114 auto str = arg(pos);
115 uint64_t ret;
116 if (!android::base::ParseUint(str, &ret)) {
117 LOG(ERROR) << "Op " << op() << " expects uint64 for argument " << name << ", got " << str;
118 return std::nullopt;
119 }
120 return ret;
121 }
122};
123
124using OpFunction = std::function<bool(const OpParameters&)>;
125using OpMap = std::map<std::string, OpFunction>;
126
127bool PerformOpResize(const OpParameters& params) {
128 if (!params.ExpectArgSize(2)) return false;
129 const auto& partition_name = params.arg(0);
130 auto size = params.uint_arg(1, "size");
131 if (!size.has_value()) return false;
132
133 auto partition = params.builder->FindPartition(partition_name);
134 if (partition == nullptr) {
135 LOG(ERROR) << "Failed to find partition " << partition_name
136 << " in dynamic partition metadata.";
137 return false;
138 }
139 if (!UnmapPartitionOnDeviceMapper(partition_name)) {
140 LOG(ERROR) << "Cannot unmap " << partition_name << " before resizing.";
141 return false;
142 }
143 if (!params.builder->ResizePartition(partition, size.value())) {
144 LOG(ERROR) << "Failed to resize partition " << partition_name << " to size " << *size << ".";
145 return false;
146 }
147 return true;
148}
149
150bool PerformOpRemove(const OpParameters& params) {
151 if (!params.ExpectArgSize(1)) return false;
152 const auto& partition_name = params.arg(0);
153
154 if (!UnmapPartitionOnDeviceMapper(partition_name)) {
155 LOG(ERROR) << "Cannot unmap " << partition_name << " before removing.";
156 return false;
157 }
158 params.builder->RemovePartition(partition_name);
159 return true;
160}
161
162bool PerformOpAdd(const OpParameters& params) {
163 if (!params.ExpectArgSize(2)) return false;
164 const auto& partition_name = params.arg(0);
165 const auto& group_name = params.arg(1);
166
167 if (params.builder->AddPartition(partition_name, group_name, LP_PARTITION_ATTR_READONLY) ==
168 nullptr) {
169 LOG(ERROR) << "Failed to add partition " << partition_name << " to group " << group_name << ".";
170 return false;
171 }
172 return true;
173}
174
175bool PerformOpMove(const OpParameters& params) {
176 if (!params.ExpectArgSize(2)) return false;
177 const auto& partition_name = params.arg(0);
178 const auto& new_group = params.arg(1);
179
180 auto partition = params.builder->FindPartition(partition_name);
181 if (partition == nullptr) {
182 LOG(ERROR) << "Cannot move partition " << partition_name << " to group " << new_group
183 << " because it is not found.";
184 return false;
185 }
186
187 auto old_group = partition->group_name();
188 if (old_group != new_group) {
189 if (!params.builder->ChangePartitionGroup(partition, new_group)) {
190 LOG(ERROR) << "Cannot move partition " << partition_name << " from group " << old_group
191 << " to group " << new_group << ".";
192 return false;
193 }
194 }
195 return true;
196}
197
198bool PerformOpAddGroup(const OpParameters& params) {
199 if (!params.ExpectArgSize(2)) return false;
200 const auto& group_name = params.arg(0);
201 auto maximum_size = params.uint_arg(1, "maximum_size");
202 if (!maximum_size.has_value()) return false;
203
204 auto group = params.builder->FindGroup(group_name);
205 if (group != nullptr) {
206 LOG(ERROR) << "Cannot add group " << group_name << " because it already exists.";
207 return false;
208 }
209
210 if (maximum_size.value() == 0) {
211 LOG(WARNING) << "Adding group " << group_name << " with no size limits.";
212 }
213
214 if (!params.builder->AddGroup(group_name, maximum_size.value())) {
215 LOG(ERROR) << "Failed to add group " << group_name << " with maximum size "
216 << maximum_size.value() << ".";
217 return false;
218 }
219 return true;
220}
221
222bool PerformOpResizeGroup(const OpParameters& params) {
223 if (!params.ExpectArgSize(2)) return false;
224 const auto& group_name = params.arg(0);
225 auto new_size = params.uint_arg(1, "maximum_size");
226 if (!new_size.has_value()) return false;
227
228 auto group = params.builder->FindGroup(group_name);
229 if (group == nullptr) {
230 LOG(ERROR) << "Cannot resize group " << group_name << " because it is not found.";
231 return false;
232 }
233
234 auto old_size = group->maximum_size();
235 if (old_size != new_size.value()) {
236 if (!params.builder->ChangeGroupSize(group_name, new_size.value())) {
237 LOG(ERROR) << "Cannot resize group " << group_name << " from " << old_size << " to "
238 << new_size.value() << ".";
239 return false;
240 }
241 }
242 return true;
243}
244
245std::vector<std::string> ListPartitionNamesInGroup(MetadataBuilder* builder,
246 const std::string& group_name) {
247 auto partitions = builder->ListPartitionsInGroup(group_name);
248 std::vector<std::string> partition_names;
249 std::transform(partitions.begin(), partitions.end(), std::back_inserter(partition_names),
250 [](Partition* partition) { return partition->name(); });
251 return partition_names;
252}
253
254bool PerformOpRemoveGroup(const OpParameters& params) {
255 if (!params.ExpectArgSize(1)) return false;
256 const auto& group_name = params.arg(0);
257
258 auto partition_names = ListPartitionNamesInGroup(params.builder, group_name);
259 if (!partition_names.empty()) {
260 LOG(ERROR) << "Cannot remove group " << group_name << " because it still contains partitions ["
261 << android::base::Join(partition_names, ", ") << "]";
262 return false;
263 }
264 params.builder->RemoveGroupAndPartitions(group_name);
265 return true;
266}
267
268bool PerformOpRemoveAllGroups(const OpParameters& params) {
269 if (!params.ExpectArgSize(0)) return false;
270
271 auto group_names = params.builder->ListGroups();
272 for (const auto& group_name : group_names) {
273 auto partition_names = ListPartitionNamesInGroup(params.builder, group_name);
274 for (const auto& partition_name : partition_names) {
275 if (!UnmapPartitionOnDeviceMapper(partition_name)) {
276 LOG(ERROR) << "Cannot unmap " << partition_name << " before removing group " << group_name
277 << ".";
278 return false;
279 }
280 }
281 params.builder->RemoveGroupAndPartitions(group_name);
282 }
283 return true;
284}
285
286} // namespace
287
288bool UpdaterRuntime::UpdateDynamicPartitions(const std::string_view op_list_value) {
289 auto super_device = GetSuperDevice();
290 auto builder = MetadataBuilder::New(PartitionOpener(), super_device, 0);
291 if (builder == nullptr) {
292 LOG(ERROR) << "Failed to load dynamic partition metadata.";
293 return false;
294 }
295
296 static const OpMap op_map{
297 // clang-format off
298 {"resize", PerformOpResize},
299 {"remove", PerformOpRemove},
300 {"add", PerformOpAdd},
301 {"move", PerformOpMove},
302 {"add_group", PerformOpAddGroup},
303 {"resize_group", PerformOpResizeGroup},
304 {"remove_group", PerformOpRemoveGroup},
305 {"remove_all_groups", PerformOpRemoveAllGroups},
306 // clang-format on
307 };
308
309 std::vector<std::string> lines = android::base::Split(std::string(op_list_value), "\n");
310 for (const auto& line : lines) {
311 auto comment_idx = line.find('#');
312 auto op_and_args = comment_idx == std::string::npos ? line : line.substr(0, comment_idx);
313 op_and_args = android::base::Trim(op_and_args);
314 if (op_and_args.empty()) continue;
315
316 auto tokens = android::base::Split(op_and_args, " ");
317 const auto& op = tokens[0];
318 auto it = op_map.find(op);
319 if (it == op_map.end()) {
320 LOG(ERROR) << "Unknown operation in op_list: " << op;
321 return false;
322 }
323 OpParameters params;
324 params.tokens = tokens;
325 params.builder = builder.get();
326 if (!it->second(params)) {
327 return false;
328 }
329 }
330
331 auto metadata = builder->Export();
332 if (metadata == nullptr) {
333 LOG(ERROR) << "Failed to export metadata.";
334 return false;
335 }
336
337 if (!UpdatePartitionTable(super_device, *metadata, 0)) {
338 LOG(ERROR) << "Failed to write metadata.";
339 return false;
340 }
341
342 return true;
343}