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