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