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