1 /*
2  * Copyright (C) 2018 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 <getopt.h>
18 #include <inttypes.h>
19 #include <sys/mount.h>
20 #include <sys/stat.h>
21 #include <sys/statvfs.h>
22 #include <sys/types.h>
23 #include <sysexits.h>
24 #include <unistd.h>
25 
26 #include <algorithm>
27 #include <iostream>
28 #include <optional>
29 #include <regex>
30 #include <string>
31 #include <vector>
32 
33 #include <android-base/parseint.h>
34 #include <android-base/properties.h>
35 #include <android-base/strings.h>
36 #ifdef __ANDROID__
37 #include <cutils/android_get_control_file.h>
38 #include <fs_mgr.h>
39 #endif
40 #include <jsonpb/jsonpb.h>
41 #include <liblp/builder.h>
42 #include <liblp/liblp.h>
43 
44 #include "dynamic_partitions_device_info.pb.h"
45 using namespace android;
46 using namespace android::fs_mgr;
47 
usage(int,char * argv[],std::ostream & cerr)48 static int usage(int /* argc */, char* argv[], std::ostream& cerr) {
49     cerr << argv[0]
50          << " - command-line tool for dumping Android Logical Partition images.\n"
51             "\n"
52             "Usage:\n"
53             "  "
54          << argv[0]
55          << " [-s <SLOT#>|--slot=<SLOT#>] [-j|--json] [FILE|DEVICE]\n"
56             "\n"
57             "Options:\n"
58             "  -s, --slot=N     Slot number or suffix.\n"
59             "  -j, --json       Print in JSON format.\n"
60             "  -d, --dump-metadata-size\n"
61             "                   Print the space reserved for metadata to stdout\n"
62             "                   in bytes.\n"
63             "  -a, --all        Dump all slots (not available in JSON mode).\n";
64     return EX_USAGE;
65 }
66 
BuildFlagString(const std::vector<std::string> & strings)67 static std::string BuildFlagString(const std::vector<std::string>& strings) {
68     return strings.empty() ? "none" : android::base::Join(strings, ",");
69 }
70 
BuildHeaderFlagString(uint32_t flags)71 static std::string BuildHeaderFlagString(uint32_t flags) {
72     std::vector<std::string> strings;
73 
74     if (flags & LP_HEADER_FLAG_VIRTUAL_AB_DEVICE) {
75         strings.emplace_back("virtual_ab_device");
76         flags &= ~LP_HEADER_FLAG_VIRTUAL_AB_DEVICE;
77     }
78 
79     for (uint32_t i = 0; i < sizeof(flags) * 8; i++) {
80         if (!(flags & (1U << i))) {
81             continue;
82         }
83         strings.emplace_back("unknown_flag_bit_" + std::to_string(i));
84     }
85     return BuildFlagString(strings);
86 }
87 
BuildAttributeString(uint32_t attrs)88 static std::string BuildAttributeString(uint32_t attrs) {
89     std::vector<std::string> strings;
90     if (attrs & LP_PARTITION_ATTR_READONLY) strings.emplace_back("readonly");
91     if (attrs & LP_PARTITION_ATTR_SLOT_SUFFIXED) strings.emplace_back("slot-suffixed");
92     if (attrs & LP_PARTITION_ATTR_UPDATED) strings.emplace_back("updated");
93     if (attrs & LP_PARTITION_ATTR_DISABLED) strings.emplace_back("disabled");
94     return BuildFlagString(strings);
95 }
96 
BuildGroupFlagString(uint32_t flags)97 static std::string BuildGroupFlagString(uint32_t flags) {
98     std::vector<std::string> strings;
99     if (flags & LP_GROUP_SLOT_SUFFIXED) strings.emplace_back("slot-suffixed");
100     return BuildFlagString(strings);
101 }
102 
BuildBlockDeviceFlagString(uint32_t flags)103 static std::string BuildBlockDeviceFlagString(uint32_t flags) {
104     std::vector<std::string> strings;
105     if (flags & LP_BLOCK_DEVICE_SLOT_SUFFIXED) strings.emplace_back("slot-suffixed");
106     return BuildFlagString(strings);
107 }
108 
109 // Reimplementation of fs_mgr_get_slot_suffix() without reading
110 // kernel commandline.
GetSlotSuffix()111 static std::string GetSlotSuffix() {
112     return base::GetProperty("ro.boot.slot_suffix", "");
113 }
114 
115 // Reimplementation of fs_mgr_get_super_partition_name() without reading
116 // kernel commandline. Always return the super partition at current slot.
GetSuperPartitionName(const std::optional<uint32_t> & slot={})117 static std::string GetSuperPartitionName(const std::optional<uint32_t>& slot = {}) {
118     std::string super_partition = base::GetProperty("ro.boot.super_partition", "");
119     if (super_partition.empty()) {
120         return LP_METADATA_DEFAULT_PARTITION_NAME;
121     }
122     if (slot.has_value()) {
123         return super_partition + SlotSuffixForSlotNumber(slot.value());
124     }
125     return super_partition + GetSlotSuffix();
126 }
127 
RemoveSuffix(const std::string & s,const std::string & suffix)128 static std::string RemoveSuffix(const std::string& s, const std::string& suffix) {
129     if (base::EndsWith(s, suffix)) {
130         return s.substr(0, s.length() - suffix.length());
131     }
132     return s;
133 }
134 
135 // Merge proto with information from metadata.
MergeMetadata(const LpMetadata * metadata,DynamicPartitionsDeviceInfoProto * proto)136 static bool MergeMetadata(const LpMetadata* metadata,
137                           DynamicPartitionsDeviceInfoProto* proto) {
138     if (!metadata) return false;
139     auto builder = MetadataBuilder::New(*metadata);
140     if (!builder) return false;
141 
142     std::string slot_suffix = GetSlotSuffix();
143 
144     for (const auto& group_name : builder->ListGroups()) {
145         auto group = builder->FindGroup(group_name);
146         if (!group) continue;
147         if (!base::EndsWith(group_name, slot_suffix)) continue;
148         auto group_proto = proto->add_groups();
149         group_proto->set_name(RemoveSuffix(group_name, slot_suffix));
150         group_proto->set_maximum_size(group->maximum_size());
151 
152         for (auto partition : builder->ListPartitionsInGroup(group_name)) {
153             auto partition_name = partition->name();
154             if (!base::EndsWith(partition_name, slot_suffix)) continue;
155             auto partition_proto = proto->add_partitions();
156             partition_proto->set_name(RemoveSuffix(partition_name, slot_suffix));
157             partition_proto->set_group_name(RemoveSuffix(group_name, slot_suffix));
158             partition_proto->set_size(partition->size());
159             partition_proto->set_is_dynamic(true);
160         }
161     }
162 
163     for (const auto& block_device : metadata->block_devices) {
164         std::string name = GetBlockDevicePartitionName(block_device);
165         BlockDeviceInfo info;
166         if (!builder->GetBlockDeviceInfo(name, &info)) {
167             continue;
168         }
169         auto block_device_proto = proto->add_block_devices();
170         block_device_proto->set_name(RemoveSuffix(name, slot_suffix));
171         block_device_proto->set_size(info.size);
172         block_device_proto->set_block_size(info.logical_block_size);
173         block_device_proto->set_alignment(info.alignment);
174         block_device_proto->set_alignment_offset(info.alignment_offset);
175     }
176 
177     auto super_device_proto = proto->mutable_super_device();
178     super_device_proto->set_name(GetSuperPartitionName());
179     super_device_proto->set_used_size(builder->UsedSpace());
180     super_device_proto->set_total_size(GetTotalSuperPartitionSize(*metadata));
181 
182     return true;
183 }
184 
185 #ifdef __ANDROID__
FindPartition(DynamicPartitionsDeviceInfoProto * proto,const std::string & partition)186 static DynamicPartitionsDeviceInfoProto::Partition* FindPartition(
187         DynamicPartitionsDeviceInfoProto* proto, const std::string& partition) {
188     for (DynamicPartitionsDeviceInfoProto::Partition& p : *proto->mutable_partitions()) {
189         if (p.name() == partition) {
190             return &p;
191         }
192     }
193     return nullptr;
194 }
195 
GetReadonlyPartitionName(const android::fs_mgr::FstabEntry & entry)196 static std::optional<std::string> GetReadonlyPartitionName(const android::fs_mgr::FstabEntry& entry) {
197     // Only report readonly partitions.
198     if ((entry.flags & MS_RDONLY) == 0) return std::nullopt;
199     std::regex regex("/([a-zA-Z_]*)$");
200     std::smatch match;
201     if (!std::regex_match(entry.mount_point, match, regex)) return std::nullopt;
202     // On system-as-root devices, fstab lists / for system partition.
203     std::string partition = match[1];
204     return partition.empty() ? "system" : partition;
205 }
206 
MergeFsUsage(DynamicPartitionsDeviceInfoProto * proto,std::ostream & cerr)207 static bool MergeFsUsage(DynamicPartitionsDeviceInfoProto* proto,
208                          std::ostream& cerr) {
209     using namespace std::string_literals;
210     Fstab fstab;
211     if (!ReadDefaultFstab(&fstab)) {
212         cerr << "Cannot read fstab\n";
213         return false;
214     }
215 
216     for (const auto& entry : fstab) {
217         auto partition = GetReadonlyPartitionName(entry);
218         if (!partition) {
219             continue;
220         }
221 
222         // system is mounted to "/";
223         const char* mount_point = (entry.mount_point == "/system")
224             ? "/" : entry.mount_point.c_str();
225 
226         struct statvfs vst;
227         if (statvfs(mount_point, &vst) == -1) {
228             continue;
229         }
230 
231         auto partition_proto = FindPartition(proto, *partition);
232         if (partition_proto == nullptr) {
233             partition_proto = proto->add_partitions();
234             partition_proto->set_name(*partition);
235             partition_proto->set_is_dynamic(false);
236         }
237         partition_proto->set_fs_size((uint64_t)vst.f_blocks * vst.f_frsize);
238 
239         if (!entry.fs_type.empty()) {
240             partition_proto->set_fs_type(entry.fs_type);
241         } else {
242             partition_proto->set_fs_type("UNKNOWN");
243         }
244 
245         if (vst.f_bavail <= vst.f_blocks) {
246             partition_proto->set_fs_used((uint64_t)(vst.f_blocks - vst.f_bavail) * vst.f_frsize);
247         }
248     }
249     return true;
250 }
251 #endif
252 
253 // Print output in JSON format.
254 // If successful, this function must write a valid JSON string to "cout" and return 0.
PrintJson(const LpMetadata * metadata,std::ostream & cout,std::ostream & cerr)255 static int PrintJson(const LpMetadata* metadata, std::ostream& cout,
256                      std::ostream& cerr) {
257     DynamicPartitionsDeviceInfoProto proto;
258 
259     if (base::GetBoolProperty("ro.boot.dynamic_partitions", false)) {
260         proto.set_enabled(true);
261     }
262 
263     if (base::GetBoolProperty("ro.boot.dynamic_partitions_retrofit", false)) {
264         proto.set_retrofit(true);
265     }
266 
267     if (!MergeMetadata(metadata, &proto)) {
268         cerr << "Warning: Failed to read metadata.\n";
269     }
270 #ifdef __ANDROID__
271     if (!MergeFsUsage(&proto, cerr)) {
272         cerr << "Warning: Failed to read filesystem size and usage.\n";
273     }
274 #endif
275 
276     auto error_or_json = jsonpb::MessageToJsonString(proto);
277     if (!error_or_json.ok()) {
278         cerr << error_or_json.error() << "\n";
279         return EX_SOFTWARE;
280     }
281     cout << *error_or_json;
282     return EX_OK;
283 }
284 
DumpMetadataSize(const LpMetadata & metadata,std::ostream & cout)285 static int DumpMetadataSize(const LpMetadata& metadata, std::ostream& cout) {
286     auto super_device = GetMetadataSuperBlockDevice(metadata);
287     uint64_t metadata_size = super_device->first_logical_sector * LP_SECTOR_SIZE;
288     cout << metadata_size << std::endl;
289     return EX_OK;
290 }
291 
292 class FileOrBlockDeviceOpener final : public PartitionOpener {
293 public:
Open(const std::string & path,int flags) const294     android::base::unique_fd Open(const std::string& path, int flags) const override {
295         // Try a local file first.
296         android::base::unique_fd fd;
297 
298 #ifdef __ANDROID__
299         fd.reset(android_get_control_file(path.c_str()));
300         if (fd >= 0) return fd;
301 #endif
302         fd.reset(open(path.c_str(), flags));
303         if (fd >= 0) return fd;
304 
305         return PartitionOpener::Open(path, flags);
306     }
307 };
308 
309 std::optional<std::tuple<std::string, uint64_t>>
ParseLinearExtentData(const LpMetadata & pt,const LpMetadataExtent & extent)310 ParseLinearExtentData(const LpMetadata& pt, const LpMetadataExtent& extent) {
311     if (extent.target_type != LP_TARGET_TYPE_LINEAR) {
312         return std::nullopt;
313     }
314     const auto& block_device = pt.block_devices[extent.target_source];
315     std::string device_name = GetBlockDevicePartitionName(block_device);
316     return std::make_tuple(std::move(device_name), extent.target_data);
317 }
318 
PrintMetadata(const LpMetadata & pt,std::ostream & cout)319 static void PrintMetadata(const LpMetadata& pt, std::ostream& cout) {
320     cout << "Metadata version: " << pt.header.major_version << "." << pt.header.minor_version
321          << "\n";
322     cout << "Metadata size: " << (pt.header.header_size + pt.header.tables_size) << " bytes\n";
323     cout << "Metadata max size: " << pt.geometry.metadata_max_size << " bytes\n";
324     cout << "Metadata slot count: " << pt.geometry.metadata_slot_count << "\n";
325     cout << "Header flags: " << BuildHeaderFlagString(pt.header.flags) << "\n";
326     cout << "Partition table:\n";
327     cout << "------------------------\n";
328 
329     std::vector<std::tuple<std::string, const LpMetadataExtent*>> extents;
330 
331     for (const auto& partition : pt.partitions) {
332         std::string name = GetPartitionName(partition);
333         std::string group_name = GetPartitionGroupName(pt.groups[partition.group_index]);
334         cout << "  Name: " << name << "\n";
335         cout << "  Group: " << group_name << "\n";
336         cout << "  Attributes: " << BuildAttributeString(partition.attributes) << "\n";
337         cout << "  Extents:\n";
338         uint64_t first_sector = 0;
339         for (size_t i = 0; i < partition.num_extents; i++) {
340             const LpMetadataExtent& extent = pt.extents[partition.first_extent_index + i];
341             cout << "    " << first_sector << " .. " << (first_sector + extent.num_sectors - 1)
342                  << " ";
343             first_sector += extent.num_sectors;
344             if (extent.target_type == LP_TARGET_TYPE_LINEAR) {
345                 const auto& block_device = pt.block_devices[extent.target_source];
346                 std::string device_name = GetBlockDevicePartitionName(block_device);
347                 cout << "linear " << device_name.c_str() << " " << extent.target_data;
348             } else if (extent.target_type == LP_TARGET_TYPE_ZERO) {
349                 cout << "zero";
350             }
351             extents.push_back(std::make_tuple(name, &extent));
352             cout << "\n";
353         }
354         cout << "------------------------\n";
355     }
356 
357     std::sort(extents.begin(), extents.end(), [&](const auto& x, const auto& y) {
358         auto x_data = ParseLinearExtentData(pt, *std::get<1>(x));
359         auto y_data = ParseLinearExtentData(pt, *std::get<1>(y));
360         return x_data < y_data;
361     });
362 
363     cout << "Super partition layout:\n";
364     cout << "------------------------\n";
365     for (auto&& [name, extent] : extents) {
366         auto data = ParseLinearExtentData(pt, *extent);
367         if (!data) continue;
368         auto&& [block_device, offset] = *data;
369         cout << block_device << ": " << offset << " .. " << (offset + extent->num_sectors)
370              << ": " << name << " (" << extent->num_sectors << " sectors)\n";
371     }
372     cout << "------------------------\n";
373 
374     cout << "Block device table:\n";
375     cout << "------------------------\n";
376     for (const auto& block_device : pt.block_devices) {
377         std::string partition_name = GetBlockDevicePartitionName(block_device);
378         cout << "  Partition name: " << partition_name << "\n";
379         cout << "  First sector: " << block_device.first_logical_sector << "\n";
380         cout << "  Size: " << block_device.size << " bytes\n";
381         cout << "  Flags: " << BuildBlockDeviceFlagString(block_device.flags) << "\n";
382         cout << "------------------------\n";
383     }
384 
385     cout << "Group table:\n";
386     cout << "------------------------\n";
387     for (const auto& group : pt.groups) {
388         std::string group_name = GetPartitionGroupName(group);
389         cout << "  Name: " << group_name << "\n";
390         cout << "  Maximum size: " << group.maximum_size << " bytes\n";
391         cout << "  Flags: " << BuildGroupFlagString(group.flags) << "\n";
392         cout << "------------------------\n";
393     }
394 }
395 
ReadDeviceOrFile(const std::string & path,uint32_t slot)396 static std::unique_ptr<LpMetadata> ReadDeviceOrFile(const std::string& path, uint32_t slot) {
397     if (IsEmptySuperImage(path)) {
398         return ReadFromImageFile(path);
399     }
400     return ReadMetadata(path, slot);
401 }
402 
LpdumpMain(int argc,char * argv[],std::ostream & cout,std::ostream & cerr)403 int LpdumpMain(int argc, char* argv[], std::ostream& cout, std::ostream& cerr) {
404     // clang-format off
405     struct option options[] = {
406         { "all", no_argument, nullptr, 'a' },
407         { "slot", required_argument, nullptr, 's' },
408         { "help", no_argument, nullptr, 'h' },
409         { "json", no_argument, nullptr, 'j' },
410         { "dump-metadata-size", no_argument, nullptr, 'd' },
411         { "is-super-empty", no_argument, nullptr, 'e' },
412         { nullptr, 0, nullptr, 0 },
413     };
414     // clang-format on
415 
416     // Allow this function to be invoked by lpdumpd multiple times.
417     optind = 1;
418 
419     int rv;
420     int index;
421     bool json = false;
422     bool dump_metadata_size = false;
423     bool dump_all = false;
424     std::optional<uint32_t> slot;
425     while ((rv = getopt_long_only(argc, argv, "s:jhde", options, &index)) != -1) {
426         switch (rv) {
427             case 'a':
428                 dump_all = true;
429                 break;
430             case 'h':
431                 usage(argc, argv, cout);
432                 return EX_OK;
433             case 's': {
434                 uint32_t slot_arg;
435                 if (android::base::ParseUint(optarg, &slot_arg)) {
436                     slot = slot_arg;
437                 } else {
438                     slot = SlotNumberForSlotSuffix(optarg);
439                 }
440                 break;
441             }
442             case 'e':
443                 // This is ignored, we now derive whether it's empty automatically.
444                 break;
445             case 'd':
446                 dump_metadata_size = true;
447                 break;
448             case 'j':
449                 json = true;
450                 break;
451             case '?':
452             case ':':
453                 return usage(argc, argv, cerr);
454         }
455     }
456 
457     if (dump_all) {
458         if (slot.has_value()) {
459             cerr << "Cannot specify both --all and --slot.\n";
460             return usage(argc, argv, cerr);
461         }
462         if (json) {
463             cerr << "Cannot specify both --all and --json.\n";
464             return usage(argc, argv, cerr);
465         }
466 
467         // When dumping everything always start from the first slot.
468         slot = 0;
469     }
470 
471 #ifdef __ANDROID__
472     // Use the current slot as a default for A/B devices.
473     auto current_slot_suffix = GetSlotSuffix();
474     if (!slot.has_value() && !current_slot_suffix.empty()) {
475         slot = SlotNumberForSlotSuffix(current_slot_suffix);
476     }
477 #endif
478 
479     // If we still haven't determined a slot yet, use the first one.
480     if (!slot.has_value()) {
481         slot = 0;
482     }
483 
484     // Determine the path to the super partition (or image). If an explicit
485     // path is given, we use it for everything. Otherwise, we will infer it
486     // at the time we need to read metadata.
487     std::string super_path;
488     bool override_super_name = (optind < argc);
489     if (override_super_name) {
490         super_path = argv[optind++];
491     } else {
492 #ifdef __ANDROID__
493         super_path = GetSuperPartitionName(slot);
494 #else
495         cerr << "Must specify a super partition image.\n";
496         return usage(argc, argv, cerr);
497 #endif
498     }
499 
500     auto pt = ReadDeviceOrFile(super_path, slot.value());
501 
502     // --json option doesn't require metadata to be present.
503     if (json) {
504         return PrintJson(pt.get(), cout, cerr);
505     }
506 
507     if (!pt) {
508         cerr << "Failed to read metadata.\n";
509         return EX_NOINPUT;
510     }
511 
512     if (dump_metadata_size) {
513         return DumpMetadataSize(*pt.get(), cout);
514     }
515 
516     // When running on the device, we can check the slot count. Otherwise we
517     // use the # of metadata slots. (There is an extra slot we don't want to
518     // dump because it is currently unused.)
519 #ifdef __ANDROID__
520     uint32_t num_slots = current_slot_suffix.empty() ? 1 : 2;
521     if (dump_all && num_slots > 1) {
522         cout << "Current slot: " << current_slot_suffix << "\n";
523     }
524 #else
525     uint32_t num_slots = pt->geometry.metadata_slot_count;
526 #endif
527     // Empty images only have one slot.
528     if (IsEmptySuperImage(super_path)) {
529         num_slots = 1;
530     }
531 
532     if (num_slots > 1) {
533         cout << "Slot " << slot.value() << ":\n";
534     }
535     PrintMetadata(*pt.get(), cout);
536 
537     if (dump_all) {
538         for (uint32_t i = 1; i < num_slots; i++) {
539             if (!override_super_name) {
540                 super_path = GetSuperPartitionName(i);
541             }
542 
543             pt = ReadDeviceOrFile(super_path, i);
544             if (!pt) {
545                 continue;
546             }
547 
548             cout << "\nSlot " << i << ":\n";
549             PrintMetadata(*pt.get(), cout);
550         }
551     }
552     return EX_OK;
553 }
554 
LpdumpMain(int argc,char * argv[])555 int LpdumpMain(int argc, char* argv[]) {
556     return LpdumpMain(argc, argv, std::cout, std::cerr);
557 }
558