xref: /netbsd-src/sbin/gpt/gpt.c (revision c0179c282a5968435315a82f4128c61372c68fc3)
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 #include <sys/cdefs.h>
30 #ifdef __FBSDID
31 __FBSDID("$FreeBSD: src/sbin/gpt/gpt.c,v 1.16 2006/07/07 02:44:23 marcel Exp $");
32 #endif
33 #ifdef __RCSID
34 __RCSID("$NetBSD: gpt.c,v 1.2 2006/10/15 22:36:29 christos Exp $");
35 #endif
36 
37 #include <sys/param.h>
38 #include <sys/types.h>
39 #include <sys/disk.h>
40 #include <sys/stat.h>
41 #include <sys/ioctl.h>
42 
43 #include <err.h>
44 #include <errno.h>
45 #include <fcntl.h>
46 #include <paths.h>
47 #include <stddef.h>
48 #include <stdio.h>
49 #include <stdlib.h>
50 #include <string.h>
51 #include <unistd.h>
52 #ifdef __NetBSD__
53 #include <util.h>
54 #include <ctype.h>
55 #include <prop/proplib.h>
56 #include <sys/drvctlio.h>
57 #endif
58 
59 #include "map.h"
60 #include "gpt.h"
61 
62 char	device_path[MAXPATHLEN];
63 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++] = 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++] = 0xd800 | ((utfchar>>10)-0x40);
230 				s16[s16idx++] = 0xdc00 | (utfchar & 0x3ff);
231 			} else
232 				s16[s16idx++] = utfchar;
233 			if (s16idx == s16len) {
234 				s16[--s16idx] = 0;
235 				return;
236 			}
237 		}
238 	} while (c != 0);
239 }
240 
241 void
242 le_uuid_dec(void const *buf, uuid_t *uuid)
243 {
244 	u_char const *p;
245 	int i;
246 
247 	p = buf;
248 	uuid->time_low = le32dec(p);
249 	uuid->time_mid = le16dec(p + 4);
250 	uuid->time_hi_and_version = le16dec(p + 6);
251 	uuid->clock_seq_hi_and_reserved = p[8];
252 	uuid->clock_seq_low = p[9];
253 	for (i = 0; i < _UUID_NODE_LEN; i++)
254 		uuid->node[i] = p[10 + i];
255 }
256 
257 void
258 le_uuid_enc(void *buf, uuid_t const *uuid)
259 {
260 	u_char *p;
261 	int i;
262 
263 	p = buf;
264 	le32enc(p, uuid->time_low);
265 	le16enc(p + 4, uuid->time_mid);
266 	le16enc(p + 6, uuid->time_hi_and_version);
267 	p[8] = uuid->clock_seq_hi_and_reserved;
268 	p[9] = uuid->clock_seq_low;
269 	for (i = 0; i < _UUID_NODE_LEN; i++)
270 		p[10 + i] = uuid->node[i];
271 }
272 
273 int
274 parse_uuid(const char *s, uuid_t *uuid)
275 {
276 	uint32_t status;
277 
278 	uuid_from_string(s, uuid, &status);
279 	if (status == uuid_s_ok)
280 		return (0);
281 
282 	switch (*s) {
283 	case 'e':
284 		if (strcmp(s, "efi") == 0) {
285 			uuid_t efi = GPT_ENT_TYPE_EFI;
286 			*uuid = efi;
287 			return (0);
288 		}
289 		break;
290 	case 'h':
291 		if (strcmp(s, "hfs") == 0) {
292 			uuid_t hfs = GPT_ENT_TYPE_APPLE_HFS;
293 			*uuid = hfs;
294 			return (0);
295 		}
296 		break;
297 	case 'l':
298 		if (strcmp(s, "linux") == 0) {
299 			uuid_t lnx = GPT_ENT_TYPE_MS_BASIC_DATA;
300 			*uuid = lnx;
301 			return (0);
302 		}
303 		break;
304 	case 's':
305 		if (strcmp(s, "swap") == 0) {
306 			uuid_t sw = GPT_ENT_TYPE_FREEBSD_SWAP;
307 			*uuid = sw;
308 			return (0);
309 		}
310 		break;
311 	case 'u':
312 		if (strcmp(s, "ufs") == 0) {
313 			uuid_t ufs = GPT_ENT_TYPE_FREEBSD_UFS;
314 			*uuid = ufs;
315 			return (0);
316 		}
317 		break;
318 	case 'w':
319 		if (strcmp(s, "windows") == 0) {
320 			uuid_t win = GPT_ENT_TYPE_MS_BASIC_DATA;
321 			*uuid = win;
322 			return (0);
323 		}
324 		break;
325 	}
326 	return (EINVAL);
327 }
328 
329 void*
330 gpt_read(int fd, off_t lba, size_t count)
331 {
332 	off_t ofs;
333 	void *buf;
334 
335 	count *= secsz;
336 	buf = malloc(count);
337 	if (buf == NULL)
338 		return (NULL);
339 
340 	ofs = lba * secsz;
341 	if (lseek(fd, ofs, SEEK_SET) == ofs &&
342 	    read(fd, buf, count) == (ssize_t)count)
343 		return (buf);
344 
345 	free(buf);
346 	return (NULL);
347 }
348 
349 int
350 gpt_write(int fd, map_t *map)
351 {
352 	off_t ofs;
353 	size_t count;
354 
355 	count = map->map_size * secsz;
356 	ofs = map->map_start * secsz;
357 	if (lseek(fd, ofs, SEEK_SET) == ofs &&
358 	    write(fd, map->map_data, count) == (ssize_t)count)
359 		return (0);
360 	return (-1);
361 }
362 
363 static int
364 gpt_mbr(int fd, off_t lba)
365 {
366 	struct mbr *mbr;
367 	map_t *m, *p;
368 	off_t size, start;
369 	unsigned int i, pmbr;
370 
371 	mbr = gpt_read(fd, lba, 1);
372 	if (mbr == NULL)
373 		return (-1);
374 
375 	if (mbr->mbr_sig != htole16(MBR_SIG)) {
376 		if (verbose)
377 			warnx("%s: MBR not found at sector %llu", device_name,
378 			    (long long)lba);
379 		free(mbr);
380 		return (0);
381 	}
382 
383 	/*
384 	 * Differentiate between a regular MBR and a PMBR. This is more
385 	 * convenient in general. A PMBR is one with a single partition
386 	 * of type 0xee.
387 	 */
388 	pmbr = 0;
389 	for (i = 0; i < 4; i++) {
390 		if (mbr->mbr_part[i].part_typ == 0)
391 			continue;
392 		if (mbr->mbr_part[i].part_typ == 0xee)
393 			pmbr++;
394 		else
395 			break;
396 	}
397 	if (pmbr && i == 4 && lba == 0) {
398 		if (pmbr != 1)
399 			warnx("%s: Suspicious PMBR at sector %llu",
400 			    device_name, (long long)lba);
401 		else if (verbose > 1)
402 			warnx("%s: PMBR at sector %llu", device_name,
403 			    (long long)lba);
404 		p = map_add(lba, 1LL, MAP_TYPE_PMBR, mbr);
405 		return ((p == NULL) ? -1 : 0);
406 	}
407 	if (pmbr)
408 		warnx("%s: Suspicious MBR at sector %llu", device_name,
409 		    (long long)lba);
410 	else if (verbose > 1)
411 		warnx("%s: MBR at sector %llu", device_name, (long long)lba);
412 
413 	p = map_add(lba, 1LL, MAP_TYPE_MBR, mbr);
414 	if (p == NULL)
415 		return (-1);
416 	for (i = 0; i < 4; i++) {
417 		if (mbr->mbr_part[i].part_typ == 0 ||
418 		    mbr->mbr_part[i].part_typ == 0xee)
419 			continue;
420 		start = le16toh(mbr->mbr_part[i].part_start_hi);
421 		start = (start << 16) + le16toh(mbr->mbr_part[i].part_start_lo);
422 		size = le16toh(mbr->mbr_part[i].part_size_hi);
423 		size = (size << 16) + le16toh(mbr->mbr_part[i].part_size_lo);
424 		if (start == 0 && size == 0) {
425 			warnx("%s: Malformed MBR at sector %llu", device_name,
426 			    (long long)lba);
427 			continue;
428 		}
429 		/* start is relative to the offset of the MBR itself. */
430 		start += lba;
431 		if (verbose > 2)
432 			warnx("%s: MBR part: type=%d, start=%llu, size=%llu",
433 			    device_name, mbr->mbr_part[i].part_typ,
434 			    (long long)start, (long long)size);
435 		if (mbr->mbr_part[i].part_typ != 15) {
436 			m = map_add(start, size, MAP_TYPE_MBR_PART, p);
437 			if (m == NULL)
438 				return (-1);
439 			m->map_index = i + 1;
440 		} else {
441 			if (gpt_mbr(fd, start) == -1)
442 				return (-1);
443 		}
444 	}
445 	return (0);
446 }
447 
448 #ifdef __NetBSD__
449 static int
450 drvctl(const char *name, u_int *sector_size, off_t *media_size)
451 {
452 	prop_dictionary_t command_dict, args_dict, results_dict, data_dict,
453 	    disk_info, geometry;
454 	prop_string_t string;
455 	prop_number_t number;
456 	int dfd, res;
457 	char *dname, *p;
458 
459 	if ((dfd = open("/dev/drvctl", O_RDONLY)) == -1)
460 		return -1;
461 
462 	command_dict = prop_dictionary_create();
463 	args_dict = prop_dictionary_create();
464 
465 	string = prop_string_create_cstring_nocopy("get-properties");
466 	prop_dictionary_set(command_dict, "drvctl-command", string);
467 	prop_object_release(string);
468 
469 	if ((dname = strdup(name[0] == 'r' ? name + 1 : name)) == NULL) {
470 		(void)close(dfd);
471 		return -1;
472 	}
473 	for (p = dname; *p; p++)
474 		continue;
475 	for (--p; p >= dname && !isdigit((unsigned char)*p); *p-- = '\0')
476 		continue;
477 
478 	string = prop_string_create_cstring(dname);
479 	free(dname);
480 	prop_dictionary_set(args_dict, "device-name", string);
481 	prop_object_release(string);
482 
483 	prop_dictionary_set(command_dict, "drvctl-arguments", args_dict);
484 	prop_object_release(args_dict);
485 
486 	res = prop_dictionary_sendrecv_ioctl(command_dict, dfd, DRVCTLCOMMAND,
487 	    &results_dict);
488 	(void)close(dfd);
489 	prop_object_release(command_dict);
490 	if (res) {
491 		errno = res;
492 		return -1;
493 	}
494 
495 	number = prop_dictionary_get(results_dict, "drvctl-error");
496 	if ((errno = prop_number_integer_value(number)) != 0)
497 		return -1;
498 
499 	data_dict = prop_dictionary_get(results_dict, "drvctl-result-data");
500 	if (data_dict == NULL)
501 		goto out;
502 
503 	disk_info = prop_dictionary_get(data_dict, "disk-info");
504 	if (disk_info == NULL)
505 		goto out;
506 
507 	geometry = prop_dictionary_get(disk_info, "geometry");
508 	if (geometry == NULL)
509 		goto out;
510 
511 	number = prop_dictionary_get(geometry, "sector-size");
512 	if (number == NULL)
513 		goto out;
514 
515 	*sector_size = prop_number_integer_value(number);
516 
517 	number = prop_dictionary_get(geometry, "sectors-per-unit");
518 	if (number == NULL)
519 		goto out;
520 
521 	*media_size = prop_number_integer_value(number) * *sector_size;
522 
523 	return 0;
524 out:
525 	errno = EINVAL;
526 	return -1;
527 }
528 #endif
529 
530 static int
531 gpt_gpt(int fd, off_t lba)
532 {
533 	uuid_t type;
534 	off_t size;
535 	struct gpt_ent *ent;
536 	struct gpt_hdr *hdr;
537 	char *p, *s;
538 	map_t *m;
539 	size_t blocks, tblsz;
540 	unsigned int i;
541 	uint32_t crc;
542 
543 	hdr = gpt_read(fd, lba, 1);
544 	if (hdr == NULL)
545 		return (-1);
546 
547 	if (memcmp(hdr->hdr_sig, GPT_HDR_SIG, sizeof(hdr->hdr_sig)))
548 		goto fail_hdr;
549 
550 	crc = le32toh(hdr->hdr_crc_self);
551 	hdr->hdr_crc_self = 0;
552 	if (crc32(hdr, le32toh(hdr->hdr_size)) != crc) {
553 		if (verbose)
554 			warnx("%s: Bad CRC in GPT header at sector %llu",
555 			    device_name, (long long)lba);
556 		goto fail_hdr;
557 	}
558 
559 	tblsz = le32toh(hdr->hdr_entries) * le32toh(hdr->hdr_entsz);
560 	blocks = tblsz / secsz + ((tblsz % secsz) ? 1 : 0);
561 
562 	/* Use generic pointer to deal with hdr->hdr_entsz != sizeof(*ent). */
563 	p = gpt_read(fd, le64toh(hdr->hdr_lba_table), blocks);
564 	if (p == NULL)
565 		return (-1);
566 
567 	if (crc32(p, tblsz) != le32toh(hdr->hdr_crc_table)) {
568 		if (verbose)
569 			warnx("%s: Bad CRC in GPT table at sector %llu",
570 			    device_name,
571 			    (long long)le64toh(hdr->hdr_lba_table));
572 		goto fail_ent;
573 	}
574 
575 	if (verbose > 1)
576 		warnx("%s: %s GPT at sector %llu", device_name,
577 		    (lba == 1) ? "Pri" : "Sec", (long long)lba);
578 
579 	m = map_add(lba, 1, (lba == 1)
580 	    ? MAP_TYPE_PRI_GPT_HDR : MAP_TYPE_SEC_GPT_HDR, hdr);
581 	if (m == NULL)
582 		return (-1);
583 
584 	m = map_add(le64toh(hdr->hdr_lba_table), blocks, (lba == 1)
585 	    ? MAP_TYPE_PRI_GPT_TBL : MAP_TYPE_SEC_GPT_TBL, p);
586 	if (m == NULL)
587 		return (-1);
588 
589 	if (lba != 1)
590 		return (0);
591 
592 	for (i = 0; i < le32toh(hdr->hdr_entries); i++) {
593 		ent = (void*)(p + i * le32toh(hdr->hdr_entsz));
594 		if (uuid_is_nil((uuid_t *)&ent->ent_type, NULL))
595 			continue;
596 
597 		size = le64toh(ent->ent_lba_end) - le64toh(ent->ent_lba_start) +
598 		    1LL;
599 		if (verbose > 2) {
600 			le_uuid_dec(&ent->ent_type, &type);
601 			uuid_to_string(&type, &s, NULL);
602 			warnx(
603 	"%s: GPT partition: type=%s, start=%llu, size=%llu", device_name, s,
604 			    (long long)le64toh(ent->ent_lba_start),
605 			    (long long)size);
606 			free(s);
607 		}
608 		m = map_add(le64toh(ent->ent_lba_start), size,
609 		    MAP_TYPE_GPT_PART, ent);
610 		if (m == NULL)
611 			return (-1);
612 		m->map_index = i + 1;
613 	}
614 	return (0);
615 
616  fail_ent:
617 	free(p);
618 
619  fail_hdr:
620 	free(hdr);
621 	return (0);
622 }
623 
624 int
625 gpt_open(const char *dev)
626 {
627 	struct stat sb;
628 	int fd, mode;
629 
630 	mode = readonly ? O_RDONLY : O_RDWR|O_EXCL;
631 
632 	device_name = device_path;
633 #ifdef __FreeBSD__
634 	strlcpy(device_path, dev, sizeof(device_path));
635 	if ((fd = open(device_path, mode)) != -1)
636 		goto found;
637 
638 	snprintf(device_path, sizeof(device_path), "%s%s", _PATH_DEV, dev);
639 	device_name = device_path + strlen(_PATH_DEV);
640 	if ((fd = open(device_path, mode)) != -1)
641 		goto found;
642 	return (-1);
643  found:
644 #endif
645 #ifdef __NetBSD__
646 	fd = opendisk(dev, mode, device_path, sizeof(device_path), 0);
647 	if (fd == -1)
648 		return -1;
649 	device_name = device_path + strlen(_PATH_DEV);
650 #endif
651 
652 	if (fstat(fd, &sb) == -1)
653 		goto close;
654 
655 	if ((sb.st_mode & S_IFMT) != S_IFREG) {
656 #ifdef DIOCGSECTORSIZE
657 		if (ioctl(fd, DIOCGSECTORSIZE, &secsz) == -1 ||
658 		    ioctl(fd, DIOCGMEDIASIZE, &mediasz) == -1)
659 			goto close;
660 #endif
661 #ifdef __NetBSD__
662 		if (drvctl(device_name, &secsz, &mediasz) == -1)
663 			goto close;
664 #endif
665 	} else {
666 		secsz = 512;	/* Fixed size for files. */
667 		if (sb.st_size % secsz) {
668 			errno = EINVAL;
669 			goto close;
670 		}
671 		mediasz = sb.st_size;
672 	}
673 
674 	/*
675 	 * We require an absolute minimum of 6 sectors. One for the MBR,
676 	 * 2 for the GPT header, 2 for the GPT table and one to hold some
677 	 * user data. Let's catch this extreme border case here so that
678 	 * we don't have to worry about it later.
679 	 */
680 	if (mediasz / secsz < 6) {
681 		errno = ENODEV;
682 		goto close;
683 	}
684 
685 	if (verbose)
686 		warnx("%s: mediasize=%llu; sectorsize=%u; blocks=%llu",
687 		    device_name, (long long)mediasz, secsz,
688 		    (long long)(mediasz / secsz));
689 
690 	map_init(mediasz / secsz);
691 
692 	if (gpt_mbr(fd, 0LL) == -1)
693 		goto close;
694 	if (gpt_gpt(fd, 1LL) == -1)
695 		goto close;
696 	if (gpt_gpt(fd, mediasz / secsz - 1LL) == -1)
697 		goto close;
698 
699 	return (fd);
700 
701  close:
702 	close(fd);
703 	return (-1);
704 }
705 
706 void
707 gpt_close(int fd)
708 {
709 	/* XXX post processing? */
710 	close(fd);
711 }
712 
713 static struct {
714 	int (*fptr)(int, char *[]);
715 	const char *name;
716 } cmdsw[] = {
717 	{ cmd_add, "add" },
718 	{ cmd_create, "create" },
719 	{ cmd_destroy, "destroy" },
720 	{ NULL, "help" },
721 	{ cmd_label, "label" },
722 	{ cmd_migrate, "migrate" },
723 	{ cmd_recover, "recover" },
724 	{ cmd_remove, "remove" },
725 	{ NULL, "rename" },
726 	{ cmd_show, "show" },
727 	{ NULL, "verify" },
728 	{ NULL, NULL }
729 };
730 
731 static void
732 usage(void)
733 {
734 
735 	fprintf(stderr,
736 	    "usage: %s [-rv] [-p nparts] command [options] device ...\n",
737 	    getprogname());
738 	exit(1);
739 }
740 
741 static void
742 prefix(const char *cmd)
743 {
744 	char *pfx;
745 	const char *prg;
746 
747 	prg = getprogname();
748 	pfx = malloc(strlen(prg) + strlen(cmd) + 2);
749 	/* Don't bother failing. It's not important */
750 	if (pfx == NULL)
751 		return;
752 
753 	sprintf(pfx, "%s %s", prg, cmd);
754 	setprogname(pfx);
755 }
756 
757 int
758 main(int argc, char *argv[])
759 {
760 	char *cmd, *p;
761 	int ch, i;
762 
763 	/* Get the generic options */
764 	while ((ch = getopt(argc, argv, "p:rv")) != -1) {
765 		switch(ch) {
766 		case 'p':
767 			if (parts > 0)
768 				usage();
769 			parts = strtol(optarg, &p, 10);
770 			if (*p != 0 || parts < 1)
771 				usage();
772 			break;
773 		case 'r':
774 			readonly = 1;
775 			break;
776 		case 'v':
777 			verbose++;
778 			break;
779 		default:
780 			usage();
781 		}
782 	}
783 	if (!parts)
784 		parts = 128;
785 
786 	if (argc == optind)
787 		usage();
788 
789 	cmd = argv[optind++];
790 	for (i = 0; cmdsw[i].name != NULL && strcmp(cmd, cmdsw[i].name); i++);
791 
792 	if (cmdsw[i].fptr == NULL)
793 		errx(1, "unknown command: %s", cmd);
794 
795 	prefix(cmd);
796 	return ((*cmdsw[i].fptr)(argc, argv));
797 }
798