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