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