gpt.c revision 1.45 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.45 2015/11/29 14:03:35 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 off_t devsz;
471
472 mode = readonly ? O_RDONLY : O_RDWR|O_EXCL;
473
474 device_arg = device_name = dev;
475 fd = opendisk(dev, mode, device_path, sizeof(device_path), 0);
476 if (fd == -1) {
477 if (!quiet)
478 warn("Cannot open `%s'", device_name);
479 return -1;
480 }
481 device_name = device_path;
482
483 if (fstat(fd, &sb) == -1) {
484 if (!quiet)
485 warn("Cannot stat `%s'", device_name);
486 goto close;
487 }
488
489 if ((sb.st_mode & S_IFMT) != S_IFREG) {
490 if (secsz == 0) {
491 #ifdef DIOCGSECTORSIZE
492 if (ioctl(fd, DIOCGSECTORSIZE, &secsz) == -1) {
493 if (!quiet)
494 warn("Cannot get sector size for `%s'",
495 device_name);
496 goto close;
497 }
498 #endif
499 if (secsz == 0) {
500 if (!quiet)
501 warnx("Sector size for `%s' can't be 0",
502 device_name);
503 goto close;
504 }
505 }
506 if (mediasz == 0) {
507 #ifdef DIOCGMEDIASIZE
508 if (ioctl(fd, DIOCGMEDIASIZE, &mediasz) == -1) {
509 if (!quiet)
510 warn("Cannot get media size for `%s'",
511 device_name);
512 goto close;
513 }
514 #endif
515 if (mediasz == 0) {
516 if (!quiet)
517 warnx("Media size for `%s' can't be 0",
518 device_name);
519 goto close;
520 }
521 }
522 } else {
523 if (secsz == 0)
524 secsz = 512; /* Fixed size for files. */
525 if (mediasz == 0) {
526 if (sb.st_size % secsz) {
527 errno = EINVAL;
528 goto close;
529 }
530 mediasz = sb.st_size;
531 }
532 }
533
534 /*
535 * We require an absolute minimum of 6 sectors. One for the MBR,
536 * 2 for the GPT header, 2 for the GPT table and one to hold some
537 * user data. Let's catch this extreme border case here so that
538 * we don't have to worry about it later.
539 */
540 devsz = mediasz / secsz;
541 if (devsz < 6) {
542 if (!quiet)
543 warnx("Need 6 sectors on '%s' we have %ju",
544 device_name, (uintmax_t)devsz);
545 goto close;
546 }
547
548 if (verbose) {
549 gpt_msg("mediasize=%ju; sectorsize=%u; blocks=%ju",
550 (uintmax_t)mediasz, secsz, (uintmax_t)devsz);
551 }
552
553 map_init(devsz);
554
555 if (gpt_mbr(fd, 0LL) == -1)
556 goto close;
557 if ((found = gpt_gpt(fd, 1LL, 1)) == -1)
558 goto close;
559 if (gpt_gpt(fd, devsz - 1LL, found) == -1)
560 goto close;
561
562 return (fd);
563
564 close:
565 close(fd);
566 return (-1);
567 }
568
569 void
570 gpt_close(int fd)
571 {
572 /* XXX post processing? */
573 close(fd);
574 }
575
576 void
577 gpt_msg(const char *fmt, ...)
578 {
579 va_list ap;
580 printf("%s: ", device_name);
581 va_start(ap, fmt);
582 vprintf(fmt, ap);
583 va_end(ap);
584 printf("\n");
585 }
586
587 static struct {
588 int (*fptr)(int, char *[]);
589 const char *name;
590 } cmdsw[] = {
591 { cmd_add, "add" },
592 #ifndef HAVE_NBTOOL_CONFIG_H
593 { cmd_backup, "backup" },
594 #endif
595 { cmd_biosboot, "biosboot" },
596 { cmd_create, "create" },
597 { cmd_destroy, "destroy" },
598 { cmd_header, "header" },
599 { NULL, "help" },
600 { cmd_label, "label" },
601 { cmd_migrate, "migrate" },
602 { cmd_recover, "recover" },
603 { cmd_remove, "remove" },
604 { NULL, "rename" },
605 { cmd_resize, "resize" },
606 { cmd_resizedisk, "resizedisk" },
607 #ifndef HAVE_NBTOOL_CONFIG_H
608 { cmd_restore, "restore" },
609 #endif
610 { cmd_set, "set" },
611 { cmd_show, "show" },
612 { cmd_type, "type" },
613 { cmd_unset, "unset" },
614 { NULL, "verify" },
615 { NULL, NULL }
616 };
617
618 __dead static void
619 usage(void)
620 {
621 extern const char addmsg1[], addmsg2[], biosbootmsg[];
622 extern const char createmsg[], destroymsg[], headermsg[], labelmsg1[];
623 extern const char labelmsg2[], labelmsg3[], migratemsg[], recovermsg[];
624 extern const char removemsg1[], removemsg2[], resizemsg[];
625 extern const char resizediskmsg[], setmsg[], showmsg[], typemsg1[];
626 extern const char typemsg2[], typemsg3[], unsetmsg[];
627 #ifndef HAVE_NBTOOL_CONFIG_H
628 extern const char backupmsg[], restoremsg[];
629 #endif
630 const char *p = getprogname();
631 const char *f =
632 "[-rv] [-m <mediasize>] [-p <partitionnum>] [-s <sectorsize>]";
633
634 fprintf(stderr,
635 "Usage: %s %s <command> [<args>]\n", p, f);
636 fprintf(stderr,
637 "Commands:\n"
638 #ifndef HAVE_NBTOOL_CONFIG_H
639 " %s\n"
640 " %s\n"
641 #endif
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 " %s\n"
658 " %s\n"
659 " %s\n"
660 " %s\n"
661 " %s\n"
662 " %s\n",
663 addmsg1, addmsg2,
664 #ifndef HAVE_NBTOOL_CONFIG_H
665 backupmsg,
666 #endif
667 biosbootmsg, createmsg, destroymsg,
668 headermsg, labelmsg1, labelmsg2, labelmsg3,
669 migratemsg, recovermsg,
670 removemsg1, removemsg2,
671 resizemsg, resizediskmsg,
672 #ifndef HAVE_NBTOOL_CONFIG_H
673 restoremsg,
674 #endif
675 setmsg, showmsg,
676 typemsg1, typemsg2, typemsg3,
677 unsetmsg);
678 exit(1);
679 }
680
681 static void
682 prefix(const char *cmd)
683 {
684 char *pfx;
685 const char *prg;
686
687 prg = getprogname();
688 pfx = malloc(strlen(prg) + strlen(cmd) + 2);
689 /* Don't bother failing. It's not important */
690 if (pfx == NULL)
691 return;
692
693 sprintf(pfx, "%s %s", prg, cmd);
694 setprogname(pfx);
695 }
696
697 int
698 main(int argc, char *argv[])
699 {
700 char *cmd, *p;
701 int ch, i;
702
703 /* Get the generic options */
704 while ((ch = getopt(argc, argv, "m:p:qrs:v")) != -1) {
705 switch(ch) {
706 case 'm':
707 if (mediasz > 0)
708 usage();
709 mediasz = strtoul(optarg, &p, 10);
710 if (*p != 0 || mediasz < 1)
711 usage();
712 break;
713 case 'p':
714 if (parts > 0)
715 usage();
716 parts = strtoul(optarg, &p, 10);
717 if (*p != 0 || parts < 1)
718 usage();
719 break;
720 case 'r':
721 readonly = 1;
722 break;
723 case 'q':
724 quiet = 1;
725 break;
726 case 's':
727 if (secsz > 0)
728 usage();
729 secsz = strtoul(optarg, &p, 10);
730 if (*p != 0 || secsz < 1)
731 usage();
732 break;
733 case 'v':
734 verbose++;
735 break;
736 default:
737 usage();
738 }
739 }
740 if (!parts)
741 parts = 128;
742
743 if (argc == optind)
744 usage();
745
746 cmd = argv[optind++];
747 for (i = 0; cmdsw[i].name != NULL && strcmp(cmd, cmdsw[i].name); i++);
748
749 if (cmdsw[i].fptr == NULL)
750 errx(1, "unknown command: %s", cmd);
751
752 prefix(cmd);
753 return ((*cmdsw[i].fptr)(argc, argv));
754 }
755