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 <stdio.h> 20 #include <sysexits.h> 21 22 #include <memory> 23 24 #include <android-base/parseint.h> 25 #include <android-base/strings.h> 26 #include <liblp/builder.h> 27 #include <liblp/liblp.h> 28 29 using namespace android; 30 using namespace android::fs_mgr; 31 32 /* Prints program usage to |where|. */ 33 static int usage(int /* argc */, char* argv[]) { 34 fprintf(stderr, 35 "%s - command-line tool for creating Android Logical Partition images.\n" 36 "\n" 37 "Usage:\n" 38 " %s [options]\n" 39 "\n" 40 "Required options:\n" 41 " -d,--device-size=SIZE Size of the block device for logical partitions.\n" 42 " -m,--metadata-size=SIZE Maximum size to reserve for partition metadata.\n" 43 " -s,--metadata-slots=COUNT Number of slots to store metadata copies.\n" 44 " -p,--partition=DATA Add a partition given the data, see below.\n" 45 " -o,--output=FILE Output file.\n" 46 "\n" 47 "Optional:\n" 48 " -b,--block-size=SIZE Physical block size, defaults to 4096.\n" 49 " -a,--alignment=N Optimal partition alignment in bytes.\n" 50 " -O,--alignment-offset=N Alignment offset in bytes to device parent.\n" 51 " -S,--sparse Output a sparse image for fastboot.\n" 52 " -i,--image=PARTITION=FILE If building a sparse image for fastboot, include\n" 53 " the given file (or sparse file) as initial data for\n" 54 " the named partition.\n" 55 " -g,--group=GROUP:SIZE Define a named partition group with the given\n" 56 " maximum size.\n" 57 " -D,--device=DATA Add a block device that the super partition\n" 58 " spans over. If specified, then -d/--device-size\n" 59 " and alignments must not be specified. The format\n" 60 " for DATA is listed below.\n" 61 " -n,--super-name=NAME Specify the name of the block device that will\n" 62 " house the super partition.\n" 63 " -x,--auto-slot-suffixing Mark the block device and partition names needing\n" 64 " slot suffixes before being used.\n" 65 " -F,--force-full-image Force a full image to be written even if no\n" 66 " partition images were specified. Normally, this\n" 67 " would produce a minimal super_empty.img which\n" 68 " cannot be flashed; force-full-image will produce\n" 69 " a flashable image.\n" 70 " --virtual-ab Add the VIRTUAL_AB_DEVICE flag to the metadata\n" 71 " header. Note that the resulting super.img will\n" 72 " require a liblp capable of parsing a v1.2 header.\n" 73 "\n" 74 "Partition data format:\n" 75 " <name>:<attributes>:<size>[:group]\n" 76 " Attrs must be 'none' or 'readonly'.\n" 77 "\n" 78 "Device data format:\n" 79 " <partition_name>:<size>[:<alignment>:<alignment_offset>]\n" 80 " The partition name is the basename of the /dev/block/by-name/ path of the\n" 81 " block device. The size is the device size in bytes. The alignment and\n" 82 " alignment offset parameters are the same as -a/--alignment and \n" 83 " -O/--alignment-offset.\n", 84 argv[0], argv[0]); 85 return EX_USAGE; 86 } 87 88 enum class Option : int { 89 // Long-only options. 90 kVirtualAB = 1, 91 92 // Short character codes. 93 kDeviceSize = 'd', 94 kMetadataSize = 'm', 95 kMetadataSlots = 's', 96 kPartition = 'p', 97 kOutput = 'o', 98 kHelp = 'h', 99 kAlignmentOffset = 'O', 100 kAlignment = 'a', 101 kSparse = 'S', 102 kBlockSize = 'b', 103 kImage = 'i', 104 kGroup = 'g', 105 kDevice = 'D', 106 kSuperName = 'n', 107 kAutoSlotSuffixing = 'x', 108 kForceFullImage = 'F', 109 }; 110 111 int main(int argc, char* argv[]) { 112 struct option options[] = { 113 { "device-size", required_argument, nullptr, (int)Option::kDeviceSize }, 114 { "metadata-size", required_argument, nullptr, (int)Option::kMetadataSize }, 115 { "metadata-slots", required_argument, nullptr, (int)Option::kMetadataSlots }, 116 { "partition", required_argument, nullptr, (int)Option::kPartition }, 117 { "output", required_argument, nullptr, (int)Option::kOutput }, 118 { "help", no_argument, nullptr, (int)Option::kOutput }, 119 { "alignment-offset", required_argument, nullptr, (int)Option::kAlignmentOffset }, 120 { "alignment", required_argument, nullptr, (int)Option::kAlignment }, 121 { "sparse", no_argument, nullptr, (int)Option::kSparse }, 122 { "block-size", required_argument, nullptr, (int)Option::kBlockSize }, 123 { "image", required_argument, nullptr, (int)Option::kImage }, 124 { "group", required_argument, nullptr, (int)Option::kGroup }, 125 { "device", required_argument, nullptr, (int)Option::kDevice }, 126 { "super-name", required_argument, nullptr, (int)Option::kSuperName }, 127 { "auto-slot-suffixing", no_argument, nullptr, (int)Option::kAutoSlotSuffixing }, 128 { "force-full-image", no_argument, nullptr, (int)Option::kForceFullImage }, 129 { "virtual-ab", no_argument, nullptr, (int)Option::kVirtualAB }, 130 { nullptr, 0, nullptr, 0 }, 131 }; 132 133 uint64_t blockdevice_size = 0; 134 uint32_t metadata_size = 0; 135 uint32_t metadata_slots = 0; 136 uint32_t alignment_offset = 0; 137 uint32_t alignment = kDefaultPartitionAlignment; 138 uint32_t block_size = 4096; 139 std::string super_name = "super"; 140 std::string output_path; 141 std::vector<std::string> partitions; 142 std::vector<std::string> groups; 143 std::vector<BlockDeviceInfo> block_devices; 144 std::map<std::string, std::string> images; 145 bool output_sparse = false; 146 bool has_implied_super = false; 147 bool auto_slot_suffixing = false; 148 bool force_full_image = false; 149 bool virtual_ab = false; 150 151 int rv; 152 int index; 153 while ((rv = getopt_long_only(argc, argv, "d:m:s:p:o:h:FSx", options, &index)) != -1) { 154 switch ((Option)rv) { 155 case Option::kHelp: 156 return usage(argc, argv); 157 case Option::kDeviceSize: 158 if (!android::base::ParseUint(optarg, &blockdevice_size) || !blockdevice_size) { 159 fprintf(stderr, "Invalid argument to --device-size.\n"); 160 return EX_USAGE; 161 } 162 has_implied_super = true; 163 break; 164 case Option::kMetadataSize: 165 if (!android::base::ParseUint(optarg, &metadata_size)) { 166 fprintf(stderr, "Invalid argument to --metadata-size.\n"); 167 return EX_USAGE; 168 } 169 break; 170 case Option::kMetadataSlots: 171 if (!android::base::ParseUint(optarg, &metadata_slots)) { 172 fprintf(stderr, "Invalid argument to --metadata-slots.\n"); 173 return EX_USAGE; 174 } 175 break; 176 case Option::kPartition: 177 partitions.push_back(optarg); 178 break; 179 case Option::kGroup: 180 groups.push_back(optarg); 181 break; 182 case Option::kOutput: 183 output_path = optarg; 184 break; 185 case Option::kAlignmentOffset: 186 if (!android::base::ParseUint(optarg, &alignment_offset)) { 187 fprintf(stderr, "Invalid argument to --alignment-offset.\n"); 188 return EX_USAGE; 189 } 190 has_implied_super = true; 191 break; 192 case Option::kAlignment: 193 if (!android::base::ParseUint(optarg, &alignment)) { 194 fprintf(stderr, "Invalid argument to --alignment.\n"); 195 return EX_USAGE; 196 } 197 has_implied_super = true; 198 break; 199 case Option::kSparse: 200 output_sparse = true; 201 break; 202 case Option::kBlockSize: 203 if (!android::base::ParseUint(optarg, &block_size) || !block_size) { 204 fprintf(stderr, "Invalid argument to --block-size.\n"); 205 return EX_USAGE; 206 } 207 break; 208 case Option::kImage: 209 { 210 char* separator = strchr(optarg, '='); 211 if (!separator || separator == optarg || !strlen(separator + 1)) { 212 fprintf(stderr, "Expected PARTITION=FILE.\n"); 213 return EX_USAGE; 214 } 215 *separator = '\0'; 216 217 std::string partition_name(optarg); 218 std::string file(separator + 1); 219 images[partition_name] = file; 220 break; 221 } 222 case Option::kSuperName: 223 super_name = optarg; 224 break; 225 case Option::kDevice: 226 { 227 std::vector<std::string> parts = android::base::Split(optarg, ":"); 228 if (parts.size() < 2) { 229 fprintf(stderr, "Block device info has invalid formatting.\n"); 230 return EX_USAGE; 231 } 232 233 BlockDeviceInfo info; 234 info.partition_name = parts[0]; 235 if (!android::base::ParseUint(parts[1].c_str(), &info.size) || !info.size) { 236 fprintf(stderr, "Block device must have a valid size.\n"); 237 return EX_USAGE; 238 } 239 info.alignment = kDefaultPartitionAlignment; 240 if (parts.size() >= 3 && 241 !android::base::ParseUint(parts[2].c_str(), &info.alignment)) { 242 fprintf(stderr, "Block device must have a valid alignment.\n"); 243 return EX_USAGE; 244 } 245 if (parts.size() >= 4 && 246 !android::base::ParseUint(parts[3].c_str(), &info.alignment_offset)) { 247 fprintf(stderr, "Block device must have a valid alignment offset.\n"); 248 return EX_USAGE; 249 } 250 block_devices.emplace_back(info); 251 break; 252 } 253 case Option::kAutoSlotSuffixing: 254 auto_slot_suffixing = true; 255 break; 256 case Option::kForceFullImage: 257 force_full_image = true; 258 break; 259 case Option::kVirtualAB: 260 virtual_ab = true; 261 break; 262 default: 263 break; 264 } 265 } 266 267 // Check for empty arguments so we can print a more helpful message rather 268 // than error on each individual missing argument. 269 if (optind == 1) { 270 return usage(argc, argv); 271 } 272 273 // Must specify a block device via the old method (--device-size etc) or 274 // via --device, but not both. 275 if ((has_implied_super && (!block_devices.empty() || !blockdevice_size)) || 276 (!has_implied_super && block_devices.empty()) || 277 (block_devices.empty() && !blockdevice_size)) { 278 fprintf(stderr, "Must specify --device OR --device-size.\n"); 279 return EX_USAGE; 280 } 281 if (!metadata_size) { 282 fprintf(stderr, "--metadata-size must be more than 0 bytes.\n"); 283 return EX_USAGE; 284 } 285 if (!metadata_slots) { 286 fprintf(stderr, "--metadata-slots must be more than 0.\n"); 287 return EX_USAGE; 288 } 289 if (output_path.empty()) { 290 fprintf(stderr, "--output must specify a valid path.\n"); 291 return EX_USAGE; 292 } 293 if (partitions.empty()) { 294 fprintf(stderr, "Partition table must have at least one entry.\n"); 295 return EX_USAGE; 296 } 297 298 // Note that we take the block_size to mean both the logical block size and 299 // the block size for libsparse. 300 if (has_implied_super) { 301 block_devices.emplace_back(super_name, blockdevice_size, alignment, alignment_offset, block_size); 302 } else { 303 // Apply the block size to each device. 304 for (auto& block_device : block_devices) { 305 block_device.logical_block_size = block_size; 306 } 307 } 308 309 std::unique_ptr<MetadataBuilder> builder = 310 MetadataBuilder::New(block_devices, super_name, metadata_size, metadata_slots); 311 if (!builder) { 312 fprintf(stderr, "Invalid metadata parameters.\n"); 313 return EX_USAGE; 314 } 315 316 if (auto_slot_suffixing) { 317 builder->SetAutoSlotSuffixing(); 318 } 319 if (virtual_ab) { 320 builder->SetVirtualABDeviceFlag(); 321 } 322 323 for (const auto& group_info : groups) { 324 std::vector<std::string> parts = android::base::Split(group_info, ":"); 325 if (parts.size() != 2) { 326 fprintf(stderr, "Partition info has invalid formatting.\n"); 327 return EX_USAGE; 328 } 329 330 std::string name = parts[0]; 331 if (name.empty()) { 332 fprintf(stderr, "Partition group must have a valid name.\n"); 333 return EX_USAGE; 334 } 335 336 uint64_t size; 337 if (!android::base::ParseUint(parts[1].c_str(), &size)) { 338 fprintf(stderr, "Partition group must have a valid maximum size.\n"); 339 return EX_USAGE; 340 } 341 342 if (!builder->AddGroup(name, size)) { 343 fprintf(stderr, "Group name %s already exists.\n", name.c_str()); 344 return EX_SOFTWARE; 345 } 346 } 347 348 for (const auto& partition_info : partitions) { 349 std::vector<std::string> parts = android::base::Split(partition_info, ":"); 350 if (parts.size() > 4) { 351 fprintf(stderr, "Partition info has invalid formatting.\n"); 352 return EX_USAGE; 353 } 354 355 std::string name = parts[0]; 356 if (name.empty()) { 357 fprintf(stderr, "Partition must have a valid name.\n"); 358 return EX_USAGE; 359 } 360 361 uint64_t size; 362 if (!android::base::ParseUint(parts[2].c_str(), &size)) { 363 fprintf(stderr, "Partition must have a valid size.\n"); 364 return EX_USAGE; 365 } 366 367 uint32_t attribute_flags = 0; 368 std::string attributes = parts[1]; 369 if (attributes == "readonly") { 370 attribute_flags |= LP_PARTITION_ATTR_READONLY; 371 } else if (attributes != "none") { 372 fprintf(stderr, "Attribute not recognized: %s\n", attributes.c_str()); 373 return EX_USAGE; 374 } 375 376 std::string group_name = "default"; 377 if (parts.size() >= 4) { 378 group_name = parts[3]; 379 } 380 381 Partition* partition = builder->AddPartition(name, group_name, attribute_flags); 382 if (!partition) { 383 fprintf(stderr, "Could not add partition: %s\n", name.c_str()); 384 return EX_SOFTWARE; 385 } 386 if (!builder->ResizePartition(partition, size)) { 387 fprintf(stderr, "Not enough space on device for partition %s with size %" PRIu64 "\n", 388 name.c_str(), size); 389 return EX_SOFTWARE; 390 } 391 } 392 393 std::unique_ptr<LpMetadata> metadata = builder->Export(); 394 if (!images.empty() || force_full_image) { 395 if (block_devices.size() == 1) { 396 if (!WriteToImageFile(output_path.c_str(), *metadata.get(), block_size, images, 397 output_sparse)) { 398 return EX_CANTCREAT; 399 } 400 } else { 401 if (!WriteSplitImageFiles(output_path, *metadata.get(), block_size, images, 402 output_sparse)) { 403 return EX_CANTCREAT; 404 } 405 } 406 } else if (!WriteToImageFile(output_path.c_str(), *metadata.get())) { 407 return EX_CANTCREAT; 408 } 409 return EX_OK; 410 } 411