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