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