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