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