Home | History | Annotate | Line # | Download | only in gpt
gpt.c revision 1.34
      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.34 2014/09/30 17:59:59 christos Exp $");
     39 #endif
     40 
     41 #include <sys/param.h>
     42 #include <sys/types.h>
     43 #include <sys/disk.h>
     44 #include <sys/stat.h>
     45 #include <sys/ioctl.h>
     46 #include <sys/bootblock.h>
     47 
     48 #include <err.h>
     49 #include <errno.h>
     50 #include <fcntl.h>
     51 #include <paths.h>
     52 #include <stddef.h>
     53 #include <stdio.h>
     54 #include <stdlib.h>
     55 #include <string.h>
     56 #include <unistd.h>
     57 #include <ctype.h>
     58 #ifndef HAVE_NBTOOL_CONFIG_H
     59 #include <prop/proplib.h>
     60 #include <sys/drvctlio.h>
     61 #endif
     62 
     63 #include "map.h"
     64 #include "gpt.h"
     65 
     66 char	device_path[MAXPATHLEN];
     67 const char *device_arg;
     68 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 ((dfd = open("/dev/drvctl", O_RDONLY)) == -1) {
    379 		warn("%s: /dev/drvctl", __func__);
    380 		return -1;
    381 	}
    382 
    383 	command_dict = prop_dictionary_create();
    384 	args_dict = prop_dictionary_create();
    385 
    386 	string = prop_string_create_cstring_nocopy("get-properties");
    387 	prop_dictionary_set(command_dict, "drvctl-command", string);
    388 	prop_object_release(string);
    389 
    390 	if ((dname = strdup(name[0] == 'r' ? name + 1 : name)) == NULL) {
    391 		(void)close(dfd);
    392 		return -1;
    393 	}
    394 	for (p = dname; *p; p++)
    395 		continue;
    396 	for (--p; p >= dname && !isdigit((unsigned char)*p); *p-- = '\0')
    397 		continue;
    398 
    399 	string = prop_string_create_cstring(dname);
    400 	free(dname);
    401 	prop_dictionary_set(args_dict, "device-name", string);
    402 	prop_object_release(string);
    403 
    404 	prop_dictionary_set(command_dict, "drvctl-arguments", args_dict);
    405 	prop_object_release(args_dict);
    406 
    407 	res = prop_dictionary_sendrecv_ioctl(command_dict, dfd, DRVCTLCOMMAND,
    408 	    &results_dict);
    409 	(void)close(dfd);
    410 	prop_object_release(command_dict);
    411 	if (res) {
    412 		warn("%s: prop_dictionary_sendrecv_ioctl", __func__);
    413 		errno = res;
    414 		return -1;
    415 	}
    416 
    417 	number = prop_dictionary_get(results_dict, "drvctl-error");
    418 	if ((errno = prop_number_integer_value(number)) != 0)
    419 		return -1;
    420 
    421 	data_dict = prop_dictionary_get(results_dict, "drvctl-result-data");
    422 	if (data_dict == NULL)
    423 		goto out;
    424 
    425 	disk_info = prop_dictionary_get(data_dict, "disk-info");
    426 	if (disk_info == NULL)
    427 		goto out;
    428 
    429 	geometry = prop_dictionary_get(disk_info, "geometry");
    430 	if (geometry == NULL)
    431 		goto out;
    432 
    433 	number = prop_dictionary_get(geometry, "sector-size");
    434 	if (number == NULL)
    435 		goto out;
    436 
    437 	*sector_size = prop_number_integer_value(number);
    438 
    439 	number = prop_dictionary_get(geometry, "sectors-per-unit");
    440 	if (number == NULL)
    441 		goto out;
    442 
    443 	*media_size = prop_number_integer_value(number) * *sector_size;
    444 
    445 	return 0;
    446 out:
    447 	errno = EINVAL;
    448 	return -1;
    449 }
    450 #endif
    451 
    452 int
    453 gpt_gpt(int fd, off_t lba, int found)
    454 {
    455 	off_t size;
    456 	struct gpt_ent *ent;
    457 	struct gpt_hdr *hdr;
    458 	char *p;
    459 	map_t *m;
    460 	size_t blocks, tblsz;
    461 	unsigned int i;
    462 	uint32_t crc;
    463 
    464 	hdr = gpt_read(fd, lba, 1);
    465 	if (hdr == NULL)
    466 		return (-1);
    467 
    468 	if (memcmp(hdr->hdr_sig, GPT_HDR_SIG, sizeof(hdr->hdr_sig)))
    469 		goto fail_hdr;
    470 
    471 	crc = le32toh(hdr->hdr_crc_self);
    472 	hdr->hdr_crc_self = 0;
    473 	if (crc32(hdr, le32toh(hdr->hdr_size)) != crc) {
    474 		if (verbose)
    475 			warnx("%s: Bad CRC in GPT header at sector %llu",
    476 			    device_name, (long long)lba);
    477 		goto fail_hdr;
    478 	}
    479 
    480 	tblsz = le32toh(hdr->hdr_entries) * le32toh(hdr->hdr_entsz);
    481 	blocks = tblsz / secsz + ((tblsz % secsz) ? 1 : 0);
    482 
    483 	/* Use generic pointer to deal with hdr->hdr_entsz != sizeof(*ent). */
    484 	p = gpt_read(fd, le64toh(hdr->hdr_lba_table), blocks);
    485 	if (p == NULL) {
    486 		if (found) {
    487 			if (verbose)
    488 				warn("%s: Cannot read LBA table at sector %llu",
    489 				    device_name, (unsigned long long)
    490 				    le64toh(hdr->hdr_lba_table));
    491 			return (-1);
    492 		}
    493 		goto fail_hdr;
    494 	}
    495 
    496 	if (crc32(p, tblsz) != le32toh(hdr->hdr_crc_table)) {
    497 		if (verbose)
    498 			warnx("%s: Bad CRC in GPT table at sector %llu",
    499 			    device_name,
    500 			    (long long)le64toh(hdr->hdr_lba_table));
    501 		goto fail_ent;
    502 	}
    503 
    504 	if (verbose > 1)
    505 		warnx("%s: %s GPT at sector %llu", device_name,
    506 		    (lba == 1) ? "Pri" : "Sec", (long long)lba);
    507 
    508 	m = map_add(lba, 1, (lba == 1)
    509 	    ? MAP_TYPE_PRI_GPT_HDR : MAP_TYPE_SEC_GPT_HDR, hdr);
    510 	if (m == NULL)
    511 		return (-1);
    512 
    513 	m = map_add(le64toh(hdr->hdr_lba_table), blocks, (lba == 1)
    514 	    ? MAP_TYPE_PRI_GPT_TBL : MAP_TYPE_SEC_GPT_TBL, p);
    515 	if (m == NULL)
    516 		return (-1);
    517 
    518 	if (lba != 1)
    519 		return (1);
    520 
    521 	for (i = 0; i < le32toh(hdr->hdr_entries); i++) {
    522 		ent = (void*)(p + i * le32toh(hdr->hdr_entsz));
    523 		if (gpt_uuid_is_nil(ent->ent_type))
    524 			continue;
    525 
    526 		size = le64toh(ent->ent_lba_end) - le64toh(ent->ent_lba_start) +
    527 		    1LL;
    528 		if (verbose > 2) {
    529 			char buf[128];
    530 			gpt_uuid_snprintf(buf, sizeof(buf), "%s",
    531 			    ent->ent_type);
    532 			warnx("%s: GPT partition: type=%s, start=%llu, "
    533 			    "size=%llu", device_name, buf,
    534 			    (long long)le64toh(ent->ent_lba_start),
    535 			    (long long)size);
    536 		}
    537 		m = map_add(le64toh(ent->ent_lba_start), size,
    538 		    MAP_TYPE_GPT_PART, ent);
    539 		if (m == NULL)
    540 			return (-1);
    541 		m->map_index = i + 1;
    542 	}
    543 	return (1);
    544 
    545  fail_ent:
    546 	free(p);
    547 
    548  fail_hdr:
    549 	free(hdr);
    550 	return (0);
    551 }
    552 
    553 int
    554 gpt_open(const char *dev)
    555 {
    556 	struct stat sb;
    557 	int fd, mode, found;
    558 
    559 	mode = readonly ? O_RDONLY : O_RDWR|O_EXCL;
    560 
    561 	device_arg = dev;
    562 	fd = opendisk(dev, mode, device_path, sizeof(device_path), 0);
    563 	if (fd == -1)
    564 		return -1;
    565 	if (strncmp(device_path, _PATH_DEV, strlen(_PATH_DEV)) == 0)
    566 		device_name = device_path + strlen(_PATH_DEV);
    567 	else
    568 		device_name = device_path;
    569 
    570 	if (fstat(fd, &sb) == -1)
    571 		goto close;
    572 
    573 	if ((sb.st_mode & S_IFMT) != S_IFREG) {
    574 #ifdef DIOCGSECTORSIZE
    575 		if (ioctl(fd, DIOCGSECTORSIZE, &secsz) == -1 ||
    576 		    ioctl(fd, DIOCGMEDIASIZE, &mediasz) == -1)
    577 			goto close;
    578 #endif
    579 #ifndef HAVE_NBTOOL_CONFIG_H
    580 		if (drvctl(device_name, &secsz, &mediasz) == -1)
    581 			goto close;
    582 #endif
    583 	} else {
    584 		secsz = 512;	/* Fixed size for files. */
    585 		if (sb.st_size % secsz) {
    586 			errno = EINVAL;
    587 			goto close;
    588 		}
    589 		mediasz = sb.st_size;
    590 	}
    591 
    592 	/*
    593 	 * We require an absolute minimum of 6 sectors. One for the MBR,
    594 	 * 2 for the GPT header, 2 for the GPT table and one to hold some
    595 	 * user data. Let's catch this extreme border case here so that
    596 	 * we don't have to worry about it later.
    597 	 */
    598 	if (mediasz / secsz < 6) {
    599 		errno = ENODEV;
    600 		goto close;
    601 	}
    602 
    603 	if (verbose)
    604 		warnx("%s: mediasize=%llu; sectorsize=%u; blocks=%llu",
    605 		    device_name, (long long)mediasz, secsz,
    606 		    (long long)(mediasz / secsz));
    607 
    608 	map_init(mediasz / secsz);
    609 
    610 	if (gpt_mbr(fd, 0LL) == -1)
    611 		goto close;
    612 	if ((found = gpt_gpt(fd, 1LL, 1)) == -1)
    613 		goto close;
    614 	if (gpt_gpt(fd, mediasz / secsz - 1LL, found) == -1)
    615 		goto close;
    616 
    617 	return (fd);
    618 
    619  close:
    620 	close(fd);
    621 	return (-1);
    622 }
    623 
    624 void
    625 gpt_close(int fd)
    626 {
    627 	/* XXX post processing? */
    628 	close(fd);
    629 }
    630 
    631 static struct {
    632 	int (*fptr)(int, char *[]);
    633 	const char *name;
    634 } cmdsw[] = {
    635 	{ cmd_add, "add" },
    636 #ifndef HAVE_NBTOOL_CONFIG_H
    637 	{ cmd_backup, "backup" },
    638 #endif
    639 	{ cmd_biosboot, "biosboot" },
    640 	{ cmd_create, "create" },
    641 	{ cmd_destroy, "destroy" },
    642 	{ NULL, "help" },
    643 	{ cmd_label, "label" },
    644 	{ cmd_migrate, "migrate" },
    645 	{ cmd_recover, "recover" },
    646 	{ cmd_remove, "remove" },
    647 	{ NULL, "rename" },
    648 	{ cmd_resize, "resize" },
    649 	{ cmd_resizedisk, "resizedisk" },
    650 #ifndef HAVE_NBTOOL_CONFIG_H
    651 	{ cmd_restore, "restore" },
    652 #endif
    653 	{ cmd_set, "set" },
    654 	{ cmd_show, "show" },
    655 	{ cmd_type, "type" },
    656 	{ cmd_unset, "unset" },
    657 	{ NULL, "verify" },
    658 	{ NULL, NULL }
    659 };
    660 
    661 __dead static void
    662 usage(void)
    663 {
    664 	extern const char addmsg1[], addmsg2[], biosbootmsg[];
    665 	extern const char createmsg[], destroymsg[], labelmsg1[], labelmsg2[];
    666 	extern const char labelmsg3[], migratemsg[], recovermsg[], removemsg1[];
    667 	extern const char removemsg2[], resizemsg[], resizediskmsg[];
    668 	extern const char setmsg[], showmsg[], typemsg1[];
    669 	extern const char typemsg2[], typemsg3[], unsetmsg[];
    670 #ifndef HAVE_NBTOOL_CONFIG_H
    671 	extern const char backupmsg[], restoremsg[];
    672 #endif
    673 
    674 
    675 	fprintf(stderr,
    676 	    "usage: %s %s\n"
    677 	    "       %s %s\n"
    678 #ifndef HAVE_NBTOOL_CONFIG_H
    679 	    "       %s %s\n"
    680 #endif
    681 	    "       %s %s\n"
    682 	    "       %s %s\n"
    683 	    "       %s %s\n"
    684 	    "       %s %s\n"
    685 	    "       %s %s\n"
    686 	    "       %*s %s\n"
    687 	    "       %s %s\n"
    688 	    "       %s %s\n"
    689 	    "       %s %s\n"
    690 	    "       %s %s\n"
    691 	    "       %s %s\n"
    692 	    "       %s %s\n"
    693 	    "       %s %s\n"
    694 #ifndef HAVE_NBTOOL_CONFIG_H
    695 	    "       %s %s\n"
    696 #endif
    697 	    "       %s %s\n"
    698 	    "       %s %s\n"
    699 	    "       %s %s\n"
    700 	    "       %*s %s\n"
    701 	    "       %s %s\n",
    702 	    getprogname(), addmsg1,
    703 	    getprogname(), addmsg2,
    704 #ifndef HAVE_NBTOOL_CONFIG_H
    705 	    getprogname(), backupmsg,
    706 #endif
    707 	    getprogname(), biosbootmsg,
    708 	    getprogname(), createmsg,
    709 	    getprogname(), destroymsg,
    710 	    getprogname(), labelmsg1,
    711 	    getprogname(), labelmsg2,
    712 	    (int)strlen(getprogname()), "", labelmsg3,
    713 	    getprogname(), migratemsg,
    714 	    getprogname(), recovermsg,
    715 	    getprogname(), removemsg1,
    716 	    getprogname(), removemsg2,
    717 	    getprogname(), resizemsg,
    718 	    getprogname(), resizediskmsg,
    719 #ifndef HAVE_NBTOOL_CONFIG_H
    720 	    getprogname(), restoremsg,
    721 #endif
    722 	    getprogname(), setmsg,
    723 	    getprogname(), showmsg,
    724 	    getprogname(), typemsg1,
    725 	    getprogname(), typemsg2,
    726 	    (int)strlen(getprogname()), "", typemsg3,
    727 	    getprogname(), unsetmsg);
    728 	exit(1);
    729 }
    730 
    731 static void
    732 prefix(const char *cmd)
    733 {
    734 	char *pfx;
    735 	const char *prg;
    736 
    737 	prg = getprogname();
    738 	pfx = malloc(strlen(prg) + strlen(cmd) + 2);
    739 	/* Don't bother failing. It's not important */
    740 	if (pfx == NULL)
    741 		return;
    742 
    743 	sprintf(pfx, "%s %s", prg, cmd);
    744 	setprogname(pfx);
    745 }
    746 
    747 int
    748 main(int argc, char *argv[])
    749 {
    750 	char *cmd, *p;
    751 	int ch, i;
    752 
    753 	/* Get the generic options */
    754 	while ((ch = getopt(argc, argv, "p:rv")) != -1) {
    755 		switch(ch) {
    756 		case 'p':
    757 			if (parts > 0)
    758 				usage();
    759 			parts = strtoul(optarg, &p, 10);
    760 			if (*p != 0 || parts < 1)
    761 				usage();
    762 			break;
    763 		case 'r':
    764 			readonly = 1;
    765 			break;
    766 		case 'v':
    767 			verbose++;
    768 			break;
    769 		default:
    770 			usage();
    771 		}
    772 	}
    773 	if (!parts)
    774 		parts = 128;
    775 
    776 	if (argc == optind)
    777 		usage();
    778 
    779 	cmd = argv[optind++];
    780 	for (i = 0; cmdsw[i].name != NULL && strcmp(cmd, cmdsw[i].name); i++);
    781 
    782 	if (cmdsw[i].fptr == NULL)
    783 		errx(1, "unknown command: %s", cmd);
    784 
    785 	prefix(cmd);
    786 	return ((*cmdsw[i].fptr)(argc, argv));
    787 }
    788