Home | History | Annotate | Line # | Download | only in gpt
gpt.c revision 1.85
      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.85 2023/09/26 15:55:46 kre 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 if ((gpt->flags & GPT_HYBRID) == 0)
    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 
    578 	if (found) {
    579 		struct map *map;
    580 		struct gpt_hdr *hdr;
    581 		uint64_t lba;
    582 
    583 		/*
    584 		 * read secondary GPT from position stored in primary header
    585 		 * when possible
    586 		 */
    587 		map = map_find(gpt, MAP_TYPE_PRI_GPT_HDR);
    588 		hdr = map ? map->map_data : NULL;
    589 		lba = le64toh(hdr->hdr_lba_alt);
    590 		if (hdr && lba > 0 && lba < (uint64_t)devsz) {
    591 			if (gpt_gpt(gpt, (off_t)lba, found) == -1)
    592 				goto close;
    593 		}
    594 	} else {
    595 		if (gpt_gpt(gpt, devsz - 1LL, found) == -1)
    596 			goto close;
    597 	}
    598 
    599 	return gpt;
    600 
    601  close:
    602 	if (gpt->fd != -1)
    603 		close(gpt->fd);
    604 	free(gpt);
    605 	return NULL;
    606 }
    607 
    608 void
    609 gpt_close(gpt_t gpt)
    610 {
    611 
    612 	if (gpt == NULL)
    613 		return;
    614 
    615 	if (!(gpt->flags & GPT_MODIFIED) || !(gpt->flags & GPT_SYNC))
    616 		goto out;
    617 
    618 	if (!(gpt->flags & GPT_NOSYNC)) {
    619 #ifdef DIOCMWEDGES
    620 		int bits;
    621 		if (ioctl(gpt->fd, DIOCMWEDGES, &bits) == -1)
    622 			gpt_warn(gpt, "Can't update wedge information");
    623 		else
    624 			goto out;
    625 #endif
    626 	}
    627 	if (!(gpt->flags & GPT_FILE))
    628 		gpt_msg(gpt, "You need to run \"dkctl %s makewedges\""
    629 		    " for the changes to take effect\n", gpt->device_name);
    630 
    631 out:
    632 	close(gpt->fd);
    633 }
    634 
    635 __printflike(2, 0)
    636 static void
    637 gpt_vwarnx(gpt_t gpt, const char *fmt, va_list ap, const char *e)
    638 {
    639 	if (gpt && (gpt->flags & GPT_QUIET))
    640 		return;
    641 	fprintf(stderr, "%s: ", getprogname());
    642 	if (gpt)
    643 		fprintf(stderr, "%s: ", gpt->device_name);
    644 	vfprintf(stderr, fmt, ap);
    645 	if (e)
    646 		fprintf(stderr, " (%s)\n", e);
    647 	else
    648 		fputc('\n', stderr);
    649 }
    650 
    651 void
    652 gpt_warnx(gpt_t gpt, const char *fmt, ...)
    653 {
    654 	va_list ap;
    655 
    656 	va_start(ap, fmt);
    657 	gpt_vwarnx(gpt, fmt, ap, NULL);
    658 	va_end(ap);
    659 }
    660 
    661 void
    662 gpt_warn(gpt_t gpt, const char *fmt, ...)
    663 {
    664 	va_list ap;
    665 
    666 	va_start(ap, fmt);
    667 	gpt_vwarnx(gpt, fmt, ap, strerror(errno));
    668 	va_end(ap);
    669 }
    670 
    671 void
    672 gpt_msg(gpt_t gpt, const char *fmt, ...)
    673 {
    674 	va_list ap;
    675 
    676 	if (gpt && (gpt->flags & GPT_QUIET))
    677 		return;
    678 	if (gpt)
    679 		printf("%s: ", gpt->device_name);
    680 	va_start(ap, fmt);
    681 	vprintf(fmt, ap);
    682 	va_end(ap);
    683 	printf("\n");
    684 }
    685 
    686 struct gpt_hdr *
    687 gpt_hdr(gpt_t gpt)
    688 {
    689 	gpt->gpt = map_find(gpt, MAP_TYPE_PRI_GPT_HDR);
    690 	if (gpt->gpt == NULL) {
    691 		gpt_warnx(gpt, "No primary GPT header; run create or recover");
    692 		return NULL;
    693 	}
    694 
    695 	gpt->tpg = map_find(gpt, MAP_TYPE_SEC_GPT_HDR);
    696 	if (gpt->tpg == NULL) {
    697 		gpt_warnx(gpt, "No secondary GPT header; run recover");
    698 		return NULL;
    699 	}
    700 
    701 	gpt->tbl = map_find(gpt, MAP_TYPE_PRI_GPT_TBL);
    702 	gpt->lbt = map_find(gpt, MAP_TYPE_SEC_GPT_TBL);
    703 	if (gpt->tbl == NULL || gpt->lbt == NULL) {
    704 		gpt_warnx(gpt, "Corrupt maps, run recover");
    705 		return NULL;
    706 	}
    707 
    708 	return gpt->gpt->map_data;
    709 }
    710 
    711 int
    712 gpt_write_crc(gpt_t gpt, map_t map, map_t tbl)
    713 {
    714 	struct gpt_hdr *hdr = map->map_data;
    715 
    716 	hdr->hdr_crc_table = htole32(crc32(tbl->map_data,
    717 	    le32toh(hdr->hdr_entries) * le32toh(hdr->hdr_entsz)));
    718 	hdr->hdr_crc_self = 0;
    719 	hdr->hdr_crc_self = htole32(crc32(hdr, le32toh(hdr->hdr_size)));
    720 
    721 	if (gpt_write(gpt, map) == -1) {
    722 		gpt_warn(gpt, "Error writing crc map");
    723 		return -1;
    724 	}
    725 
    726 	if (gpt_write(gpt, tbl) == -1) {
    727 		gpt_warn(gpt, "Error writing crc table");
    728 		return -1;
    729 	}
    730 
    731 	return 0;
    732 }
    733 
    734 int
    735 gpt_write_primary(gpt_t gpt)
    736 {
    737 	return gpt_write_crc(gpt, gpt->gpt, gpt->tbl);
    738 }
    739 
    740 
    741 int
    742 gpt_write_backup(gpt_t gpt)
    743 {
    744 	return gpt_write_crc(gpt, gpt->tpg, gpt->lbt);
    745 }
    746 
    747 void
    748 gpt_create_pmbr_part(struct mbr_part *part, off_t last, int active)
    749 {
    750 	part->part_flag = active ? 0x80 : 0;
    751 	part->part_shd = 0x00;
    752 	part->part_ssect = 0x02;
    753 	part->part_scyl = 0x00;
    754 	part->part_typ = MBR_PTYPE_PMBR;
    755 	part->part_ehd = 0xfe;
    756 	part->part_esect = 0xff;
    757 	part->part_ecyl = 0xff;
    758 	part->part_start_lo = htole16(1);
    759 	if (last > 0xffffffff) {
    760 		part->part_size_lo = htole16(0xffff);
    761 		part->part_size_hi = htole16(0xffff);
    762 	} else {
    763 		part->part_size_lo = htole16((uint16_t)last);
    764 		part->part_size_hi = htole16((uint16_t)(last >> 16));
    765 	}
    766 }
    767 
    768 struct gpt_ent *
    769 gpt_ent(map_t map, map_t tbl, unsigned int i)
    770 {
    771 	struct gpt_hdr *hdr = map->map_data;
    772 	return (void *)((char *)tbl->map_data + i * le32toh(hdr->hdr_entsz));
    773 }
    774 
    775 struct gpt_ent *
    776 gpt_ent_primary(gpt_t gpt, unsigned int i)
    777 {
    778 	return gpt_ent(gpt->gpt, gpt->tbl, i);
    779 }
    780 
    781 struct gpt_ent *
    782 gpt_ent_backup(gpt_t gpt, unsigned int i)
    783 {
    784 	return gpt_ent(gpt->tpg, gpt->lbt, i);
    785 }
    786 
    787 int
    788 gpt_usage(const char *prefix, const struct gpt_cmd *cmd)
    789 {
    790 	const char **a = cmd->help;
    791 	size_t hlen = cmd->hlen;
    792 	size_t i;
    793 
    794 	if (prefix == NULL) {
    795 		const char *pname = getprogname();
    796 		const char *d1, *d2, *d = " <device>";
    797 		int len = (int)strlen(pname);
    798 		if (strcmp(pname, "gpt") == 0) {
    799 			d1 = "";
    800 			d2 = d;
    801 		} else {
    802 			d2 = "";
    803 			d1 = d;
    804 		}
    805 		fprintf(stderr, "Usage: %s%s %s %s%s\n", pname,
    806 		    d1, cmd->name, a[0], d2);
    807 		for (i = 1; i < hlen; i++) {
    808 			fprintf(stderr,
    809 			    "       %*s%s %s %s%s\n", len, "",
    810 			    d1, cmd->name, a[i], d2);
    811 		}
    812 	} else {
    813 		for (i = 0; i < hlen; i++)
    814 		    fprintf(stderr, "%s%s %s\n", prefix, cmd->name, a[i]);
    815 	}
    816 	return -1;
    817 }
    818 
    819 off_t
    820 gpt_last(gpt_t gpt)
    821 {
    822 	return gpt->mediasz / gpt->secsz - 1LL;
    823 }
    824 
    825 off_t
    826 gpt_create(gpt_t gpt, off_t last, u_int parts, int primary_only)
    827 {
    828 	off_t blocks;
    829 	map_t map;
    830 	struct gpt_hdr *hdr;
    831 	struct gpt_ent *ent;
    832 	unsigned int i;
    833 	void *p;
    834 
    835 	if (map_find(gpt, MAP_TYPE_PRI_GPT_HDR) != NULL ||
    836 	    map_find(gpt, MAP_TYPE_SEC_GPT_HDR) != NULL) {
    837 		gpt_warnx(gpt, "Device already contains a GPT, "
    838 		    "destroy it first");
    839 		return -1;
    840 	}
    841 
    842 	/* Get the amount of free space after the MBR */
    843 	blocks = map_free(gpt, 1LL, 0LL);
    844 	if (blocks == 0LL) {
    845 		gpt_warnx(gpt, "No room for the GPT header");
    846 		return -1;
    847 	}
    848 
    849 	/* Don't create more than parts entries. */
    850 	if ((uint64_t)(blocks - 1) * gpt->secsz >
    851 	    parts * sizeof(struct gpt_ent)) {
    852 		blocks = (off_t)((parts * sizeof(struct gpt_ent)) / gpt->secsz);
    853 		if ((parts * sizeof(struct gpt_ent)) % gpt->secsz)
    854 			blocks++;
    855 		blocks++;		/* Don't forget the header itself */
    856 	}
    857 
    858 	/* Never cross the median of the device. */
    859 	if ((blocks + 1LL) > ((last + 1LL) >> 1))
    860 		blocks = ((last + 1LL) >> 1) - 1LL;
    861 
    862 	/*
    863 	 * Get the amount of free space at the end of the device and
    864 	 * calculate the size for the GPT structures.
    865 	 */
    866 	map = map_last(gpt);
    867 	if (map->map_type != MAP_TYPE_UNUSED) {
    868 		gpt_warnx(gpt, "No room for the backup header");
    869 		return -1;
    870 	}
    871 
    872 	if (map->map_size < blocks)
    873 		blocks = map->map_size;
    874 	if (blocks == 1LL) {
    875 		gpt_warnx(gpt, "No room for the GPT table");
    876 		return -1;
    877 	}
    878 
    879 	blocks--;		/* Number of blocks in the GPT table. */
    880 
    881 	if (gpt_add_hdr(gpt, MAP_TYPE_PRI_GPT_HDR, 1) == -1)
    882 		return -1;
    883 
    884 	if ((p = calloc((size_t)blocks, gpt->secsz)) == NULL) {
    885 		gpt_warnx(gpt, "Can't allocate the primary GPT table");
    886 		return -1;
    887 	}
    888 	if ((gpt->tbl = map_add(gpt, 2LL, blocks,
    889 	    MAP_TYPE_PRI_GPT_TBL, p, 1)) == NULL) {
    890 		free(p);
    891 		gpt_warnx(gpt, "Can't add the primary GPT table");
    892 		return -1;
    893 	}
    894 
    895 	hdr = gpt->gpt->map_data;
    896 	memcpy(hdr->hdr_sig, GPT_HDR_SIG, sizeof(hdr->hdr_sig));
    897 
    898 	/*
    899 	 * XXX struct gpt_hdr is not a multiple of 8 bytes in size and thus
    900 	 * contains padding we must not include in the size.
    901 	 */
    902 	hdr->hdr_revision = htole32(GPT_HDR_REVISION);
    903 	hdr->hdr_size = htole32(GPT_HDR_SIZE);
    904 	hdr->hdr_lba_self = htole64((uint64_t)gpt->gpt->map_start);
    905 	hdr->hdr_lba_alt = htole64((uint64_t)last);
    906 	hdr->hdr_lba_start = htole64((uint64_t)(gpt->tbl->map_start + blocks));
    907 	hdr->hdr_lba_end = htole64((uint64_t)(last - blocks - 1LL));
    908 	if (gpt_uuid_generate(gpt, hdr->hdr_guid) == -1)
    909 		return -1;
    910 	hdr->hdr_lba_table = htole64((uint64_t)(gpt->tbl->map_start));
    911 	hdr->hdr_entries = htole32((uint32_t)(((uint64_t)blocks * gpt->secsz) /
    912 	    sizeof(struct gpt_ent)));
    913 	if (le32toh(hdr->hdr_entries) > parts)
    914 		hdr->hdr_entries = htole32(parts);
    915 	hdr->hdr_entsz = htole32(sizeof(struct gpt_ent));
    916 
    917 	ent = gpt->tbl->map_data;
    918 	for (i = 0; i < le32toh(hdr->hdr_entries); i++) {
    919 		if (gpt_uuid_generate(gpt, ent[i].ent_guid) == -1)
    920 			return -1;
    921 	}
    922 
    923 	/*
    924 	 * Create backup GPT if the user didn't suppress it.
    925 	 */
    926 	if (primary_only)
    927 		return last;
    928 
    929 	if (gpt_add_hdr(gpt, MAP_TYPE_SEC_GPT_HDR, last) == -1)
    930 		return -1;
    931 
    932 	if ((gpt->lbt = map_add(gpt, last - blocks, blocks,
    933 	    MAP_TYPE_SEC_GPT_TBL, gpt->tbl->map_data, 0)) == NULL) {
    934 		gpt_warnx(gpt, "Can't add the secondary GPT table");
    935 		return -1;
    936 	}
    937 
    938 	memcpy(gpt->tpg->map_data, gpt->gpt->map_data, gpt->secsz);
    939 
    940 	hdr = gpt->tpg->map_data;
    941 	hdr->hdr_lba_self = htole64((uint64_t)gpt->tpg->map_start);
    942 	hdr->hdr_lba_alt = htole64((uint64_t)gpt->gpt->map_start);
    943 	hdr->hdr_lba_table = htole64((uint64_t)gpt->lbt->map_start);
    944 	return last;
    945 }
    946 
    947 static int
    948 gpt_size_get(gpt_t gpt, off_t *size)
    949 {
    950 	off_t sectors;
    951 	int64_t human_num;
    952 	char *p;
    953 
    954 	if (*size > 0)
    955 		return -1;
    956 	sectors = strtoll(optarg, &p, 10);
    957 	if (sectors < 1)
    958 		return -1;
    959 	if (*p == '\0' || ((*p == 's' || *p == 'S') && p[1] == '\0')) {
    960 		*size = sectors * gpt->secsz;
    961 		return 0;
    962 	}
    963 	if ((*p == 'b' || *p == 'B') && p[1] == '\0') {
    964 		*size = sectors;
    965 		return 0;
    966 	}
    967 	if (dehumanize_number(optarg, &human_num) < 0)
    968 		return -1;
    969 	*size = human_num;
    970 	return 0;
    971 }
    972 
    973 int
    974 gpt_human_get(gpt_t gpt, off_t *human)
    975 {
    976 	int64_t human_num;
    977 
    978 	if (*human > 0) {
    979 		gpt_warn(gpt, "Already set to %jd new `%s'", (intmax_t)*human,
    980 		    optarg);
    981 		return -1;
    982 	}
    983 	if (dehumanize_number(optarg, &human_num) < 0) {
    984 		gpt_warn(gpt, "Bad number `%s'", optarg);
    985 		return -1;
    986 	}
    987 	*human = human_num;
    988 	if (*human < 1) {
    989 		gpt_warn(gpt, "Number `%s' < 1", optarg);
    990 		return -1;
    991 	}
    992 	return 0;
    993 }
    994 
    995 int
    996 gpt_add_find(gpt_t gpt, struct gpt_find *find, int ch)
    997 {
    998 	switch (ch) {
    999 	case 'a':
   1000 		if (find->all > 0) {
   1001 			gpt_warn(gpt, "-a is already set");
   1002 			return -1;
   1003 		}
   1004 		find->all = 1;
   1005 		break;
   1006 	case 'b':
   1007 		if (gpt_human_get(gpt, &find->block) == -1)
   1008 			return -1;
   1009 		break;
   1010 	case 'i':
   1011 		if (gpt_uint_get(gpt, &find->entry) == -1)
   1012 			return -1;
   1013 		break;
   1014 	case 'L':
   1015 		if (gpt_name_get(gpt, &find->label) == -1)
   1016 			return -1;
   1017 		break;
   1018 	case 's':
   1019 		if (gpt_size_get(gpt, &find->size) == -1)
   1020 			return -1;
   1021 		break;
   1022 	case 't':
   1023 		if (!gpt_uuid_is_nil(find->type))
   1024 			return -1;
   1025 		if (gpt_uuid_parse(optarg, find->type) != 0)
   1026 			return -1;
   1027 		break;
   1028 	default:
   1029 		gpt_warn(gpt, "Unknown find option `%c'", ch);
   1030 		return -1;
   1031 	}
   1032 	return 0;
   1033 }
   1034 
   1035 int
   1036 gpt_change_ent(gpt_t gpt, const struct gpt_find *find,
   1037     void (*cfn)(struct gpt_ent *, void *, int), void *v)
   1038 {
   1039 	map_t m;
   1040 	struct gpt_hdr *hdr;
   1041 	struct gpt_ent *ent;
   1042 	unsigned int i;
   1043 	uint8_t utfbuf[__arraycount(ent->ent_name) * 3 + 1];
   1044 
   1045 	if (!find->all ^
   1046 	    (find->block > 0 || find->entry > 0 || find->label != NULL
   1047 	    || find->size > 0 || !gpt_uuid_is_nil(find->type)))
   1048 		return -1;
   1049 
   1050 	if ((hdr = gpt_hdr(gpt)) == NULL)
   1051 		return -1;
   1052 
   1053 	/* Relabel all matching entries in the map. */
   1054 	for (m = map_first(gpt); m != NULL; m = m->map_next) {
   1055 		if (m->map_type != MAP_TYPE_GPT_PART || m->map_index < 1)
   1056 			continue;
   1057 		if (find->entry > 0 && find->entry != m->map_index)
   1058 			continue;
   1059 		if (find->block > 0 && find->block != m->map_start)
   1060 			continue;
   1061 		if (find->size > 0 && find->size != m->map_size)
   1062 			continue;
   1063 
   1064 		i = m->map_index - 1;
   1065 
   1066 		ent = gpt_ent_primary(gpt, i);
   1067 		if (find->label != NULL) {
   1068 			utf16_to_utf8(ent->ent_name,
   1069 			    __arraycount(ent->ent_name),
   1070 			    utfbuf, __arraycount(utfbuf));
   1071 			if (strcmp((char *)find->label, (char *)utfbuf) != 0)
   1072 				continue;
   1073 		}
   1074 
   1075 		if (!gpt_uuid_is_nil(find->type) &&
   1076 		    !gpt_uuid_equal(find->type, ent->ent_type))
   1077 			continue;
   1078 
   1079 		/* Change the primary entry. */
   1080 		(*cfn)(ent, v, 0);
   1081 
   1082 		if (gpt_write_primary(gpt) == -1)
   1083 			return -1;
   1084 
   1085 		ent = gpt_ent_backup(gpt, i);
   1086 		/* Change the secondary entry. */
   1087 		(*cfn)(ent, v, 1);
   1088 
   1089 		if (gpt_write_backup(gpt) == -1)
   1090 			return -1;
   1091 
   1092 		gpt_msg(gpt, "Partition %d %s", m->map_index, find->msg);
   1093 	}
   1094 	return 0;
   1095 }
   1096 
   1097 int
   1098 gpt_change_hdr(gpt_t gpt, const struct gpt_find *find,
   1099     void (*cfn)(struct gpt_hdr *, void *, int), void *v)
   1100 {
   1101 	struct gpt_hdr *hdr;
   1102 
   1103 	if ((hdr = gpt_hdr(gpt)) == NULL)
   1104 		return -1;
   1105 
   1106 	/* Change the primary header. */
   1107 	(*cfn)(hdr, v, 0);
   1108 
   1109 	if (gpt_write_primary(gpt) == -1)
   1110 		return -1;
   1111 
   1112 	hdr = gpt->tpg->map_data;
   1113 	/* Change the secondary header. */
   1114 	(*cfn)(hdr, v, 1);
   1115 
   1116 	if (gpt_write_backup(gpt) == -1)
   1117 		return -1;
   1118 
   1119 	gpt_msg(gpt, "Header %s", find->msg);
   1120 
   1121 	return 0;
   1122 }
   1123 
   1124 int
   1125 gpt_add_ais(gpt_t gpt, off_t *alignment, u_int *entry, off_t *size, int ch)
   1126 {
   1127 	switch (ch) {
   1128 	case 'a':
   1129 		if (gpt_human_get(gpt, alignment) == -1)
   1130 			return -1;
   1131 		return 0;
   1132 	case 'i':
   1133 		if (gpt_uint_get(gpt, entry) == -1)
   1134 			return -1;
   1135 		return 0;
   1136 	case 's':
   1137 		if (gpt_size_get(gpt, size) == -1)
   1138 			return -1;
   1139 		return 0;
   1140 	default:
   1141 		gpt_warn(gpt, "Unknown alignment/index/size option `%c'", ch);
   1142 		return -1;
   1143 	}
   1144 }
   1145 
   1146 off_t
   1147 gpt_check_ais(gpt_t gpt, off_t alignment, u_int entry, off_t size)
   1148 {
   1149 	if (entry == 0) {
   1150 		gpt_warnx(gpt, "Entry not specified");
   1151 		return -1;
   1152 	}
   1153 	if (alignment % gpt->secsz != 0) {
   1154 		gpt_warnx(gpt, "Alignment (%#jx) must be a multiple of "
   1155 		    "sector size (%#x)", (uintmax_t)alignment, gpt->secsz);
   1156 		return -1;
   1157 	}
   1158 
   1159 	if (size % gpt->secsz != 0) {
   1160 		gpt_warnx(gpt, "Size (%#jx) must be a multiple of "
   1161 		    "sector size (%#x)", (uintmax_t)size, gpt->secsz);
   1162 		return -1;
   1163 	}
   1164 	if (size > 0)
   1165 		return size / gpt->secsz;
   1166 	return 0;
   1167 }
   1168 
   1169 static const struct nvd {
   1170 	const char *name;
   1171 	uint64_t mask;
   1172 	const char *description;
   1173 } gpt_attr[] = {
   1174 	{
   1175 		"biosboot",
   1176 		GPT_ENT_ATTR_LEGACY_BIOS_BOOTABLE,
   1177 		"Legacy BIOS boot partition",
   1178 	},
   1179 	{
   1180 		"bootme",
   1181 		GPT_ENT_ATTR_BOOTME,
   1182 		"Bootable partition",
   1183 	},
   1184 	{
   1185 		"bootfailed",
   1186 		GPT_ENT_ATTR_BOOTFAILED,
   1187 		"Partition that marked bootonce failed to boot",
   1188 	},
   1189 	{
   1190 		"bootonce",
   1191 		GPT_ENT_ATTR_BOOTONCE,
   1192 		"Attempt to boot this partition only once",
   1193 	},
   1194 	{
   1195 		"noblockio",
   1196 		GPT_ENT_ATTR_NO_BLOCK_IO_PROTOCOL,
   1197 		"UEFI won't recognize file system for block I/O",
   1198 	},
   1199 	{
   1200 		"required",
   1201 		GPT_ENT_ATTR_REQUIRED_PARTITION,
   1202 		"Partition required for platform to function",
   1203 	},
   1204 };
   1205 
   1206 int
   1207 gpt_attr_get(gpt_t gpt, uint64_t *attributes)
   1208 {
   1209 	size_t i;
   1210 	int rv = 0;
   1211 	char *ptr;
   1212 
   1213 	*attributes = 0;
   1214 
   1215 	for (ptr = strtok(optarg, ","); ptr; ptr = strtok(NULL, ",")) {
   1216 		for (i = 0; i < __arraycount(gpt_attr); i++)
   1217 			if (strcmp(gpt_attr[i].name, ptr) == 0)
   1218 				break;
   1219 		if (i == __arraycount(gpt_attr)) {
   1220 			gpt_warnx(gpt, "Unrecognized attribute `%s'", ptr);
   1221 			rv = -1;
   1222 		} else
   1223 			*attributes |= gpt_attr[i].mask;
   1224 	}
   1225 	return rv;
   1226 }
   1227 
   1228 void
   1229 gpt_attr_help(const char *prefix)
   1230 {
   1231 	size_t i;
   1232 
   1233 	for (i = 0; i < __arraycount(gpt_attr); i++)
   1234 		printf("%s%10.10s\t%s\n", prefix, gpt_attr[i].name,
   1235 		    gpt_attr[i].description);
   1236 }
   1237 
   1238 const char *
   1239 gpt_attr_list(char *buf, size_t len, uint64_t attributes)
   1240 {
   1241 	size_t i;
   1242 	/*
   1243 	 * a uint64_t (attributes) has at most 16 hex digits
   1244 	 * in its representation, add 2 for "0x", and 2 more
   1245 	 * for surrounding [ ], plus one for a trailing \0,
   1246 	 * and we need 21 bytes, round that up to 24
   1247 	 */
   1248 	char xbuf[24];
   1249 
   1250 	strlcpy(buf, "", len);
   1251 
   1252 	for (i = 0; i < __arraycount(gpt_attr); i++) {
   1253 		/*
   1254 		 * if the attribute is specified in one of bits
   1255 		 * 48..63, it should depend upon the defining
   1256 		 * partition type for that attribute.   Currently
   1257 		 * we have no idea what that is, so...
   1258 		 *
   1259 		 * Also note that for some partition types, these
   1260 		 * fields are not a single bit boolean, but several
   1261 		 * bits to form a numeric value.  That we could handle.
   1262 		 */
   1263 
   1264 		if (attributes & gpt_attr[i].mask) {
   1265 			strlcat(buf, buf[0] ? ", " : "", len);
   1266 			strlcat(buf, gpt_attr[i].name, len);
   1267 #if 0
   1268 	/*
   1269 	 * there are none currently defined, so this is untestable
   1270 	 * (it does build however).
   1271 	 */
   1272 			if (gpt_attr[i].mask & (gpt_attr[i].mask - 1)) {
   1273 				/* This only happens in bits 46..63 */
   1274 
   1275 				/*
   1276 				 * xbuf is big enough for "=65535\0"
   1277 				 * which is the biggest possible value
   1278 				 */
   1279 				snprintf(xbuf, sizeof xbuf, "=%ju",
   1280 				    (uintmax_t) (
   1281 				      (attributes & gpt_attr[i].mask) >>
   1282 				      (ffs((int)(gpt_attr[i].mask >> 48)) + 47)
   1283 				    ));
   1284 
   1285 				strlcat(buf, xbuf, len);
   1286 			}
   1287 #endif
   1288 			attributes &=~ gpt_attr[i].mask;
   1289 		}
   1290 	}
   1291 
   1292 	if (attributes != 0) {
   1293 		snprintf(xbuf, sizeof xbuf, "[%#jx]", (uintmax_t)attributes);
   1294 		strlcat(buf, buf[0] ? ", " : "", len);
   1295 		strlcat(buf, xbuf, len);
   1296 	}
   1297 
   1298 	return buf;
   1299 }
   1300 
   1301 int
   1302 gpt_attr_update(gpt_t gpt, u_int entry, uint64_t set, uint64_t clr)
   1303 {
   1304 	struct gpt_hdr *hdr;
   1305 	struct gpt_ent *ent;
   1306 	unsigned int i;
   1307 
   1308 	if (entry == 0 || (set == 0 && clr == 0)) {
   1309 		gpt_warnx(gpt, "Nothing to set");
   1310 		return -1;
   1311 	}
   1312 
   1313 	if ((hdr = gpt_hdr(gpt)) == NULL)
   1314 		return -1;
   1315 
   1316 	if (entry > le32toh(hdr->hdr_entries)) {
   1317 		gpt_warnx(gpt, "Index %u out of range (%u max)",
   1318 		    entry, le32toh(hdr->hdr_entries));
   1319 		return -1;
   1320 	}
   1321 
   1322 	i = entry - 1;
   1323 	ent = gpt_ent_primary(gpt, i);
   1324 	if (gpt_uuid_is_nil(ent->ent_type)) {
   1325 		gpt_warnx(gpt, "Entry at index %u is unused", entry);
   1326 		return -1;
   1327 	}
   1328 
   1329 	ent->ent_attr &= ~clr;
   1330 	ent->ent_attr |= set;
   1331 
   1332 	if (gpt_write_primary(gpt) == -1)
   1333 		return -1;
   1334 
   1335 	ent = gpt_ent_backup(gpt, i);
   1336 	ent->ent_attr &= ~clr;
   1337 	ent->ent_attr |= set;
   1338 
   1339 	if (gpt_write_backup(gpt) == -1)
   1340 		return -1;
   1341 	gpt_msg(gpt, "Partition %d attributes updated", entry);
   1342 	return 0;
   1343 }
   1344 
   1345 int
   1346 gpt_uint_get(gpt_t gpt, u_int *entry)
   1347 {
   1348 	char *p;
   1349 	if (*entry > 0)
   1350 		return -1;
   1351 	*entry = (u_int)strtoul(optarg, &p, 10);
   1352 	if (*p != 0 || *entry < 1) {
   1353 		gpt_warn(gpt, "Bad number `%s'", optarg);
   1354 		return -1;
   1355 	}
   1356 	return 0;
   1357 }
   1358 int
   1359 gpt_uuid_get(gpt_t gpt, gpt_uuid_t *uuid)
   1360 {
   1361 	if (!gpt_uuid_is_nil(*uuid))
   1362 		return -1;
   1363 	if (gpt_uuid_parse(optarg, *uuid) != 0) {
   1364 		gpt_warnx(gpt, "Can't parse uuid/type `%s'", optarg);
   1365 		return -1;
   1366 	}
   1367 	return 0;
   1368 }
   1369 
   1370 int
   1371 gpt_name_get(gpt_t gpt, void *v)
   1372 {
   1373 	char **name = v;
   1374 	if (*name != NULL)
   1375 		return -1;
   1376 	*name = strdup(optarg);
   1377 	if (*name == NULL) {
   1378 		gpt_warn(gpt, "Can't copy string");
   1379 		return -1;
   1380 	}
   1381 	return 0;
   1382 }
   1383 
   1384 void
   1385 gpt_show_num(const char *prompt, uintmax_t num)
   1386 {
   1387 #ifdef HN_AUTOSCALE
   1388 	char human_num[5];
   1389 	if (humanize_number(human_num, 5, (int64_t)num ,
   1390 	    "", HN_AUTOSCALE, HN_NOSPACE|HN_B) < 0)
   1391 		human_num[0] = '\0';
   1392 #endif
   1393 	printf("%s: %ju", prompt, num);
   1394 #ifdef HN_AUTOSCALE
   1395 	if (human_num[0] != '\0')
   1396 		printf(" (%s)", human_num);
   1397 #endif
   1398 	printf("\n");
   1399 }
   1400 
   1401 int
   1402 gpt_add_hdr(gpt_t gpt, int type, off_t loc)
   1403 {
   1404 	void *p;
   1405 	map_t *t;
   1406 	const char *msg;
   1407 
   1408 	switch (type) {
   1409 	case MAP_TYPE_PRI_GPT_HDR:
   1410 		t = &gpt->gpt;
   1411 		msg = "primary";
   1412 		break;
   1413 	case MAP_TYPE_SEC_GPT_HDR:
   1414 		t = &gpt->tpg;
   1415 		msg = "secondary";
   1416 		break;
   1417 	default:
   1418 		gpt_warnx(gpt, "Unknown GPT header type %d", type);
   1419 		return -1;
   1420 	}
   1421 
   1422 	if ((p = calloc(1, gpt->secsz)) == NULL) {
   1423 		gpt_warn(gpt, "Error allocating %s GPT header", msg);
   1424 		return -1;
   1425 	}
   1426 
   1427 	*t = map_add(gpt, loc, 1LL, type, p, 1);
   1428 	if (*t == NULL) {
   1429 		gpt_warn(gpt, "Error adding %s GPT header", msg);
   1430 		free(p);
   1431 		return -1;
   1432 	}
   1433 	return 0;
   1434 }
   1435