Home | History | Annotate | Line # | Download | only in gpt
gpt.c revision 1.64
      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.64 2015/12/06 00:39:26 christos 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 void
    125 utf16_to_utf8(const uint16_t *s16, uint8_t *s8, size_t s8len)
    126 {
    127 	size_t s8idx, s16idx, s16len;
    128 	uint32_t utfchar;
    129 	unsigned int c;
    130 
    131 	s16len = 0;
    132 	while (s16[s16len++] != 0)
    133 		continue;
    134 	s8idx = s16idx = 0;
    135 	while (s16idx < s16len) {
    136 		utfchar = le16toh(s16[s16idx++]);
    137 		if ((utfchar & 0xf800) == 0xd800) {
    138 			c = le16toh(s16[s16idx]);
    139 			if ((utfchar & 0x400) != 0 || (c & 0xfc00) != 0xdc00)
    140 				utfchar = 0xfffd;
    141 			else
    142 				s16idx++;
    143 		}
    144 		if (utfchar < 0x80) {
    145 			if (s8idx + 1 >= s8len)
    146 				break;
    147 			s8[s8idx++] = (uint8_t)utfchar;
    148 		} else if (utfchar < 0x800) {
    149 			if (s8idx + 2 >= s8len)
    150 				break;
    151 			s8[s8idx++] = (uint8_t)(0xc0 | (utfchar >> 6));
    152 			s8[s8idx++] = (uint8_t)(0x80 | (utfchar & 0x3f));
    153 		} else if (utfchar < 0x10000) {
    154 			if (s8idx + 3 >= s8len)
    155 				break;
    156 			s8[s8idx++] = (uint8_t)(0xe0 | (utfchar >> 12));
    157 			s8[s8idx++] = (uint8_t)(0x80 | ((utfchar >> 6) & 0x3f));
    158 			s8[s8idx++] = (uint8_t)(0x80 | (utfchar & 0x3f));
    159 		} else if (utfchar < 0x200000) {
    160 			if (s8idx + 4 >= s8len)
    161 				break;
    162 			s8[s8idx++] = (uint8_t)(0xf0 | (utfchar >> 18));
    163 			s8[s8idx++] = (uint8_t)(0x80 | ((utfchar >> 12) & 0x3f));
    164 			s8[s8idx++] = (uint8_t)(0x80 | ((utfchar >> 6) & 0x3f));
    165 			s8[s8idx++] = (uint8_t)(0x80 | (utfchar & 0x3f));
    166 		}
    167 	}
    168 	s8[s8idx] = 0;
    169 }
    170 
    171 void
    172 utf8_to_utf16(const uint8_t *s8, uint16_t *s16, size_t s16len)
    173 {
    174 	size_t s16idx, s8idx, s8len;
    175 	uint32_t utfchar = 0;
    176 	unsigned int c, utfbytes;
    177 
    178 	s8len = 0;
    179 	while (s8[s8len++] != 0)
    180 		;
    181 	s8idx = s16idx = 0;
    182 	utfbytes = 0;
    183 	do {
    184 		c = s8[s8idx++];
    185 		if ((c & 0xc0) != 0x80) {
    186 			/* Initial characters. */
    187 			if (utfbytes != 0) {
    188 				/* Incomplete encoding. */
    189 				s16[s16idx++] = htole16(0xfffd);
    190 				if (s16idx == s16len) {
    191 					s16[--s16idx] = 0;
    192 					return;
    193 				}
    194 			}
    195 			if ((c & 0xf8) == 0xf0) {
    196 				utfchar = c & 0x07;
    197 				utfbytes = 3;
    198 			} else if ((c & 0xf0) == 0xe0) {
    199 				utfchar = c & 0x0f;
    200 				utfbytes = 2;
    201 			} else if ((c & 0xe0) == 0xc0) {
    202 				utfchar = c & 0x1f;
    203 				utfbytes = 1;
    204 			} else {
    205 				utfchar = c & 0x7f;
    206 				utfbytes = 0;
    207 			}
    208 		} else {
    209 			/* Followup characters. */
    210 			if (utfbytes > 0) {
    211 				utfchar = (utfchar << 6) + (c & 0x3f);
    212 				utfbytes--;
    213 			} else if (utfbytes == 0)
    214 				utfbytes = (u_int)~0;
    215 		}
    216 		if (utfbytes == 0) {
    217 			if (utfchar >= 0x10000 && s16idx + 2 >= s16len)
    218 				utfchar = 0xfffd;
    219 			if (utfchar >= 0x10000) {
    220 				s16[s16idx++] = htole16((uint16_t)
    221 				    (0xd800 | ((utfchar>>10) - 0x40)));
    222 				s16[s16idx++] = htole16((uint16_t)
    223 				    (0xdc00 | (utfchar & 0x3ff)));
    224 			} else
    225 				s16[s16idx++] = htole16((uint16_t)utfchar);
    226 			if (s16idx == s16len) {
    227 				s16[--s16idx] = 0;
    228 				return;
    229 			}
    230 		}
    231 	} while (c != 0);
    232 }
    233 
    234 void *
    235 gpt_read(gpt_t gpt, off_t lba, size_t count)
    236 {
    237 	off_t ofs;
    238 	void *buf;
    239 
    240 	count *= gpt->secsz;
    241 	buf = malloc(count);
    242 	if (buf == NULL)
    243 		return NULL;
    244 
    245 	ofs = lba * gpt->secsz;
    246 	if (lseek(gpt->fd, ofs, SEEK_SET) == ofs &&
    247 	    read(gpt->fd, buf, count) == (ssize_t)count)
    248 		return buf;
    249 
    250 	free(buf);
    251 	return NULL;
    252 }
    253 
    254 int
    255 gpt_write(gpt_t gpt, map_t map)
    256 {
    257 	off_t ofs;
    258 	size_t count;
    259 
    260 	count = (size_t)(map->map_size * gpt->secsz);
    261 	ofs = map->map_start * gpt->secsz;
    262 	if (lseek(gpt->fd, ofs, SEEK_SET) != ofs ||
    263 	    write(gpt->fd, map->map_data, count) != (ssize_t)count)
    264 		return -1;
    265 	gpt->flags |= GPT_MODIFIED;
    266 	return 0;
    267 }
    268 
    269 static int
    270 gpt_mbr(gpt_t gpt, off_t lba)
    271 {
    272 	struct mbr *mbr;
    273 	map_t m, p;
    274 	off_t size, start;
    275 	unsigned int i, pmbr;
    276 
    277 	mbr = gpt_read(gpt, lba, 1);
    278 	if (mbr == NULL) {
    279 		gpt_warn(gpt, "Read failed");
    280 		return -1;
    281 	}
    282 
    283 	if (mbr->mbr_sig != htole16(MBR_SIG)) {
    284 		if (gpt->verbose)
    285 			gpt_msg(gpt,
    286 			    "MBR not found at sector %ju", (uintmax_t)lba);
    287 		free(mbr);
    288 		return 0;
    289 	}
    290 
    291 	/*
    292 	 * Differentiate between a regular MBR and a PMBR. This is more
    293 	 * convenient in general. A PMBR is one with a single partition
    294 	 * of type 0xee.
    295 	 */
    296 	pmbr = 0;
    297 	for (i = 0; i < 4; i++) {
    298 		if (mbr->mbr_part[i].part_typ == MBR_PTYPE_UNUSED)
    299 			continue;
    300 		if (mbr->mbr_part[i].part_typ == MBR_PTYPE_PMBR)
    301 			pmbr++;
    302 		else
    303 			break;
    304 	}
    305 	if (pmbr && i == 4 && lba == 0) {
    306 		if (pmbr != 1)
    307 			gpt_warnx(gpt, "Suspicious PMBR at sector %ju",
    308 			    (uintmax_t)lba);
    309 		else if (gpt->verbose > 1)
    310 			gpt_msg(gpt, "PMBR at sector %ju", (uintmax_t)lba);
    311 		p = map_add(gpt, lba, 1LL, MAP_TYPE_PMBR, mbr, 1);
    312 		goto out;
    313 	}
    314 	if (pmbr)
    315 		gpt_warnx(gpt, "Suspicious MBR at sector %ju", (uintmax_t)lba);
    316 	else if (gpt->verbose > 1)
    317 		gpt_msg(gpt, "MBR at sector %ju", (uintmax_t)lba);
    318 
    319 	p = map_add(gpt, lba, 1LL, MAP_TYPE_MBR, mbr, 1);
    320 	if (p == NULL)
    321 		goto out;
    322 
    323 	for (i = 0; i < 4; i++) {
    324 		if (mbr->mbr_part[i].part_typ == MBR_PTYPE_UNUSED ||
    325 		    mbr->mbr_part[i].part_typ == MBR_PTYPE_PMBR)
    326 			continue;
    327 		start = le16toh(mbr->mbr_part[i].part_start_hi);
    328 		start = (start << 16) + le16toh(mbr->mbr_part[i].part_start_lo);
    329 		size = le16toh(mbr->mbr_part[i].part_size_hi);
    330 		size = (size << 16) + le16toh(mbr->mbr_part[i].part_size_lo);
    331 		if (start == 0 && size == 0) {
    332 			gpt_warnx(gpt, "Malformed MBR at sector %ju",
    333 			    (uintmax_t)lba);
    334 			continue;
    335 		}
    336 		/* start is relative to the offset of the MBR itself. */
    337 		start += lba;
    338 		if (gpt->verbose > 2)
    339 			gpt_msg(gpt, "MBR part: type=%d, start=%ju, size=%ju",
    340 			    mbr->mbr_part[i].part_typ,
    341 			    (uintmax_t)start, (uintmax_t)size);
    342 		if (mbr->mbr_part[i].part_typ != MBR_PTYPE_EXT_LBA) {
    343 			m = map_add(gpt, start, size, MAP_TYPE_MBR_PART, p, 0);
    344 			if (m == NULL)
    345 				return -1;
    346 			m->map_index = i + 1;
    347 		} else {
    348 			if (gpt_mbr(gpt, start) == -1)
    349 				return -1;
    350 		}
    351 	}
    352 	return 0;
    353 out:
    354 	if (p == NULL) {
    355 		free(mbr);
    356 		return -1;
    357 	}
    358 	return 0;
    359 }
    360 
    361 int
    362 gpt_gpt(gpt_t gpt, off_t lba, int found)
    363 {
    364 	off_t size;
    365 	struct gpt_ent *ent;
    366 	struct gpt_hdr *hdr;
    367 	char *p;
    368 	map_t m;
    369 	size_t blocks, tblsz;
    370 	unsigned int i;
    371 	uint32_t crc;
    372 
    373 	hdr = gpt_read(gpt, lba, 1);
    374 	if (hdr == NULL)
    375 		return -1;
    376 
    377 	if (memcmp(hdr->hdr_sig, GPT_HDR_SIG, sizeof(hdr->hdr_sig)))
    378 		goto fail_hdr;
    379 
    380 	crc = le32toh(hdr->hdr_crc_self);
    381 	hdr->hdr_crc_self = 0;
    382 	if (crc32(hdr, le32toh(hdr->hdr_size)) != crc) {
    383 		if (gpt->verbose)
    384 			gpt_msg(gpt, "Bad CRC in GPT header at sector %ju",
    385 			    (uintmax_t)lba);
    386 		goto fail_hdr;
    387 	}
    388 
    389 	tblsz = le32toh(hdr->hdr_entries) * le32toh(hdr->hdr_entsz);
    390 	blocks = tblsz / gpt->secsz + ((tblsz % gpt->secsz) ? 1 : 0);
    391 
    392 	/* Use generic pointer to deal with hdr->hdr_entsz != sizeof(*ent). */
    393 	p = gpt_read(gpt, (off_t)le64toh((uint64_t)hdr->hdr_lba_table), blocks);
    394 	if (p == NULL) {
    395 		if (found) {
    396 			if (gpt->verbose)
    397 				gpt_msg(gpt,
    398 				    "Cannot read LBA table at sector %ju",
    399 				    (uintmax_t)le64toh(hdr->hdr_lba_table));
    400 			return -1;
    401 		}
    402 		goto fail_hdr;
    403 	}
    404 
    405 	if (crc32(p, tblsz) != le32toh(hdr->hdr_crc_table)) {
    406 		if (gpt->verbose)
    407 			gpt_msg(gpt, "Bad CRC in GPT table at sector %ju",
    408 			    (uintmax_t)le64toh(hdr->hdr_lba_table));
    409 		goto fail_ent;
    410 	}
    411 
    412 	if (gpt->verbose > 1)
    413 		gpt_msg(gpt, "%s GPT at sector %ju",
    414 		    (lba == 1) ? "Pri" : "Sec", (uintmax_t)lba);
    415 
    416 	m = map_add(gpt, lba, 1, (lba == 1)
    417 	    ? MAP_TYPE_PRI_GPT_HDR : MAP_TYPE_SEC_GPT_HDR, hdr, 1);
    418 	if (m == NULL)
    419 		return (-1);
    420 
    421 	m = map_add(gpt, (off_t)le64toh((uint64_t)hdr->hdr_lba_table),
    422 	    (off_t)blocks,
    423 	    lba == 1 ? MAP_TYPE_PRI_GPT_TBL : MAP_TYPE_SEC_GPT_TBL, p, 1);
    424 	if (m == NULL)
    425 		return (-1);
    426 
    427 	if (lba != 1)
    428 		return (1);
    429 
    430 	for (i = 0; i < le32toh(hdr->hdr_entries); i++) {
    431 		ent = (void*)(p + i * le32toh(hdr->hdr_entsz));
    432 		if (gpt_uuid_is_nil(ent->ent_type))
    433 			continue;
    434 
    435 		size = (off_t)(le64toh((uint64_t)ent->ent_lba_end) -
    436 		    le64toh((uint64_t)ent->ent_lba_start) + 1LL);
    437 		if (gpt->verbose > 2) {
    438 			char buf[128];
    439 			gpt_uuid_snprintf(buf, sizeof(buf), "%s",
    440 			    ent->ent_type);
    441 			gpt_msg(gpt, "GPT partition: type=%s, start=%ju, "
    442 			    "size=%ju", buf,
    443 			    (uintmax_t)le64toh(ent->ent_lba_start),
    444 			    (uintmax_t)size);
    445 		}
    446 		m = map_add(gpt, (off_t)le64toh((uint64_t)ent->ent_lba_start),
    447 		    size, MAP_TYPE_GPT_PART, ent, 0);
    448 		if (m == NULL)
    449 			return (-1);
    450 		m->map_index = i + 1;
    451 	}
    452 	return (1);
    453 
    454  fail_ent:
    455 	free(p);
    456 
    457  fail_hdr:
    458 	free(hdr);
    459 	return (0);
    460 }
    461 
    462 gpt_t
    463 gpt_open(const char *dev, int flags, int verbose, off_t mediasz, u_int secsz)
    464 {
    465 	int mode, found;
    466 	off_t devsz;
    467 	gpt_t gpt;
    468 
    469 
    470 	if ((gpt = calloc(1, sizeof(*gpt))) == NULL) {
    471 		if (!(flags & GPT_QUIET))
    472 			warn("Cannot allocate `%s'", dev);
    473 		return NULL;
    474 	}
    475 	gpt->flags = flags;
    476 	gpt->verbose = verbose;
    477 	gpt->mediasz = mediasz;
    478 	gpt->secsz = secsz;
    479 
    480 	mode = (gpt->flags & GPT_READONLY) ? O_RDONLY : O_RDWR|O_EXCL;
    481 
    482 	gpt->fd = opendisk(dev, mode, gpt->device_name,
    483 	    sizeof(gpt->device_name), 0);
    484 	if (gpt->fd == -1) {
    485 		strlcpy(gpt->device_name, dev, sizeof(gpt->device_name));
    486 		gpt_warn(gpt, "Cannot open");
    487 		goto close;
    488 	}
    489 
    490 	if (fstat(gpt->fd, &gpt->sb) == -1) {
    491 		gpt_warn(gpt, "Cannot stat");
    492 		goto close;
    493 	}
    494 
    495 	if ((gpt->sb.st_mode & S_IFMT) != S_IFREG) {
    496 		if (gpt->secsz == 0) {
    497 #ifdef DIOCGSECTORSIZE
    498 			if (ioctl(gpt->fd, DIOCGSECTORSIZE, &gpt->secsz) == -1) {
    499 				gpt_warn(gpt, "Cannot get sector size");
    500 				goto close;
    501 			}
    502 #endif
    503 			if (gpt->secsz == 0) {
    504 				gpt_warnx(gpt, "Sector size can't be 0");
    505 				goto close;
    506 			}
    507 		}
    508 		if (gpt->mediasz == 0) {
    509 #ifdef DIOCGMEDIASIZE
    510 			if (ioctl(gpt->fd, DIOCGMEDIASIZE, &gpt->mediasz) == -1) {
    511 				gpt_warn(gpt, "Cannot get media size");
    512 				goto close;
    513 			}
    514 #endif
    515 			if (gpt->mediasz == 0) {
    516 				gpt_warnx(gpt, "Media size can't be 0");
    517 				goto close;
    518 			}
    519 		}
    520 	} else {
    521 		gpt->flags |= GPT_FILE;
    522 		if (gpt->secsz == 0)
    523 			gpt->secsz = 512;	/* Fixed size for files. */
    524 		if (gpt->mediasz == 0) {
    525 			if (gpt->sb.st_size % gpt->secsz) {
    526 				errno = EINVAL;
    527 				goto close;
    528 			}
    529 			gpt->mediasz = gpt->sb.st_size;
    530 		}
    531 		gpt->flags |= GPT_NOSYNC;
    532 	}
    533 
    534 	/*
    535 	 * We require an absolute minimum of 6 sectors. One for the MBR,
    536 	 * 2 for the GPT header, 2 for the GPT table and one to hold some
    537 	 * user data. Let's catch this extreme border case here so that
    538 	 * we don't have to worry about it later.
    539 	 */
    540 	devsz = gpt->mediasz / gpt->secsz;
    541 	if (devsz < 6) {
    542 		gpt_warnx(gpt, "Need 6 sectors, we have %ju",
    543 		    (uintmax_t)devsz);
    544 		goto close;
    545 	}
    546 
    547 	if (gpt->verbose) {
    548 		gpt_msg(gpt, "mediasize=%ju; sectorsize=%u; blocks=%ju",
    549 		    (uintmax_t)gpt->mediasz, gpt->secsz, (uintmax_t)devsz);
    550 	}
    551 
    552 	if (map_init(gpt, devsz) == -1)
    553 		goto close;
    554 
    555 	if (gpt_mbr(gpt, 0LL) == -1)
    556 		goto close;
    557 	if ((found = gpt_gpt(gpt, 1LL, 1)) == -1)
    558 		goto close;
    559 	if (gpt_gpt(gpt, devsz - 1LL, found) == -1)
    560 		goto close;
    561 
    562 	return gpt;
    563 
    564  close:
    565 	if (gpt->fd != -1)
    566 		close(gpt->fd);
    567 	free(gpt);
    568 	return NULL;
    569 }
    570 
    571 void
    572 gpt_close(gpt_t gpt)
    573 {
    574 
    575 	if (!(gpt->flags & GPT_MODIFIED))
    576 		goto out;
    577 
    578 	if (!(gpt->flags & GPT_NOSYNC)) {
    579 #ifdef DIOCMWEDGES
    580 		int bits;
    581 		if (ioctl(gpt->fd, DIOCMWEDGES, &bits) == -1)
    582 			gpt_warn(gpt, "Can't update wedge information");
    583 		else
    584 			goto out;
    585 #endif
    586 	}
    587 	if (!(gpt->flags & GPT_FILE))
    588 		gpt_msg(gpt, "You need to run \"dkctl %s makewedges\""
    589 		    " for the changes to take effect\n", gpt->device_name);
    590 
    591 out:
    592 	close(gpt->fd);
    593 }
    594 
    595 void
    596 gpt_warnx(gpt_t gpt, const char *fmt, ...)
    597 {
    598 	va_list ap;
    599 
    600 	if (gpt->flags & GPT_QUIET)
    601 		return;
    602 	fprintf(stderr, "%s: %s: ", getprogname(), gpt->device_name);
    603 	va_start(ap, fmt);
    604 	vfprintf(stderr, fmt, ap);
    605 	va_end(ap);
    606 	fprintf(stderr, "\n");
    607 }
    608 
    609 void
    610 gpt_warn(gpt_t gpt, const char *fmt, ...)
    611 {
    612 	va_list ap;
    613 	int e = errno;
    614 
    615 	if (gpt->flags & GPT_QUIET)
    616 		return;
    617 	fprintf(stderr, "%s: %s: ", getprogname(), gpt->device_name);
    618 	va_start(ap, fmt);
    619 	vfprintf(stderr, fmt, ap);
    620 	va_end(ap);
    621 	fprintf(stderr, " (%s)\n", strerror(e));
    622 	errno = e;
    623 }
    624 
    625 void
    626 gpt_msg(gpt_t gpt, const char *fmt, ...)
    627 {
    628 	va_list ap;
    629 
    630 	if (gpt->flags & GPT_QUIET)
    631 		return;
    632 	printf("%s: ", gpt->device_name);
    633 	va_start(ap, fmt);
    634 	vprintf(fmt, ap);
    635 	va_end(ap);
    636 	printf("\n");
    637 }
    638 
    639 struct gpt_hdr *
    640 gpt_hdr(gpt_t gpt)
    641 {
    642 	gpt->gpt = map_find(gpt, MAP_TYPE_PRI_GPT_HDR);
    643 	if (gpt->gpt == NULL) {
    644 		gpt_warnx(gpt, "No primary GPT header; run create or recover");
    645 		return NULL;
    646 	}
    647 
    648 	gpt->tpg = map_find(gpt, MAP_TYPE_SEC_GPT_HDR);
    649 	if (gpt->tpg == NULL) {
    650 		gpt_warnx(gpt, "No secondary GPT header; run recover");
    651 		return NULL;
    652 	}
    653 
    654 	gpt->tbl = map_find(gpt, MAP_TYPE_PRI_GPT_TBL);
    655 	gpt->lbt = map_find(gpt, MAP_TYPE_SEC_GPT_TBL);
    656 	if (gpt->tbl == NULL || gpt->lbt == NULL) {
    657 		gpt_warnx(gpt, "Corrupt maps, run recover");
    658 		return NULL;
    659 	}
    660 
    661 	return gpt->gpt->map_data;
    662 }
    663 
    664 int
    665 gpt_write_crc(gpt_t gpt, map_t map, map_t tbl)
    666 {
    667 	struct gpt_hdr *hdr = map->map_data;
    668 
    669 	hdr->hdr_crc_table = htole32(crc32(tbl->map_data,
    670 	    le32toh(hdr->hdr_entries) * le32toh(hdr->hdr_entsz)));
    671 	hdr->hdr_crc_self = 0;
    672 	hdr->hdr_crc_self = htole32(crc32(hdr, le32toh(hdr->hdr_size)));
    673 
    674 	if (gpt_write(gpt, map) == -1) {
    675 		gpt_warn(gpt, "Error writing crc map");
    676 		return -1;
    677 	}
    678 
    679 	if (gpt_write(gpt, tbl) == -1) {
    680 		gpt_warn(gpt, "Error writing crc table");
    681 		return -1;
    682 	}
    683 
    684 	return 0;
    685 }
    686 
    687 int
    688 gpt_write_primary(gpt_t gpt)
    689 {
    690 	return gpt_write_crc(gpt, gpt->gpt, gpt->tbl);
    691 }
    692 
    693 
    694 int
    695 gpt_write_backup(gpt_t gpt)
    696 {
    697 	return gpt_write_crc(gpt, gpt->tpg, gpt->lbt);
    698 }
    699 
    700 void
    701 gpt_create_pmbr_part(struct mbr_part *part, off_t last)
    702 {
    703 	part->part_shd = 0x00;
    704 	part->part_ssect = 0x02;
    705 	part->part_scyl = 0x00;
    706 	part->part_typ = MBR_PTYPE_PMBR;
    707 	part->part_ehd = 0xfe;
    708 	part->part_esect = 0xff;
    709 	part->part_ecyl = 0xff;
    710 	part->part_start_lo = htole16(1);
    711 	if (last > 0xffffffff) {
    712 		part->part_size_lo = htole16(0xffff);
    713 		part->part_size_hi = htole16(0xffff);
    714 	} else {
    715 		part->part_size_lo = htole16((uint16_t)last);
    716 		part->part_size_hi = htole16((uint16_t)(last >> 16));
    717 	}
    718 }
    719 
    720 struct gpt_ent *
    721 gpt_ent(map_t map, map_t tbl, unsigned int i)
    722 {
    723 	struct gpt_hdr *hdr = map->map_data;
    724 	return (void *)((char *)tbl->map_data + i * le32toh(hdr->hdr_entsz));
    725 }
    726 
    727 struct gpt_ent *
    728 gpt_ent_primary(gpt_t gpt, unsigned int i)
    729 {
    730 	return gpt_ent(gpt->gpt, gpt->tbl, i);
    731 }
    732 
    733 struct gpt_ent *
    734 gpt_ent_backup(gpt_t gpt, unsigned int i)
    735 {
    736 	return gpt_ent(gpt->tpg, gpt->lbt, i);
    737 }
    738 
    739 int
    740 gpt_usage(const char *prefix, const struct gpt_cmd *cmd)
    741 {
    742 	const char **a = cmd->help;
    743 	size_t hlen = cmd->hlen;
    744 	size_t i;
    745 
    746 	if (prefix == NULL) {
    747 		const char *pname = getprogname();
    748 		const char *d1, *d2, *d = " <device>";
    749 		int len = (int)strlen(pname);
    750 		if (strcmp(pname, "gpt") == 0) {
    751 			d1 = "";
    752 			d2 = d;
    753 		} else {
    754 			d2 = "";
    755 			d1 = d;
    756 		}
    757 		fprintf(stderr, "Usage: %s%s %s %s%s\n", pname,
    758 		    d1, cmd->name, a[0], d2);
    759 		for (i = 1; i < hlen; i++) {
    760 			fprintf(stderr,
    761 			    "       %*s%s %s %s%s\n", len, "",
    762 			    d1, cmd->name, a[i], d2);
    763 		}
    764 	} else {
    765 		for (i = 0; i < hlen; i++)
    766 		    fprintf(stderr, "%s%s %s\n", prefix, cmd->name, a[i]);
    767 	}
    768 	return -1;
    769 }
    770 
    771 off_t
    772 gpt_last(gpt_t gpt)
    773 {
    774 	return gpt->mediasz / gpt->secsz - 1LL;
    775 }
    776 
    777 off_t
    778 gpt_create(gpt_t gpt, off_t last, u_int parts, int primary_only)
    779 {
    780 	off_t blocks;
    781 	map_t map;
    782 	struct gpt_hdr *hdr;
    783 	struct gpt_ent *ent;
    784 	unsigned int i;
    785 	void *p;
    786 
    787 	if (map_find(gpt, MAP_TYPE_PRI_GPT_HDR) != NULL ||
    788 	    map_find(gpt, MAP_TYPE_SEC_GPT_HDR) != NULL) {
    789 		gpt_warnx(gpt, "Device already contains a GPT");
    790 		return -1;
    791 	}
    792 
    793 	/* Get the amount of free space after the MBR */
    794 	blocks = map_free(gpt, 1LL, 0LL);
    795 	if (blocks == 0LL) {
    796 		gpt_warnx(gpt, "No room for the GPT header");
    797 		return -1;
    798 	}
    799 
    800 	/* Don't create more than parts entries. */
    801 	if ((uint64_t)(blocks - 1) * gpt->secsz >
    802 	    parts * sizeof(struct gpt_ent)) {
    803 		blocks = (off_t)((parts * sizeof(struct gpt_ent)) / gpt->secsz);
    804 		if ((parts * sizeof(struct gpt_ent)) % gpt->secsz)
    805 			blocks++;
    806 		blocks++;		/* Don't forget the header itself */
    807 	}
    808 
    809 	/* Never cross the median of the device. */
    810 	if ((blocks + 1LL) > ((last + 1LL) >> 1))
    811 		blocks = ((last + 1LL) >> 1) - 1LL;
    812 
    813 	/*
    814 	 * Get the amount of free space at the end of the device and
    815 	 * calculate the size for the GPT structures.
    816 	 */
    817 	map = map_last(gpt);
    818 	if (map->map_type != MAP_TYPE_UNUSED) {
    819 		gpt_warnx(gpt, "No room for the backup header");
    820 		return -1;
    821 	}
    822 
    823 	if (map->map_size < blocks)
    824 		blocks = map->map_size;
    825 	if (blocks == 1LL) {
    826 		gpt_warnx(gpt, "No room for the GPT table");
    827 		return -1;
    828 	}
    829 
    830 	blocks--;		/* Number of blocks in the GPT table. */
    831 
    832 	if (gpt_add_hdr(gpt, MAP_TYPE_PRI_GPT_HDR, 1) == -1)
    833 		return -1;
    834 
    835 	if ((p = calloc((size_t)blocks, gpt->secsz)) == NULL) {
    836 		gpt_warnx(gpt, "Can't allocate the primary GPT table");
    837 		return -1;
    838 	}
    839 	if ((gpt->tbl = map_add(gpt, 2LL, blocks,
    840 	    MAP_TYPE_PRI_GPT_TBL, p, 1)) == NULL) {
    841 		free(p);
    842 		gpt_warnx(gpt, "Can't add the primary GPT table");
    843 		return -1;
    844 	}
    845 
    846 	hdr = gpt->gpt->map_data;
    847 	memcpy(hdr->hdr_sig, GPT_HDR_SIG, sizeof(hdr->hdr_sig));
    848 
    849 	/*
    850 	 * XXX struct gpt_hdr is not a multiple of 8 bytes in size and thus
    851 	 * contains padding we must not include in the size.
    852 	 */
    853 	hdr->hdr_revision = htole32(GPT_HDR_REVISION);
    854 	hdr->hdr_size = htole32(GPT_HDR_SIZE);
    855 	hdr->hdr_lba_self = htole64((uint64_t)gpt->gpt->map_start);
    856 	hdr->hdr_lba_alt = htole64((uint64_t)last);
    857 	hdr->hdr_lba_start = htole64((uint64_t)(gpt->tbl->map_start + blocks));
    858 	hdr->hdr_lba_end = htole64((uint64_t)(last - blocks - 1LL));
    859 	if (gpt_uuid_generate(gpt, hdr->hdr_guid) == -1)
    860 		return -1;
    861 	hdr->hdr_lba_table = htole64((uint64_t)(gpt->tbl->map_start));
    862 	hdr->hdr_entries = htole32((uint32_t)(((uint64_t)blocks * gpt->secsz) /
    863 	    sizeof(struct gpt_ent)));
    864 	if (le32toh(hdr->hdr_entries) > parts)
    865 		hdr->hdr_entries = htole32(parts);
    866 	hdr->hdr_entsz = htole32(sizeof(struct gpt_ent));
    867 
    868 	ent = gpt->tbl->map_data;
    869 	for (i = 0; i < le32toh(hdr->hdr_entries); i++) {
    870 		if (gpt_uuid_generate(gpt, ent[i].ent_guid) == -1)
    871 			return -1;
    872 	}
    873 
    874 	/*
    875 	 * Create backup GPT if the user didn't suppress it.
    876 	 */
    877 	if (primary_only)
    878 		return last;
    879 
    880 	if (gpt_add_hdr(gpt, MAP_TYPE_SEC_GPT_HDR, last) == -1)
    881 		return -1;
    882 
    883 	if ((gpt->lbt = map_add(gpt, last - blocks, blocks,
    884 	    MAP_TYPE_SEC_GPT_TBL, gpt->tbl->map_data, 0)) == NULL) {
    885 		gpt_warnx(gpt, "Can't add the secondary GPT table");
    886 		return -1;
    887 	}
    888 
    889 	memcpy(gpt->tpg->map_data, gpt->gpt->map_data, gpt->secsz);
    890 
    891 	hdr = gpt->tpg->map_data;
    892 	hdr->hdr_lba_self = htole64((uint64_t)gpt->tpg->map_start);
    893 	hdr->hdr_lba_alt = htole64((uint64_t)gpt->gpt->map_start);
    894 	hdr->hdr_lba_table = htole64((uint64_t)gpt->lbt->map_start);
    895 	return last;
    896 }
    897 
    898 static int
    899 gpt_size_get(gpt_t gpt, off_t *size)
    900 {
    901 	off_t sectors;
    902 	int64_t human_num;
    903 	char *p;
    904 
    905 	if (*size > 0)
    906 		return -1;
    907 	sectors = strtoll(optarg, &p, 10);
    908 	if (sectors < 1)
    909 		return -1;
    910 	if (*p == '\0' || ((*p == 's' || *p == 'S') && p[1] == '\0')) {
    911 		*size = sectors * gpt->secsz;
    912 		return 0;
    913 	}
    914 	if ((*p == 'b' || *p == 'B') && p[1] == '\0') {
    915 		*size = sectors;
    916 		return 0;
    917 	}
    918 	if (dehumanize_number(optarg, &human_num) < 0)
    919 		return -1;
    920 	*size = human_num;
    921 	return 0;
    922 }
    923 
    924 int
    925 gpt_human_get(off_t *human)
    926 {
    927 	int64_t human_num;
    928 
    929 	if (*human > 0)
    930 		return -1;
    931 	if (dehumanize_number(optarg, &human_num) < 0)
    932 		return -1;
    933 	*human = human_num;
    934 	if (*human < 1)
    935 		return -1;
    936 	return 0;
    937 }
    938 
    939 int
    940 gpt_add_find(gpt_t gpt, struct gpt_find *find, int ch)
    941 {
    942 	switch (ch) {
    943 	case 'a':
    944 		if (find->all > 0)
    945 			return -1;
    946 		find->all = 1;
    947 		break;
    948 	case 'b':
    949 		if (gpt_human_get(&find->block) == -1)
    950 			return -1;
    951 		break;
    952 	case 'i':
    953 		if (gpt_uint_get(&find->entry) == -1)
    954 			return -1;
    955 		break;
    956 	case 'L':
    957 		if (gpt_name_get(gpt, &find->label) == -1)
    958 			return -1;
    959 		break;
    960 	case 's':
    961 		if (gpt_size_get(gpt, &find->size) == -1)
    962 			return -1;
    963 		break;
    964 	case 't':
    965 		if (!gpt_uuid_is_nil(find->type))
    966 			return -1;
    967 		if (gpt_uuid_parse(optarg, find->type) != 0)
    968 			return -1;
    969 		break;
    970 	default:
    971 		return -1;
    972 	}
    973 	return 0;
    974 }
    975 
    976 int
    977 gpt_change_ent(gpt_t gpt, const struct gpt_find *find,
    978     void (*cfn)(struct gpt_ent *, void *), void *v)
    979 {
    980 	map_t m;
    981 	struct gpt_hdr *hdr;
    982 	struct gpt_ent *ent;
    983 	unsigned int i;
    984 	uint8_t utfbuf[__arraycount(ent->ent_name) * 3 + 1];
    985 
    986 	if (!find->all ^
    987 	    (find->block > 0 || find->entry > 0 || find->label != NULL
    988 	    || find->size > 0 || !gpt_uuid_is_nil(find->type)))
    989 		return -1;
    990 
    991 	if ((hdr = gpt_hdr(gpt)) == NULL)
    992 		return -1;
    993 
    994 	/* Relabel all matching entries in the map. */
    995 	for (m = map_first(gpt); m != NULL; m = m->map_next) {
    996 		if (m->map_type != MAP_TYPE_GPT_PART || m->map_index < 1)
    997 			continue;
    998 		if (find->entry > 0 && find->entry != m->map_index)
    999 			continue;
   1000 		if (find->block > 0 && find->block != m->map_start)
   1001 			continue;
   1002 		if (find->size > 0 && find->size != m->map_size)
   1003 			continue;
   1004 
   1005 		i = m->map_index - 1;
   1006 
   1007 		ent = gpt_ent_primary(gpt, i);
   1008 		if (find->label != NULL) {
   1009 			utf16_to_utf8(ent->ent_name, utfbuf, sizeof(utfbuf));
   1010 			if (strcmp((char *)find->label, (char *)utfbuf) == 0)
   1011 				continue;
   1012 		}
   1013 
   1014 		if (!gpt_uuid_is_nil(find->type) &&
   1015 		    !gpt_uuid_equal(find->type, ent->ent_type))
   1016 			continue;
   1017 
   1018 		/* Change the primary entry. */
   1019 		(*cfn)(ent, v);
   1020 
   1021 		if (gpt_write_primary(gpt) == -1)
   1022 			return -1;
   1023 
   1024 		ent = gpt_ent_backup(gpt, i);
   1025 		/* Change the secondary entry. */
   1026 		(*cfn)(ent, v);
   1027 
   1028 		if (gpt_write_backup(gpt) == -1)
   1029 			return -1;
   1030 
   1031 		gpt_msg(gpt, "Partition %d %s", m->map_index, find->msg);
   1032 	}
   1033 	return 0;
   1034 }
   1035 
   1036 int
   1037 gpt_add_ais(gpt_t gpt, off_t *alignment, u_int *entry, off_t *size, int ch)
   1038 {
   1039 	switch (ch) {
   1040 	case 'a':
   1041 		if (gpt_human_get(alignment) == -1)
   1042 			return -1;
   1043 		return 0;
   1044 	case 'i':
   1045 		if (gpt_uint_get(entry) == -1)
   1046 			return -1;
   1047 		return 0;
   1048 	case 's':
   1049 		if (gpt_size_get(gpt, size) == -1)
   1050 			return -1;
   1051 		return 0;
   1052 	default:
   1053 		return -1;
   1054 	}
   1055 }
   1056 
   1057 off_t
   1058 gpt_check_ais(gpt_t gpt, off_t alignment, u_int entry, off_t size)
   1059 {
   1060 	if (entry == 0) {
   1061 		gpt_warnx(gpt, "Entry not specified");
   1062 		return -1;
   1063 	}
   1064 	if (alignment % gpt->secsz != 0) {
   1065 		gpt_warnx(gpt, "Alignment (%#jx) must be a multiple of "
   1066 		    "sector size (%#x)", (uintmax_t)alignment, gpt->secsz);
   1067 		return -1;
   1068 	}
   1069 
   1070 	if (size % gpt->secsz != 0) {
   1071 		gpt_warnx(gpt, "Size (%#jx) must be a multiple of "
   1072 		    "sector size (%#x)", (uintmax_t)size, gpt->secsz);
   1073 		return -1;
   1074 	}
   1075 	if (size > 0)
   1076 		return size / gpt->secsz;
   1077 	return 0;
   1078 }
   1079 
   1080 static const struct nvd {
   1081 	const char *name;
   1082 	uint64_t mask;
   1083 	const char *description;
   1084 } gpt_attr[] = {
   1085 	{
   1086 		"biosboot",
   1087 		GPT_ENT_ATTR_LEGACY_BIOS_BOOTABLE,
   1088 		"Legacy BIOS boot partition",
   1089 	},
   1090 	{
   1091 		"bootme",
   1092 		GPT_ENT_ATTR_BOOTME,
   1093 		"Bootable partition",
   1094 	},
   1095 	{
   1096 		"bootfailed",
   1097 		GPT_ENT_ATTR_BOOTFAILED,
   1098 		"Partition that marked bootonce failed to boot",
   1099 	},
   1100 	{
   1101 		"bootonce",
   1102 		GPT_ENT_ATTR_BOOTONCE,
   1103 		"Attempt to boot this partition only once",
   1104 	},
   1105 	{
   1106 		"noblockio",
   1107 		GPT_ENT_ATTR_NO_BLOCK_IO_PROTOCOL,
   1108 		"UEFI won't recognize file system for block I/O",
   1109 	},
   1110 	{
   1111 		"required",
   1112 		GPT_ENT_ATTR_REQUIRED_PARTITION,
   1113 		"Partition required for platform to function",
   1114 	},
   1115 };
   1116 
   1117 int
   1118 gpt_attr_get(gpt_t gpt, uint64_t *attributes)
   1119 {
   1120 	size_t i;
   1121 	int rv = 0;
   1122 	char *ptr;
   1123 
   1124 	*attributes = 0;
   1125 
   1126 	for (ptr = strtok(optarg, ","); ptr; ptr = strtok(NULL, ",")) {
   1127 		for (i = 0; i < __arraycount(gpt_attr); i++)
   1128 			if (strcmp(gpt_attr[i].name, ptr) == 0)
   1129 				break;
   1130 		if (i == __arraycount(gpt_attr)) {
   1131 			gpt_warnx(gpt, "Unregognized attribute `%s'", ptr);
   1132 			rv = -1;
   1133 		} else
   1134 			*attributes |= gpt_attr[i].mask;
   1135 	}
   1136 	return rv;
   1137 }
   1138 
   1139 void
   1140 gpt_attr_help(const char *prefix)
   1141 {
   1142 	size_t i;
   1143 
   1144 	for (i = 0; i < __arraycount(gpt_attr); i++)
   1145 		printf("%s%10.10s\t%s\n", prefix, gpt_attr[i].name,
   1146 		    gpt_attr[i].description);
   1147 }
   1148 
   1149 const char *
   1150 gpt_attr_list(char *buf, size_t len, uint64_t attributes)
   1151 {
   1152 	size_t i;
   1153 	strlcpy(buf, "", len);
   1154 
   1155 	for (i = 0; i < __arraycount(gpt_attr); i++)
   1156 		if (attributes & gpt_attr[i].mask) {
   1157 			strlcat(buf, buf[0] ? "," : "", len);
   1158 			strlcat(buf, gpt_attr[i].name, len);
   1159 		}
   1160 	return buf;
   1161 }
   1162 
   1163 int
   1164 gpt_attr_update(gpt_t gpt, u_int entry, uint64_t set, uint64_t clr)
   1165 {
   1166 	struct gpt_hdr *hdr;
   1167 	struct gpt_ent *ent;
   1168 	unsigned int i;
   1169 
   1170 	if (entry == 0 || (set == 0 && clr == 0))
   1171 		return -1;
   1172 
   1173 	if ((hdr = gpt_hdr(gpt)) == NULL)
   1174 		return -1;
   1175 
   1176 	if (entry > le32toh(hdr->hdr_entries)) {
   1177 		gpt_warnx(gpt, "Index %u out of range (%u max)",
   1178 		    entry, le32toh(hdr->hdr_entries));
   1179 		return -1;
   1180 	}
   1181 
   1182 	i = entry - 1;
   1183 	ent = gpt_ent_primary(gpt, i);
   1184 	if (gpt_uuid_is_nil(ent->ent_type)) {
   1185 		gpt_warnx(gpt, "Entry at index %u is unused", entry);
   1186 		return -1;
   1187 	}
   1188 
   1189 	ent->ent_attr &= ~clr;
   1190 	ent->ent_attr |= set;
   1191 
   1192 	if (gpt_write_primary(gpt) == -1)
   1193 		return -1;
   1194 
   1195 	ent = gpt_ent_backup(gpt, i);
   1196 	ent->ent_attr &= ~clr;
   1197 	ent->ent_attr |= set;
   1198 
   1199 	if (gpt_write_backup(gpt) == -1)
   1200 		return -1;
   1201 	gpt_msg(gpt, "Partition %d attributes updated", entry);
   1202 	return 0;
   1203 }
   1204 
   1205 int
   1206 gpt_uint_get(u_int *entry)
   1207 {
   1208 	char *p;
   1209 	if (*entry > 0)
   1210 		return -1;
   1211 	*entry = (u_int)strtoul(optarg, &p, 10);
   1212 	if (*p != 0 || *entry < 1)
   1213 		return -1;
   1214 	return 0;
   1215 }
   1216 int
   1217 gpt_uuid_get(gpt_t gpt, gpt_uuid_t *uuid)
   1218 {
   1219 	if (!gpt_uuid_is_nil(*uuid))
   1220 		return -1;
   1221 	if (gpt_uuid_parse(optarg, *uuid) != 0) {
   1222 		gpt_warn(gpt, "Can't parse uuid");
   1223 		return -1;
   1224 	}
   1225 	return 0;
   1226 }
   1227 
   1228 int
   1229 gpt_name_get(gpt_t gpt, void *v)
   1230 {
   1231 	char **name = v;
   1232 	if (*name != NULL)
   1233 		return -1;
   1234 	*name = strdup(optarg);
   1235 	if (*name == NULL) {
   1236 		gpt_warn(gpt, "Can't copy string");
   1237 		return -1;
   1238 	}
   1239 	return 0;
   1240 }
   1241 
   1242 void
   1243 gpt_show_num(const char *prompt, uintmax_t num)
   1244 {
   1245 #ifdef HN_AUTOSCALE
   1246 	char human_num[5];
   1247 	if (humanize_number(human_num, 5, (int64_t)num ,
   1248 	    "", HN_AUTOSCALE, HN_NOSPACE|HN_B) < 0)
   1249 		human_num[0] = '\0';
   1250 #endif
   1251 	printf("%s: %ju", prompt, num);
   1252 #ifdef HN_AUTOSCALE
   1253 	if (human_num[0] != '\0')
   1254 		printf(" (%s)", human_num);
   1255 #endif
   1256 	printf("\n");
   1257 }
   1258 
   1259 int
   1260 gpt_add_hdr(gpt_t gpt, int type, off_t loc)
   1261 {
   1262 	void *p;
   1263 	map_t *t;
   1264 	const char *msg;
   1265 
   1266 	switch (type) {
   1267 	case MAP_TYPE_PRI_GPT_HDR:
   1268 		t = &gpt->gpt;
   1269 		msg = "primary";
   1270 		break;
   1271 	case MAP_TYPE_SEC_GPT_HDR:
   1272 		t = &gpt->tpg;
   1273 		msg = "secondary";
   1274 		break;
   1275 	default:
   1276 		gpt_warnx(gpt, "Unknown GPT header type %d", type);
   1277 		return -1;
   1278 	}
   1279 
   1280 	if ((p = calloc(1, gpt->secsz)) == NULL) {
   1281 		gpt_warn(gpt, "Error allocating %s GPT header", msg);
   1282 		return -1;
   1283 	}
   1284 
   1285 	*t = map_add(gpt, loc, 1LL, type, p, 1);
   1286 	if (*t == NULL) {
   1287 		gpt_warn(gpt, "Error adding %s GPT header", msg);
   1288 		free(p);
   1289 		return -1;
   1290 	}
   1291 	return 0;
   1292 }
   1293