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