| /* |
| * Copyright (C) 2018 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #include <getopt.h> |
| #include <inttypes.h> |
| #include <stdio.h> |
| #include <sysexits.h> |
| |
| #include <memory> |
| |
| #include <android-base/parseint.h> |
| #include <android-base/strings.h> |
| #include <liblp/builder.h> |
| #include <liblp/liblp.h> |
| |
| using namespace android; |
| using namespace android::fs_mgr; |
| |
| /* Prints program usage to |where|. */ |
| static int usage(int /* argc */, char* argv[]) { |
| fprintf(stderr, |
| "%s - command-line tool for creating Android Logical Partition images.\n" |
| "\n" |
| "Usage:\n" |
| " %s [options]\n" |
| "\n" |
| "Required options:\n" |
| " -d,--device-size=SIZE Size of the block device for logical partitions.\n" |
| " -m,--metadata-size=SIZE Maximum size to reserve for partition metadata.\n" |
| " -s,--metadata-slots=COUNT Number of slots to store metadata copies.\n" |
| " -p,--partition=DATA Add a partition given the data, see below.\n" |
| " -o,--output=FILE Output file.\n" |
| "\n" |
| "Optional:\n" |
| " -b,--block-size=SIZE Physical block size, defaults to 4096.\n" |
| " -a,--alignment=N Optimal partition alignment in bytes.\n" |
| " -O,--alignment-offset=N Alignment offset in bytes to device parent.\n" |
| " -S,--sparse Output a sparse image for fastboot.\n" |
| "\n" |
| "Partition data format:\n" |
| " <name>:<guid>:<attributes>:<size>\n" |
| " Attrs must be 'none' or 'readonly'.\n", |
| argv[0], argv[0]); |
| return EX_USAGE; |
| } |
| |
| int main(int argc, char* argv[]) { |
| struct option options[] = { |
| { "device-size", required_argument, nullptr, 'd' }, |
| { "metadata-size", required_argument, nullptr, 'm' }, |
| { "metadata-slots", required_argument, nullptr, 's' }, |
| { "partition", required_argument, nullptr, 'p' }, |
| { "output", required_argument, nullptr, 'o' }, |
| { "help", no_argument, nullptr, 'h' }, |
| { "alignment-offset", required_argument, nullptr, 'O' }, |
| { "alignment", required_argument, nullptr, 'a' }, |
| { "sparse", no_argument, nullptr, 'S' }, |
| { "block-size", required_argument, nullptr, 'b' }, |
| { nullptr, 0, nullptr, 0 }, |
| }; |
| |
| uint64_t blockdevice_size = 0; |
| uint32_t metadata_size = 0; |
| uint32_t metadata_slots = 0; |
| uint32_t alignment_offset = 0; |
| uint32_t alignment = kDefaultPartitionAlignment; |
| uint32_t block_size = 4096; |
| std::string output_path; |
| std::vector<std::string> partitions; |
| bool output_sparse = false; |
| |
| int rv; |
| int index; |
| while ((rv = getopt_long_only(argc, argv, "d:m:s:p:o:h", options, &index)) != -1) { |
| switch (rv) { |
| case 'h': |
| return usage(argc, argv); |
| case 'd': |
| if (!android::base::ParseUint(optarg, &blockdevice_size)) { |
| fprintf(stderr, "Invalid argument to --device-size.\n"); |
| return EX_USAGE; |
| } |
| break; |
| case 'm': |
| if (!android::base::ParseUint(optarg, &metadata_size)) { |
| fprintf(stderr, "Invalid argument to --metadata-size.\n"); |
| return EX_USAGE; |
| } |
| break; |
| case 's': |
| if (!android::base::ParseUint(optarg, &metadata_slots)) { |
| fprintf(stderr, "Invalid argument to --metadata-slots.\n"); |
| return EX_USAGE; |
| } |
| break; |
| case 'p': |
| partitions.push_back(optarg); |
| break; |
| case 'o': |
| output_path = optarg; |
| break; |
| case 'O': |
| if (!android::base::ParseUint(optarg, &alignment_offset)) { |
| fprintf(stderr, "Invalid argument to --alignment-offset.\n"); |
| return EX_USAGE; |
| } |
| break; |
| case 'a': |
| if (!android::base::ParseUint(optarg, &alignment)) { |
| fprintf(stderr, "Invalid argument to --alignment.\n"); |
| return EX_USAGE; |
| } |
| break; |
| case 'S': |
| output_sparse = true; |
| break; |
| case 'b': |
| if (!android::base::ParseUint(optarg, &block_size) || !block_size) { |
| fprintf(stderr, "Invalid argument to --block-size.\n"); |
| return EX_USAGE; |
| } |
| break; |
| default: |
| break; |
| } |
| } |
| |
| // Check for empty arguments so we can print a more helpful message rather |
| // than error on each individual missing argument. |
| if (optind == 1) { |
| return usage(argc, argv); |
| } |
| |
| if (!blockdevice_size) { |
| fprintf(stderr, "--device-size needs more than 0 bytes of disk space.\n"); |
| return EX_USAGE; |
| } |
| if (!metadata_size) { |
| fprintf(stderr, "--metadata-size must be more than 0 bytes.\n"); |
| return EX_USAGE; |
| } |
| if (!metadata_slots) { |
| fprintf(stderr, "--metadata-slots must be more than 0.\n"); |
| return EX_USAGE; |
| } |
| if (output_path.empty()) { |
| fprintf(stderr, "--output must specify a valid path.\n"); |
| return EX_USAGE; |
| } |
| if (partitions.empty()) { |
| fprintf(stderr, "Partition table must have at least one entry.\n"); |
| return EX_USAGE; |
| } |
| |
| BlockDeviceInfo device_info(blockdevice_size, alignment, alignment_offset); |
| |
| std::unique_ptr<MetadataBuilder> builder = |
| MetadataBuilder::New(device_info, metadata_size, metadata_slots); |
| |
| for (const auto& partition_info : partitions) { |
| std::vector<std::string> parts = android::base::Split(partition_info, ":"); |
| if (parts.size() != 4) { |
| fprintf(stderr, "Partition info has invalid formatting.\n"); |
| return EX_USAGE; |
| } |
| |
| std::string name = parts[0]; |
| if (!name.length()) { |
| fprintf(stderr, "Partition must have a valid name.\n"); |
| return EX_USAGE; |
| } |
| |
| uint64_t size; |
| if (!android::base::ParseUint(parts[3].c_str(), &size)) { |
| fprintf(stderr, "Partition must have a valid size.\n"); |
| return EX_USAGE; |
| } |
| |
| uint32_t attribute_flags = 0; |
| std::string attributes = parts[2]; |
| if (attributes == "readonly") { |
| attribute_flags |= LP_PARTITION_ATTR_READONLY; |
| } else if (attributes != "none") { |
| fprintf(stderr, "Attribute not recognized: %s\n", attributes.c_str()); |
| return EX_USAGE; |
| } |
| |
| Partition* partition = builder->AddPartition(name, parts[1], attribute_flags); |
| if (!builder->GrowPartition(partition, size)) { |
| fprintf(stderr, "Not enough space on device for partition %s with size %" PRIu64 "\n", |
| name.c_str(), size); |
| return EX_SOFTWARE; |
| } |
| } |
| |
| std::unique_ptr<LpMetadata> metadata = builder->Export(); |
| if (output_sparse) { |
| if (!WriteToSparseFile(output_path.c_str(), *metadata.get(), block_size)) { |
| return EX_CANTCREAT; |
| } |
| } else if (!WriteToImageFile(output_path.c_str(), *metadata.get())) { |
| return EX_CANTCREAT; |
| } |
| return EX_OK; |
| } |