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