Home | History | Annotate | Line # | Download | only in gpt
gpt.c revision 1.61
      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.61 2015/12/03 21:30:54 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 		if (gpt->secsz == 0)
    522 			gpt->secsz = 512;	/* Fixed size for files. */
    523 		if (gpt->mediasz == 0) {
    524 			if (gpt->sb.st_size % gpt->secsz) {
    525 				errno = EINVAL;
    526 				goto close;
    527 			}
    528 			gpt->mediasz = gpt->sb.st_size;
    529 		}
    530 		gpt->flags |= GPT_NOSYNC;
    531 	}
    532 
    533 	/*
    534 	 * We require an absolute minimum of 6 sectors. One for the MBR,
    535 	 * 2 for the GPT header, 2 for the GPT table and one to hold some
    536 	 * user data. Let's catch this extreme border case here so that
    537 	 * we don't have to worry about it later.
    538 	 */
    539 	devsz = gpt->mediasz / gpt->secsz;
    540 	if (devsz < 6) {
    541 		gpt_warnx(gpt, "Need 6 sectorso, we have %ju",
    542 		    (uintmax_t)devsz);
    543 		goto close;
    544 	}
    545 
    546 	if (gpt->verbose) {
    547 		gpt_msg(gpt, "mediasize=%ju; sectorsize=%u; blocks=%ju",
    548 		    (uintmax_t)gpt->mediasz, gpt->secsz, (uintmax_t)devsz);
    549 	}
    550 
    551 	if (map_init(gpt, devsz) == -1)
    552 		goto close;
    553 
    554 	if (gpt_mbr(gpt, 0LL) == -1)
    555 		goto close;
    556 	if ((found = gpt_gpt(gpt, 1LL, 1)) == -1)
    557 		goto close;
    558 	if (gpt_gpt(gpt, devsz - 1LL, found) == -1)
    559 		goto close;
    560 
    561 	return gpt;
    562 
    563  close:
    564 	if (gpt->fd != -1)
    565 		close(gpt->fd);
    566 	free(gpt);
    567 	return NULL;
    568 }
    569 
    570 void
    571 gpt_close(gpt_t gpt)
    572 {
    573 
    574 	if (!(gpt->flags & GPT_MODIFIED))
    575 		goto out;
    576 
    577 	if (!(gpt->flags & GPT_NOSYNC)) {
    578 #ifdef DIOCMWEDGES
    579 		int bits;
    580 		if (ioctl(gpt->fd, DIOCMWEDGES, &bits) == -1)
    581 			gpt_warn(gpt, "Can't update wedge information");
    582 		else
    583 			goto out;
    584 #endif
    585 	}
    586 	gpt_msg(gpt, "You need to run \"dkctl %s makewedges\""
    587 	    " for the changes to take effect\n", gpt->device_name);
    588 
    589 out:
    590 	close(gpt->fd);
    591 }
    592 
    593 void
    594 gpt_warnx(gpt_t gpt, const char *fmt, ...)
    595 {
    596 	va_list ap;
    597 
    598 	if (gpt->flags & GPT_QUIET)
    599 		return;
    600 	fprintf(stderr, "%s: %s: ", getprogname(), gpt->device_name);
    601 	va_start(ap, fmt);
    602 	vfprintf(stderr, fmt, ap);
    603 	va_end(ap);
    604 	fprintf(stderr, "\n");
    605 }
    606 
    607 void
    608 gpt_warn(gpt_t gpt, const char *fmt, ...)
    609 {
    610 	va_list ap;
    611 	int e = errno;
    612 
    613 	if (gpt->flags & GPT_QUIET)
    614 		return;
    615 	fprintf(stderr, "%s: %s: ", getprogname(), gpt->device_name);
    616 	va_start(ap, fmt);
    617 	vfprintf(stderr, fmt, ap);
    618 	va_end(ap);
    619 	fprintf(stderr, " (%s)\n", strerror(e));
    620 	errno = e;
    621 }
    622 
    623 void
    624 gpt_msg(gpt_t gpt, const char *fmt, ...)
    625 {
    626 	va_list ap;
    627 
    628 	if (gpt->flags & GPT_QUIET)
    629 		return;
    630 	printf("%s: ", gpt->device_name);
    631 	va_start(ap, fmt);
    632 	vprintf(fmt, ap);
    633 	va_end(ap);
    634 	printf("\n");
    635 }
    636 
    637 struct gpt_hdr *
    638 gpt_hdr(gpt_t gpt)
    639 {
    640 	gpt->gpt = map_find(gpt, MAP_TYPE_PRI_GPT_HDR);
    641 	if (gpt->gpt == NULL) {
    642 		gpt_warnx(gpt, "No primary GPT header; run create or recover");
    643 		return NULL;
    644 	}
    645 
    646 	gpt->tpg = map_find(gpt, MAP_TYPE_SEC_GPT_HDR);
    647 	if (gpt->tpg == NULL) {
    648 		gpt_warnx(gpt, "No secondary GPT header; run recover");
    649 		return NULL;
    650 	}
    651 
    652 	gpt->tbl = map_find(gpt, MAP_TYPE_PRI_GPT_TBL);
    653 	gpt->lbt = map_find(gpt, MAP_TYPE_SEC_GPT_TBL);
    654 	if (gpt->tbl == NULL || gpt->lbt == NULL) {
    655 		gpt_warnx(gpt, "Corrupt maps, run recover");
    656 		return NULL;
    657 	}
    658 
    659 	return gpt->gpt->map_data;
    660 }
    661 
    662 int
    663 gpt_write_crc(gpt_t gpt, map_t map, map_t tbl)
    664 {
    665 	struct gpt_hdr *hdr = map->map_data;
    666 
    667 	hdr->hdr_crc_table = htole32(crc32(tbl->map_data,
    668 	    le32toh(hdr->hdr_entries) * le32toh(hdr->hdr_entsz)));
    669 	hdr->hdr_crc_self = 0;
    670 	hdr->hdr_crc_self = htole32(crc32(hdr, le32toh(hdr->hdr_size)));
    671 
    672 	if (gpt_write(gpt, map) == -1) {
    673 		gpt_warn(gpt, "Error writing crc map");
    674 		return -1;
    675 	}
    676 
    677 	if (gpt_write(gpt, tbl) == -1) {
    678 		gpt_warn(gpt, "Error writing crc table");
    679 		return -1;
    680 	}
    681 
    682 	return 0;
    683 }
    684 
    685 int
    686 gpt_write_primary(gpt_t gpt)
    687 {
    688 	return gpt_write_crc(gpt, gpt->gpt, gpt->tbl);
    689 }
    690 
    691 
    692 int
    693 gpt_write_backup(gpt_t gpt)
    694 {
    695 	return gpt_write_crc(gpt, gpt->tpg, gpt->lbt);
    696 }
    697 
    698 void
    699 gpt_create_pmbr_part(struct mbr_part *part, off_t last)
    700 {
    701 	part->part_shd = 0x00;
    702 	part->part_ssect = 0x02;
    703 	part->part_scyl = 0x00;
    704 	part->part_typ = MBR_PTYPE_PMBR;
    705 	part->part_ehd = 0xfe;
    706 	part->part_esect = 0xff;
    707 	part->part_ecyl = 0xff;
    708 	part->part_start_lo = htole16(1);
    709 	if (last > 0xffffffff) {
    710 		part->part_size_lo = htole16(0xffff);
    711 		part->part_size_hi = htole16(0xffff);
    712 	} else {
    713 		part->part_size_lo = htole16((uint16_t)last);
    714 		part->part_size_hi = htole16((uint16_t)(last >> 16));
    715 	}
    716 }
    717 
    718 
    719 struct gpt_ent *
    720 gpt_ent(map_t map, map_t tbl, unsigned int i)
    721 {
    722 	struct gpt_hdr *hdr = map->map_data;
    723 	return (void *)((char *)tbl->map_data + i * le32toh(hdr->hdr_entsz));
    724 }
    725 
    726 struct gpt_ent *
    727 gpt_ent_primary(gpt_t gpt, unsigned int i)
    728 {
    729 	return gpt_ent(gpt->gpt, gpt->tbl, i);
    730 }
    731 
    732 struct gpt_ent *
    733 gpt_ent_backup(gpt_t gpt, unsigned int i)
    734 {
    735 	return gpt_ent(gpt->tpg, gpt->lbt, i);
    736 }
    737 
    738 int
    739 gpt_usage(const char *prefix, const struct gpt_cmd *cmd)
    740 {
    741 	const char **a = cmd->help;
    742 	size_t hlen = cmd->hlen;
    743 	size_t i;
    744 
    745 	if (prefix == NULL) {
    746 		const char *pname = getprogname();
    747 		const char *d1, *d2, *d = " <device>";
    748 		int len = (int)strlen(pname);
    749 		if (strcmp(pname, "gpt") == 0) {
    750 			d1 = "";
    751 			d2 = d;
    752 		} else {
    753 			d2 = "";
    754 			d1 = d;
    755 		}
    756 		fprintf(stderr, "Usage: %s%s %s %s%s\n", pname,
    757 		    d1, cmd->name, a[0], d2);
    758 		for (i = 1; i < hlen; i++) {
    759 			fprintf(stderr,
    760 			    "       %*s%s %s %s%s\n", len, "",
    761 			    d1, cmd->name, a[i], d2);
    762 		}
    763 	} else {
    764 		for (i = 0; i < hlen; i++)
    765 		    fprintf(stderr, "%s%s %s\n", prefix, cmd->name, a[i]);
    766 	}
    767 	return -1;
    768 }
    769 
    770 off_t
    771 gpt_last(gpt_t gpt)
    772 {
    773 	return gpt->mediasz / gpt->secsz - 1LL;
    774 }
    775 
    776 off_t
    777 gpt_create(gpt_t gpt, off_t last, u_int parts, int primary_only)
    778 {
    779 	off_t blocks;
    780 	map_t map;
    781 	struct gpt_hdr *hdr;
    782 	struct gpt_ent *ent;
    783 	unsigned int i;
    784 	void *p;
    785 
    786 	if (map_find(gpt, MAP_TYPE_PRI_GPT_HDR) != NULL ||
    787 	    map_find(gpt, MAP_TYPE_SEC_GPT_HDR) != NULL) {
    788 		gpt_warnx(gpt, "Device already contains a GPT");
    789 		return -1;
    790 	}
    791 
    792 	/* Get the amount of free space after the MBR */
    793 	blocks = map_free(gpt, 1LL, 0LL);
    794 	if (blocks == 0LL) {
    795 		gpt_warnx(gpt, "No room for the GPT header");
    796 		return -1;
    797 	}
    798 
    799 	/* Don't create more than parts entries. */
    800 	if ((uint64_t)(blocks - 1) * gpt->secsz >
    801 	    parts * sizeof(struct gpt_ent)) {
    802 		blocks = (off_t)((parts * sizeof(struct gpt_ent)) / gpt->secsz);
    803 		if ((parts * sizeof(struct gpt_ent)) % gpt->secsz)
    804 			blocks++;
    805 		blocks++;		/* Don't forget the header itself */
    806 	}
    807 
    808 	/* Never cross the median of the device. */
    809 	if ((blocks + 1LL) > ((last + 1LL) >> 1))
    810 		blocks = ((last + 1LL) >> 1) - 1LL;
    811 
    812 	/*
    813 	 * Get the amount of free space at the end of the device and
    814 	 * calculate the size for the GPT structures.
    815 	 */
    816 	map = map_last(gpt);
    817 	if (map->map_type != MAP_TYPE_UNUSED) {
    818 		gpt_warnx(gpt, "No room for the backup header");
    819 		return -1;
    820 	}
    821 
    822 	if (map->map_size < blocks)
    823 		blocks = map->map_size;
    824 	if (blocks == 1LL) {
    825 		gpt_warnx(gpt, "No room for the GPT table");
    826 		return -1;
    827 	}
    828 
    829 	blocks--;		/* Number of blocks in the GPT table. */
    830 
    831 	if ((p = calloc(1, gpt->secsz)) == NULL) {
    832 		gpt_warnx(gpt, "Can't allocate the primary GPT");
    833 		return -1;
    834 	}
    835 	if ((gpt->gpt = map_add(gpt, 1LL, 1LL,
    836 	    MAP_TYPE_PRI_GPT_HDR, p, 1)) == NULL) {
    837 		free(p);
    838 		gpt_warnx(gpt, "Can't add the primary GPT");
    839 		return -1;
    840 	}
    841 
    842 	if ((p = calloc((size_t)blocks, gpt->secsz)) == NULL) {
    843 		gpt_warnx(gpt, "Can't allocate the primary GPT table");
    844 		return -1;
    845 	}
    846 	if ((gpt->tbl = map_add(gpt, 2LL, blocks,
    847 	    MAP_TYPE_PRI_GPT_TBL, p, 1)) == NULL) {
    848 		free(p);
    849 		gpt_warnx(gpt, "Can't add the primary GPT table");
    850 		return -1;
    851 	}
    852 
    853 	hdr = gpt->gpt->map_data;
    854 	memcpy(hdr->hdr_sig, GPT_HDR_SIG, sizeof(hdr->hdr_sig));
    855 
    856 	/*
    857 	 * XXX struct gpt_hdr is not a multiple of 8 bytes in size and thus
    858 	 * contains padding we must not include in the size.
    859 	 */
    860 	hdr->hdr_revision = htole32(GPT_HDR_REVISION);
    861 	hdr->hdr_size = htole32(GPT_HDR_SIZE);
    862 	hdr->hdr_lba_self = htole64((uint64_t)gpt->gpt->map_start);
    863 	hdr->hdr_lba_alt = htole64((uint64_t)last);
    864 	hdr->hdr_lba_start = htole64((uint64_t)(gpt->tbl->map_start + blocks));
    865 	hdr->hdr_lba_end = htole64((uint64_t)(last - blocks - 1LL));
    866 	if (gpt_uuid_generate(gpt, hdr->hdr_guid) == -1)
    867 		return -1;
    868 	hdr->hdr_lba_table = htole64((uint64_t)(gpt->tbl->map_start));
    869 	hdr->hdr_entries = htole32((uint32_t)(((uint64_t)blocks * gpt->secsz) /
    870 	    sizeof(struct gpt_ent)));
    871 	if (le32toh(hdr->hdr_entries) > parts)
    872 		hdr->hdr_entries = htole32(parts);
    873 	hdr->hdr_entsz = htole32(sizeof(struct gpt_ent));
    874 
    875 	ent = gpt->tbl->map_data;
    876 	for (i = 0; i < le32toh(hdr->hdr_entries); i++) {
    877 		if (gpt_uuid_generate(gpt, ent[i].ent_guid) == -1)
    878 			return -1;
    879 	}
    880 
    881 	/*
    882 	 * Create backup GPT if the user didn't suppress it.
    883 	 */
    884 	if (primary_only)
    885 		return last;
    886 
    887 	if ((p = calloc(1, gpt->secsz)) == NULL) {
    888 		gpt_warnx(gpt, "Can't allocate the secondary GPT");
    889 		return -1;
    890 	}
    891 
    892 	if ((gpt->tpg = map_add(gpt, last, 1LL,
    893 	    MAP_TYPE_SEC_GPT_HDR, p, 1)) == NULL) {
    894 		gpt_warnx(gpt, "Can't add the secondary GPT");
    895 		return -1;
    896 	}
    897 
    898 	if ((gpt->lbt = map_add(gpt, last - blocks, blocks,
    899 	    MAP_TYPE_SEC_GPT_TBL, gpt->tbl->map_data, 0)) == NULL) {
    900 		gpt_warnx(gpt, "Can't add the secondary GPT table");
    901 		return -1;
    902 	}
    903 
    904 	memcpy(gpt->tpg->map_data, gpt->gpt->map_data, gpt->secsz);
    905 
    906 	hdr = gpt->tpg->map_data;
    907 	hdr->hdr_lba_self = htole64((uint64_t)gpt->tpg->map_start);
    908 	hdr->hdr_lba_alt = htole64((uint64_t)gpt->gpt->map_start);
    909 	hdr->hdr_lba_table = htole64((uint64_t)gpt->lbt->map_start);
    910 	return last;
    911 }
    912 
    913 static int
    914 gpt_size_get(gpt_t gpt, off_t *size)
    915 {
    916 	off_t sectors;
    917 	int64_t human_num;
    918 	char *p;
    919 
    920 	if (*size > 0)
    921 		return -1;
    922 	sectors = strtoll(optarg, &p, 10);
    923 	if (sectors < 1)
    924 		return -1;
    925 	if (*p == '\0' || ((*p == 's' || *p == 'S') && p[1] == '\0')) {
    926 		*size = sectors * gpt->secsz;
    927 		return 0;
    928 	}
    929 	if ((*p == 'b' || *p == 'B') && p[1] == '\0') {
    930 		*size = sectors;
    931 		return 0;
    932 	}
    933 	if (dehumanize_number(optarg, &human_num) < 0)
    934 		return -1;
    935 	*size = human_num;
    936 	return 0;
    937 }
    938 
    939 int
    940 gpt_human_get(off_t *human)
    941 {
    942 	int64_t human_num;
    943 
    944 	if (*human > 0)
    945 		return -1;
    946 	if (dehumanize_number(optarg, &human_num) < 0)
    947 		return -1;
    948 	*human = human_num;
    949 	if (*human < 1)
    950 		return -1;
    951 	return 0;
    952 }
    953 
    954 int
    955 gpt_add_find(gpt_t gpt, struct gpt_find *find, int ch)
    956 {
    957 	switch (ch) {
    958 	case 'a':
    959 		if (find->all > 0)
    960 			return -1;
    961 		find->all = 1;
    962 		break;
    963 	case 'b':
    964 		if (gpt_human_get(&find->block) == -1)
    965 			return -1;
    966 		break;
    967 	case 'i':
    968 		if (gpt_uint_get(&find->entry) == -1)
    969 			return -1;
    970 		break;
    971 	case 'L':
    972 		if (gpt_name_get(gpt, &find->label) == -1)
    973 			return -1;
    974 		break;
    975 	case 's':
    976 		if (gpt_size_get(gpt, &find->size) == -1)
    977 			return -1;
    978 		break;
    979 	case 't':
    980 		if (!gpt_uuid_is_nil(find->type))
    981 			return -1;
    982 		if (gpt_uuid_parse(optarg, find->type) != 0)
    983 			return -1;
    984 		break;
    985 	default:
    986 		return -1;
    987 	}
    988 	return 0;
    989 }
    990 
    991 int
    992 gpt_change_ent(gpt_t gpt, const struct gpt_find *find,
    993     void (*cfn)(struct gpt_ent *, void *), void *v)
    994 {
    995 	map_t m;
    996 	struct gpt_hdr *hdr;
    997 	struct gpt_ent *ent;
    998 	unsigned int i;
    999 	uint8_t utfbuf[__arraycount(ent->ent_name) * 3 + 1];
   1000 
   1001 	if (!find->all ^
   1002 	    (find->block > 0 || find->entry > 0 || find->label != NULL
   1003 	    || find->size > 0 || !gpt_uuid_is_nil(find->type)))
   1004 		return -1;
   1005 
   1006 	if ((hdr = gpt_hdr(gpt)) == NULL)
   1007 		return -1;
   1008 
   1009 	/* Relabel all matching entries in the map. */
   1010 	for (m = map_first(gpt); m != NULL; m = m->map_next) {
   1011 		if (m->map_type != MAP_TYPE_GPT_PART || m->map_index < 1)
   1012 			continue;
   1013 		if (find->entry > 0 && find->entry != m->map_index)
   1014 			continue;
   1015 		if (find->block > 0 && find->block != m->map_start)
   1016 			continue;
   1017 		if (find->size > 0 && find->size != m->map_size)
   1018 			continue;
   1019 
   1020 		i = m->map_index - 1;
   1021 
   1022 		ent = gpt_ent_primary(gpt, i);
   1023 		if (find->label != NULL) {
   1024 			utf16_to_utf8(ent->ent_name, utfbuf, sizeof(utfbuf));
   1025 			if (strcmp((char *)find->label, (char *)utfbuf) == 0)
   1026 				continue;
   1027 		}
   1028 
   1029 		if (!gpt_uuid_is_nil(find->type) &&
   1030 		    !gpt_uuid_equal(find->type, ent->ent_type))
   1031 			continue;
   1032 
   1033 		/* Change the primary entry. */
   1034 		(*cfn)(ent, v);
   1035 
   1036 		if (gpt_write_primary(gpt) == -1)
   1037 			return -1;
   1038 
   1039 		ent = gpt_ent_backup(gpt, i);
   1040 		/* Change the secondary entry. */
   1041 		(*cfn)(ent, v);
   1042 
   1043 		if (gpt_write_backup(gpt) == -1)
   1044 			return -1;
   1045 
   1046 		gpt_msg(gpt, "Partition %d %s", m->map_index, find->msg);
   1047 	}
   1048 	return 0;
   1049 }
   1050 
   1051 int
   1052 gpt_add_ais(gpt_t gpt, off_t *alignment, u_int *entry, off_t *size, int ch)
   1053 {
   1054 	switch (ch) {
   1055 	case 'a':
   1056 		if (gpt_human_get(alignment) == -1)
   1057 			return -1;
   1058 		return 0;
   1059 	case 'i':
   1060 		if (gpt_uint_get(entry) == -1)
   1061 			return -1;
   1062 		return 0;
   1063 	case 's':
   1064 		if (gpt_size_get(gpt, size) == -1)
   1065 			return -1;
   1066 		return 0;
   1067 	default:
   1068 		return -1;
   1069 	}
   1070 }
   1071 
   1072 off_t
   1073 gpt_check_ais(gpt_t gpt, off_t alignment, u_int entry, off_t size)
   1074 {
   1075 	if (entry == 0) {
   1076 		gpt_warnx(gpt, "Entry not specified");
   1077 		return -1;
   1078 	}
   1079 	if (alignment % gpt->secsz != 0) {
   1080 		gpt_warnx(gpt, "Alignment (%#jx) must be a multiple of "
   1081 		    "sector size (%#x)", (uintmax_t)alignment, gpt->secsz);
   1082 		return -1;
   1083 	}
   1084 
   1085 	if (size % gpt->secsz != 0) {
   1086 		gpt_warnx(gpt, "Size (%#jx) must be a multiple of "
   1087 		    "sector size (%#x)", (uintmax_t)size, gpt->secsz);
   1088 		return -1;
   1089 	}
   1090 	if (size > 0)
   1091 		return size / gpt->secsz;
   1092 	return 0;
   1093 }
   1094 int
   1095 gpt_attr_get(uint64_t *attributes)
   1096 {
   1097 	if (strcmp(optarg, "biosboot") == 0)
   1098 		*attributes |= GPT_ENT_ATTR_LEGACY_BIOS_BOOTABLE;
   1099 	else if (strcmp(optarg, "bootme") == 0)
   1100 		*attributes |= GPT_ENT_ATTR_BOOTME;
   1101 	else if (strcmp(optarg, "bootonce") == 0)
   1102 		*attributes |= GPT_ENT_ATTR_BOOTONCE;
   1103 	else if (strcmp(optarg, "bootfailed") == 0)
   1104 		*attributes |= GPT_ENT_ATTR_BOOTFAILED;
   1105 	else
   1106 		return -1;
   1107 	return 0;
   1108 }
   1109 int
   1110 gpt_attr_update(gpt_t gpt, u_int entry, uint64_t set, uint64_t clr)
   1111 {
   1112 	struct gpt_hdr *hdr;
   1113 	struct gpt_ent *ent;
   1114 	unsigned int i;
   1115 
   1116 	if (entry == 0 || (set == 0 && clr == 0))
   1117 		return -1;
   1118 
   1119 	if ((hdr = gpt_hdr(gpt)) == NULL)
   1120 		return -1;
   1121 
   1122 	if (entry > le32toh(hdr->hdr_entries)) {
   1123 		gpt_warnx(gpt, "Index %u out of range (%u max)",
   1124 		    entry, le32toh(hdr->hdr_entries));
   1125 		return -1;
   1126 	}
   1127 
   1128 	i = entry - 1;
   1129 	ent = gpt_ent_primary(gpt, i);
   1130 	if (gpt_uuid_is_nil(ent->ent_type)) {
   1131 		gpt_warnx(gpt, "Entry at index %u is unused", entry);
   1132 		return -1;
   1133 	}
   1134 
   1135 	ent->ent_attr &= ~clr;
   1136 	ent->ent_attr |= set;
   1137 
   1138 	if (gpt_write_primary(gpt) == -1)
   1139 		return -1;
   1140 
   1141 	ent = gpt_ent_backup(gpt, i);
   1142 	ent->ent_attr &= ~clr;
   1143 	ent->ent_attr |= set;
   1144 
   1145 	if (gpt_write_backup(gpt) == -1)
   1146 		return -1;
   1147 	gpt_msg(gpt, "Partition %d attributes updated", entry);
   1148 	return 0;
   1149 }
   1150 
   1151 int
   1152 gpt_uint_get(u_int *entry)
   1153 {
   1154 	char *p;
   1155 	if (*entry > 0)
   1156 		return -1;
   1157 	*entry = (u_int)strtoul(optarg, &p, 10);
   1158 	if (*p != 0 || *entry < 1)
   1159 		return -1;
   1160 	return 0;
   1161 }
   1162 int
   1163 gpt_uuid_get(gpt_t gpt, gpt_uuid_t *uuid)
   1164 {
   1165 	if (!gpt_uuid_is_nil(*uuid))
   1166 		return -1;
   1167 	if (gpt_uuid_parse(optarg, *uuid) != 0) {
   1168 		gpt_warn(gpt, "Can't parse uuid");
   1169 		return -1;
   1170 	}
   1171 	return 0;
   1172 }
   1173 
   1174 int
   1175 gpt_name_get(gpt_t gpt, void *v)
   1176 {
   1177 	char **name = v;
   1178 	if (*name != NULL)
   1179 		return -1;
   1180 	*name = strdup(optarg);
   1181 	if (*name == NULL) {
   1182 		gpt_warn(gpt, "Can't copy string");
   1183 		return -1;
   1184 	}
   1185 	return 0;
   1186 }
   1187 
   1188 void
   1189 gpt_show_num(const char *prompt, uintmax_t num)
   1190 {
   1191 #ifdef HN_AUTOSCALE
   1192 	char human_num[5];
   1193 	if (humanize_number(human_num, 5, (int64_t)num ,
   1194 	    "", HN_AUTOSCALE, HN_NOSPACE|HN_B) < 0)
   1195 		human_num[0] = '\0';
   1196 #endif
   1197 	printf("%s: %ju", prompt, num);
   1198 #ifdef HN_AUTOSCALE
   1199 	if (human_num[0] != '\0')
   1200 		printf(" (%s)", human_num);
   1201 #endif
   1202 	printf("\n");
   1203 }
   1204