xref: /netbsd-src/sbin/gpt/gpt.c (revision 8b0f9554ff8762542c4defc4f70e1eb76fb508fa)
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.4 2007/06/11 04:22:00 dyoung 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 		warn("%s: /dev/drvctl", __func__);
461 		return -1;
462 	}
463 
464 	command_dict = prop_dictionary_create();
465 	args_dict = prop_dictionary_create();
466 
467 	string = prop_string_create_cstring_nocopy("get-properties");
468 	prop_dictionary_set(command_dict, "drvctl-command", string);
469 	prop_object_release(string);
470 
471 	if ((dname = strdup(name[0] == 'r' ? name + 1 : name)) == NULL) {
472 		(void)close(dfd);
473 		return -1;
474 	}
475 	for (p = dname; *p; p++)
476 		continue;
477 	for (--p; p >= dname && !isdigit((unsigned char)*p); *p-- = '\0')
478 		continue;
479 
480 	string = prop_string_create_cstring(dname);
481 	free(dname);
482 	prop_dictionary_set(args_dict, "device-name", string);
483 	prop_object_release(string);
484 
485 	prop_dictionary_set(command_dict, "drvctl-arguments", args_dict);
486 	prop_object_release(args_dict);
487 
488 	res = prop_dictionary_sendrecv_ioctl(command_dict, dfd, DRVCTLCOMMAND,
489 	    &results_dict);
490 	(void)close(dfd);
491 	prop_object_release(command_dict);
492 	if (res) {
493 		warn("%s: prop_dictionary_sendrecv_ioctl", __func__);
494 		errno = res;
495 		return -1;
496 	}
497 
498 	number = prop_dictionary_get(results_dict, "drvctl-error");
499 	if ((errno = prop_number_integer_value(number)) != 0)
500 		return -1;
501 
502 	data_dict = prop_dictionary_get(results_dict, "drvctl-result-data");
503 	if (data_dict == NULL)
504 		goto out;
505 
506 	disk_info = prop_dictionary_get(data_dict, "disk-info");
507 	if (disk_info == NULL)
508 		goto out;
509 
510 	geometry = prop_dictionary_get(disk_info, "geometry");
511 	if (geometry == NULL)
512 		goto out;
513 
514 	number = prop_dictionary_get(geometry, "sector-size");
515 	if (number == NULL)
516 		goto out;
517 
518 	*sector_size = prop_number_integer_value(number);
519 
520 	number = prop_dictionary_get(geometry, "sectors-per-unit");
521 	if (number == NULL)
522 		goto out;
523 
524 	*media_size = prop_number_integer_value(number) * *sector_size;
525 
526 	return 0;
527 out:
528 	errno = EINVAL;
529 	return -1;
530 }
531 #endif
532 
533 static int
534 gpt_gpt(int fd, off_t lba)
535 {
536 	uuid_t type;
537 	off_t size;
538 	struct gpt_ent *ent;
539 	struct gpt_hdr *hdr;
540 	char *p, *s;
541 	map_t *m;
542 	size_t blocks, tblsz;
543 	unsigned int i;
544 	uint32_t crc;
545 
546 	hdr = gpt_read(fd, lba, 1);
547 	if (hdr == NULL)
548 		return (-1);
549 
550 	if (memcmp(hdr->hdr_sig, GPT_HDR_SIG, sizeof(hdr->hdr_sig)))
551 		goto fail_hdr;
552 
553 	crc = le32toh(hdr->hdr_crc_self);
554 	hdr->hdr_crc_self = 0;
555 	if (crc32(hdr, le32toh(hdr->hdr_size)) != crc) {
556 		if (verbose)
557 			warnx("%s: Bad CRC in GPT header at sector %llu",
558 			    device_name, (long long)lba);
559 		goto fail_hdr;
560 	}
561 
562 	tblsz = le32toh(hdr->hdr_entries) * le32toh(hdr->hdr_entsz);
563 	blocks = tblsz / secsz + ((tblsz % secsz) ? 1 : 0);
564 
565 	/* Use generic pointer to deal with hdr->hdr_entsz != sizeof(*ent). */
566 	p = gpt_read(fd, le64toh(hdr->hdr_lba_table), blocks);
567 	if (p == NULL)
568 		return (-1);
569 
570 	if (crc32(p, tblsz) != le32toh(hdr->hdr_crc_table)) {
571 		if (verbose)
572 			warnx("%s: Bad CRC in GPT table at sector %llu",
573 			    device_name,
574 			    (long long)le64toh(hdr->hdr_lba_table));
575 		goto fail_ent;
576 	}
577 
578 	if (verbose > 1)
579 		warnx("%s: %s GPT at sector %llu", device_name,
580 		    (lba == 1) ? "Pri" : "Sec", (long long)lba);
581 
582 	m = map_add(lba, 1, (lba == 1)
583 	    ? MAP_TYPE_PRI_GPT_HDR : MAP_TYPE_SEC_GPT_HDR, hdr);
584 	if (m == NULL)
585 		return (-1);
586 
587 	m = map_add(le64toh(hdr->hdr_lba_table), blocks, (lba == 1)
588 	    ? MAP_TYPE_PRI_GPT_TBL : MAP_TYPE_SEC_GPT_TBL, p);
589 	if (m == NULL)
590 		return (-1);
591 
592 	if (lba != 1)
593 		return (0);
594 
595 	for (i = 0; i < le32toh(hdr->hdr_entries); i++) {
596 		ent = (void*)(p + i * le32toh(hdr->hdr_entsz));
597 		if (uuid_is_nil((uuid_t *)&ent->ent_type, NULL))
598 			continue;
599 
600 		size = le64toh(ent->ent_lba_end) - le64toh(ent->ent_lba_start) +
601 		    1LL;
602 		if (verbose > 2) {
603 			le_uuid_dec(&ent->ent_type, &type);
604 			uuid_to_string(&type, &s, NULL);
605 			warnx(
606 	"%s: GPT partition: type=%s, start=%llu, size=%llu", device_name, s,
607 			    (long long)le64toh(ent->ent_lba_start),
608 			    (long long)size);
609 			free(s);
610 		}
611 		m = map_add(le64toh(ent->ent_lba_start), size,
612 		    MAP_TYPE_GPT_PART, ent);
613 		if (m == NULL)
614 			return (-1);
615 		m->map_index = i + 1;
616 	}
617 	return (0);
618 
619  fail_ent:
620 	free(p);
621 
622  fail_hdr:
623 	free(hdr);
624 	return (0);
625 }
626 
627 int
628 gpt_open(const char *dev)
629 {
630 	struct stat sb;
631 	int fd, mode;
632 
633 	mode = readonly ? O_RDONLY : O_RDWR|O_EXCL;
634 
635 	device_name = device_path;
636 #ifdef __FreeBSD__
637 	strlcpy(device_path, dev, sizeof(device_path));
638 	if ((fd = open(device_path, mode)) != -1)
639 		goto found;
640 
641 	snprintf(device_path, sizeof(device_path), "%s%s", _PATH_DEV, dev);
642 	device_name = device_path + strlen(_PATH_DEV);
643 	if ((fd = open(device_path, mode)) != -1)
644 		goto found;
645 	return (-1);
646  found:
647 #endif
648 #ifdef __NetBSD__
649 	fd = opendisk(dev, mode, device_path, sizeof(device_path), 0);
650 	if (fd == -1)
651 		return -1;
652 	device_name = device_path + strlen(_PATH_DEV);
653 #endif
654 
655 	if (fstat(fd, &sb) == -1)
656 		goto close;
657 
658 	if ((sb.st_mode & S_IFMT) != S_IFREG) {
659 #ifdef DIOCGSECTORSIZE
660 		if (ioctl(fd, DIOCGSECTORSIZE, &secsz) == -1 ||
661 		    ioctl(fd, DIOCGMEDIASIZE, &mediasz) == -1)
662 			goto close;
663 #endif
664 #ifdef __NetBSD__
665 		if (drvctl(device_name, &secsz, &mediasz) == -1)
666 			goto close;
667 #endif
668 	} else {
669 		secsz = 512;	/* Fixed size for files. */
670 		if (sb.st_size % secsz) {
671 			errno = EINVAL;
672 			goto close;
673 		}
674 		mediasz = sb.st_size;
675 	}
676 
677 	/*
678 	 * We require an absolute minimum of 6 sectors. One for the MBR,
679 	 * 2 for the GPT header, 2 for the GPT table and one to hold some
680 	 * user data. Let's catch this extreme border case here so that
681 	 * we don't have to worry about it later.
682 	 */
683 	if (mediasz / secsz < 6) {
684 		errno = ENODEV;
685 		goto close;
686 	}
687 
688 	if (verbose)
689 		warnx("%s: mediasize=%llu; sectorsize=%u; blocks=%llu",
690 		    device_name, (long long)mediasz, secsz,
691 		    (long long)(mediasz / secsz));
692 
693 	map_init(mediasz / secsz);
694 
695 	if (gpt_mbr(fd, 0LL) == -1)
696 		goto close;
697 	if (gpt_gpt(fd, 1LL) == -1)
698 		goto close;
699 	if (gpt_gpt(fd, mediasz / secsz - 1LL) == -1)
700 		goto close;
701 
702 	return (fd);
703 
704  close:
705 	close(fd);
706 	return (-1);
707 }
708 
709 void
710 gpt_close(int fd)
711 {
712 	/* XXX post processing? */
713 	close(fd);
714 }
715 
716 static struct {
717 	int (*fptr)(int, char *[]);
718 	const char *name;
719 } cmdsw[] = {
720 	{ cmd_add, "add" },
721 	{ cmd_create, "create" },
722 	{ cmd_destroy, "destroy" },
723 	{ NULL, "help" },
724 	{ cmd_label, "label" },
725 	{ cmd_migrate, "migrate" },
726 	{ cmd_recover, "recover" },
727 	{ cmd_remove, "remove" },
728 	{ NULL, "rename" },
729 	{ cmd_show, "show" },
730 	{ NULL, "verify" },
731 	{ NULL, NULL }
732 };
733 
734 static void
735 usage(void)
736 {
737 
738 	fprintf(stderr,
739 	    "usage: %s [-rv] [-p nparts] command [options] device ...\n",
740 	    getprogname());
741 	exit(1);
742 }
743 
744 static void
745 prefix(const char *cmd)
746 {
747 	char *pfx;
748 	const char *prg;
749 
750 	prg = getprogname();
751 	pfx = malloc(strlen(prg) + strlen(cmd) + 2);
752 	/* Don't bother failing. It's not important */
753 	if (pfx == NULL)
754 		return;
755 
756 	sprintf(pfx, "%s %s", prg, cmd);
757 	setprogname(pfx);
758 }
759 
760 int
761 main(int argc, char *argv[])
762 {
763 	char *cmd, *p;
764 	int ch, i;
765 
766 	/* Get the generic options */
767 	while ((ch = getopt(argc, argv, "p:rv")) != -1) {
768 		switch(ch) {
769 		case 'p':
770 			if (parts > 0)
771 				usage();
772 			parts = strtoul(optarg, &p, 10);
773 			if (*p != 0 || parts < 1)
774 				usage();
775 			break;
776 		case 'r':
777 			readonly = 1;
778 			break;
779 		case 'v':
780 			verbose++;
781 			break;
782 		default:
783 			usage();
784 		}
785 	}
786 	if (!parts)
787 		parts = 128;
788 
789 	if (argc == optind)
790 		usage();
791 
792 	cmd = argv[optind++];
793 	for (i = 0; cmdsw[i].name != NULL && strcmp(cmd, cmdsw[i].name); i++);
794 
795 	if (cmdsw[i].fptr == NULL)
796 		errx(1, "unknown command: %s", cmd);
797 
798 	prefix(cmd);
799 	return ((*cmdsw[i].fptr)(argc, argv));
800 }
801