xref: /netbsd-src/sbin/gpt/gpt.c (revision 4817a0b0b8fe9612e8ebe21a9bf2d97b95038a97)
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.12 2010/04/02 19:33:09 christos 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, int found)
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 		if (found) {
599 			if (verbose)
600 				warn("%s: Cannot read LBA table at sector %llu",
601 				    device_name, (unsigned long long)
602 				    le64toh(hdr->hdr_lba_table));
603 			return (-1);
604 		}
605 		goto fail_hdr;
606 	}
607 
608 	if (crc32(p, tblsz) != le32toh(hdr->hdr_crc_table)) {
609 		if (verbose)
610 			warnx("%s: Bad CRC in GPT table at sector %llu",
611 			    device_name,
612 			    (long long)le64toh(hdr->hdr_lba_table));
613 		goto fail_ent;
614 	}
615 
616 	if (verbose > 1)
617 		warnx("%s: %s GPT at sector %llu", device_name,
618 		    (lba == 1) ? "Pri" : "Sec", (long long)lba);
619 
620 	m = map_add(lba, 1, (lba == 1)
621 	    ? MAP_TYPE_PRI_GPT_HDR : MAP_TYPE_SEC_GPT_HDR, hdr);
622 	if (m == NULL)
623 		return (-1);
624 
625 	m = map_add(le64toh(hdr->hdr_lba_table), blocks, (lba == 1)
626 	    ? MAP_TYPE_PRI_GPT_TBL : MAP_TYPE_SEC_GPT_TBL, p);
627 	if (m == NULL)
628 		return (-1);
629 
630 	if (lba != 1)
631 		return (1);
632 
633 	for (i = 0; i < le32toh(hdr->hdr_entries); i++) {
634 		ent = (void*)(p + i * le32toh(hdr->hdr_entsz));
635 		if (uuid_is_nil((uuid_t *)&ent->ent_type, NULL))
636 			continue;
637 
638 		size = le64toh(ent->ent_lba_end) - le64toh(ent->ent_lba_start) +
639 		    1LL;
640 		if (verbose > 2) {
641 			le_uuid_dec(&ent->ent_type, &type);
642 			uuid_to_string(&type, &s, NULL);
643 			warnx(
644 	"%s: GPT partition: type=%s, start=%llu, size=%llu", device_name, s,
645 			    (long long)le64toh(ent->ent_lba_start),
646 			    (long long)size);
647 			free(s);
648 		}
649 		m = map_add(le64toh(ent->ent_lba_start), size,
650 		    MAP_TYPE_GPT_PART, ent);
651 		if (m == NULL)
652 			return (-1);
653 		m->map_index = i + 1;
654 	}
655 	return (1);
656 
657  fail_ent:
658 	free(p);
659 
660  fail_hdr:
661 	free(hdr);
662 	return (0);
663 }
664 
665 int
666 gpt_open(const char *dev)
667 {
668 	struct stat sb;
669 	int fd, mode, found;
670 
671 	mode = readonly ? O_RDONLY : O_RDWR|O_EXCL;
672 
673 	device_arg = dev;
674 #ifdef __FreeBSD__
675 	strlcpy(device_path, dev, sizeof(device_path));
676 	if ((fd = open(device_path, mode)) != -1)
677 		goto found;
678 
679 	snprintf(device_path, sizeof(device_path), "%s%s", _PATH_DEV, dev);
680 	device_name = device_path + strlen(_PATH_DEV);
681 	if ((fd = open(device_path, mode)) != -1)
682 		goto found;
683 	return (-1);
684  found:
685 #endif
686 #ifdef __NetBSD__
687 	device_name = device_path + strlen(_PATH_DEV);
688 	fd = opendisk(dev, mode, device_path, sizeof(device_path), 0);
689 	if (fd == -1)
690 		return -1;
691 #endif
692 
693 	if (fstat(fd, &sb) == -1)
694 		goto close;
695 
696 	if ((sb.st_mode & S_IFMT) != S_IFREG) {
697 #ifdef DIOCGSECTORSIZE
698 		if (ioctl(fd, DIOCGSECTORSIZE, &secsz) == -1 ||
699 		    ioctl(fd, DIOCGMEDIASIZE, &mediasz) == -1)
700 			goto close;
701 #endif
702 #ifdef __NetBSD__
703 		if (drvctl(device_name, &secsz, &mediasz) == -1)
704 			goto close;
705 #endif
706 	} else {
707 		secsz = 512;	/* Fixed size for files. */
708 		if (sb.st_size % secsz) {
709 			errno = EINVAL;
710 			goto close;
711 		}
712 		mediasz = sb.st_size;
713 	}
714 
715 	/*
716 	 * We require an absolute minimum of 6 sectors. One for the MBR,
717 	 * 2 for the GPT header, 2 for the GPT table and one to hold some
718 	 * user data. Let's catch this extreme border case here so that
719 	 * we don't have to worry about it later.
720 	 */
721 	if (mediasz / secsz < 6) {
722 		errno = ENODEV;
723 		goto close;
724 	}
725 
726 	if (verbose)
727 		warnx("%s: mediasize=%llu; sectorsize=%u; blocks=%llu",
728 		    device_name, (long long)mediasz, secsz,
729 		    (long long)(mediasz / secsz));
730 
731 	map_init(mediasz / secsz);
732 
733 	if (gpt_mbr(fd, 0LL) == -1)
734 		goto close;
735 	if ((found = gpt_gpt(fd, 1LL, 1)) == -1)
736 		goto close;
737 	if (gpt_gpt(fd, mediasz / secsz - 1LL, found) == -1)
738 		goto close;
739 
740 	return (fd);
741 
742  close:
743 	close(fd);
744 	return (-1);
745 }
746 
747 void
748 gpt_close(int fd)
749 {
750 	/* XXX post processing? */
751 	close(fd);
752 }
753 
754 static struct {
755 	int (*fptr)(int, char *[]);
756 	const char *name;
757 } cmdsw[] = {
758 	{ cmd_add, "add" },
759 	{ cmd_create, "create" },
760 	{ cmd_destroy, "destroy" },
761 	{ NULL, "help" },
762 	{ cmd_label, "label" },
763 	{ cmd_migrate, "migrate" },
764 	{ cmd_recover, "recover" },
765 	{ cmd_remove, "remove" },
766 	{ NULL, "rename" },
767 	{ cmd_show, "show" },
768 	{ NULL, "verify" },
769 	{ NULL, NULL }
770 };
771 
772 static void
773 usage(void)
774 {
775 	extern const char addmsg[], createmsg[], destroymsg[];
776 	extern const char labelmsg1[], labelmsg2[], labelmsg3[];
777 	extern const char migratemsg[], recovermsg[], removemsg1[];
778 	extern const char removemsg2[], showmsg[];
779 
780 	fprintf(stderr,
781 	    "usage: %s %s\n"
782 	    "       %s %s\n"
783 	    "       %s %s\n"
784 	    "       %s %s\n"
785 	    "       %s %s\n"
786 	    "       %*s %s\n"
787 	    "       %s %s\n"
788 	    "       %s %s\n"
789 	    "       %s %s\n"
790 	    "       %s %s\n"
791 	    "       %s %s\n",
792 	    getprogname(), addmsg,
793 	    getprogname(), createmsg,
794 	    getprogname(), destroymsg,
795 	    getprogname(), labelmsg1,
796 	    getprogname(), labelmsg2,
797 	    (int)strlen(getprogname()), "", labelmsg3,
798 	    getprogname(), migratemsg,
799 	    getprogname(), recovermsg,
800 	    getprogname(), removemsg1,
801 	    getprogname(), removemsg2,
802 	    getprogname(), showmsg);
803 	exit(1);
804 }
805 
806 static void
807 prefix(const char *cmd)
808 {
809 	char *pfx;
810 	const char *prg;
811 
812 	prg = getprogname();
813 	pfx = malloc(strlen(prg) + strlen(cmd) + 2);
814 	/* Don't bother failing. It's not important */
815 	if (pfx == NULL)
816 		return;
817 
818 	sprintf(pfx, "%s %s", prg, cmd);
819 	setprogname(pfx);
820 }
821 
822 int
823 main(int argc, char *argv[])
824 {
825 	char *cmd, *p;
826 	int ch, i;
827 
828 	/* Get the generic options */
829 	while ((ch = getopt(argc, argv, "p:rv")) != -1) {
830 		switch(ch) {
831 		case 'p':
832 			if (parts > 0)
833 				usage();
834 			parts = strtoul(optarg, &p, 10);
835 			if (*p != 0 || parts < 1)
836 				usage();
837 			break;
838 		case 'r':
839 			readonly = 1;
840 			break;
841 		case 'v':
842 			verbose++;
843 			break;
844 		default:
845 			usage();
846 		}
847 	}
848 	if (!parts)
849 		parts = 128;
850 
851 	if (argc == optind)
852 		usage();
853 
854 	cmd = argv[optind++];
855 	for (i = 0; cmdsw[i].name != NULL && strcmp(cmd, cmdsw[i].name); i++);
856 
857 	if (cmdsw[i].fptr == NULL)
858 		errx(1, "unknown command: %s", cmd);
859 
860 	prefix(cmd);
861 	return ((*cmdsw[i].fptr)(argc, argv));
862 }
863