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