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