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/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
35 using android::dm::DeviceMapper;
36 using android::dm::DmDeviceState;
37 using android::fs_mgr::CreateLogicalPartition;
38 using android::fs_mgr::CreateLogicalPartitionParams;
39 using android::fs_mgr::DestroyLogicalPartition;
40 using android::fs_mgr::LpMetadata;
41 using android::fs_mgr::MetadataBuilder;
42 using android::fs_mgr::Partition;
43 using android::fs_mgr::PartitionOpener;
44 using android::fs_mgr::SlotNumberForSlotSuffix;
45
46 static constexpr std::chrono::milliseconds kMapTimeout{ 1000 };
47
GetSuperDevice()48 static std::string GetSuperDevice() {
49 return "/dev/block/by-name/" + fs_mgr_get_super_partition_name();
50 }
51
AddSlotSuffix(const std::string & partition_name)52 static std::string AddSlotSuffix(const std::string& partition_name) {
53 return partition_name + fs_mgr_get_slot_suffix();
54 }
55
UnmapPartitionWithSuffixOnDeviceMapper(const std::string & partition_name_suffix)56 static bool UnmapPartitionWithSuffixOnDeviceMapper(const std::string& partition_name_suffix) {
57 auto state = DeviceMapper::Instance().GetState(partition_name_suffix);
58 if (state == DmDeviceState::INVALID) {
59 return true;
60 }
61 if (state == DmDeviceState::ACTIVE) {
62 return DestroyLogicalPartition(partition_name_suffix);
63 }
64 LOG(ERROR) << "Unknown device mapper state: "
65 << static_cast<std::underlying_type_t<DmDeviceState>>(state);
66 return false;
67 }
68
MapPartitionOnDeviceMapper(const std::string & partition_name,std::string * path)69 bool UpdaterRuntime::MapPartitionOnDeviceMapper(const std::string& partition_name,
70 std::string* path) {
71 auto partition_name_suffix = AddSlotSuffix(partition_name);
72 auto state = DeviceMapper::Instance().GetState(partition_name_suffix);
73 if (state == DmDeviceState::INVALID) {
74 CreateLogicalPartitionParams params = {
75 .block_device = GetSuperDevice(),
76 // 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,
82 .force_writable = true,
83 .timeout_ms = kMapTimeout,
84 };
85 return CreateLogicalPartition(params, path);
86 }
87
88 if (state == DmDeviceState::ACTIVE) {
89 return DeviceMapper::Instance().GetDmDevicePathByName(partition_name_suffix, path);
90 }
91 LOG(ERROR) << "Unknown device mapper state: "
92 << static_cast<std::underlying_type_t<DmDeviceState>>(state);
93 return false;
94 }
95
UnmapPartitionOnDeviceMapper(const std::string & partition_name)96 bool UpdaterRuntime::UnmapPartitionOnDeviceMapper(const std::string& partition_name) {
97 return ::UnmapPartitionWithSuffixOnDeviceMapper(AddSlotSuffix(partition_name));
98 }
99
100 namespace { // Ops
101
102 struct OpParameters {
103 std::vector<std::string> tokens;
104 MetadataBuilder* builder;
105
ExpectArgSize__anon33c7e9380111::OpParameters106 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 }
op__anon33c7e9380111::OpParameters115 const std::string& op() const {
116 CHECK(!tokens.empty());
117 return tokens[0];
118 }
arg__anon33c7e9380111::OpParameters119 const std::string& arg(size_t pos) const {
120 CHECK_LE(pos + 1, tokens.size());
121 return tokens[pos + 1];
122 }
uint_arg__anon33c7e9380111::OpParameters123 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
134 using OpFunction = std::function<bool(const OpParameters&)>;
135 using OpMap = std::map<std::string, OpFunction>;
136
PerformOpResize(const OpParameters & params)137 bool PerformOpResize(const OpParameters& params) {
138 if (!params.ExpectArgSize(2)) return false;
139 const auto& partition_name_suffix = AddSlotSuffix(params.arg(0));
140 auto size = params.uint_arg(1, "size");
141 if (!size.has_value()) return false;
142
143 auto partition = params.builder->FindPartition(partition_name_suffix);
144 if (partition == nullptr) {
145 LOG(ERROR) << "Failed to find partition " << partition_name_suffix
146 << " in dynamic partition metadata.";
147 return false;
148 }
149 if (!UnmapPartitionWithSuffixOnDeviceMapper(partition_name_suffix)) {
150 LOG(ERROR) << "Cannot unmap " << partition_name_suffix << " before resizing.";
151 return false;
152 }
153 if (!params.builder->ResizePartition(partition, size.value())) {
154 LOG(ERROR) << "Failed to resize partition " << partition_name_suffix << " to size " << *size
155 << ".";
156 return false;
157 }
158 return true;
159 }
160
PerformOpRemove(const OpParameters & params)161 bool PerformOpRemove(const OpParameters& params) {
162 if (!params.ExpectArgSize(1)) return false;
163 const auto& partition_name_suffix = AddSlotSuffix(params.arg(0));
164
165 if (!UnmapPartitionWithSuffixOnDeviceMapper(partition_name_suffix)) {
166 LOG(ERROR) << "Cannot unmap " << partition_name_suffix << " before removing.";
167 return false;
168 }
169 params.builder->RemovePartition(partition_name_suffix);
170 return true;
171 }
172
PerformOpAdd(const OpParameters & params)173 bool PerformOpAdd(const OpParameters& params) {
174 if (!params.ExpectArgSize(2)) return false;
175 const auto& partition_name_suffix = AddSlotSuffix(params.arg(0));
176 const auto& group_name_suffix = AddSlotSuffix(params.arg(1));
177
178 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 << ".";
182 return false;
183 }
184 return true;
185 }
186
PerformOpMove(const OpParameters & params)187 bool PerformOpMove(const OpParameters& params) {
188 if (!params.ExpectArgSize(2)) return false;
189 const auto& partition_name_suffix = AddSlotSuffix(params.arg(0));
190 const auto& new_group_name_suffix = AddSlotSuffix(params.arg(1));
191
192 auto partition = params.builder->FindPartition(partition_name_suffix);
193 if (partition == nullptr) {
194 LOG(ERROR) << "Cannot move partition " << partition_name_suffix << " to group "
195 << new_group_name_suffix << " because it is not found.";
196 return false;
197 }
198
199 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 << ".";
204 return false;
205 }
206 }
207 return true;
208 }
209
PerformOpAddGroup(const OpParameters & params)210 bool PerformOpAddGroup(const OpParameters& params) {
211 if (!params.ExpectArgSize(2)) return false;
212 const auto& group_name_suffix = AddSlotSuffix(params.arg(0));
213 auto maximum_size = params.uint_arg(1, "maximum_size");
214 if (!maximum_size.has_value()) return false;
215
216 auto group = params.builder->FindGroup(group_name_suffix);
217 if (group != nullptr) {
218 LOG(ERROR) << "Cannot add group " << group_name_suffix << " because it already exists.";
219 return false;
220 }
221
222 if (maximum_size.value() == 0) {
223 LOG(WARNING) << "Adding group " << group_name_suffix << " with no size limits.";
224 }
225
226 if (!params.builder->AddGroup(group_name_suffix, maximum_size.value())) {
227 LOG(ERROR) << "Failed to add group " << group_name_suffix << " with maximum size "
228 << maximum_size.value() << ".";
229 return false;
230 }
231 return true;
232 }
233
PerformOpResizeGroup(const OpParameters & params)234 bool PerformOpResizeGroup(const OpParameters& params) {
235 if (!params.ExpectArgSize(2)) return false;
236 const auto& group_name_suffix = AddSlotSuffix(params.arg(0));
237 auto new_size = params.uint_arg(1, "maximum_size");
238 if (!new_size.has_value()) return false;
239
240 auto group = params.builder->FindGroup(group_name_suffix);
241 if (group == nullptr) {
242 LOG(ERROR) << "Cannot resize group " << group_name_suffix << " because it is not found.";
243 return false;
244 }
245
246 auto old_size = group->maximum_size();
247 if (old_size != new_size.value()) {
248 if (!params.builder->ChangeGroupSize(group_name_suffix, new_size.value())) {
249 LOG(ERROR) << "Cannot resize group " << group_name_suffix << " from " << old_size << " to "
250 << new_size.value() << ".";
251 return false;
252 }
253 }
254 return true;
255 }
256
ListPartitionNamesInGroup(MetadataBuilder * builder,const std::string & group_name_suffix)257 std::vector<std::string> ListPartitionNamesInGroup(MetadataBuilder* builder,
258 const std::string& group_name_suffix) {
259 auto partitions = builder->ListPartitionsInGroup(group_name_suffix);
260 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
PerformOpRemoveGroup(const OpParameters & params)266 bool PerformOpRemoveGroup(const OpParameters& params) {
267 if (!params.ExpectArgSize(1)) return false;
268 const auto& group_name_suffix = AddSlotSuffix(params.arg(0));
269
270 auto partition_names = ListPartitionNamesInGroup(params.builder, group_name_suffix);
271 if (!partition_names.empty()) {
272 LOG(ERROR) << "Cannot remove group " << group_name_suffix
273 << " because it still contains partitions ["
274 << android::base::Join(partition_names, ", ") << "]";
275 return false;
276 }
277 params.builder->RemoveGroupAndPartitions(group_name_suffix);
278 return true;
279 }
280
PerformOpRemoveAllGroups(const OpParameters & params)281 bool PerformOpRemoveAllGroups(const OpParameters& params) {
282 if (!params.ExpectArgSize(0)) return false;
283
284 auto group_names = params.builder->ListGroups();
285 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 << ".";
291 return false;
292 }
293 }
294 params.builder->RemoveGroupAndPartitions(group_name_suffix);
295 }
296 return true;
297 }
298
299 } // namespace
300
UpdateDynamicPartitions(const std::string_view op_list_value)301 bool 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 }
357