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