Home | History | Annotate | Line # | Download | only in gpt
gpt.c revision 1.31
      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 #if HAVE_NBTOOL_CONFIG_H
     30 #include "nbtool_config.h"
     31 #endif
     32 
     33 #include <sys/cdefs.h>
     34 #ifdef __FBSDID
     35 __FBSDID("$FreeBSD: src/sbin/gpt/gpt.c,v 1.16 2006/07/07 02:44:23 marcel Exp $");
     36 #endif
     37 #ifdef __RCSID
     38 __RCSID("$NetBSD: gpt.c,v 1.31 2014/09/29 20:28:57 christos Exp $");
     39 #endif
     40 
     41 #include <sys/param.h>
     42 #include <sys/types.h>
     43 #include <sys/disk.h>
     44 #include <sys/stat.h>
     45 #include <sys/ioctl.h>
     46 #include <sys/bootblock.h>
     47 
     48 #include <err.h>
     49 #include <errno.h>
     50 #include <fcntl.h>
     51 #include <paths.h>
     52 #include <stddef.h>
     53 #include <stdio.h>
     54 #include <stdlib.h>
     55 #include <string.h>
     56 #include <unistd.h>
     57 #include <util.h>
     58 #include <ctype.h>
     59 #include <prop/proplib.h>
     60 #include <sys/drvctlio.h>
     61 
     62 #include "map.h"
     63 #include "gpt.h"
     64 
     65 char	device_path[MAXPATHLEN];
     66 const char *device_arg;
     67 char	*device_name;
     68 
     69 off_t	mediasz;
     70 
     71 u_int	parts;
     72 u_int	secsz;
     73 
     74 int	readonly, verbose;
     75 
     76 static uint32_t crc32_tab[] = {
     77 	0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f,
     78 	0xe963a535, 0x9e6495a3,	0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
     79 	0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2,
     80 	0xf3b97148, 0x84be41de,	0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
     81 	0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec,	0x14015c4f, 0x63066cd9,
     82 	0xfa0f3d63, 0x8d080df5,	0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,
     83 	0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b,	0x35b5a8fa, 0x42b2986c,
     84 	0xdbbbc9d6, 0xacbcf940,	0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
     85 	0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423,
     86 	0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
     87 	0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d,	0x76dc4190, 0x01db7106,
     88 	0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
     89 	0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d,
     90 	0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,
     91 	0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950,
     92 	0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
     93 	0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7,
     94 	0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,
     95 	0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa,
     96 	0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
     97 	0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81,
     98 	0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a,
     99 	0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84,
    100 	0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
    101 	0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb,
    102 	0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,
    103 	0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e,
    104 	0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
    105 	0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55,
    106 	0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
    107 	0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28,
    108 	0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
    109 	0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f,
    110 	0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38,
    111 	0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242,
    112 	0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
    113 	0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69,
    114 	0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2,
    115 	0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc,
    116 	0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
    117 	0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693,
    118 	0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
    119 	0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d
    120 };
    121 
    122 uint32_t
    123 crc32(const void *buf, size_t size)
    124 {
    125 	const uint8_t *p;
    126 	uint32_t crc;
    127 
    128 	p = buf;
    129 	crc = ~0U;
    130 
    131 	while (size--)
    132 		crc = crc32_tab[(crc ^ *p++) & 0xFF] ^ (crc >> 8);
    133 
    134 	return crc ^ ~0U;
    135 }
    136 
    137 uint8_t *
    138 utf16_to_utf8(uint16_t *s16)
    139 {
    140 	static uint8_t *s8 = NULL;
    141 	static size_t s8len = 0;
    142 	size_t s8idx, s16idx, s16len;
    143 	uint32_t utfchar;
    144 	unsigned int c;
    145 
    146 	s16len = 0;
    147 	while (s16[s16len++] != 0)
    148 		;
    149 	if (s8len < s16len * 3) {
    150 		if (s8 != NULL)
    151 			free(s8);
    152 		s8len = s16len * 3;
    153 		s8 = calloc(s16len, 3);
    154 	}
    155 	s8idx = s16idx = 0;
    156 	while (s16idx < s16len) {
    157 		utfchar = le16toh(s16[s16idx++]);
    158 		if ((utfchar & 0xf800) == 0xd800) {
    159 			c = le16toh(s16[s16idx]);
    160 			if ((utfchar & 0x400) != 0 || (c & 0xfc00) != 0xdc00)
    161 				utfchar = 0xfffd;
    162 			else
    163 				s16idx++;
    164 		}
    165 		if (utfchar < 0x80) {
    166 			s8[s8idx++] = utfchar;
    167 		} else if (utfchar < 0x800) {
    168 			s8[s8idx++] = 0xc0 | (utfchar >> 6);
    169 			s8[s8idx++] = 0x80 | (utfchar & 0x3f);
    170 		} else if (utfchar < 0x10000) {
    171 			s8[s8idx++] = 0xe0 | (utfchar >> 12);
    172 			s8[s8idx++] = 0x80 | ((utfchar >> 6) & 0x3f);
    173 			s8[s8idx++] = 0x80 | (utfchar & 0x3f);
    174 		} else if (utfchar < 0x200000) {
    175 			s8[s8idx++] = 0xf0 | (utfchar >> 18);
    176 			s8[s8idx++] = 0x80 | ((utfchar >> 12) & 0x3f);
    177 			s8[s8idx++] = 0x80 | ((utfchar >> 6) & 0x3f);
    178 			s8[s8idx++] = 0x80 | (utfchar & 0x3f);
    179 		}
    180 	}
    181 	return (s8);
    182 }
    183 
    184 void
    185 utf8_to_utf16(const uint8_t *s8, uint16_t *s16, size_t s16len)
    186 {
    187 	size_t s16idx, s8idx, s8len;
    188 	uint32_t utfchar = 0;
    189 	unsigned int c, utfbytes;
    190 
    191 	s8len = 0;
    192 	while (s8[s8len++] != 0)
    193 		;
    194 	s8idx = s16idx = 0;
    195 	utfbytes = 0;
    196 	do {
    197 		c = s8[s8idx++];
    198 		if ((c & 0xc0) != 0x80) {
    199 			/* Initial characters. */
    200 			if (utfbytes != 0) {
    201 				/* Incomplete encoding. */
    202 				s16[s16idx++] = htole16(0xfffd);
    203 				if (s16idx == s16len) {
    204 					s16[--s16idx] = 0;
    205 					return;
    206 				}
    207 			}
    208 			if ((c & 0xf8) == 0xf0) {
    209 				utfchar = c & 0x07;
    210 				utfbytes = 3;
    211 			} else if ((c & 0xf0) == 0xe0) {
    212 				utfchar = c & 0x0f;
    213 				utfbytes = 2;
    214 			} else if ((c & 0xe0) == 0xc0) {
    215 				utfchar = c & 0x1f;
    216 				utfbytes = 1;
    217 			} else {
    218 				utfchar = c & 0x7f;
    219 				utfbytes = 0;
    220 			}
    221 		} else {
    222 			/* Followup characters. */
    223 			if (utfbytes > 0) {
    224 				utfchar = (utfchar << 6) + (c & 0x3f);
    225 				utfbytes--;
    226 			} else if (utfbytes == 0)
    227 				utfbytes = -1;
    228 		}
    229 		if (utfbytes == 0) {
    230 			if (utfchar >= 0x10000 && s16idx + 2 >= s16len)
    231 				utfchar = 0xfffd;
    232 			if (utfchar >= 0x10000) {
    233 				s16[s16idx++] =
    234 				    htole16(0xd800 | ((utfchar>>10)-0x40));
    235 				s16[s16idx++] =
    236 				    htole16(0xdc00 | (utfchar & 0x3ff));
    237 			} else
    238 				s16[s16idx++] = htole16(utfchar);
    239 			if (s16idx == s16len) {
    240 				s16[--s16idx] = 0;
    241 				return;
    242 			}
    243 		}
    244 	} while (c != 0);
    245 }
    246 
    247 int
    248 parse_uuid(const char *s, uuid_t *uuid)
    249 {
    250 	uint32_t status;
    251 
    252 	uuid_from_string(s, uuid, &status);
    253 	if (status == uuid_s_ok)
    254 		return (0);
    255 
    256 	switch (*s) {
    257 	case 'b':
    258 		if (strcmp(s, "bios") == 0) {
    259 			static const uuid_t bios = GPT_ENT_TYPE_BIOS;
    260 			*uuid = bios;
    261 			return (0);
    262 		}
    263 		break;
    264 	case 'c':
    265 		if (strcmp(s, "ccd") == 0) {
    266 			static const uuid_t ccd = GPT_ENT_TYPE_NETBSD_CCD;
    267 			*uuid = ccd;
    268 			return (0);
    269 		} else if (strcmp(s, "cgd") == 0) {
    270 			static const uuid_t cgd = GPT_ENT_TYPE_NETBSD_CGD;
    271 			*uuid = cgd;
    272 			return (0);
    273 		}
    274 		break;
    275 	case 'e':
    276 		if (strcmp(s, "efi") == 0) {
    277 			static const uuid_t efi = GPT_ENT_TYPE_EFI;
    278 			*uuid = efi;
    279 			return (0);
    280 		}
    281 		break;
    282 	case 'f':
    283 		if (strcmp(s, "ffs") == 0) {
    284 			static const uuid_t nb_ffs = GPT_ENT_TYPE_NETBSD_FFS;
    285 			*uuid = nb_ffs;
    286 			return (0);
    287 		}
    288 		break;
    289 	case 'h':
    290 		if (strcmp(s, "hfs") == 0) {
    291 			static const uuid_t hfs = GPT_ENT_TYPE_APPLE_HFS;
    292 			*uuid = hfs;
    293 			return (0);
    294 		}
    295 		break;
    296 	case 'l':
    297 		if (strcmp(s, "lfs") == 0) {
    298 			static const uuid_t lfs = GPT_ENT_TYPE_NETBSD_LFS;
    299 			*uuid = lfs;
    300 			return (0);
    301 		} else if (strcmp(s, "linux") == 0) {
    302 			static const uuid_t lnx = GPT_ENT_TYPE_LINUX_DATA;
    303 			*uuid = lnx;
    304 			return (0);
    305 		}
    306 		break;
    307 	case 'r':
    308 		if (strcmp(s, "raid") == 0) {
    309 			static const uuid_t raid = GPT_ENT_TYPE_NETBSD_RAIDFRAME;
    310 			*uuid = raid;
    311 			return (0);
    312 		}
    313 		break;
    314 	case 's':
    315 		if (strcmp(s, "swap") == 0) {
    316 			static const uuid_t sw = GPT_ENT_TYPE_NETBSD_SWAP;
    317 			*uuid = sw;
    318 			return (0);
    319 		}
    320 		break;
    321 	case 'u':
    322 		if (strcmp(s, "ufs") == 0) {
    323 			static const uuid_t ufs = GPT_ENT_TYPE_NETBSD_FFS;
    324 			*uuid = ufs;
    325 			return (0);
    326 		}
    327 		break;
    328 	case 'w':
    329 		if (strcmp(s, "windows") == 0) {
    330 			static const uuid_t win = GPT_ENT_TYPE_MS_BASIC_DATA;
    331 			*uuid = win;
    332 			return (0);
    333 		}
    334 		break;
    335 	}
    336 	return (EINVAL);
    337 }
    338 
    339 void*
    340 gpt_read(int fd, off_t lba, size_t count)
    341 {
    342 	off_t ofs;
    343 	void *buf;
    344 
    345 	count *= secsz;
    346 	buf = malloc(count);
    347 	if (buf == NULL)
    348 		return (NULL);
    349 
    350 	ofs = lba * secsz;
    351 	if (lseek(fd, ofs, SEEK_SET) == ofs &&
    352 	    read(fd, buf, count) == (ssize_t)count)
    353 		return (buf);
    354 
    355 	free(buf);
    356 	return (NULL);
    357 }
    358 
    359 int
    360 gpt_write(int fd, map_t *map)
    361 {
    362 	off_t ofs;
    363 	size_t count;
    364 
    365 	count = map->map_size * secsz;
    366 	ofs = map->map_start * secsz;
    367 	if (lseek(fd, ofs, SEEK_SET) == ofs &&
    368 	    write(fd, map->map_data, count) == (ssize_t)count)
    369 		return (0);
    370 	return (-1);
    371 }
    372 
    373 static int
    374 gpt_mbr(int fd, off_t lba)
    375 {
    376 	struct mbr *mbr;
    377 	map_t *m, *p;
    378 	off_t size, start;
    379 	unsigned int i, pmbr;
    380 
    381 	mbr = gpt_read(fd, lba, 1);
    382 	if (mbr == NULL)
    383 		return (-1);
    384 
    385 	if (mbr->mbr_sig != htole16(MBR_SIG)) {
    386 		if (verbose)
    387 			warnx("%s: MBR not found at sector %llu", device_name,
    388 			    (long long)lba);
    389 		free(mbr);
    390 		return (0);
    391 	}
    392 
    393 	/*
    394 	 * Differentiate between a regular MBR and a PMBR. This is more
    395 	 * convenient in general. A PMBR is one with a single partition
    396 	 * of type 0xee.
    397 	 */
    398 	pmbr = 0;
    399 	for (i = 0; i < 4; i++) {
    400 		if (mbr->mbr_part[i].part_typ == MBR_PTYPE_UNUSED)
    401 			continue;
    402 		if (mbr->mbr_part[i].part_typ == MBR_PTYPE_PMBR)
    403 			pmbr++;
    404 		else
    405 			break;
    406 	}
    407 	if (pmbr && i == 4 && lba == 0) {
    408 		if (pmbr != 1)
    409 			warnx("%s: Suspicious PMBR at sector %llu",
    410 			    device_name, (long long)lba);
    411 		else if (verbose > 1)
    412 			warnx("%s: PMBR at sector %llu", device_name,
    413 			    (long long)lba);
    414 		p = map_add(lba, 1LL, MAP_TYPE_PMBR, mbr);
    415 		return ((p == NULL) ? -1 : 0);
    416 	}
    417 	if (pmbr)
    418 		warnx("%s: Suspicious MBR at sector %llu", device_name,
    419 		    (long long)lba);
    420 	else if (verbose > 1)
    421 		warnx("%s: MBR at sector %llu", device_name, (long long)lba);
    422 
    423 	p = map_add(lba, 1LL, MAP_TYPE_MBR, mbr);
    424 	if (p == NULL)
    425 		return (-1);
    426 	for (i = 0; i < 4; i++) {
    427 		if (mbr->mbr_part[i].part_typ == MBR_PTYPE_UNUSED ||
    428 		    mbr->mbr_part[i].part_typ == MBR_PTYPE_PMBR)
    429 			continue;
    430 		start = le16toh(mbr->mbr_part[i].part_start_hi);
    431 		start = (start << 16) + le16toh(mbr->mbr_part[i].part_start_lo);
    432 		size = le16toh(mbr->mbr_part[i].part_size_hi);
    433 		size = (size << 16) + le16toh(mbr->mbr_part[i].part_size_lo);
    434 		if (start == 0 && size == 0) {
    435 			warnx("%s: Malformed MBR at sector %llu", device_name,
    436 			    (long long)lba);
    437 			continue;
    438 		}
    439 		/* start is relative to the offset of the MBR itself. */
    440 		start += lba;
    441 		if (verbose > 2)
    442 			warnx("%s: MBR part: type=%d, start=%llu, size=%llu",
    443 			    device_name, mbr->mbr_part[i].part_typ,
    444 			    (long long)start, (long long)size);
    445 		if (mbr->mbr_part[i].part_typ != MBR_PTYPE_EXT_LBA) {
    446 			m = map_add(start, size, MAP_TYPE_MBR_PART, p);
    447 			if (m == NULL)
    448 				return (-1);
    449 			m->map_index = i + 1;
    450 		} else {
    451 			if (gpt_mbr(fd, start) == -1)
    452 				return (-1);
    453 		}
    454 	}
    455 	return (0);
    456 }
    457 
    458 static int
    459 drvctl(const char *name, u_int *sector_size, off_t *media_size)
    460 {
    461 	prop_dictionary_t command_dict, args_dict, results_dict, data_dict,
    462 	    disk_info, geometry;
    463 	prop_string_t string;
    464 	prop_number_t number;
    465 	int dfd, res;
    466 	char *dname, *p;
    467 
    468 	if ((dfd = open("/dev/drvctl", O_RDONLY)) == -1) {
    469 		warn("%s: /dev/drvctl", __func__);
    470 		return -1;
    471 	}
    472 
    473 	command_dict = prop_dictionary_create();
    474 	args_dict = prop_dictionary_create();
    475 
    476 	string = prop_string_create_cstring_nocopy("get-properties");
    477 	prop_dictionary_set(command_dict, "drvctl-command", string);
    478 	prop_object_release(string);
    479 
    480 	if ((dname = strdup(name[0] == 'r' ? name + 1 : name)) == NULL) {
    481 		(void)close(dfd);
    482 		return -1;
    483 	}
    484 	for (p = dname; *p; p++)
    485 		continue;
    486 	for (--p; p >= dname && !isdigit((unsigned char)*p); *p-- = '\0')
    487 		continue;
    488 
    489 	string = prop_string_create_cstring(dname);
    490 	free(dname);
    491 	prop_dictionary_set(args_dict, "device-name", string);
    492 	prop_object_release(string);
    493 
    494 	prop_dictionary_set(command_dict, "drvctl-arguments", args_dict);
    495 	prop_object_release(args_dict);
    496 
    497 	res = prop_dictionary_sendrecv_ioctl(command_dict, dfd, DRVCTLCOMMAND,
    498 	    &results_dict);
    499 	(void)close(dfd);
    500 	prop_object_release(command_dict);
    501 	if (res) {
    502 		warn("%s: prop_dictionary_sendrecv_ioctl", __func__);
    503 		errno = res;
    504 		return -1;
    505 	}
    506 
    507 	number = prop_dictionary_get(results_dict, "drvctl-error");
    508 	if ((errno = prop_number_integer_value(number)) != 0)
    509 		return -1;
    510 
    511 	data_dict = prop_dictionary_get(results_dict, "drvctl-result-data");
    512 	if (data_dict == NULL)
    513 		goto out;
    514 
    515 	disk_info = prop_dictionary_get(data_dict, "disk-info");
    516 	if (disk_info == NULL)
    517 		goto out;
    518 
    519 	geometry = prop_dictionary_get(disk_info, "geometry");
    520 	if (geometry == NULL)
    521 		goto out;
    522 
    523 	number = prop_dictionary_get(geometry, "sector-size");
    524 	if (number == NULL)
    525 		goto out;
    526 
    527 	*sector_size = prop_number_integer_value(number);
    528 
    529 	number = prop_dictionary_get(geometry, "sectors-per-unit");
    530 	if (number == NULL)
    531 		goto out;
    532 
    533 	*media_size = prop_number_integer_value(number) * *sector_size;
    534 
    535 	return 0;
    536 out:
    537 	errno = EINVAL;
    538 	return -1;
    539 }
    540 
    541 int
    542 gpt_gpt(int fd, off_t lba, int found)
    543 {
    544 	uuid_t type;
    545 	off_t size;
    546 	struct gpt_ent *ent;
    547 	struct gpt_hdr *hdr;
    548 	char *p, *s;
    549 	map_t *m;
    550 	size_t blocks, tblsz;
    551 	unsigned int i;
    552 	uint32_t crc;
    553 
    554 	hdr = gpt_read(fd, lba, 1);
    555 	if (hdr == NULL)
    556 		return (-1);
    557 
    558 	if (memcmp(hdr->hdr_sig, GPT_HDR_SIG, sizeof(hdr->hdr_sig)))
    559 		goto fail_hdr;
    560 
    561 	crc = le32toh(hdr->hdr_crc_self);
    562 	hdr->hdr_crc_self = 0;
    563 	if (crc32(hdr, le32toh(hdr->hdr_size)) != crc) {
    564 		if (verbose)
    565 			warnx("%s: Bad CRC in GPT header at sector %llu",
    566 			    device_name, (long long)lba);
    567 		goto fail_hdr;
    568 	}
    569 
    570 	tblsz = le32toh(hdr->hdr_entries) * le32toh(hdr->hdr_entsz);
    571 	blocks = tblsz / secsz + ((tblsz % secsz) ? 1 : 0);
    572 
    573 	/* Use generic pointer to deal with hdr->hdr_entsz != sizeof(*ent). */
    574 	p = gpt_read(fd, le64toh(hdr->hdr_lba_table), blocks);
    575 	if (p == NULL) {
    576 		if (found) {
    577 			if (verbose)
    578 				warn("%s: Cannot read LBA table at sector %llu",
    579 				    device_name, (unsigned long long)
    580 				    le64toh(hdr->hdr_lba_table));
    581 			return (-1);
    582 		}
    583 		goto fail_hdr;
    584 	}
    585 
    586 	if (crc32(p, tblsz) != le32toh(hdr->hdr_crc_table)) {
    587 		if (verbose)
    588 			warnx("%s: Bad CRC in GPT table at sector %llu",
    589 			    device_name,
    590 			    (long long)le64toh(hdr->hdr_lba_table));
    591 		goto fail_ent;
    592 	}
    593 
    594 	if (verbose > 1)
    595 		warnx("%s: %s GPT at sector %llu", device_name,
    596 		    (lba == 1) ? "Pri" : "Sec", (long long)lba);
    597 
    598 	m = map_add(lba, 1, (lba == 1)
    599 	    ? MAP_TYPE_PRI_GPT_HDR : MAP_TYPE_SEC_GPT_HDR, hdr);
    600 	if (m == NULL)
    601 		return (-1);
    602 
    603 	m = map_add(le64toh(hdr->hdr_lba_table), blocks, (lba == 1)
    604 	    ? MAP_TYPE_PRI_GPT_TBL : MAP_TYPE_SEC_GPT_TBL, p);
    605 	if (m == NULL)
    606 		return (-1);
    607 
    608 	if (lba != 1)
    609 		return (1);
    610 
    611 	for (i = 0; i < le32toh(hdr->hdr_entries); i++) {
    612 		ent = (void*)(p + i * le32toh(hdr->hdr_entsz));
    613 		if (uuid_is_nil((uuid_t *)&ent->ent_type, NULL))
    614 			continue;
    615 
    616 		size = le64toh(ent->ent_lba_end) - le64toh(ent->ent_lba_start) +
    617 		    1LL;
    618 		if (verbose > 2) {
    619 			le_uuid_dec(&ent->ent_type, &type);
    620 			uuid_to_string(&type, &s, NULL);
    621 			warnx(
    622 	"%s: GPT partition: type=%s, start=%llu, size=%llu", device_name, s,
    623 			    (long long)le64toh(ent->ent_lba_start),
    624 			    (long long)size);
    625 			free(s);
    626 		}
    627 		m = map_add(le64toh(ent->ent_lba_start), size,
    628 		    MAP_TYPE_GPT_PART, ent);
    629 		if (m == NULL)
    630 			return (-1);
    631 		m->map_index = i + 1;
    632 	}
    633 	return (1);
    634 
    635  fail_ent:
    636 	free(p);
    637 
    638  fail_hdr:
    639 	free(hdr);
    640 	return (0);
    641 }
    642 
    643 int
    644 gpt_open(const char *dev)
    645 {
    646 	struct stat sb;
    647 	int fd, mode, found;
    648 
    649 	mode = readonly ? O_RDONLY : O_RDWR|O_EXCL;
    650 
    651 	device_arg = dev;
    652 	fd = opendisk(dev, mode, device_path, sizeof(device_path), 0);
    653 	if (fd == -1)
    654 		return -1;
    655 	if (strncmp(device_path, _PATH_DEV, strlen(_PATH_DEV)) == 0)
    656 		device_name = device_path + strlen(_PATH_DEV);
    657 	else
    658 		device_name = device_path;
    659 
    660 	if (fstat(fd, &sb) == -1)
    661 		goto close;
    662 
    663 	if ((sb.st_mode & S_IFMT) != S_IFREG) {
    664 #ifdef DIOCGSECTORSIZE
    665 		if (ioctl(fd, DIOCGSECTORSIZE, &secsz) == -1 ||
    666 		    ioctl(fd, DIOCGMEDIASIZE, &mediasz) == -1)
    667 			goto close;
    668 #endif
    669 		if (drvctl(device_name, &secsz, &mediasz) == -1)
    670 			goto close;
    671 	} else {
    672 		secsz = 512;	/* Fixed size for files. */
    673 		if (sb.st_size % secsz) {
    674 			errno = EINVAL;
    675 			goto close;
    676 		}
    677 		mediasz = sb.st_size;
    678 	}
    679 
    680 	/*
    681 	 * We require an absolute minimum of 6 sectors. One for the MBR,
    682 	 * 2 for the GPT header, 2 for the GPT table and one to hold some
    683 	 * user data. Let's catch this extreme border case here so that
    684 	 * we don't have to worry about it later.
    685 	 */
    686 	if (mediasz / secsz < 6) {
    687 		errno = ENODEV;
    688 		goto close;
    689 	}
    690 
    691 	if (verbose)
    692 		warnx("%s: mediasize=%llu; sectorsize=%u; blocks=%llu",
    693 		    device_name, (long long)mediasz, secsz,
    694 		    (long long)(mediasz / secsz));
    695 
    696 	map_init(mediasz / secsz);
    697 
    698 	if (gpt_mbr(fd, 0LL) == -1)
    699 		goto close;
    700 	if ((found = gpt_gpt(fd, 1LL, 1)) == -1)
    701 		goto close;
    702 	if (gpt_gpt(fd, mediasz / secsz - 1LL, found) == -1)
    703 		goto close;
    704 
    705 	return (fd);
    706 
    707  close:
    708 	close(fd);
    709 	return (-1);
    710 }
    711 
    712 void
    713 gpt_close(int fd)
    714 {
    715 	/* XXX post processing? */
    716 	close(fd);
    717 }
    718 
    719 static struct {
    720 	int (*fptr)(int, char *[]);
    721 	const char *name;
    722 } cmdsw[] = {
    723 	{ cmd_add, "add" },
    724 	{ cmd_backup, "backup" },
    725 	{ cmd_biosboot, "biosboot" },
    726 	{ cmd_create, "create" },
    727 	{ cmd_destroy, "destroy" },
    728 	{ NULL, "help" },
    729 	{ cmd_label, "label" },
    730 	{ cmd_migrate, "migrate" },
    731 	{ cmd_recover, "recover" },
    732 	{ cmd_remove, "remove" },
    733 	{ NULL, "rename" },
    734 	{ cmd_resize, "resize" },
    735 	{ cmd_resizedisk, "resizedisk" },
    736 	{ cmd_restore, "restore" },
    737 	{ cmd_set, "set" },
    738 	{ cmd_show, "show" },
    739 	{ cmd_type, "type" },
    740 	{ cmd_unset, "unset" },
    741 	{ NULL, "verify" },
    742 	{ NULL, NULL }
    743 };
    744 
    745 __dead static void
    746 usage(void)
    747 {
    748 	extern const char addmsg1[], addmsg2[], backupmsg[], biosbootmsg[];
    749 	extern const char createmsg[], destroymsg[], labelmsg1[], labelmsg2[];
    750 	extern const char labelmsg3[], migratemsg[], recovermsg[], removemsg1[];
    751 	extern const char removemsg2[], resizemsg[], resizediskmsg[];
    752 	extern const char restoremsg[], setmsg[], showmsg[], typemsg1[];
    753 	extern const char typemsg2[], typemsg3[], unsetmsg[];
    754 
    755 	fprintf(stderr,
    756 	    "usage: %s %s\n"
    757 	    "       %s %s\n"
    758 	    "       %s %s\n"
    759 	    "       %s %s\n"
    760 	    "       %s %s\n"
    761 	    "       %s %s\n"
    762 	    "       %s %s\n"
    763 	    "       %s %s\n"
    764 	    "       %*s %s\n"
    765 	    "       %s %s\n"
    766 	    "       %s %s\n"
    767 	    "       %s %s\n"
    768 	    "       %s %s\n"
    769 	    "       %s %s\n"
    770 	    "       %s %s\n"
    771 	    "       %s %s\n"
    772 	    "       %s %s\n"
    773 	    "       %s %s\n"
    774 	    "       %s %s\n"
    775 	    "       %s %s\n"
    776 	    "       %*s %s\n"
    777 	    "       %s %s\n",
    778 	    getprogname(), addmsg1,
    779 	    getprogname(), addmsg2,
    780 	    getprogname(), backupmsg,
    781 	    getprogname(), biosbootmsg,
    782 	    getprogname(), createmsg,
    783 	    getprogname(), destroymsg,
    784 	    getprogname(), labelmsg1,
    785 	    getprogname(), labelmsg2,
    786 	    (int)strlen(getprogname()), "", labelmsg3,
    787 	    getprogname(), migratemsg,
    788 	    getprogname(), recovermsg,
    789 	    getprogname(), removemsg1,
    790 	    getprogname(), removemsg2,
    791 	    getprogname(), resizemsg,
    792 	    getprogname(), resizediskmsg,
    793 	    getprogname(), restoremsg,
    794 	    getprogname(), setmsg,
    795 	    getprogname(), showmsg,
    796 	    getprogname(), typemsg1,
    797 	    getprogname(), typemsg2,
    798 	    (int)strlen(getprogname()), "", typemsg3,
    799 	    getprogname(), unsetmsg);
    800 	exit(1);
    801 }
    802 
    803 static void
    804 prefix(const char *cmd)
    805 {
    806 	char *pfx;
    807 	const char *prg;
    808 
    809 	prg = getprogname();
    810 	pfx = malloc(strlen(prg) + strlen(cmd) + 2);
    811 	/* Don't bother failing. It's not important */
    812 	if (pfx == NULL)
    813 		return;
    814 
    815 	sprintf(pfx, "%s %s", prg, cmd);
    816 	setprogname(pfx);
    817 }
    818 
    819 int
    820 main(int argc, char *argv[])
    821 {
    822 	char *cmd, *p;
    823 	int ch, i;
    824 
    825 	/* Get the generic options */
    826 	while ((ch = getopt(argc, argv, "p:rv")) != -1) {
    827 		switch(ch) {
    828 		case 'p':
    829 			if (parts > 0)
    830 				usage();
    831 			parts = strtoul(optarg, &p, 10);
    832 			if (*p != 0 || parts < 1)
    833 				usage();
    834 			break;
    835 		case 'r':
    836 			readonly = 1;
    837 			break;
    838 		case 'v':
    839 			verbose++;
    840 			break;
    841 		default:
    842 			usage();
    843 		}
    844 	}
    845 	if (!parts)
    846 		parts = 128;
    847 
    848 	if (argc == optind)
    849 		usage();
    850 
    851 	cmd = argv[optind++];
    852 	for (i = 0; cmdsw[i].name != NULL && strcmp(cmd, cmdsw[i].name); i++);
    853 
    854 	if (cmdsw[i].fptr == NULL)
    855 		errx(1, "unknown command: %s", cmd);
    856 
    857 	prefix(cmd);
    858 	return ((*cmdsw[i].fptr)(argc, argv));
    859 }
    860