Home | History | Annotate | Line # | Download | only in gpt
gpt.c revision 1.77
      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.77 2019/01/27 13:16:05 martin 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 #include "gpt_private.h"
     62 
     63 static uint32_t crc32_tab[] = {
     64 	0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f,
     65 	0xe963a535, 0x9e6495a3,	0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
     66 	0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2,
     67 	0xf3b97148, 0x84be41de,	0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
     68 	0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec,	0x14015c4f, 0x63066cd9,
     69 	0xfa0f3d63, 0x8d080df5,	0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,
     70 	0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b,	0x35b5a8fa, 0x42b2986c,
     71 	0xdbbbc9d6, 0xacbcf940,	0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
     72 	0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423,
     73 	0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
     74 	0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d,	0x76dc4190, 0x01db7106,
     75 	0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
     76 	0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d,
     77 	0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,
     78 	0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950,
     79 	0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
     80 	0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7,
     81 	0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,
     82 	0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa,
     83 	0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
     84 	0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81,
     85 	0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a,
     86 	0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84,
     87 	0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
     88 	0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb,
     89 	0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,
     90 	0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e,
     91 	0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
     92 	0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55,
     93 	0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
     94 	0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28,
     95 	0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
     96 	0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f,
     97 	0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38,
     98 	0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242,
     99 	0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
    100 	0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69,
    101 	0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2,
    102 	0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc,
    103 	0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
    104 	0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693,
    105 	0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
    106 	0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d
    107 };
    108 
    109 uint32_t
    110 crc32(const void *buf, size_t size)
    111 {
    112 	const uint8_t *p;
    113 	uint32_t crc;
    114 
    115 	p = buf;
    116 	crc = ~0U;
    117 
    118 	while (size--)
    119 		crc = crc32_tab[(crc ^ *p++) & 0xFF] ^ (crc >> 8);
    120 
    121 	return crc ^ ~0U;
    122 }
    123 
    124 /*
    125  * Produce a NUL-terminated utf-8 string from the non-NUL-terminated
    126  * utf16 string.
    127  */
    128 void
    129 utf16_to_utf8(const uint16_t *s16, size_t s16len, uint8_t *s8, size_t s8len)
    130 {
    131 	size_t s8idx, s16idx;
    132 	uint32_t utfchar;
    133 	unsigned int c;
    134 
    135 	for (s16idx = 0; s16idx < s16len; s16idx++)
    136 		if (s16[s16idx] == 0)
    137 			break;
    138 
    139 	s16len = s16idx;
    140 	s8idx = s16idx = 0;
    141 	while (s16idx < s16len) {
    142 		utfchar = le16toh(s16[s16idx++]);
    143 		if ((utfchar & 0xf800) == 0xd800) {
    144 			c = le16toh(s16[s16idx]);
    145 			if ((utfchar & 0x400) != 0 || (c & 0xfc00) != 0xdc00)
    146 				utfchar = 0xfffd;
    147 			else
    148 				s16idx++;
    149 		}
    150 		if (utfchar < 0x80) {
    151 			if (s8idx + 1 >= s8len)
    152 				break;
    153 			s8[s8idx++] = (uint8_t)utfchar;
    154 		} else if (utfchar < 0x800) {
    155 			if (s8idx + 2 >= s8len)
    156 				break;
    157 			s8[s8idx++] = (uint8_t)(0xc0 | (utfchar >> 6));
    158 			s8[s8idx++] = (uint8_t)(0x80 | (utfchar & 0x3f));
    159 		} else if (utfchar < 0x10000) {
    160 			if (s8idx + 3 >= s8len)
    161 				break;
    162 			s8[s8idx++] = (uint8_t)(0xe0 | (utfchar >> 12));
    163 			s8[s8idx++] = (uint8_t)(0x80 | ((utfchar >> 6) & 0x3f));
    164 			s8[s8idx++] = (uint8_t)(0x80 | (utfchar & 0x3f));
    165 		} else if (utfchar < 0x200000) {
    166 			if (s8idx + 4 >= s8len)
    167 				break;
    168 			s8[s8idx++] = (uint8_t)(0xf0 | (utfchar >> 18));
    169 			s8[s8idx++] = (uint8_t)(0x80 | ((utfchar >> 12) & 0x3f));
    170 			s8[s8idx++] = (uint8_t)(0x80 | ((utfchar >> 6) & 0x3f));
    171 			s8[s8idx++] = (uint8_t)(0x80 | (utfchar & 0x3f));
    172 		}
    173 	}
    174 	s8[s8idx] = 0;
    175 }
    176 
    177 /*
    178  * Produce a non-NUL-terminated utf-16 string from the NUL-terminated
    179  * utf8 string.
    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 = (u_int)~0;
    225 		}
    226 		if (utfbytes == 0) {
    227 			if (utfchar >= 0x10000 && s16idx + 2 >= s16len)
    228 				utfchar = 0xfffd;
    229 			if (utfchar >= 0x10000) {
    230 				s16[s16idx++] = htole16((uint16_t)
    231 				    (0xd800 | ((utfchar>>10) - 0x40)));
    232 				s16[s16idx++] = htole16((uint16_t)
    233 				    (0xdc00 | (utfchar & 0x3ff)));
    234 			} else
    235 				s16[s16idx++] = htole16((uint16_t)utfchar);
    236 			if (s16idx == s16len) {
    237 				return;
    238 			}
    239 		}
    240 	} while (c != 0);
    241 
    242 	while (s16idx < s16len)
    243 		s16[s16idx++] = 0;
    244 }
    245 
    246 void *
    247 gpt_read(gpt_t gpt, off_t lba, size_t count)
    248 {
    249 	off_t ofs;
    250 	void *buf;
    251 
    252 	count *= gpt->secsz;
    253 	buf = malloc(count);
    254 	if (buf == NULL)
    255 		return NULL;
    256 
    257 	ofs = lba * gpt->secsz;
    258 	if (lseek(gpt->fd, ofs, SEEK_SET) == ofs &&
    259 	    read(gpt->fd, buf, count) == (ssize_t)count)
    260 		return buf;
    261 
    262 	free(buf);
    263 	return NULL;
    264 }
    265 
    266 int
    267 gpt_write(gpt_t gpt, map_t map)
    268 {
    269 	off_t ofs;
    270 	size_t count;
    271 
    272 	count = (size_t)(map->map_size * gpt->secsz);
    273 	ofs = map->map_start * gpt->secsz;
    274 	if (lseek(gpt->fd, ofs, SEEK_SET) != ofs ||
    275 	    write(gpt->fd, map->map_data, count) != (ssize_t)count)
    276 		return -1;
    277 	gpt->flags |= GPT_MODIFIED;
    278 	return 0;
    279 }
    280 
    281 static int
    282 gpt_mbr(gpt_t gpt, off_t lba, unsigned int *next_index, off_t ext_offset)
    283 {
    284 	struct mbr *mbr;
    285 	map_t m, p;
    286 	off_t size, start;
    287 	unsigned int i, pmbr;
    288 
    289 	mbr = gpt_read(gpt, lba, 1);
    290 	if (mbr == NULL) {
    291 		gpt_warn(gpt, "Read failed");
    292 		return -1;
    293 	}
    294 
    295 	if (mbr->mbr_sig != htole16(MBR_SIG)) {
    296 		if (gpt->verbose)
    297 			gpt_msg(gpt,
    298 			    "MBR not found at sector %ju", (uintmax_t)lba);
    299 		free(mbr);
    300 		return 0;
    301 	}
    302 
    303 	/*
    304 	 * Differentiate between a regular MBR and a PMBR. This is more
    305 	 * convenient in general. A PMBR is one with a single partition
    306 	 * of type 0xee.
    307 	 */
    308 	pmbr = 0;
    309 	for (i = 0; i < 4; i++) {
    310 		if (mbr->mbr_part[i].part_typ == MBR_PTYPE_UNUSED)
    311 			continue;
    312 		if (mbr->mbr_part[i].part_typ == MBR_PTYPE_PMBR)
    313 			pmbr++;
    314 		else
    315 			break;
    316 	}
    317 	if (pmbr && i == 4 && lba == 0) {
    318 		if (pmbr != 1)
    319 			gpt_warnx(gpt, "Suspicious PMBR at sector %ju",
    320 			    (uintmax_t)lba);
    321 		else if (gpt->verbose > 1)
    322 			gpt_msg(gpt, "PMBR at sector %ju", (uintmax_t)lba);
    323 		p = map_add(gpt, lba, 1LL, MAP_TYPE_PMBR, mbr, 1);
    324 		goto out;
    325 	}
    326 	if (pmbr)
    327 		gpt_warnx(gpt, "Suspicious MBR at sector %ju", (uintmax_t)lba);
    328 	else if (gpt->verbose > 1)
    329 		gpt_msg(gpt, "MBR at sector %ju", (uintmax_t)lba);
    330 
    331 	p = map_add(gpt, lba, 1LL, MAP_TYPE_MBR, mbr, 1);
    332 	if (p == NULL)
    333 		goto out;
    334 
    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 			gpt_warnx(gpt, "Malformed MBR at sector %ju",
    345 			    (uintmax_t)lba);
    346 			continue;
    347 		}
    348 		if (gpt->verbose > 2)
    349 			gpt_msg(gpt, "MBR part: flag=%#x type=%d, start=%ju, "
    350 			    "size=%ju", mbr->mbr_part[i].part_flag,
    351 			    mbr->mbr_part[i].part_typ,
    352 			    (uintmax_t)start, (uintmax_t)size);
    353 		if (!MBR_IS_EXTENDED(mbr->mbr_part[i].part_typ)) {
    354 			start += lba;
    355 			m = map_add(gpt, start, size, MAP_TYPE_MBR_PART, p, 0);
    356 			if (m == NULL)
    357 				return -1;
    358 			m->map_index = *next_index;
    359 			(*next_index)++;
    360 		} else {
    361 			start += ext_offset;
    362 			if (gpt_mbr(gpt, start, next_index,
    363 			    ext_offset ? ext_offset : start) == -1)
    364 				return -1;
    365 		}
    366 	}
    367 	return 0;
    368 out:
    369 	if (p == NULL) {
    370 		free(mbr);
    371 		return -1;
    372 	}
    373 	return 0;
    374 }
    375 
    376 int
    377 gpt_gpt(gpt_t gpt, off_t lba, int found)
    378 {
    379 	off_t size;
    380 	struct gpt_ent *ent;
    381 	struct gpt_hdr *hdr;
    382 	char *p;
    383 	map_t m;
    384 	size_t blocks, tblsz;
    385 	unsigned int i;
    386 	uint32_t crc;
    387 
    388 	hdr = gpt_read(gpt, lba, 1);
    389 	if (hdr == NULL)
    390 		return -1;
    391 
    392 	if (memcmp(hdr->hdr_sig, GPT_HDR_SIG, sizeof(hdr->hdr_sig)))
    393 		goto fail_hdr;
    394 
    395 	crc = le32toh(hdr->hdr_crc_self);
    396 	hdr->hdr_crc_self = 0;
    397 	if (crc32(hdr, le32toh(hdr->hdr_size)) != crc) {
    398 		if (gpt->verbose)
    399 			gpt_msg(gpt, "Bad CRC in GPT header at sector %ju",
    400 			    (uintmax_t)lba);
    401 		goto fail_hdr;
    402 	}
    403 
    404 	tblsz = le32toh(hdr->hdr_entries) * le32toh(hdr->hdr_entsz);
    405 	blocks = tblsz / gpt->secsz + ((tblsz % gpt->secsz) ? 1 : 0);
    406 
    407 	/* Use generic pointer to deal with hdr->hdr_entsz != sizeof(*ent). */
    408 	p = gpt_read(gpt, (off_t)le64toh((uint64_t)hdr->hdr_lba_table), blocks);
    409 	if (p == NULL) {
    410 		if (found) {
    411 			if (gpt->verbose)
    412 				gpt_msg(gpt,
    413 				    "Cannot read LBA table at sector %ju",
    414 				    (uintmax_t)le64toh(hdr->hdr_lba_table));
    415 			return -1;
    416 		}
    417 		goto fail_hdr;
    418 	}
    419 
    420 	if (crc32(p, tblsz) != le32toh(hdr->hdr_crc_table)) {
    421 		if (gpt->verbose)
    422 			gpt_msg(gpt, "Bad CRC in GPT table at sector %ju",
    423 			    (uintmax_t)le64toh(hdr->hdr_lba_table));
    424 		goto fail_ent;
    425 	}
    426 
    427 	if (gpt->verbose > 1)
    428 		gpt_msg(gpt, "%s GPT at sector %ju",
    429 		    (lba == 1) ? "Pri" : "Sec", (uintmax_t)lba);
    430 
    431 	m = map_add(gpt, lba, 1, (lba == 1)
    432 	    ? MAP_TYPE_PRI_GPT_HDR : MAP_TYPE_SEC_GPT_HDR, hdr, 1);
    433 	if (m == NULL)
    434 		return (-1);
    435 
    436 	m = map_add(gpt, (off_t)le64toh((uint64_t)hdr->hdr_lba_table),
    437 	    (off_t)blocks,
    438 	    lba == 1 ? MAP_TYPE_PRI_GPT_TBL : MAP_TYPE_SEC_GPT_TBL, p, 1);
    439 	if (m == NULL)
    440 		return (-1);
    441 
    442 	if (lba != 1)
    443 		return (1);
    444 
    445 	for (i = 0; i < le32toh(hdr->hdr_entries); i++) {
    446 		ent = (void*)(p + i * le32toh(hdr->hdr_entsz));
    447 		if (gpt_uuid_is_nil(ent->ent_type))
    448 			continue;
    449 
    450 		size = (off_t)(le64toh((uint64_t)ent->ent_lba_end) -
    451 		    le64toh((uint64_t)ent->ent_lba_start) + 1LL);
    452 		if (gpt->verbose > 2) {
    453 			char buf[128];
    454 			gpt_uuid_snprintf(buf, sizeof(buf), "%s",
    455 			    ent->ent_type);
    456 			gpt_msg(gpt, "GPT partition: type=%s, start=%ju, "
    457 			    "size=%ju", buf,
    458 			    (uintmax_t)le64toh(ent->ent_lba_start),
    459 			    (uintmax_t)size);
    460 		}
    461 		m = map_add(gpt, (off_t)le64toh((uint64_t)ent->ent_lba_start),
    462 		    size, MAP_TYPE_GPT_PART, ent, 0);
    463 		if (m == NULL)
    464 			return (-1);
    465 		m->map_index = i + 1;
    466 	}
    467 	return (1);
    468 
    469  fail_ent:
    470 	free(p);
    471 
    472  fail_hdr:
    473 	free(hdr);
    474 	return (0);
    475 }
    476 
    477 gpt_t
    478 gpt_open(const char *dev, int flags, int verbose, off_t mediasz, u_int secsz,
    479     time_t timestamp)
    480 {
    481 	int mode, found;
    482 	off_t devsz;
    483 	gpt_t gpt;
    484 	unsigned int index;
    485 
    486 	if ((gpt = calloc(1, sizeof(*gpt))) == NULL) {
    487 		if (!(flags & GPT_QUIET))
    488 			warn("Cannot allocate `%s'", dev);
    489 		return NULL;
    490 	}
    491 	gpt->flags = flags;
    492 	gpt->verbose = verbose;
    493 	gpt->mediasz = mediasz;
    494 	gpt->secsz = secsz;
    495 	gpt->timestamp = timestamp;
    496 
    497 	mode = (gpt->flags & GPT_READONLY) ? O_RDONLY : O_RDWR|O_EXCL;
    498 
    499 	gpt->fd = opendisk(dev, mode, gpt->device_name,
    500 	    sizeof(gpt->device_name), 0);
    501 	if (gpt->fd == -1) {
    502 		strlcpy(gpt->device_name, dev, sizeof(gpt->device_name));
    503 		gpt_warn(gpt, "Cannot open");
    504 		goto close;
    505 	}
    506 
    507 	if (fstat(gpt->fd, &gpt->sb) == -1) {
    508 		gpt_warn(gpt, "Cannot stat");
    509 		goto close;
    510 	}
    511 
    512 	if ((gpt->sb.st_mode & S_IFMT) != S_IFREG) {
    513 		if (gpt->secsz == 0) {
    514 #ifdef DIOCGSECTORSIZE
    515 			if (ioctl(gpt->fd, DIOCGSECTORSIZE, &gpt->secsz) == -1) {
    516 				gpt_warn(gpt, "Cannot get sector size");
    517 				goto close;
    518 			}
    519 #endif
    520 			if (gpt->secsz == 0) {
    521 				gpt_warnx(gpt, "Sector size can't be 0");
    522 				goto close;
    523 			}
    524 		}
    525 		if (gpt->mediasz == 0) {
    526 #ifdef DIOCGMEDIASIZE
    527 			if (ioctl(gpt->fd, DIOCGMEDIASIZE, &gpt->mediasz) == -1) {
    528 				gpt_warn(gpt, "Cannot get media size");
    529 				goto close;
    530 			}
    531 #endif
    532 			if (gpt->mediasz == 0) {
    533 				gpt_warnx(gpt, "Media size can't be 0");
    534 				goto close;
    535 			}
    536 		}
    537 	} else {
    538 		gpt->flags |= GPT_FILE;
    539 		if (gpt->secsz == 0)
    540 			gpt->secsz = 512;	/* Fixed size for files. */
    541 		if (gpt->mediasz == 0) {
    542 			if (gpt->sb.st_size % gpt->secsz) {
    543 				errno = EINVAL;
    544 				goto close;
    545 			}
    546 			gpt->mediasz = gpt->sb.st_size;
    547 		}
    548 		gpt->flags |= GPT_NOSYNC;
    549 	}
    550 
    551 	/*
    552 	 * We require an absolute minimum of 6 sectors. One for the MBR,
    553 	 * 2 for the GPT header, 2 for the GPT table and one to hold some
    554 	 * user data. Let's catch this extreme border case here so that
    555 	 * we don't have to worry about it later.
    556 	 */
    557 	devsz = gpt->mediasz / gpt->secsz;
    558 	if (devsz < 6) {
    559 		gpt_warnx(gpt, "Need 6 sectors, we have %ju",
    560 		    (uintmax_t)devsz);
    561 		goto close;
    562 	}
    563 
    564 	if (gpt->verbose) {
    565 		gpt_msg(gpt, "mediasize=%ju; sectorsize=%u; blocks=%ju",
    566 		    (uintmax_t)gpt->mediasz, gpt->secsz, (uintmax_t)devsz);
    567 	}
    568 
    569 	if (map_init(gpt, devsz) == -1)
    570 		goto close;
    571 
    572 	index = 1;
    573 	if (gpt_mbr(gpt, 0LL, &index, 0U) == -1)
    574 		goto close;
    575 	if ((found = gpt_gpt(gpt, 1LL, 1)) == -1)
    576 		goto close;
    577 	if (gpt_gpt(gpt, devsz - 1LL, found) == -1)
    578 		goto close;
    579 
    580 	return gpt;
    581 
    582  close:
    583 	if (gpt->fd != -1)
    584 		close(gpt->fd);
    585 	free(gpt);
    586 	return NULL;
    587 }
    588 
    589 void
    590 gpt_close(gpt_t gpt)
    591 {
    592 
    593 	if (!(gpt->flags & GPT_MODIFIED) || !(gpt->flags & GPT_SYNC))
    594 		goto out;
    595 
    596 	if (!(gpt->flags & GPT_NOSYNC)) {
    597 #ifdef DIOCMWEDGES
    598 		int bits;
    599 		if (ioctl(gpt->fd, DIOCMWEDGES, &bits) == -1)
    600 			gpt_warn(gpt, "Can't update wedge information");
    601 		else
    602 			goto out;
    603 #endif
    604 	}
    605 	if (!(gpt->flags & GPT_FILE))
    606 		gpt_msg(gpt, "You need to run \"dkctl %s makewedges\""
    607 		    " for the changes to take effect\n", gpt->device_name);
    608 
    609 out:
    610 	close(gpt->fd);
    611 }
    612 
    613 __printflike(2, 0)
    614 static void
    615 gpt_vwarnx(gpt_t gpt, const char *fmt, va_list ap, const char *e)
    616 {
    617 	if (gpt && (gpt->flags & GPT_QUIET))
    618 		return;
    619 	fprintf(stderr, "%s: ", getprogname());
    620 	if (gpt)
    621 		fprintf(stderr, "%s: ", gpt->device_name);
    622 	vfprintf(stderr, fmt, ap);
    623 	if (e)
    624 		fprintf(stderr, " (%s)\n", e);
    625 	else
    626 		fputc('\n', stderr);
    627 }
    628 
    629 void
    630 gpt_warnx(gpt_t gpt, const char *fmt, ...)
    631 {
    632 	va_list ap;
    633 
    634 	va_start(ap, fmt);
    635 	gpt_vwarnx(gpt, fmt, ap, NULL);
    636 	va_end(ap);
    637 }
    638 
    639 void
    640 gpt_warn(gpt_t gpt, const char *fmt, ...)
    641 {
    642 	va_list ap;
    643 
    644 	va_start(ap, fmt);
    645 	gpt_vwarnx(gpt, fmt, ap, strerror(errno));
    646 	va_end(ap);
    647 }
    648 
    649 void
    650 gpt_msg(gpt_t gpt, const char *fmt, ...)
    651 {
    652 	va_list ap;
    653 
    654 	if (gpt && (gpt->flags & GPT_QUIET))
    655 		return;
    656 	if (gpt)
    657 		printf("%s: ", gpt->device_name);
    658 	va_start(ap, fmt);
    659 	vprintf(fmt, ap);
    660 	va_end(ap);
    661 	printf("\n");
    662 }
    663 
    664 struct gpt_hdr *
    665 gpt_hdr(gpt_t gpt)
    666 {
    667 	gpt->gpt = map_find(gpt, MAP_TYPE_PRI_GPT_HDR);
    668 	if (gpt->gpt == NULL) {
    669 		gpt_warnx(gpt, "No primary GPT header; run create or recover");
    670 		return NULL;
    671 	}
    672 
    673 	gpt->tpg = map_find(gpt, MAP_TYPE_SEC_GPT_HDR);
    674 	if (gpt->tpg == NULL) {
    675 		gpt_warnx(gpt, "No secondary GPT header; run recover");
    676 		return NULL;
    677 	}
    678 
    679 	gpt->tbl = map_find(gpt, MAP_TYPE_PRI_GPT_TBL);
    680 	gpt->lbt = map_find(gpt, MAP_TYPE_SEC_GPT_TBL);
    681 	if (gpt->tbl == NULL || gpt->lbt == NULL) {
    682 		gpt_warnx(gpt, "Corrupt maps, run recover");
    683 		return NULL;
    684 	}
    685 
    686 	return gpt->gpt->map_data;
    687 }
    688 
    689 int
    690 gpt_write_crc(gpt_t gpt, map_t map, map_t tbl)
    691 {
    692 	struct gpt_hdr *hdr = map->map_data;
    693 
    694 	hdr->hdr_crc_table = htole32(crc32(tbl->map_data,
    695 	    le32toh(hdr->hdr_entries) * le32toh(hdr->hdr_entsz)));
    696 	hdr->hdr_crc_self = 0;
    697 	hdr->hdr_crc_self = htole32(crc32(hdr, le32toh(hdr->hdr_size)));
    698 
    699 	if (gpt_write(gpt, map) == -1) {
    700 		gpt_warn(gpt, "Error writing crc map");
    701 		return -1;
    702 	}
    703 
    704 	if (gpt_write(gpt, tbl) == -1) {
    705 		gpt_warn(gpt, "Error writing crc table");
    706 		return -1;
    707 	}
    708 
    709 	return 0;
    710 }
    711 
    712 int
    713 gpt_write_primary(gpt_t gpt)
    714 {
    715 	return gpt_write_crc(gpt, gpt->gpt, gpt->tbl);
    716 }
    717 
    718 
    719 int
    720 gpt_write_backup(gpt_t gpt)
    721 {
    722 	return gpt_write_crc(gpt, gpt->tpg, gpt->lbt);
    723 }
    724 
    725 void
    726 gpt_create_pmbr_part(struct mbr_part *part, off_t last, int active)
    727 {
    728 	part->part_flag = active ? 0x80 : 0;
    729 	part->part_shd = 0x00;
    730 	part->part_ssect = 0x02;
    731 	part->part_scyl = 0x00;
    732 	part->part_typ = MBR_PTYPE_PMBR;
    733 	part->part_ehd = 0xfe;
    734 	part->part_esect = 0xff;
    735 	part->part_ecyl = 0xff;
    736 	part->part_start_lo = htole16(1);
    737 	if (last > 0xffffffff) {
    738 		part->part_size_lo = htole16(0xffff);
    739 		part->part_size_hi = htole16(0xffff);
    740 	} else {
    741 		part->part_size_lo = htole16((uint16_t)last);
    742 		part->part_size_hi = htole16((uint16_t)(last >> 16));
    743 	}
    744 }
    745 
    746 struct gpt_ent *
    747 gpt_ent(map_t map, map_t tbl, unsigned int i)
    748 {
    749 	struct gpt_hdr *hdr = map->map_data;
    750 	return (void *)((char *)tbl->map_data + i * le32toh(hdr->hdr_entsz));
    751 }
    752 
    753 struct gpt_ent *
    754 gpt_ent_primary(gpt_t gpt, unsigned int i)
    755 {
    756 	return gpt_ent(gpt->gpt, gpt->tbl, i);
    757 }
    758 
    759 struct gpt_ent *
    760 gpt_ent_backup(gpt_t gpt, unsigned int i)
    761 {
    762 	return gpt_ent(gpt->tpg, gpt->lbt, i);
    763 }
    764 
    765 int
    766 gpt_usage(const char *prefix, const struct gpt_cmd *cmd)
    767 {
    768 	const char **a = cmd->help;
    769 	size_t hlen = cmd->hlen;
    770 	size_t i;
    771 
    772 	if (prefix == NULL) {
    773 		const char *pname = getprogname();
    774 		const char *d1, *d2, *d = " <device>";
    775 		int len = (int)strlen(pname);
    776 		if (strcmp(pname, "gpt") == 0) {
    777 			d1 = "";
    778 			d2 = d;
    779 		} else {
    780 			d2 = "";
    781 			d1 = d;
    782 		}
    783 		fprintf(stderr, "Usage: %s%s %s %s%s\n", pname,
    784 		    d1, cmd->name, a[0], d2);
    785 		for (i = 1; i < hlen; i++) {
    786 			fprintf(stderr,
    787 			    "       %*s%s %s %s%s\n", len, "",
    788 			    d1, cmd->name, a[i], d2);
    789 		}
    790 	} else {
    791 		for (i = 0; i < hlen; i++)
    792 		    fprintf(stderr, "%s%s %s\n", prefix, cmd->name, a[i]);
    793 	}
    794 	return -1;
    795 }
    796 
    797 off_t
    798 gpt_last(gpt_t gpt)
    799 {
    800 	return gpt->mediasz / gpt->secsz - 1LL;
    801 }
    802 
    803 off_t
    804 gpt_create(gpt_t gpt, off_t last, u_int parts, int primary_only)
    805 {
    806 	off_t blocks;
    807 	map_t map;
    808 	struct gpt_hdr *hdr;
    809 	struct gpt_ent *ent;
    810 	unsigned int i;
    811 	void *p;
    812 
    813 	if (map_find(gpt, MAP_TYPE_PRI_GPT_HDR) != NULL ||
    814 	    map_find(gpt, MAP_TYPE_SEC_GPT_HDR) != NULL) {
    815 		gpt_warnx(gpt, "Device already contains a GPT, "
    816 		    "destroy it first");
    817 		return -1;
    818 	}
    819 
    820 	/* Get the amount of free space after the MBR */
    821 	blocks = map_free(gpt, 1LL, 0LL);
    822 	if (blocks == 0LL) {
    823 		gpt_warnx(gpt, "No room for the GPT header");
    824 		return -1;
    825 	}
    826 
    827 	/* Don't create more than parts entries. */
    828 	if ((uint64_t)(blocks - 1) * gpt->secsz >
    829 	    parts * sizeof(struct gpt_ent)) {
    830 		blocks = (off_t)((parts * sizeof(struct gpt_ent)) / gpt->secsz);
    831 		if ((parts * sizeof(struct gpt_ent)) % gpt->secsz)
    832 			blocks++;
    833 		blocks++;		/* Don't forget the header itself */
    834 	}
    835 
    836 	/* Never cross the median of the device. */
    837 	if ((blocks + 1LL) > ((last + 1LL) >> 1))
    838 		blocks = ((last + 1LL) >> 1) - 1LL;
    839 
    840 	/*
    841 	 * Get the amount of free space at the end of the device and
    842 	 * calculate the size for the GPT structures.
    843 	 */
    844 	map = map_last(gpt);
    845 	if (map->map_type != MAP_TYPE_UNUSED) {
    846 		gpt_warnx(gpt, "No room for the backup header");
    847 		return -1;
    848 	}
    849 
    850 	if (map->map_size < blocks)
    851 		blocks = map->map_size;
    852 	if (blocks == 1LL) {
    853 		gpt_warnx(gpt, "No room for the GPT table");
    854 		return -1;
    855 	}
    856 
    857 	blocks--;		/* Number of blocks in the GPT table. */
    858 
    859 	if (gpt_add_hdr(gpt, MAP_TYPE_PRI_GPT_HDR, 1) == -1)
    860 		return -1;
    861 
    862 	if ((p = calloc((size_t)blocks, gpt->secsz)) == NULL) {
    863 		gpt_warnx(gpt, "Can't allocate the primary GPT table");
    864 		return -1;
    865 	}
    866 	if ((gpt->tbl = map_add(gpt, 2LL, blocks,
    867 	    MAP_TYPE_PRI_GPT_TBL, p, 1)) == NULL) {
    868 		free(p);
    869 		gpt_warnx(gpt, "Can't add the primary GPT table");
    870 		return -1;
    871 	}
    872 
    873 	hdr = gpt->gpt->map_data;
    874 	memcpy(hdr->hdr_sig, GPT_HDR_SIG, sizeof(hdr->hdr_sig));
    875 
    876 	/*
    877 	 * XXX struct gpt_hdr is not a multiple of 8 bytes in size and thus
    878 	 * contains padding we must not include in the size.
    879 	 */
    880 	hdr->hdr_revision = htole32(GPT_HDR_REVISION);
    881 	hdr->hdr_size = htole32(GPT_HDR_SIZE);
    882 	hdr->hdr_lba_self = htole64((uint64_t)gpt->gpt->map_start);
    883 	hdr->hdr_lba_alt = htole64((uint64_t)last);
    884 	hdr->hdr_lba_start = htole64((uint64_t)(gpt->tbl->map_start + blocks));
    885 	hdr->hdr_lba_end = htole64((uint64_t)(last - blocks - 1LL));
    886 	if (gpt_uuid_generate(gpt, hdr->hdr_guid) == -1)
    887 		return -1;
    888 	hdr->hdr_lba_table = htole64((uint64_t)(gpt->tbl->map_start));
    889 	hdr->hdr_entries = htole32((uint32_t)(((uint64_t)blocks * gpt->secsz) /
    890 	    sizeof(struct gpt_ent)));
    891 	if (le32toh(hdr->hdr_entries) > parts)
    892 		hdr->hdr_entries = htole32(parts);
    893 	hdr->hdr_entsz = htole32(sizeof(struct gpt_ent));
    894 
    895 	ent = gpt->tbl->map_data;
    896 	for (i = 0; i < le32toh(hdr->hdr_entries); i++) {
    897 		if (gpt_uuid_generate(gpt, ent[i].ent_guid) == -1)
    898 			return -1;
    899 	}
    900 
    901 	/*
    902 	 * Create backup GPT if the user didn't suppress it.
    903 	 */
    904 	if (primary_only)
    905 		return last;
    906 
    907 	if (gpt_add_hdr(gpt, MAP_TYPE_SEC_GPT_HDR, last) == -1)
    908 		return -1;
    909 
    910 	if ((gpt->lbt = map_add(gpt, last - blocks, blocks,
    911 	    MAP_TYPE_SEC_GPT_TBL, gpt->tbl->map_data, 0)) == NULL) {
    912 		gpt_warnx(gpt, "Can't add the secondary GPT table");
    913 		return -1;
    914 	}
    915 
    916 	memcpy(gpt->tpg->map_data, gpt->gpt->map_data, gpt->secsz);
    917 
    918 	hdr = gpt->tpg->map_data;
    919 	hdr->hdr_lba_self = htole64((uint64_t)gpt->tpg->map_start);
    920 	hdr->hdr_lba_alt = htole64((uint64_t)gpt->gpt->map_start);
    921 	hdr->hdr_lba_table = htole64((uint64_t)gpt->lbt->map_start);
    922 	return last;
    923 }
    924 
    925 static int
    926 gpt_size_get(gpt_t gpt, off_t *size)
    927 {
    928 	off_t sectors;
    929 	int64_t human_num;
    930 	char *p;
    931 
    932 	if (*size > 0)
    933 		return -1;
    934 	sectors = strtoll(optarg, &p, 10);
    935 	if (sectors < 1)
    936 		return -1;
    937 	if (*p == '\0' || ((*p == 's' || *p == 'S') && p[1] == '\0')) {
    938 		*size = sectors * gpt->secsz;
    939 		return 0;
    940 	}
    941 	if ((*p == 'b' || *p == 'B') && p[1] == '\0') {
    942 		*size = sectors;
    943 		return 0;
    944 	}
    945 	if (dehumanize_number(optarg, &human_num) < 0)
    946 		return -1;
    947 	*size = human_num;
    948 	return 0;
    949 }
    950 
    951 int
    952 gpt_human_get(gpt_t gpt, off_t *human)
    953 {
    954 	int64_t human_num;
    955 
    956 	if (*human > 0) {
    957 		gpt_warn(gpt, "Already set to %jd new `%s'", (intmax_t)*human,
    958 		    optarg);
    959 		return -1;
    960 	}
    961 	if (dehumanize_number(optarg, &human_num) < 0) {
    962 		gpt_warn(gpt, "Bad number `%s'", optarg);
    963 		return -1;
    964 	}
    965 	*human = human_num;
    966 	if (*human < 1) {
    967 		gpt_warn(gpt, "Number `%s' < 1", optarg);
    968 		return -1;
    969 	}
    970 	return 0;
    971 }
    972 
    973 int
    974 gpt_add_find(gpt_t gpt, struct gpt_find *find, int ch)
    975 {
    976 	switch (ch) {
    977 	case 'a':
    978 		if (find->all > 0) {
    979 			gpt_warn(gpt, "-a is already set");
    980 			return -1;
    981 		}
    982 		find->all = 1;
    983 		break;
    984 	case 'b':
    985 		if (gpt_human_get(gpt, &find->block) == -1)
    986 			return -1;
    987 		break;
    988 	case 'i':
    989 		if (gpt_uint_get(gpt, &find->entry) == -1)
    990 			return -1;
    991 		break;
    992 	case 'L':
    993 		if (gpt_name_get(gpt, &find->label) == -1)
    994 			return -1;
    995 		break;
    996 	case 's':
    997 		if (gpt_size_get(gpt, &find->size) == -1)
    998 			return -1;
    999 		break;
   1000 	case 't':
   1001 		if (!gpt_uuid_is_nil(find->type))
   1002 			return -1;
   1003 		if (gpt_uuid_parse(optarg, find->type) != 0)
   1004 			return -1;
   1005 		break;
   1006 	default:
   1007 		gpt_warn(gpt, "Unknown find option `%c'", ch);
   1008 		return -1;
   1009 	}
   1010 	return 0;
   1011 }
   1012 
   1013 int
   1014 gpt_change_ent(gpt_t gpt, const struct gpt_find *find,
   1015     void (*cfn)(struct gpt_ent *, void *), void *v)
   1016 {
   1017 	map_t m;
   1018 	struct gpt_hdr *hdr;
   1019 	struct gpt_ent *ent;
   1020 	unsigned int i;
   1021 	uint8_t utfbuf[__arraycount(ent->ent_name) * 3 + 1];
   1022 
   1023 	if (!find->all ^
   1024 	    (find->block > 0 || find->entry > 0 || find->label != NULL
   1025 	    || find->size > 0 || !gpt_uuid_is_nil(find->type)))
   1026 		return -1;
   1027 
   1028 	if ((hdr = gpt_hdr(gpt)) == NULL)
   1029 		return -1;
   1030 
   1031 	/* Relabel all matching entries in the map. */
   1032 	for (m = map_first(gpt); m != NULL; m = m->map_next) {
   1033 		if (m->map_type != MAP_TYPE_GPT_PART || m->map_index < 1)
   1034 			continue;
   1035 		if (find->entry > 0 && find->entry != m->map_index)
   1036 			continue;
   1037 		if (find->block > 0 && find->block != m->map_start)
   1038 			continue;
   1039 		if (find->size > 0 && find->size != m->map_size)
   1040 			continue;
   1041 
   1042 		i = m->map_index - 1;
   1043 
   1044 		ent = gpt_ent_primary(gpt, i);
   1045 		if (find->label != NULL) {
   1046 			utf16_to_utf8(ent->ent_name,
   1047 			    __arraycount(ent->ent_name),
   1048 			    utfbuf, __arraycount(utfbuf));
   1049 			if (strcmp((char *)find->label, (char *)utfbuf) != 0)
   1050 				continue;
   1051 		}
   1052 
   1053 		if (!gpt_uuid_is_nil(find->type) &&
   1054 		    !gpt_uuid_equal(find->type, ent->ent_type))
   1055 			continue;
   1056 
   1057 		/* Change the primary entry. */
   1058 		(*cfn)(ent, v);
   1059 
   1060 		if (gpt_write_primary(gpt) == -1)
   1061 			return -1;
   1062 
   1063 		ent = gpt_ent_backup(gpt, i);
   1064 		/* Change the secondary entry. */
   1065 		(*cfn)(ent, v);
   1066 
   1067 		if (gpt_write_backup(gpt) == -1)
   1068 			return -1;
   1069 
   1070 		gpt_msg(gpt, "Partition %d %s", m->map_index, find->msg);
   1071 	}
   1072 	return 0;
   1073 }
   1074 
   1075 int
   1076 gpt_add_ais(gpt_t gpt, off_t *alignment, u_int *entry, off_t *size, int ch)
   1077 {
   1078 	switch (ch) {
   1079 	case 'a':
   1080 		if (gpt_human_get(gpt, alignment) == -1)
   1081 			return -1;
   1082 		return 0;
   1083 	case 'i':
   1084 		if (gpt_uint_get(gpt, entry) == -1)
   1085 			return -1;
   1086 		return 0;
   1087 	case 's':
   1088 		if (gpt_size_get(gpt, size) == -1)
   1089 			return -1;
   1090 		return 0;
   1091 	default:
   1092 		gpt_warn(gpt, "Unknown alignment/index/size option `%c'", ch);
   1093 		return -1;
   1094 	}
   1095 }
   1096 
   1097 off_t
   1098 gpt_check_ais(gpt_t gpt, off_t alignment, u_int entry, off_t size)
   1099 {
   1100 	if (entry == 0) {
   1101 		gpt_warnx(gpt, "Entry not specified");
   1102 		return -1;
   1103 	}
   1104 	if (alignment % gpt->secsz != 0) {
   1105 		gpt_warnx(gpt, "Alignment (%#jx) must be a multiple of "
   1106 		    "sector size (%#x)", (uintmax_t)alignment, gpt->secsz);
   1107 		return -1;
   1108 	}
   1109 
   1110 	if (size % gpt->secsz != 0) {
   1111 		gpt_warnx(gpt, "Size (%#jx) must be a multiple of "
   1112 		    "sector size (%#x)", (uintmax_t)size, gpt->secsz);
   1113 		return -1;
   1114 	}
   1115 	if (size > 0)
   1116 		return size / gpt->secsz;
   1117 	return 0;
   1118 }
   1119 
   1120 static const struct nvd {
   1121 	const char *name;
   1122 	uint64_t mask;
   1123 	const char *description;
   1124 } gpt_attr[] = {
   1125 	{
   1126 		"biosboot",
   1127 		GPT_ENT_ATTR_LEGACY_BIOS_BOOTABLE,
   1128 		"Legacy BIOS boot partition",
   1129 	},
   1130 	{
   1131 		"bootme",
   1132 		GPT_ENT_ATTR_BOOTME,
   1133 		"Bootable partition",
   1134 	},
   1135 	{
   1136 		"bootfailed",
   1137 		GPT_ENT_ATTR_BOOTFAILED,
   1138 		"Partition that marked bootonce failed to boot",
   1139 	},
   1140 	{
   1141 		"bootonce",
   1142 		GPT_ENT_ATTR_BOOTONCE,
   1143 		"Attempt to boot this partition only once",
   1144 	},
   1145 	{
   1146 		"noblockio",
   1147 		GPT_ENT_ATTR_NO_BLOCK_IO_PROTOCOL,
   1148 		"UEFI won't recognize file system for block I/O",
   1149 	},
   1150 	{
   1151 		"required",
   1152 		GPT_ENT_ATTR_REQUIRED_PARTITION,
   1153 		"Partition required for platform to function",
   1154 	},
   1155 };
   1156 
   1157 int
   1158 gpt_attr_get(gpt_t gpt, uint64_t *attributes)
   1159 {
   1160 	size_t i;
   1161 	int rv = 0;
   1162 	char *ptr;
   1163 
   1164 	*attributes = 0;
   1165 
   1166 	for (ptr = strtok(optarg, ","); ptr; ptr = strtok(NULL, ",")) {
   1167 		for (i = 0; i < __arraycount(gpt_attr); i++)
   1168 			if (strcmp(gpt_attr[i].name, ptr) == 0)
   1169 				break;
   1170 		if (i == __arraycount(gpt_attr)) {
   1171 			gpt_warnx(gpt, "Unrecognized attribute `%s'", ptr);
   1172 			rv = -1;
   1173 		} else
   1174 			*attributes |= gpt_attr[i].mask;
   1175 	}
   1176 	return rv;
   1177 }
   1178 
   1179 void
   1180 gpt_attr_help(const char *prefix)
   1181 {
   1182 	size_t i;
   1183 
   1184 	for (i = 0; i < __arraycount(gpt_attr); i++)
   1185 		printf("%s%10.10s\t%s\n", prefix, gpt_attr[i].name,
   1186 		    gpt_attr[i].description);
   1187 }
   1188 
   1189 const char *
   1190 gpt_attr_list(char *buf, size_t len, uint64_t attributes)
   1191 {
   1192 	size_t i;
   1193 	strlcpy(buf, "", len);
   1194 
   1195 	for (i = 0; i < __arraycount(gpt_attr); i++)
   1196 		if (attributes & gpt_attr[i].mask) {
   1197 			strlcat(buf, buf[0] ? ", " : "", len);
   1198 			strlcat(buf, gpt_attr[i].name, len);
   1199 		}
   1200 	return buf;
   1201 }
   1202 
   1203 int
   1204 gpt_attr_update(gpt_t gpt, u_int entry, uint64_t set, uint64_t clr)
   1205 {
   1206 	struct gpt_hdr *hdr;
   1207 	struct gpt_ent *ent;
   1208 	unsigned int i;
   1209 
   1210 	if (entry == 0 || (set == 0 && clr == 0)) {
   1211 		gpt_warnx(gpt, "Nothing to set");
   1212 		return -1;
   1213 	}
   1214 
   1215 	if ((hdr = gpt_hdr(gpt)) == NULL)
   1216 		return -1;
   1217 
   1218 	if (entry > le32toh(hdr->hdr_entries)) {
   1219 		gpt_warnx(gpt, "Index %u out of range (%u max)",
   1220 		    entry, le32toh(hdr->hdr_entries));
   1221 		return -1;
   1222 	}
   1223 
   1224 	i = entry - 1;
   1225 	ent = gpt_ent_primary(gpt, i);
   1226 	if (gpt_uuid_is_nil(ent->ent_type)) {
   1227 		gpt_warnx(gpt, "Entry at index %u is unused", entry);
   1228 		return -1;
   1229 	}
   1230 
   1231 	ent->ent_attr &= ~clr;
   1232 	ent->ent_attr |= set;
   1233 
   1234 	if (gpt_write_primary(gpt) == -1)
   1235 		return -1;
   1236 
   1237 	ent = gpt_ent_backup(gpt, i);
   1238 	ent->ent_attr &= ~clr;
   1239 	ent->ent_attr |= set;
   1240 
   1241 	if (gpt_write_backup(gpt) == -1)
   1242 		return -1;
   1243 	gpt_msg(gpt, "Partition %d attributes updated", entry);
   1244 	return 0;
   1245 }
   1246 
   1247 int
   1248 gpt_uint_get(gpt_t gpt, u_int *entry)
   1249 {
   1250 	char *p;
   1251 	if (*entry > 0)
   1252 		return -1;
   1253 	*entry = (u_int)strtoul(optarg, &p, 10);
   1254 	if (*p != 0 || *entry < 1) {
   1255 		gpt_warn(gpt, "Bad number `%s'", optarg);
   1256 		return -1;
   1257 	}
   1258 	return 0;
   1259 }
   1260 int
   1261 gpt_uuid_get(gpt_t gpt, gpt_uuid_t *uuid)
   1262 {
   1263 	if (!gpt_uuid_is_nil(*uuid))
   1264 		return -1;
   1265 	if (gpt_uuid_parse(optarg, *uuid) != 0) {
   1266 		gpt_warn(gpt, "Can't parse uuid");
   1267 		return -1;
   1268 	}
   1269 	return 0;
   1270 }
   1271 
   1272 int
   1273 gpt_name_get(gpt_t gpt, void *v)
   1274 {
   1275 	char **name = v;
   1276 	if (*name != NULL)
   1277 		return -1;
   1278 	*name = strdup(optarg);
   1279 	if (*name == NULL) {
   1280 		gpt_warn(gpt, "Can't copy string");
   1281 		return -1;
   1282 	}
   1283 	return 0;
   1284 }
   1285 
   1286 void
   1287 gpt_show_num(const char *prompt, uintmax_t num)
   1288 {
   1289 #ifdef HN_AUTOSCALE
   1290 	char human_num[5];
   1291 	if (humanize_number(human_num, 5, (int64_t)num ,
   1292 	    "", HN_AUTOSCALE, HN_NOSPACE|HN_B) < 0)
   1293 		human_num[0] = '\0';
   1294 #endif
   1295 	printf("%s: %ju", prompt, num);
   1296 #ifdef HN_AUTOSCALE
   1297 	if (human_num[0] != '\0')
   1298 		printf(" (%s)", human_num);
   1299 #endif
   1300 	printf("\n");
   1301 }
   1302 
   1303 int
   1304 gpt_add_hdr(gpt_t gpt, int type, off_t loc)
   1305 {
   1306 	void *p;
   1307 	map_t *t;
   1308 	const char *msg;
   1309 
   1310 	switch (type) {
   1311 	case MAP_TYPE_PRI_GPT_HDR:
   1312 		t = &gpt->gpt;
   1313 		msg = "primary";
   1314 		break;
   1315 	case MAP_TYPE_SEC_GPT_HDR:
   1316 		t = &gpt->tpg;
   1317 		msg = "secondary";
   1318 		break;
   1319 	default:
   1320 		gpt_warnx(gpt, "Unknown GPT header type %d", type);
   1321 		return -1;
   1322 	}
   1323 
   1324 	if ((p = calloc(1, gpt->secsz)) == NULL) {
   1325 		gpt_warn(gpt, "Error allocating %s GPT header", msg);
   1326 		return -1;
   1327 	}
   1328 
   1329 	*t = map_add(gpt, loc, 1LL, type, p, 1);
   1330 	if (*t == NULL) {
   1331 		gpt_warn(gpt, "Error adding %s GPT header", msg);
   1332 		free(p);
   1333 		return -1;
   1334 	}
   1335 	return 0;
   1336 }
   1337