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 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 67 static std::string BuildFlagString(const std::vector<std::string>& strings) { 68 return strings.empty() ? "none" : android::base::Join(strings, ","); 69 } 70 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 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 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 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. 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. 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 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. 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 return true; 177 } 178 179 #ifdef __ANDROID__ 180 static DynamicPartitionsDeviceInfoProto::Partition* FindPartition( 181 DynamicPartitionsDeviceInfoProto* proto, const std::string& partition) { 182 for (DynamicPartitionsDeviceInfoProto::Partition& p : *proto->mutable_partitions()) { 183 if (p.name() == partition) { 184 return &p; 185 } 186 } 187 return nullptr; 188 } 189 190 static std::optional<std::string> GetReadonlyPartitionName(const android::fs_mgr::FstabEntry& entry) { 191 // Only report readonly partitions. 192 if ((entry.flags & MS_RDONLY) == 0) return std::nullopt; 193 std::regex regex("/([a-zA-Z_]*)$"); 194 std::smatch match; 195 if (!std::regex_match(entry.mount_point, match, regex)) return std::nullopt; 196 // On system-as-root devices, fstab lists / for system partition. 197 std::string partition = match[1]; 198 return partition.empty() ? "system" : partition; 199 } 200 201 static bool MergeFsUsage(DynamicPartitionsDeviceInfoProto* proto, 202 std::ostream& cerr) { 203 using namespace std::string_literals; 204 Fstab fstab; 205 if (!ReadDefaultFstab(&fstab)) { 206 cerr << "Cannot read fstab\n"; 207 return false; 208 } 209 210 for (const auto& entry : fstab) { 211 auto partition = GetReadonlyPartitionName(entry); 212 if (!partition) { 213 continue; 214 } 215 216 // system is mounted to "/"; 217 const char* mount_point = (entry.mount_point == "/system") 218 ? "/" : entry.mount_point.c_str(); 219 220 struct statvfs vst; 221 if (statvfs(mount_point, &vst) == -1) { 222 continue; 223 } 224 225 auto partition_proto = FindPartition(proto, *partition); 226 if (partition_proto == nullptr) { 227 partition_proto = proto->add_partitions(); 228 partition_proto->set_name(*partition); 229 partition_proto->set_is_dynamic(false); 230 } 231 partition_proto->set_fs_size((uint64_t)vst.f_blocks * vst.f_frsize); 232 if (vst.f_bavail <= vst.f_blocks) { 233 partition_proto->set_fs_used((uint64_t)(vst.f_blocks - vst.f_bavail) * vst.f_frsize); 234 } 235 } 236 return true; 237 } 238 #endif 239 240 // Print output in JSON format. 241 // If successful, this function must write a valid JSON string to "cout" and return 0. 242 static int PrintJson(const LpMetadata* metadata, std::ostream& cout, 243 std::ostream& cerr) { 244 DynamicPartitionsDeviceInfoProto proto; 245 246 if (base::GetBoolProperty("ro.boot.dynamic_partitions", false)) { 247 proto.set_enabled(true); 248 } 249 250 if (base::GetBoolProperty("ro.boot.dynamic_partitions_retrofit", false)) { 251 proto.set_retrofit(true); 252 } 253 254 if (!MergeMetadata(metadata, &proto)) { 255 cerr << "Warning: Failed to read metadata.\n"; 256 } 257 #ifdef __ANDROID__ 258 if (!MergeFsUsage(&proto, cerr)) { 259 cerr << "Warning: Failed to read filesystem size and usage.\n"; 260 } 261 #endif 262 263 auto error_or_json = jsonpb::MessageToJsonString(proto); 264 if (!error_or_json.ok()) { 265 cerr << error_or_json.error() << "\n"; 266 return EX_SOFTWARE; 267 } 268 cout << *error_or_json; 269 return EX_OK; 270 } 271 272 static int DumpMetadataSize(const LpMetadata& metadata, std::ostream& cout) { 273 auto super_device = GetMetadataSuperBlockDevice(metadata); 274 uint64_t metadata_size = super_device->first_logical_sector * LP_SECTOR_SIZE; 275 cout << metadata_size << std::endl; 276 return EX_OK; 277 } 278 279 class FileOrBlockDeviceOpener final : public PartitionOpener { 280 public: 281 android::base::unique_fd Open(const std::string& path, int flags) const override { 282 // Try a local file first. 283 android::base::unique_fd fd; 284 285 #ifdef __ANDROID__ 286 fd.reset(android_get_control_file(path.c_str())); 287 if (fd >= 0) return fd; 288 #endif 289 fd.reset(open(path.c_str(), flags)); 290 if (fd >= 0) return fd; 291 292 return PartitionOpener::Open(path, flags); 293 } 294 }; 295 296 std::optional<std::tuple<std::string, uint64_t>> 297 ParseLinearExtentData(const LpMetadata& pt, const LpMetadataExtent& extent) { 298 if (extent.target_type != LP_TARGET_TYPE_LINEAR) { 299 return std::nullopt; 300 } 301 const auto& block_device = pt.block_devices[extent.target_source]; 302 std::string device_name = GetBlockDevicePartitionName(block_device); 303 return std::make_tuple(std::move(device_name), extent.target_data); 304 } 305 306 static void PrintMetadata(const LpMetadata& pt, std::ostream& cout) { 307 cout << "Metadata version: " << pt.header.major_version << "." << pt.header.minor_version 308 << "\n"; 309 cout << "Metadata size: " << (pt.header.header_size + pt.header.tables_size) << " bytes\n"; 310 cout << "Metadata max size: " << pt.geometry.metadata_max_size << " bytes\n"; 311 cout << "Metadata slot count: " << pt.geometry.metadata_slot_count << "\n"; 312 cout << "Header flags: " << BuildHeaderFlagString(pt.header.flags) << "\n"; 313 cout << "Partition table:\n"; 314 cout << "------------------------\n"; 315 316 std::vector<std::tuple<std::string, const LpMetadataExtent*>> extents; 317 318 for (const auto& partition : pt.partitions) { 319 std::string name = GetPartitionName(partition); 320 std::string group_name = GetPartitionGroupName(pt.groups[partition.group_index]); 321 cout << " Name: " << name << "\n"; 322 cout << " Group: " << group_name << "\n"; 323 cout << " Attributes: " << BuildAttributeString(partition.attributes) << "\n"; 324 cout << " Extents:\n"; 325 uint64_t first_sector = 0; 326 for (size_t i = 0; i < partition.num_extents; i++) { 327 const LpMetadataExtent& extent = pt.extents[partition.first_extent_index + i]; 328 cout << " " << first_sector << " .. " << (first_sector + extent.num_sectors - 1) 329 << " "; 330 first_sector += extent.num_sectors; 331 if (extent.target_type == LP_TARGET_TYPE_LINEAR) { 332 const auto& block_device = pt.block_devices[extent.target_source]; 333 std::string device_name = GetBlockDevicePartitionName(block_device); 334 cout << "linear " << device_name.c_str() << " " << extent.target_data; 335 } else if (extent.target_type == LP_TARGET_TYPE_ZERO) { 336 cout << "zero"; 337 } 338 extents.push_back(std::make_tuple(name, &extent)); 339 cout << "\n"; 340 } 341 cout << "------------------------\n"; 342 } 343 344 std::sort(extents.begin(), extents.end(), [&](const auto& x, const auto& y) { 345 auto x_data = ParseLinearExtentData(pt, *std::get<1>(x)); 346 auto y_data = ParseLinearExtentData(pt, *std::get<1>(y)); 347 return x_data < y_data; 348 }); 349 350 cout << "Super partition layout:\n"; 351 cout << "------------------------\n"; 352 for (auto&& [name, extent] : extents) { 353 auto data = ParseLinearExtentData(pt, *extent); 354 if (!data) continue; 355 auto&& [block_device, offset] = *data; 356 cout << block_device << ": " << offset << " .. " << (offset + extent->num_sectors) 357 << ": " << name << " (" << extent->num_sectors << " sectors)\n"; 358 } 359 cout << "------------------------\n"; 360 361 cout << "Block device table:\n"; 362 cout << "------------------------\n"; 363 for (const auto& block_device : pt.block_devices) { 364 std::string partition_name = GetBlockDevicePartitionName(block_device); 365 cout << " Partition name: " << partition_name << "\n"; 366 cout << " First sector: " << block_device.first_logical_sector << "\n"; 367 cout << " Size: " << block_device.size << " bytes\n"; 368 cout << " Flags: " << BuildBlockDeviceFlagString(block_device.flags) << "\n"; 369 cout << "------------------------\n"; 370 } 371 372 cout << "Group table:\n"; 373 cout << "------------------------\n"; 374 for (const auto& group : pt.groups) { 375 std::string group_name = GetPartitionGroupName(group); 376 cout << " Name: " << group_name << "\n"; 377 cout << " Maximum size: " << group.maximum_size << " bytes\n"; 378 cout << " Flags: " << BuildGroupFlagString(group.flags) << "\n"; 379 cout << "------------------------\n"; 380 } 381 } 382 383 static std::unique_ptr<LpMetadata> ReadDeviceOrFile(const std::string& path, uint32_t slot) { 384 if (IsEmptySuperImage(path)) { 385 return ReadFromImageFile(path); 386 } 387 return ReadMetadata(path, slot); 388 } 389 390 int LpdumpMain(int argc, char* argv[], std::ostream& cout, std::ostream& cerr) { 391 // clang-format off 392 struct option options[] = { 393 { "all", no_argument, nullptr, 'a' }, 394 { "slot", required_argument, nullptr, 's' }, 395 { "help", no_argument, nullptr, 'h' }, 396 { "json", no_argument, nullptr, 'j' }, 397 { "dump-metadata-size", no_argument, nullptr, 'd' }, 398 { "is-super-empty", no_argument, nullptr, 'e' }, 399 { nullptr, 0, nullptr, 0 }, 400 }; 401 // clang-format on 402 403 // Allow this function to be invoked by lpdumpd multiple times. 404 optind = 1; 405 406 int rv; 407 int index; 408 bool json = false; 409 bool dump_metadata_size = false; 410 bool dump_all = false; 411 std::optional<uint32_t> slot; 412 while ((rv = getopt_long_only(argc, argv, "s:jhde", options, &index)) != -1) { 413 switch (rv) { 414 case 'a': 415 dump_all = true; 416 break; 417 case 'h': 418 usage(argc, argv, cout); 419 return EX_OK; 420 case 's': { 421 uint32_t slot_arg; 422 if (android::base::ParseUint(optarg, &slot_arg)) { 423 slot = slot_arg; 424 } else { 425 slot = SlotNumberForSlotSuffix(optarg); 426 } 427 break; 428 } 429 case 'e': 430 // This is ignored, we now derive whether it's empty automatically. 431 break; 432 case 'd': 433 dump_metadata_size = true; 434 break; 435 case 'j': 436 json = true; 437 break; 438 case '?': 439 case ':': 440 return usage(argc, argv, cerr); 441 } 442 } 443 444 if (dump_all) { 445 if (slot.has_value()) { 446 cerr << "Cannot specify both --all and --slot.\n"; 447 return usage(argc, argv, cerr); 448 } 449 if (json) { 450 cerr << "Cannot specify both --all and --json.\n"; 451 return usage(argc, argv, cerr); 452 } 453 454 // When dumping everything always start from the first slot. 455 slot = 0; 456 } 457 458 #ifdef __ANDROID__ 459 // Use the current slot as a default for A/B devices. 460 auto current_slot_suffix = GetSlotSuffix(); 461 if (!slot.has_value() && !current_slot_suffix.empty()) { 462 slot = SlotNumberForSlotSuffix(current_slot_suffix); 463 } 464 #endif 465 466 // If we still haven't determined a slot yet, use the first one. 467 if (!slot.has_value()) { 468 slot = 0; 469 } 470 471 // Determine the path to the super partition (or image). If an explicit 472 // path is given, we use it for everything. Otherwise, we will infer it 473 // at the time we need to read metadata. 474 std::string super_path; 475 bool override_super_name = (optind < argc); 476 if (override_super_name) { 477 super_path = argv[optind++]; 478 } else { 479 #ifdef __ANDROID__ 480 super_path = GetSuperPartitionName(slot); 481 #else 482 cerr << "Must specify a super partition image.\n"; 483 return usage(argc, argv, cerr); 484 #endif 485 } 486 487 auto pt = ReadDeviceOrFile(super_path, slot.value()); 488 489 // --json option doesn't require metadata to be present. 490 if (json) { 491 return PrintJson(pt.get(), cout, cerr); 492 } 493 494 if (!pt) { 495 cerr << "Failed to read metadata.\n"; 496 return EX_NOINPUT; 497 } 498 499 if (dump_metadata_size) { 500 return DumpMetadataSize(*pt.get(), cout); 501 } 502 503 // When running on the device, we can check the slot count. Otherwise we 504 // use the # of metadata slots. (There is an extra slot we don't want to 505 // dump because it is currently unused.) 506 #ifdef __ANDROID__ 507 uint32_t num_slots = current_slot_suffix.empty() ? 1 : 2; 508 if (dump_all && num_slots > 1) { 509 cout << "Current slot: " << current_slot_suffix << "\n"; 510 } 511 #else 512 uint32_t num_slots = pt->geometry.metadata_slot_count; 513 #endif 514 // Empty images only have one slot. 515 if (IsEmptySuperImage(super_path)) { 516 num_slots = 1; 517 } 518 519 if (num_slots > 1) { 520 cout << "Slot " << slot.value() << ":\n"; 521 } 522 PrintMetadata(*pt.get(), cout); 523 524 if (dump_all) { 525 for (uint32_t i = 1; i < num_slots; i++) { 526 if (!override_super_name) { 527 super_path = GetSuperPartitionName(i); 528 } 529 530 pt = ReadDeviceOrFile(super_path, i); 531 if (!pt) { 532 continue; 533 } 534 535 cout << "\nSlot " << i << ":\n"; 536 PrintMetadata(*pt.get(), cout); 537 } 538 } 539 return EX_OK; 540 } 541 542 int LpdumpMain(int argc, char* argv[]) { 543 return LpdumpMain(argc, argv, std::cout, std::cerr); 544 } 545