blob: b50dd75f97b7b2835921b606d6eb617a2ddea5a2 [file] [log] [blame]
Yifan Hong8ff84d72018-12-19 16:21:55 -08001/*
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
45using android::base::ParseUint;
46using android::dm::DeviceMapper;
47using android::dm::DmDeviceState;
48using android::fs_mgr::CreateLogicalPartition;
49using android::fs_mgr::DestroyLogicalPartition;
50using android::fs_mgr::LpMetadata;
51using android::fs_mgr::MetadataBuilder;
52using android::fs_mgr::Partition;
53using android::fs_mgr::PartitionOpener;
54
55static constexpr std::chrono::milliseconds kMapTimeout{ 1000 };
56static constexpr char kMetadataUpdatedMarker[] = "/dynamic_partition_metadata.UPDATED";
57
58static std::string GetSuperDevice() {
59 return "/dev/block/by-name/" + fs_mgr_get_super_partition_name();
60}
61
62static 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
92static 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
105static 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
120Value* 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
128Value* 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
138namespace { // Ops
139
140struct OpParameters {
141 std::vector<std::string> tokens;
142 MetadataBuilder* builder;
143
144 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 }
153 const std::string& op() const {
154 CHECK(!tokens.empty());
155 return tokens[0];
156 }
157 const std::string& arg(size_t pos) const {
158 CHECK_LE(pos + 1, tokens.size());
159 return tokens[pos + 1];
160 }
161 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
172using OpFunction = std::function<bool(const OpParameters&)>;
173using OpMap = std::map<std::string, OpFunction>;
174
175bool 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
198bool 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
210bool 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
223bool 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
246bool 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
270bool 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
293std::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
302bool 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
316bool 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
336Value* 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
431void RegisterDynamicPartitionsFunctions() {
432 RegisterFunction("unmap_partition", UnmapPartitionFn);
433 RegisterFunction("map_partition", MapPartitionFn);
434 RegisterFunction("update_dynamic_partitions", UpdateDynamicPartitionsFn);
435}