• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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