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