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