gpt.c revision 1.42 1 /*-
2 * Copyright (c) 2002 Marcel Moolenaar
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 *
26 * CRC32 code derived from work by Gary S. Brown.
27 */
28
29 #if HAVE_NBTOOL_CONFIG_H
30 #include "nbtool_config.h"
31 #endif
32
33 #include <sys/cdefs.h>
34 #ifdef __FBSDID
35 __FBSDID("$FreeBSD: src/sbin/gpt/gpt.c,v 1.16 2006/07/07 02:44:23 marcel Exp $");
36 #endif
37 #ifdef __RCSID
38 __RCSID("$NetBSD: gpt.c,v 1.42 2015/11/29 00:14:46 christos Exp $");
39 #endif
40
41 #include <sys/param.h>
42 #include <sys/types.h>
43 #include <sys/stat.h>
44 #include <sys/ioctl.h>
45 #include <sys/bootblock.h>
46
47 #include <err.h>
48 #include <errno.h>
49 #include <fcntl.h>
50 #include <paths.h>
51 #include <stddef.h>
52 #include <stdarg.h>
53 #include <stdio.h>
54 #include <stdlib.h>
55 #include <string.h>
56 #include <unistd.h>
57 #include <ctype.h>
58
59 #include "map.h"
60 #include "gpt.h"
61
62 char device_path[MAXPATHLEN];
63 const char *device_arg;
64 const char *device_name;
65
66 off_t mediasz;
67
68 u_int parts;
69 u_int secsz;
70
71 int readonly, verbose, quiet;
72
73 static uint32_t crc32_tab[] = {
74 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f,
75 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
76 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2,
77 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
78 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9,
79 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,
80 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c,
81 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
82 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423,
83 0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
84 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106,
85 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
86 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d,
87 0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,
88 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950,
89 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
90 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7,
91 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,
92 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa,
93 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
94 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81,
95 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a,
96 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84,
97 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
98 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb,
99 0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,
100 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e,
101 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
102 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55,
103 0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
104 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28,
105 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
106 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f,
107 0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38,
108 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242,
109 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
110 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69,
111 0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2,
112 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc,
113 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
114 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693,
115 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
116 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d
117 };
118
119 uint32_t
120 crc32(const void *buf, size_t size)
121 {
122 const uint8_t *p;
123 uint32_t crc;
124
125 p = buf;
126 crc = ~0U;
127
128 while (size--)
129 crc = crc32_tab[(crc ^ *p++) & 0xFF] ^ (crc >> 8);
130
131 return crc ^ ~0U;
132 }
133
134 uint8_t *
135 utf16_to_utf8(uint16_t *s16)
136 {
137 static uint8_t *s8 = NULL;
138 static size_t s8len = 0;
139 size_t s8idx, s16idx, s16len;
140 uint32_t utfchar;
141 unsigned int c;
142
143 s16len = 0;
144 while (s16[s16len++] != 0)
145 ;
146 if (s8len < s16len * 3) {
147 if (s8 != NULL)
148 free(s8);
149 s8len = s16len * 3;
150 s8 = calloc(s16len, 3);
151 }
152 s8idx = s16idx = 0;
153 while (s16idx < s16len) {
154 utfchar = le16toh(s16[s16idx++]);
155 if ((utfchar & 0xf800) == 0xd800) {
156 c = le16toh(s16[s16idx]);
157 if ((utfchar & 0x400) != 0 || (c & 0xfc00) != 0xdc00)
158 utfchar = 0xfffd;
159 else
160 s16idx++;
161 }
162 if (utfchar < 0x80) {
163 s8[s8idx++] = utfchar;
164 } else if (utfchar < 0x800) {
165 s8[s8idx++] = 0xc0 | (utfchar >> 6);
166 s8[s8idx++] = 0x80 | (utfchar & 0x3f);
167 } else if (utfchar < 0x10000) {
168 s8[s8idx++] = 0xe0 | (utfchar >> 12);
169 s8[s8idx++] = 0x80 | ((utfchar >> 6) & 0x3f);
170 s8[s8idx++] = 0x80 | (utfchar & 0x3f);
171 } else if (utfchar < 0x200000) {
172 s8[s8idx++] = 0xf0 | (utfchar >> 18);
173 s8[s8idx++] = 0x80 | ((utfchar >> 12) & 0x3f);
174 s8[s8idx++] = 0x80 | ((utfchar >> 6) & 0x3f);
175 s8[s8idx++] = 0x80 | (utfchar & 0x3f);
176 }
177 }
178 return (s8);
179 }
180
181 void
182 utf8_to_utf16(const uint8_t *s8, uint16_t *s16, size_t s16len)
183 {
184 size_t s16idx, s8idx, s8len;
185 uint32_t utfchar = 0;
186 unsigned int c, utfbytes;
187
188 s8len = 0;
189 while (s8[s8len++] != 0)
190 ;
191 s8idx = s16idx = 0;
192 utfbytes = 0;
193 do {
194 c = s8[s8idx++];
195 if ((c & 0xc0) != 0x80) {
196 /* Initial characters. */
197 if (utfbytes != 0) {
198 /* Incomplete encoding. */
199 s16[s16idx++] = htole16(0xfffd);
200 if (s16idx == s16len) {
201 s16[--s16idx] = 0;
202 return;
203 }
204 }
205 if ((c & 0xf8) == 0xf0) {
206 utfchar = c & 0x07;
207 utfbytes = 3;
208 } else if ((c & 0xf0) == 0xe0) {
209 utfchar = c & 0x0f;
210 utfbytes = 2;
211 } else if ((c & 0xe0) == 0xc0) {
212 utfchar = c & 0x1f;
213 utfbytes = 1;
214 } else {
215 utfchar = c & 0x7f;
216 utfbytes = 0;
217 }
218 } else {
219 /* Followup characters. */
220 if (utfbytes > 0) {
221 utfchar = (utfchar << 6) + (c & 0x3f);
222 utfbytes--;
223 } else if (utfbytes == 0)
224 utfbytes = -1;
225 }
226 if (utfbytes == 0) {
227 if (utfchar >= 0x10000 && s16idx + 2 >= s16len)
228 utfchar = 0xfffd;
229 if (utfchar >= 0x10000) {
230 s16[s16idx++] =
231 htole16(0xd800 | ((utfchar>>10)-0x40));
232 s16[s16idx++] =
233 htole16(0xdc00 | (utfchar & 0x3ff));
234 } else
235 s16[s16idx++] = htole16(utfchar);
236 if (s16idx == s16len) {
237 s16[--s16idx] = 0;
238 return;
239 }
240 }
241 } while (c != 0);
242 }
243
244 void*
245 gpt_read(int fd, off_t lba, size_t count)
246 {
247 off_t ofs;
248 void *buf;
249
250 count *= secsz;
251 buf = malloc(count);
252 if (buf == NULL)
253 return (NULL);
254
255 ofs = lba * secsz;
256 if (lseek(fd, ofs, SEEK_SET) == ofs &&
257 read(fd, buf, count) == (ssize_t)count)
258 return (buf);
259
260 free(buf);
261 return (NULL);
262 }
263
264 int
265 gpt_write(int fd, map_t *map)
266 {
267 off_t ofs;
268 size_t count;
269
270 count = map->map_size * secsz;
271 ofs = map->map_start * secsz;
272 if (lseek(fd, ofs, SEEK_SET) == ofs &&
273 write(fd, map->map_data, count) == (ssize_t)count)
274 return (0);
275 return (-1);
276 }
277
278 static int
279 gpt_mbr(int fd, off_t lba)
280 {
281 struct mbr *mbr;
282 map_t *m, *p;
283 off_t size, start;
284 unsigned int i, pmbr;
285
286 mbr = gpt_read(fd, lba, 1);
287 if (mbr == NULL) {
288 if (!quiet)
289 warn("%s: read failed", device_name);
290 return (-1);
291 }
292
293 if (mbr->mbr_sig != htole16(MBR_SIG)) {
294 if (verbose)
295 gpt_msg("MBR not found at sector %ju", (uintmax_t)lba);
296 free(mbr);
297 return (0);
298 }
299
300 /*
301 * Differentiate between a regular MBR and a PMBR. This is more
302 * convenient in general. A PMBR is one with a single partition
303 * of type 0xee.
304 */
305 pmbr = 0;
306 for (i = 0; i < 4; i++) {
307 if (mbr->mbr_part[i].part_typ == MBR_PTYPE_UNUSED)
308 continue;
309 if (mbr->mbr_part[i].part_typ == MBR_PTYPE_PMBR)
310 pmbr++;
311 else
312 break;
313 }
314 if (pmbr && i == 4 && lba == 0) {
315 if (pmbr != 1 && !quiet)
316 warnx("%s: Suspicious PMBR at sector %ju",
317 device_name, (uintmax_t)lba);
318 else if (verbose > 1)
319 gpt_msg("PMBR at sector %ju", (uintmax_t)lba);
320 p = map_add(lba, 1LL, MAP_TYPE_PMBR, mbr);
321 return ((p == NULL) ? -1 : 0);
322 }
323 if (pmbr && !quiet)
324 warnx("%s: Suspicious MBR at sector %ju", device_name,
325 (uintmax_t)lba);
326 else if (verbose > 1)
327 gpt_msg("MBR at sector %ju", (uintmax_t)lba);
328
329 p = map_add(lba, 1LL, MAP_TYPE_MBR, mbr);
330 if (p == NULL)
331 return (-1);
332 for (i = 0; i < 4; i++) {
333 if (mbr->mbr_part[i].part_typ == MBR_PTYPE_UNUSED ||
334 mbr->mbr_part[i].part_typ == MBR_PTYPE_PMBR)
335 continue;
336 start = le16toh(mbr->mbr_part[i].part_start_hi);
337 start = (start << 16) + le16toh(mbr->mbr_part[i].part_start_lo);
338 size = le16toh(mbr->mbr_part[i].part_size_hi);
339 size = (size << 16) + le16toh(mbr->mbr_part[i].part_size_lo);
340 if (start == 0 && size == 0) {
341 warnx("%s: Malformed MBR at sector %llu", device_name,
342 (long long)lba);
343 continue;
344 }
345 /* start is relative to the offset of the MBR itself. */
346 start += lba;
347 if (verbose > 2)
348 gpt_msg("MBR part: type=%d, start=%ju, size=%ju",
349 mbr->mbr_part[i].part_typ,
350 (uintmax_t)start, (uintmax_t)size);
351 if (mbr->mbr_part[i].part_typ != MBR_PTYPE_EXT_LBA) {
352 m = map_add(start, size, MAP_TYPE_MBR_PART, p);
353 if (m == NULL)
354 return (-1);
355 m->map_index = i + 1;
356 } else {
357 if (gpt_mbr(fd, start) == -1)
358 return (-1);
359 }
360 }
361 return (0);
362 }
363
364 int
365 gpt_gpt(int fd, off_t lba, int found)
366 {
367 off_t size;
368 struct gpt_ent *ent;
369 struct gpt_hdr *hdr;
370 char *p;
371 map_t *m;
372 size_t blocks, tblsz;
373 unsigned int i;
374 uint32_t crc;
375
376 hdr = gpt_read(fd, lba, 1);
377 if (hdr == NULL)
378 return (-1);
379
380 if (memcmp(hdr->hdr_sig, GPT_HDR_SIG, sizeof(hdr->hdr_sig)))
381 goto fail_hdr;
382
383 crc = le32toh(hdr->hdr_crc_self);
384 hdr->hdr_crc_self = 0;
385 if (crc32(hdr, le32toh(hdr->hdr_size)) != crc) {
386 if (verbose)
387 warnx("%s: Bad CRC in GPT header at sector %llu",
388 device_name, (long long)lba);
389 goto fail_hdr;
390 }
391
392 tblsz = le32toh(hdr->hdr_entries) * le32toh(hdr->hdr_entsz);
393 blocks = tblsz / secsz + ((tblsz % secsz) ? 1 : 0);
394
395 /* Use generic pointer to deal with hdr->hdr_entsz != sizeof(*ent). */
396 p = gpt_read(fd, le64toh(hdr->hdr_lba_table), blocks);
397 if (p == NULL) {
398 if (found) {
399 if (verbose)
400 warn("%s: Cannot read LBA table at sector %llu",
401 device_name, (unsigned long long)
402 le64toh(hdr->hdr_lba_table));
403 return (-1);
404 }
405 goto fail_hdr;
406 }
407
408 if (crc32(p, tblsz) != le32toh(hdr->hdr_crc_table)) {
409 if (verbose)
410 warnx("%s: Bad CRC in GPT table at sector %llu",
411 device_name,
412 (long long)le64toh(hdr->hdr_lba_table));
413 goto fail_ent;
414 }
415
416 if (verbose > 1)
417 warnx("%s: %s GPT at sector %llu", device_name,
418 (lba == 1) ? "Pri" : "Sec", (long long)lba);
419
420 m = map_add(lba, 1, (lba == 1)
421 ? MAP_TYPE_PRI_GPT_HDR : MAP_TYPE_SEC_GPT_HDR, hdr);
422 if (m == NULL)
423 return (-1);
424
425 m = map_add(le64toh(hdr->hdr_lba_table), blocks, (lba == 1)
426 ? MAP_TYPE_PRI_GPT_TBL : MAP_TYPE_SEC_GPT_TBL, p);
427 if (m == NULL)
428 return (-1);
429
430 if (lba != 1)
431 return (1);
432
433 for (i = 0; i < le32toh(hdr->hdr_entries); i++) {
434 ent = (void*)(p + i * le32toh(hdr->hdr_entsz));
435 if (gpt_uuid_is_nil(ent->ent_type))
436 continue;
437
438 size = le64toh(ent->ent_lba_end) - le64toh(ent->ent_lba_start) +
439 1LL;
440 if (verbose > 2) {
441 char buf[128];
442 gpt_uuid_snprintf(buf, sizeof(buf), "%s",
443 ent->ent_type);
444 warnx("%s: GPT partition: type=%s, start=%llu, "
445 "size=%llu", device_name, buf,
446 (long long)le64toh(ent->ent_lba_start),
447 (long long)size);
448 }
449 m = map_add(le64toh(ent->ent_lba_start), size,
450 MAP_TYPE_GPT_PART, ent);
451 if (m == NULL)
452 return (-1);
453 m->map_index = i + 1;
454 }
455 return (1);
456
457 fail_ent:
458 free(p);
459
460 fail_hdr:
461 free(hdr);
462 return (0);
463 }
464
465 int
466 gpt_open(const char *dev, int flags)
467 {
468 struct stat sb;
469 int fd, mode, found;
470
471 mode = readonly ? O_RDONLY : O_RDWR|O_EXCL;
472
473 device_arg = device_name = dev;
474 fd = opendisk(dev, mode, device_path, sizeof(device_path), 0);
475 if (fd == -1) {
476 if (!quiet)
477 warn("Cannot open `%s'", device_name);
478 return -1;
479 }
480 device_name = device_path;
481
482 if (fstat(fd, &sb) == -1) {
483 if (!quiet)
484 warn("Cannot stat `%s'", device_name);
485 goto close;
486 }
487
488 if ((sb.st_mode & S_IFMT) != S_IFREG) {
489 if (secsz == 0) {
490 if (ioctl(fd, DIOCGSECTORSIZE, &secsz) == -1) {
491 if (!quiet)
492 warn("Cannot get sector size for `%s'",
493 device_name);
494 goto close;
495 }
496 if (secsz == 0) {
497 if (!quiet)
498 warnx("Sector size for `%s' can't be 0",
499 device_name);
500 goto close;
501 }
502 }
503 if (mediasz == 0) {
504 if (ioctl(fd, DIOCGMEDIASIZE, &mediasz) == -1) {
505 if (!quiet)
506 warn("Cannot get media size for `%s'",
507 device_name);
508 goto close;
509 }
510 if (mediasz == 0) {
511 if (!quiet)
512 warnx("Media size for `%s' can't be 0",
513 device_name);
514 goto close;
515 }
516 }
517 } else {
518 if (secsz == 0)
519 secsz = 512; /* Fixed size for files. */
520 if (mediasz == 0) {
521 if (sb.st_size % secsz) {
522 errno = EINVAL;
523 goto close;
524 }
525 mediasz = sb.st_size;
526 }
527 }
528
529 /*
530 * We require an absolute minimum of 6 sectors. One for the MBR,
531 * 2 for the GPT header, 2 for the GPT table and one to hold some
532 * user data. Let's catch this extreme border case here so that
533 * we don't have to worry about it later.
534 */
535 if (mediasz / secsz < 6) {
536 if (!quiet)
537 warnx("Need 6 sectors on '%s' we have %llu",
538 device_name, (unsigned long long)(mediasz / secsz));
539 goto close;
540 }
541
542 if (verbose) {
543 gpt_msg("mediasize=%ju; sectorsize=%u; blocks=%ju",
544 (uintmax_t)mediasz, secsz, (uintmax_t)(mediasz / secsz));
545 }
546
547 map_init(mediasz / secsz);
548
549 if (gpt_mbr(fd, 0LL) == -1)
550 goto close;
551 if ((found = gpt_gpt(fd, 1LL, 1)) == -1)
552 goto close;
553 if (gpt_gpt(fd, mediasz / secsz - 1LL, found) == -1)
554 goto close;
555
556 return (fd);
557
558 close:
559 close(fd);
560 return (-1);
561 }
562
563 void
564 gpt_close(int fd)
565 {
566 /* XXX post processing? */
567 close(fd);
568 }
569
570 void
571 gpt_msg(const char *fmt, ...)
572 {
573 va_list ap;
574 printf("%s: ", device_name);
575 va_start(ap, fmt);
576 vprintf(fmt, ap);
577 va_end(ap);
578 printf("\n");
579 }
580
581 static struct {
582 int (*fptr)(int, char *[]);
583 const char *name;
584 } cmdsw[] = {
585 { cmd_add, "add" },
586 #ifndef HAVE_NBTOOL_CONFIG_H
587 { cmd_backup, "backup" },
588 #endif
589 { cmd_biosboot, "biosboot" },
590 { cmd_create, "create" },
591 { cmd_destroy, "destroy" },
592 { cmd_header, "header" },
593 { NULL, "help" },
594 { cmd_label, "label" },
595 { cmd_migrate, "migrate" },
596 { cmd_recover, "recover" },
597 { cmd_remove, "remove" },
598 { NULL, "rename" },
599 { cmd_resize, "resize" },
600 { cmd_resizedisk, "resizedisk" },
601 #ifndef HAVE_NBTOOL_CONFIG_H
602 { cmd_restore, "restore" },
603 #endif
604 { cmd_set, "set" },
605 { cmd_show, "show" },
606 { cmd_type, "type" },
607 { cmd_unset, "unset" },
608 { NULL, "verify" },
609 { NULL, NULL }
610 };
611
612 __dead static void
613 usage(void)
614 {
615 extern const char addmsg1[], addmsg2[], biosbootmsg[];
616 extern const char createmsg[], destroymsg[], headermsg[], labelmsg1[];
617 extern const char labelmsg2[], labelmsg3[], migratemsg[], recovermsg[];
618 extern const char removemsg1[], removemsg2[], resizemsg[];
619 extern const char resizediskmsg[], setmsg[], showmsg[], typemsg1[];
620 extern const char typemsg2[], typemsg3[], unsetmsg[];
621 #ifndef HAVE_NBTOOL_CONFIG_H
622 extern const char backupmsg[], restoremsg[];
623 #endif
624 const char *p = getprogname();
625 const char *f =
626 "[-rv] [-m <mediasize>] [-p <partitionnum>] [-s <sectorsize>]";
627
628 fprintf(stderr,
629 "Usage: %s %s <command> [<args>]\n", p, f);
630 fprintf(stderr,
631 "Commands:\n"
632 #ifndef HAVE_NBTOOL_CONFIG_H
633 " %s\n"
634 " %s\n"
635 #endif
636 " %s\n"
637 " %s\n"
638 " %s\n"
639 " %s\n"
640 " %s\n"
641 " %s\n"
642 " %s\n"
643 " %s\n"
644 " %s\n"
645 " %s\n"
646 " %s\n"
647 " %s\n"
648 " %s\n"
649 " %s\n"
650 " %s\n"
651 " %s\n"
652 " %s\n"
653 " %s\n"
654 " %s\n"
655 " %s\n"
656 " %s\n",
657 addmsg1, addmsg2,
658 #ifndef HAVE_NBTOOL_CONFIG_H
659 backupmsg,
660 #endif
661 biosbootmsg, createmsg, destroymsg,
662 headermsg, labelmsg1, labelmsg2, labelmsg3,
663 migratemsg, recovermsg,
664 removemsg1, removemsg2,
665 resizemsg, resizediskmsg,
666 #ifndef HAVE_NBTOOL_CONFIG_H
667 restoremsg,
668 #endif
669 setmsg, showmsg,
670 typemsg1, typemsg2, typemsg3,
671 unsetmsg);
672 exit(1);
673 }
674
675 static void
676 prefix(const char *cmd)
677 {
678 char *pfx;
679 const char *prg;
680
681 prg = getprogname();
682 pfx = malloc(strlen(prg) + strlen(cmd) + 2);
683 /* Don't bother failing. It's not important */
684 if (pfx == NULL)
685 return;
686
687 sprintf(pfx, "%s %s", prg, cmd);
688 setprogname(pfx);
689 }
690
691 int
692 main(int argc, char *argv[])
693 {
694 char *cmd, *p;
695 int ch, i;
696
697 /* Get the generic options */
698 while ((ch = getopt(argc, argv, "m:p:qrs:v")) != -1) {
699 switch(ch) {
700 case 'm':
701 if (mediasz > 0)
702 usage();
703 mediasz = strtoul(optarg, &p, 10);
704 if (*p != 0 || mediasz < 1)
705 usage();
706 break;
707 case 'p':
708 if (parts > 0)
709 usage();
710 parts = strtoul(optarg, &p, 10);
711 if (*p != 0 || parts < 1)
712 usage();
713 break;
714 case 'r':
715 readonly = 1;
716 break;
717 case 'q':
718 quiet = 1;
719 break;
720 case 's':
721 if (secsz > 0)
722 usage();
723 secsz = strtoul(optarg, &p, 10);
724 if (*p != 0 || secsz < 1)
725 usage();
726 break;
727 case 'v':
728 verbose++;
729 break;
730 default:
731 usage();
732 }
733 }
734 if (!parts)
735 parts = 128;
736
737 if (argc == optind)
738 usage();
739
740 cmd = argv[optind++];
741 for (i = 0; cmdsw[i].name != NULL && strcmp(cmd, cmdsw[i].name); i++);
742
743 if (cmdsw[i].fptr == NULL)
744 errx(1, "unknown command: %s", cmd);
745
746 prefix(cmd);
747 return ((*cmdsw[i].fptr)(argc, argv));
748 }
749