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