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