1 /*
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/dynamic_partitions.h"
18
19 #include <sys/stat.h>
20 #include <sys/types.h>
21
22 #include <algorithm>
23 #include <chrono>
24 #include <iterator>
25 #include <memory>
26 #include <optional>
27 #include <string>
28 #include <type_traits>
29 #include <vector>
30
31 #include <android-base/file.h>
32 #include <android-base/logging.h>
33 #include <android-base/parseint.h>
34 #include <android-base/strings.h>
35 #include <fs_mgr.h>
36 #include <fs_mgr_dm_linear.h>
37 #include <libdm/dm.h>
38 #include <liblp/builder.h>
39
40 #include "edify/expr.h"
41 #include "otautil/error_code.h"
42 #include "otautil/paths.h"
43 #include "private/utils.h"
44
45 using android::base::ParseUint;
46 using android::dm::DeviceMapper;
47 using android::dm::DmDeviceState;
48 using android::fs_mgr::CreateLogicalPartition;
49 using android::fs_mgr::DestroyLogicalPartition;
50 using android::fs_mgr::LpMetadata;
51 using android::fs_mgr::MetadataBuilder;
52 using android::fs_mgr::Partition;
53 using android::fs_mgr::PartitionOpener;
54
55 static constexpr std::chrono::milliseconds kMapTimeout{ 1000 };
56 static constexpr char kMetadataUpdatedMarker[] = "/dynamic_partition_metadata.UPDATED";
57
GetSuperDevice()58 static std::string GetSuperDevice() {
59 return "/dev/block/by-name/" + fs_mgr_get_super_partition_name();
60 }
61
ReadStringArgs(const char * name,State * state,const std::vector<std::unique_ptr<Expr>> & argv,const std::vector<std::string> & arg_names)62 static std::vector<std::string> ReadStringArgs(const char* name, State* state,
63 const std::vector<std::unique_ptr<Expr>>& argv,
64 const std::vector<std::string>& arg_names) {
65 if (argv.size() != arg_names.size()) {
66 ErrorAbort(state, kArgsParsingFailure, "%s expects %zu arguments, got %zu", name,
67 arg_names.size(), argv.size());
68 return {};
69 }
70
71 std::vector<std::unique_ptr<Value>> args;
72 if (!ReadValueArgs(state, argv, &args)) {
73 return {};
74 }
75
76 CHECK_EQ(args.size(), arg_names.size());
77
78 for (size_t i = 0; i < arg_names.size(); ++i) {
79 if (args[i]->type != Value::Type::STRING) {
80 ErrorAbort(state, kArgsParsingFailure, "%s argument to %s must be string",
81 arg_names[i].c_str(), name);
82 return {};
83 }
84 }
85
86 std::vector<std::string> ret;
87 std::transform(args.begin(), args.end(), std::back_inserter(ret),
88 [](const auto& arg) { return arg->data; });
89 return ret;
90 }
91
UnmapPartitionOnDeviceMapper(const std::string & partition_name)92 static bool UnmapPartitionOnDeviceMapper(const std::string& partition_name) {
93 auto state = DeviceMapper::Instance().GetState(partition_name);
94 if (state == DmDeviceState::INVALID) {
95 return true;
96 }
97 if (state == DmDeviceState::ACTIVE) {
98 return DestroyLogicalPartition(partition_name, kMapTimeout);
99 }
100 LOG(ERROR) << "Unknown device mapper state: "
101 << static_cast<std::underlying_type_t<DmDeviceState>>(state);
102 return false;
103 }
104
MapPartitionOnDeviceMapper(const std::string & partition_name,std::string * path)105 static bool MapPartitionOnDeviceMapper(const std::string& partition_name, std::string* path) {
106 auto state = DeviceMapper::Instance().GetState(partition_name);
107 if (state == DmDeviceState::INVALID) {
108 return CreateLogicalPartition(GetSuperDevice(), 0 /* metadata slot */, partition_name,
109 true /* force writable */, kMapTimeout, path);
110 }
111
112 if (state == DmDeviceState::ACTIVE) {
113 return DeviceMapper::Instance().GetDmDevicePathByName(partition_name, path);
114 }
115 LOG(ERROR) << "Unknown device mapper state: "
116 << static_cast<std::underlying_type_t<DmDeviceState>>(state);
117 return false;
118 }
119
UnmapPartitionFn(const char * name,State * state,const std::vector<std::unique_ptr<Expr>> & argv)120 Value* UnmapPartitionFn(const char* name, State* state,
121 const std::vector<std::unique_ptr<Expr>>& argv) {
122 auto args = ReadStringArgs(name, state, argv, { "name" });
123 if (args.empty()) return StringValue("");
124
125 return UnmapPartitionOnDeviceMapper(args[0]) ? StringValue("t") : StringValue("");
126 }
127
MapPartitionFn(const char * name,State * state,const std::vector<std::unique_ptr<Expr>> & argv)128 Value* MapPartitionFn(const char* name, State* state,
129 const std::vector<std::unique_ptr<Expr>>& argv) {
130 auto args = ReadStringArgs(name, state, argv, { "name" });
131 if (args.empty()) return StringValue("");
132
133 std::string path;
134 bool result = MapPartitionOnDeviceMapper(args[0], &path);
135 return result ? StringValue(path) : StringValue("");
136 }
137
138 namespace { // Ops
139
140 struct OpParameters {
141 std::vector<std::string> tokens;
142 MetadataBuilder* builder;
143
ExpectArgSize__anon7d3d847a0211::OpParameters144 bool ExpectArgSize(size_t size) const {
145 CHECK(!tokens.empty());
146 auto actual = tokens.size() - 1;
147 if (actual != size) {
148 LOG(ERROR) << "Op " << op() << " expects " << size << " args, got " << actual;
149 return false;
150 }
151 return true;
152 }
op__anon7d3d847a0211::OpParameters153 const std::string& op() const {
154 CHECK(!tokens.empty());
155 return tokens[0];
156 }
arg__anon7d3d847a0211::OpParameters157 const std::string& arg(size_t pos) const {
158 CHECK_LE(pos + 1, tokens.size());
159 return tokens[pos + 1];
160 }
uint_arg__anon7d3d847a0211::OpParameters161 std::optional<uint64_t> uint_arg(size_t pos, const std::string& name) const {
162 auto str = arg(pos);
163 uint64_t ret;
164 if (!ParseUint(str, &ret)) {
165 LOG(ERROR) << "Op " << op() << " expects uint64 for argument " << name << ", got " << str;
166 return std::nullopt;
167 }
168 return ret;
169 }
170 };
171
172 using OpFunction = std::function<bool(const OpParameters&)>;
173 using OpMap = std::map<std::string, OpFunction>;
174
PerformOpResize(const OpParameters & params)175 bool PerformOpResize(const OpParameters& params) {
176 if (!params.ExpectArgSize(2)) return false;
177 const auto& partition_name = params.arg(0);
178 auto size = params.uint_arg(1, "size");
179 if (!size.has_value()) return false;
180
181 auto partition = params.builder->FindPartition(partition_name);
182 if (partition == nullptr) {
183 LOG(ERROR) << "Failed to find partition " << partition_name
184 << " in dynamic partition metadata.";
185 return false;
186 }
187 if (!UnmapPartitionOnDeviceMapper(partition_name)) {
188 LOG(ERROR) << "Cannot unmap " << partition_name << " before resizing.";
189 return false;
190 }
191 if (!params.builder->ResizePartition(partition, size.value())) {
192 LOG(ERROR) << "Failed to resize partition " << partition_name << " to size " << *size << ".";
193 return false;
194 }
195 return true;
196 }
197
PerformOpRemove(const OpParameters & params)198 bool PerformOpRemove(const OpParameters& params) {
199 if (!params.ExpectArgSize(1)) return false;
200 const auto& partition_name = params.arg(0);
201
202 if (!UnmapPartitionOnDeviceMapper(partition_name)) {
203 LOG(ERROR) << "Cannot unmap " << partition_name << " before removing.";
204 return false;
205 }
206 params.builder->RemovePartition(partition_name);
207 return true;
208 }
209
PerformOpAdd(const OpParameters & params)210 bool PerformOpAdd(const OpParameters& params) {
211 if (!params.ExpectArgSize(2)) return false;
212 const auto& partition_name = params.arg(0);
213 const auto& group_name = params.arg(1);
214
215 if (params.builder->AddPartition(partition_name, group_name, LP_PARTITION_ATTR_READONLY) ==
216 nullptr) {
217 LOG(ERROR) << "Failed to add partition " << partition_name << " to group " << group_name << ".";
218 return false;
219 }
220 return true;
221 }
222
PerformOpMove(const OpParameters & params)223 bool PerformOpMove(const OpParameters& params) {
224 if (!params.ExpectArgSize(2)) return false;
225 const auto& partition_name = params.arg(0);
226 const auto& new_group = params.arg(1);
227
228 auto partition = params.builder->FindPartition(partition_name);
229 if (partition == nullptr) {
230 LOG(ERROR) << "Cannot move partition " << partition_name << " to group " << new_group
231 << " because it is not found.";
232 return false;
233 }
234
235 auto old_group = partition->group_name();
236 if (old_group != new_group) {
237 if (!params.builder->ChangePartitionGroup(partition, new_group)) {
238 LOG(ERROR) << "Cannot move partition " << partition_name << " from group " << old_group
239 << " to group " << new_group << ".";
240 return false;
241 }
242 }
243 return true;
244 }
245
PerformOpAddGroup(const OpParameters & params)246 bool PerformOpAddGroup(const OpParameters& params) {
247 if (!params.ExpectArgSize(2)) return false;
248 const auto& group_name = params.arg(0);
249 auto maximum_size = params.uint_arg(1, "maximum_size");
250 if (!maximum_size.has_value()) return false;
251
252 auto group = params.builder->FindGroup(group_name);
253 if (group != nullptr) {
254 LOG(ERROR) << "Cannot add group " << group_name << " because it already exists.";
255 return false;
256 }
257
258 if (maximum_size.value() == 0) {
259 LOG(WARNING) << "Adding group " << group_name << " with no size limits.";
260 }
261
262 if (!params.builder->AddGroup(group_name, maximum_size.value())) {
263 LOG(ERROR) << "Failed to add group " << group_name << " with maximum size "
264 << maximum_size.value() << ".";
265 return false;
266 }
267 return true;
268 }
269
PerformOpResizeGroup(const OpParameters & params)270 bool PerformOpResizeGroup(const OpParameters& params) {
271 if (!params.ExpectArgSize(2)) return false;
272 const auto& group_name = params.arg(0);
273 auto new_size = params.uint_arg(1, "maximum_size");
274 if (!new_size.has_value()) return false;
275
276 auto group = params.builder->FindGroup(group_name);
277 if (group == nullptr) {
278 LOG(ERROR) << "Cannot resize group " << group_name << " because it is not found.";
279 return false;
280 }
281
282 auto old_size = group->maximum_size();
283 if (old_size != new_size.value()) {
284 if (!params.builder->ChangeGroupSize(group_name, new_size.value())) {
285 LOG(ERROR) << "Cannot resize group " << group_name << " from " << old_size << " to "
286 << new_size.value() << ".";
287 return false;
288 }
289 }
290 return true;
291 }
292
ListPartitionNamesInGroup(MetadataBuilder * builder,const std::string & group_name)293 std::vector<std::string> ListPartitionNamesInGroup(MetadataBuilder* builder,
294 const std::string& group_name) {
295 auto partitions = builder->ListPartitionsInGroup(group_name);
296 std::vector<std::string> partition_names;
297 std::transform(partitions.begin(), partitions.end(), std::back_inserter(partition_names),
298 [](Partition* partition) { return partition->name(); });
299 return partition_names;
300 }
301
PerformOpRemoveGroup(const OpParameters & params)302 bool PerformOpRemoveGroup(const OpParameters& params) {
303 if (!params.ExpectArgSize(1)) return false;
304 const auto& group_name = params.arg(0);
305
306 auto partition_names = ListPartitionNamesInGroup(params.builder, group_name);
307 if (!partition_names.empty()) {
308 LOG(ERROR) << "Cannot remove group " << group_name << " because it still contains partitions ["
309 << android::base::Join(partition_names, ", ") << "]";
310 return false;
311 }
312 params.builder->RemoveGroupAndPartitions(group_name);
313 return true;
314 }
315
PerformOpRemoveAllGroups(const OpParameters & params)316 bool PerformOpRemoveAllGroups(const OpParameters& params) {
317 if (!params.ExpectArgSize(0)) return false;
318
319 auto group_names = params.builder->ListGroups();
320 for (const auto& group_name : group_names) {
321 auto partition_names = ListPartitionNamesInGroup(params.builder, group_name);
322 for (const auto& partition_name : partition_names) {
323 if (!UnmapPartitionOnDeviceMapper(partition_name)) {
324 LOG(ERROR) << "Cannot unmap " << partition_name << " before removing group " << group_name
325 << ".";
326 return false;
327 }
328 }
329 params.builder->RemoveGroupAndPartitions(group_name);
330 }
331 return true;
332 }
333
334 } // namespace
335
UpdateDynamicPartitionsFn(const char * name,State * state,const std::vector<std::unique_ptr<Expr>> & argv)336 Value* UpdateDynamicPartitionsFn(const char* name, State* state,
337 const std::vector<std::unique_ptr<Expr>>& argv) {
338 if (argv.size() != 1) {
339 ErrorAbort(state, kArgsParsingFailure, "%s expects 1 arguments, got %zu", name, argv.size());
340 return StringValue("");
341 }
342 std::vector<std::unique_ptr<Value>> args;
343 if (!ReadValueArgs(state, argv, &args)) {
344 return nullptr;
345 }
346 const std::unique_ptr<Value>& op_list_value = args[0];
347 if (op_list_value->type != Value::Type::BLOB) {
348 ErrorAbort(state, kArgsParsingFailure, "op_list argument to %s must be blob", name);
349 return StringValue("");
350 }
351
352 std::string updated_marker = Paths::Get().stash_directory_base() + kMetadataUpdatedMarker;
353 if (state->is_retry) {
354 struct stat sb;
355 int result = stat(updated_marker.c_str(), &sb);
356 if (result == 0) {
357 LOG(INFO) << "Skipping already updated dynamic partition metadata based on marker";
358 return StringValue("t");
359 }
360 } else {
361 // Delete the obsolete marker if any.
362 std::string err;
363 if (!android::base::RemoveFileIfExists(updated_marker, &err)) {
364 LOG(ERROR) << "Failed to remove dynamic partition metadata updated marker " << updated_marker
365 << ": " << err;
366 return StringValue("");
367 }
368 }
369
370 auto super_device = GetSuperDevice();
371 auto builder = MetadataBuilder::New(PartitionOpener(), super_device, 0);
372 if (builder == nullptr) {
373 LOG(ERROR) << "Failed to load dynamic partition metadata.";
374 return StringValue("");
375 }
376
377 static const OpMap op_map{
378 // clang-format off
379 {"resize", PerformOpResize},
380 {"remove", PerformOpRemove},
381 {"add", PerformOpAdd},
382 {"move", PerformOpMove},
383 {"add_group", PerformOpAddGroup},
384 {"resize_group", PerformOpResizeGroup},
385 {"remove_group", PerformOpRemoveGroup},
386 {"remove_all_groups", PerformOpRemoveAllGroups},
387 // clang-format on
388 };
389
390 std::vector<std::string> lines = android::base::Split(op_list_value->data, "\n");
391 for (const auto& line : lines) {
392 auto comment_idx = line.find('#');
393 auto op_and_args = comment_idx == std::string::npos ? line : line.substr(0, comment_idx);
394 op_and_args = android::base::Trim(op_and_args);
395 if (op_and_args.empty()) continue;
396
397 auto tokens = android::base::Split(op_and_args, " ");
398 const auto& op = tokens[0];
399 auto it = op_map.find(op);
400 if (it == op_map.end()) {
401 LOG(ERROR) << "Unknown operation in op_list: " << op;
402 return StringValue("");
403 }
404 OpParameters params;
405 params.tokens = tokens;
406 params.builder = builder.get();
407 if (!it->second(params)) {
408 return StringValue("");
409 }
410 }
411
412 auto metadata = builder->Export();
413 if (metadata == nullptr) {
414 LOG(ERROR) << "Failed to export metadata.";
415 return StringValue("");
416 }
417
418 if (!UpdatePartitionTable(super_device, *metadata, 0)) {
419 LOG(ERROR) << "Failed to write metadata.";
420 return StringValue("");
421 }
422
423 if (!SetUpdatedMarker(updated_marker)) {
424 LOG(ERROR) << "Failed to set metadata updated marker.";
425 return StringValue("");
426 }
427
428 return StringValue("t");
429 }
430
RegisterDynamicPartitionsFunctions()431 void RegisterDynamicPartitionsFunctions() {
432 RegisterFunction("unmap_partition", UnmapPartitionFn);
433 RegisterFunction("map_partition", MapPartitionFn);
434 RegisterFunction("update_dynamic_partitions", UpdateDynamicPartitionsFn);
435 }
436