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