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