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