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