Home | History | Annotate | Line # | Download | only in gpt
gpt.c revision 1.58
      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.58 2015/12/02 20:09:33 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++] = utfchar;
    148 		} else if (utfchar < 0x800) {
    149 			if (s8idx + 2 >= s8len)
    150 				break;
    151 			s8[s8idx++] = 0xc0 | (utfchar >> 6);
    152 			s8[s8idx++] = 0x80 | (utfchar & 0x3f);
    153 		} else if (utfchar < 0x10000) {
    154 			if (s8idx + 3 >= s8len)
    155 				break;
    156 			s8[s8idx++] = 0xe0 | (utfchar >> 12);
    157 			s8[s8idx++] = 0x80 | ((utfchar >> 6) & 0x3f);
    158 			s8[s8idx++] = 0x80 | (utfchar & 0x3f);
    159 		} else if (utfchar < 0x200000) {
    160 			if (s8idx + 4 >= s8len)
    161 				break;
    162 			s8[s8idx++] = 0xf0 | (utfchar >> 18);
    163 			s8[s8idx++] = 0x80 | ((utfchar >> 12) & 0x3f);
    164 			s8[s8idx++] = 0x80 | ((utfchar >> 6) & 0x3f);
    165 			s8[s8idx++] = 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 = -1;
    215 		}
    216 		if (utfbytes == 0) {
    217 			if (utfchar >= 0x10000 && s16idx + 2 >= s16len)
    218 				utfchar = 0xfffd;
    219 			if (utfchar >= 0x10000) {
    220 				s16[s16idx++] =
    221 				    htole16(0xd800 | ((utfchar>>10)-0x40));
    222 				s16[s16idx++] =
    223 				    htole16(0xdc00 | (utfchar & 0x3ff));
    224 			} else
    225 				s16[s16idx++] = htole16(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 = 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);
    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);
    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 			// XXX: map add with non-allocated memory
    344 			m = map_add(gpt, start, size, MAP_TYPE_MBR_PART, p);
    345 			if (m == NULL)
    346 				return -1;
    347 			m->map_index = i + 1;
    348 		} else {
    349 			if (gpt_mbr(gpt, start) == -1)
    350 				return -1;
    351 		}
    352 	}
    353 	return 0;
    354 out:
    355 	if (p == NULL) {
    356 		free(mbr);
    357 		return -1;
    358 	}
    359 	return 0;
    360 }
    361 
    362 int
    363 gpt_gpt(gpt_t gpt, off_t lba, int found)
    364 {
    365 	off_t size;
    366 	struct gpt_ent *ent;
    367 	struct gpt_hdr *hdr;
    368 	char *p;
    369 	map_t m;
    370 	size_t blocks, tblsz;
    371 	unsigned int i;
    372 	uint32_t crc;
    373 
    374 	hdr = gpt_read(gpt, lba, 1);
    375 	if (hdr == NULL)
    376 		return -1;
    377 
    378 	if (memcmp(hdr->hdr_sig, GPT_HDR_SIG, sizeof(hdr->hdr_sig)))
    379 		goto fail_hdr;
    380 
    381 	crc = le32toh(hdr->hdr_crc_self);
    382 	hdr->hdr_crc_self = 0;
    383 	if (crc32(hdr, le32toh(hdr->hdr_size)) != crc) {
    384 		if (gpt->verbose)
    385 			gpt_msg(gpt, "Bad CRC in GPT header at sector %ju",
    386 			    (uintmax_t)lba);
    387 		goto fail_hdr;
    388 	}
    389 
    390 	tblsz = le32toh(hdr->hdr_entries) * le32toh(hdr->hdr_entsz);
    391 	blocks = tblsz / gpt->secsz + ((tblsz % gpt->secsz) ? 1 : 0);
    392 
    393 	/* Use generic pointer to deal with hdr->hdr_entsz != sizeof(*ent). */
    394 	p = gpt_read(gpt, le64toh(hdr->hdr_lba_table), blocks);
    395 	if (p == NULL) {
    396 		if (found) {
    397 			if (gpt->verbose)
    398 				gpt_msg(gpt,
    399 				    "Cannot read LBA table at sector %ju",
    400 				    (uintmax_t)le64toh(hdr->hdr_lba_table));
    401 			return -1;
    402 		}
    403 		goto fail_hdr;
    404 	}
    405 
    406 	if (crc32(p, tblsz) != le32toh(hdr->hdr_crc_table)) {
    407 		if (gpt->verbose)
    408 			gpt_msg(gpt, "Bad CRC in GPT table at sector %ju",
    409 			    (uintmax_t)le64toh(hdr->hdr_lba_table));
    410 		goto fail_ent;
    411 	}
    412 
    413 	if (gpt->verbose > 1)
    414 		gpt_msg(gpt, "%s GPT at sector %ju",
    415 		    (lba == 1) ? "Pri" : "Sec", (uintmax_t)lba);
    416 
    417 	m = map_add(gpt, lba, 1, (lba == 1)
    418 	    ? MAP_TYPE_PRI_GPT_HDR : MAP_TYPE_SEC_GPT_HDR, hdr);
    419 	if (m == NULL)
    420 		return (-1);
    421 
    422 	m = map_add(gpt, le64toh(hdr->hdr_lba_table), blocks, (lba == 1)
    423 	    ? MAP_TYPE_PRI_GPT_TBL : MAP_TYPE_SEC_GPT_TBL, p);
    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 = le64toh(ent->ent_lba_end) - le64toh(ent->ent_lba_start) +
    436 		    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 		// XXX: map add with not allocated memory.
    447 		m = map_add(gpt, le64toh(ent->ent_lba_start), size,
    448 		    MAP_TYPE_GPT_PART, ent);
    449 		if (m == NULL)
    450 			return (-1);
    451 		m->map_index = i + 1;
    452 	}
    453 	return (1);
    454 
    455  fail_ent:
    456 	free(p);
    457 
    458  fail_hdr:
    459 	free(hdr);
    460 	return (0);
    461 }
    462 
    463 gpt_t
    464 gpt_open(const char *dev, int flags, int verbose, off_t mediasz, u_int secsz)
    465 {
    466 	int mode, found;
    467 	off_t devsz;
    468 	gpt_t gpt;
    469 
    470 
    471 	if ((gpt = calloc(1, sizeof(*gpt))) == NULL) {
    472 		if (!(flags & GPT_QUIET))
    473 			warn("Cannot allocate `%s'", dev);
    474 		return NULL;
    475 	}
    476 	gpt->flags = flags;
    477 	gpt->verbose = verbose;
    478 	gpt->mediasz = mediasz;
    479 	gpt->secsz = secsz;
    480 
    481 	mode = (gpt->flags & GPT_READONLY) ? O_RDONLY : O_RDWR|O_EXCL;
    482 
    483 	gpt->fd = opendisk(dev, mode, gpt->device_name,
    484 	    sizeof(gpt->device_name), 0);
    485 	if (gpt->fd == -1) {
    486 		strlcpy(gpt->device_name, dev, sizeof(gpt->device_name));
    487 		gpt_warn(gpt, "Cannot open");
    488 		goto close;
    489 	}
    490 
    491 	if (fstat(gpt->fd, &gpt->sb) == -1) {
    492 		gpt_warn(gpt, "Cannot stat");
    493 		goto close;
    494 	}
    495 
    496 	if ((gpt->sb.st_mode & S_IFMT) != S_IFREG) {
    497 		if (gpt->secsz == 0) {
    498 #ifdef DIOCGSECTORSIZE
    499 			if (ioctl(gpt->fd, DIOCGSECTORSIZE, &gpt->secsz) == -1) {
    500 				gpt_warn(gpt, "Cannot get sector size");
    501 				goto close;
    502 			}
    503 #endif
    504 			if (gpt->secsz == 0) {
    505 				gpt_warnx(gpt, "Sector size can't be 0");
    506 				goto close;
    507 			}
    508 		}
    509 		if (gpt->mediasz == 0) {
    510 #ifdef DIOCGMEDIASIZE
    511 			if (ioctl(gpt->fd, DIOCGMEDIASIZE, &gpt->mediasz) == -1) {
    512 				gpt_warn(gpt, "Cannot get media size");
    513 				goto close;
    514 			}
    515 #endif
    516 			if (gpt->mediasz == 0) {
    517 				gpt_warnx(gpt, "Media size can't be 0");
    518 				goto close;
    519 			}
    520 		}
    521 	} else {
    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 sectorso, 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 	gpt_msg(gpt, "You need to run \"dkctl %s makewedges\""
    588 	    " for the changes to take effect\n", gpt->device_name);
    589 
    590 out:
    591 	close(gpt->fd);
    592 }
    593 
    594 void
    595 gpt_warnx(gpt_t gpt, const char *fmt, ...)
    596 {
    597 	va_list ap;
    598 
    599 	if (gpt->flags & GPT_QUIET)
    600 		return;
    601 	fprintf(stderr, "%s: %s: ", getprogname(), gpt->device_name);
    602 	va_start(ap, fmt);
    603 	vfprintf(stderr, fmt, ap);
    604 	va_end(ap);
    605 	fprintf(stderr, "\n");
    606 }
    607 
    608 void
    609 gpt_warn(gpt_t gpt, const char *fmt, ...)
    610 {
    611 	va_list ap;
    612 	int e = errno;
    613 
    614 	if (gpt->flags & GPT_QUIET)
    615 		return;
    616 	fprintf(stderr, "%s: %s: ", getprogname(), gpt->device_name);
    617 	va_start(ap, fmt);
    618 	vfprintf(stderr, fmt, ap);
    619 	va_end(ap);
    620 	fprintf(stderr, " (%s)\n", strerror(e));
    621 	errno = e;
    622 }
    623 
    624 void
    625 gpt_msg(gpt_t gpt, const char *fmt, ...)
    626 {
    627 	va_list ap;
    628 
    629 	if (gpt->flags & GPT_QUIET)
    630 		return;
    631 	printf("%s: ", gpt->device_name);
    632 	va_start(ap, fmt);
    633 	vprintf(fmt, ap);
    634 	va_end(ap);
    635 	printf("\n");
    636 }
    637 
    638 struct gpt_hdr *
    639 gpt_hdr(gpt_t gpt)
    640 {
    641 	gpt->gpt = map_find(gpt, MAP_TYPE_PRI_GPT_HDR);
    642 	if (gpt->gpt == NULL) {
    643 		gpt_warnx(gpt, "No primary GPT header; run create or recover");
    644 		return NULL;
    645 	}
    646 
    647 	gpt->tpg = map_find(gpt, MAP_TYPE_SEC_GPT_HDR);
    648 	if (gpt->tpg == NULL) {
    649 		gpt_warnx(gpt, "No secondary GPT header; run recover");
    650 		return NULL;
    651 	}
    652 
    653 	gpt->tbl = map_find(gpt, MAP_TYPE_PRI_GPT_TBL);
    654 	gpt->lbt = map_find(gpt, MAP_TYPE_SEC_GPT_TBL);
    655 	if (gpt->tbl == NULL || gpt->lbt == NULL) {
    656 		gpt_warnx(gpt, "Corrupt maps, run recover");
    657 		return NULL;
    658 	}
    659 
    660 	return gpt->gpt->map_data;
    661 }
    662 
    663 int
    664 gpt_write_crc(gpt_t gpt, map_t map, map_t tbl)
    665 {
    666 	struct gpt_hdr *hdr = map->map_data;
    667 
    668 	hdr->hdr_crc_table = htole32(crc32(tbl->map_data,
    669 	    le32toh(hdr->hdr_entries) * le32toh(hdr->hdr_entsz)));
    670 	hdr->hdr_crc_self = 0;
    671 	hdr->hdr_crc_self = htole32(crc32(hdr, le32toh(hdr->hdr_size)));
    672 
    673 	if (gpt_write(gpt, map) == -1) {
    674 		gpt_warn(gpt, "Error writing crc map");
    675 		return -1;
    676 	}
    677 
    678 	if (gpt_write(gpt, tbl) == -1) {
    679 		gpt_warn(gpt, "Error writing crc table");
    680 		return -1;
    681 	}
    682 
    683 	return 0;
    684 }
    685 
    686 int
    687 gpt_write_primary(gpt_t gpt)
    688 {
    689 	return gpt_write_crc(gpt, gpt->gpt, gpt->tbl);
    690 }
    691 
    692 
    693 int
    694 gpt_write_backup(gpt_t gpt)
    695 {
    696 	return gpt_write_crc(gpt, gpt->tpg, gpt->lbt);
    697 }
    698 
    699 void
    700 gpt_create_pmbr_part(struct mbr_part *part, off_t last)
    701 {
    702 	part->part_shd = 0x00;
    703 	part->part_ssect = 0x02;
    704 	part->part_scyl = 0x00;
    705 	part->part_typ = MBR_PTYPE_PMBR;
    706 	part->part_ehd = 0xfe;
    707 	part->part_esect = 0xff;
    708 	part->part_ecyl = 0xff;
    709 	part->part_start_lo = htole16(1);
    710 	if (last > 0xffffffff) {
    711 		part->part_size_lo = htole16(0xffff);
    712 		part->part_size_hi = htole16(0xffff);
    713 	} else {
    714 		part->part_size_lo = htole16(last);
    715 		part->part_size_hi = htole16(last >> 16);
    716 	}
    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 int
    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 = (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 ((p = calloc(1, gpt->secsz)) == NULL) {
    833 		gpt_warnx(gpt, "Can't allocate the primary GPT");
    834 		return -1;
    835 	}
    836 	if ((gpt->gpt = map_add(gpt, 1LL, 1LL,
    837 	    MAP_TYPE_PRI_GPT_HDR, p)) == NULL) {
    838 		free(p);
    839 		gpt_warnx(gpt, "Can't add the primary GPT");
    840 		return -1;
    841 	}
    842 
    843 	if ((p = calloc(blocks, gpt->secsz)) == NULL) {
    844 		gpt_warnx(gpt, "Can't allocate the primary GPT table");
    845 		return -1;
    846 	}
    847 	if ((gpt->tbl = map_add(gpt, 2LL, blocks,
    848 	    MAP_TYPE_PRI_GPT_TBL, p)) == NULL) {
    849 		free(p);
    850 		gpt_warnx(gpt, "Can't add the primary GPT table");
    851 		return -1;
    852 	}
    853 
    854 	hdr = gpt->gpt->map_data;
    855 	memcpy(hdr->hdr_sig, GPT_HDR_SIG, sizeof(hdr->hdr_sig));
    856 
    857 	/*
    858 	 * XXX struct gpt_hdr is not a multiple of 8 bytes in size and thus
    859 	 * contains padding we must not include in the size.
    860 	 */
    861 	hdr->hdr_revision = htole32(GPT_HDR_REVISION);
    862 	hdr->hdr_size = htole32(GPT_HDR_SIZE);
    863 	hdr->hdr_lba_self = htole64(gpt->gpt->map_start);
    864 	hdr->hdr_lba_alt = htole64(last);
    865 	hdr->hdr_lba_start = htole64(gpt->tbl->map_start + blocks);
    866 	hdr->hdr_lba_end = htole64(last - blocks - 1LL);
    867 	if (gpt_uuid_generate(gpt, hdr->hdr_guid) == -1)
    868 		return -1;
    869 	hdr->hdr_lba_table = htole64(gpt->tbl->map_start);
    870 	hdr->hdr_entries = htole32((blocks * gpt->secsz) /
    871 	    sizeof(struct gpt_ent));
    872 	if (le32toh(hdr->hdr_entries) > parts)
    873 		hdr->hdr_entries = htole32(parts);
    874 	hdr->hdr_entsz = htole32(sizeof(struct gpt_ent));
    875 
    876 	ent = gpt->tbl->map_data;
    877 	for (i = 0; i < le32toh(hdr->hdr_entries); i++) {
    878 		if (gpt_uuid_generate(gpt, ent[i].ent_guid) == -1)
    879 			return -1;
    880 	}
    881 
    882 	/*
    883 	 * Create backup GPT if the user didn't suppress it.
    884 	 */
    885 	if (primary_only)
    886 		return last;
    887 
    888 	if ((p = calloc(1, gpt->secsz)) == NULL) {
    889 		gpt_warnx(gpt, "Can't allocate the secondary GPT");
    890 		return -1;
    891 	}
    892 
    893 	if ((gpt->tpg = map_add(gpt, last, 1LL,
    894 	    MAP_TYPE_SEC_GPT_HDR, p)) == NULL) {
    895 		gpt_warnx(gpt, "Can't add the secondary GPT");
    896 		return -1;
    897 	}
    898 
    899 	if ((gpt->lbt = map_add(gpt, last - blocks, blocks,
    900 	    MAP_TYPE_SEC_GPT_TBL, gpt->tbl->map_data)) == NULL) {
    901 		gpt_warnx(gpt, "Can't add the secondary GPT table");
    902 		return -1;
    903 	}
    904 
    905 	memcpy(gpt->tpg->map_data, gpt->gpt->map_data, gpt->secsz);
    906 
    907 	hdr = gpt->tpg->map_data;
    908 	hdr->hdr_lba_self = htole64(gpt->tpg->map_start);
    909 	hdr->hdr_lba_alt = htole64(gpt->gpt->map_start);
    910 	hdr->hdr_lba_table = htole64(gpt->lbt->map_start);
    911 	return last;
    912 }
    913 
    914 static int
    915 gpt_size_get(gpt_t gpt, off_t *size)
    916 {
    917 	off_t sectors;
    918 	int64_t human_num;
    919 	char *p;
    920 
    921 	if (*size > 0)
    922 		return -1;
    923 	sectors = strtoll(optarg, &p, 10);
    924 	if (sectors < 1)
    925 		return -1;
    926 	if (*p == '\0' || ((*p == 's' || *p == 'S') && p[1] == '\0')) {
    927 		*size = sectors * gpt->secsz;
    928 		return 0;
    929 	}
    930 	if ((*p == 'b' || *p == 'B') && p[1] == '\0') {
    931 		*size = sectors;
    932 		return 0;
    933 	}
    934 	if (dehumanize_number(optarg, &human_num) < 0)
    935 		return -1;
    936 	*size = human_num;
    937 	return 0;
    938 }
    939 
    940 int
    941 gpt_human_get(off_t *human)
    942 {
    943 	int64_t human_num;
    944 
    945 	if (*human > 0)
    946 		return -1;
    947 	if (dehumanize_number(optarg, &human_num) < 0)
    948 		return -1;
    949 	*human = human_num;
    950 	if (*human < 1)
    951 		return -1;
    952 	return 0;
    953 }
    954 
    955 int
    956 gpt_add_find(gpt_t gpt, struct gpt_find *find, int ch)
    957 {
    958 	switch (ch) {
    959 	case 'a':
    960 		if (find->all > 0)
    961 			return -1;
    962 		find->all = 1;
    963 		break;
    964 	case 'b':
    965 		if (gpt_human_get(&find->block) == -1)
    966 			return -1;
    967 		break;
    968 	case 'i':
    969 		if (gpt_entry_get(&find->entry) == -1)
    970 			return -1;
    971 		break;
    972 	case 'L':
    973 		if (gpt_name_get(gpt, &find->label) == -1)
    974 			return -1;
    975 		break;
    976 	case 's':
    977 		if (gpt_size_get(gpt, &find->size) == -1)
    978 			return -1;
    979 		break;
    980 	case 't':
    981 		if (!gpt_uuid_is_nil(find->type))
    982 			return -1;
    983 		if (gpt_uuid_parse(optarg, find->type) != 0)
    984 			return -1;
    985 		break;
    986 	default:
    987 		return -1;
    988 	}
    989 	return 0;
    990 }
    991 
    992 int
    993 gpt_change_ent(gpt_t gpt, const struct gpt_find *find,
    994     void (*cfn)(struct gpt_ent *, void *), void *v)
    995 {
    996 	map_t m;
    997 	struct gpt_hdr *hdr;
    998 	struct gpt_ent *ent;
    999 	unsigned int i;
   1000 	uint8_t utfbuf[__arraycount(ent->ent_name) * 3 + 1];
   1001 
   1002 	if (!find->all ^
   1003 	    (find->block > 0 || find->entry > 0 || find->label != NULL
   1004 	    || find->size > 0 || !gpt_uuid_is_nil(find->type)))
   1005 		return -1;
   1006 
   1007 	if ((hdr = gpt_hdr(gpt)) == NULL)
   1008 		return -1;
   1009 
   1010 	/* Relabel all matching entries in the map. */
   1011 	for (m = map_first(gpt); m != NULL; m = m->map_next) {
   1012 		if (m->map_type != MAP_TYPE_GPT_PART || m->map_index < 1)
   1013 			continue;
   1014 		if (find->entry > 0 && find->entry != m->map_index)
   1015 			continue;
   1016 		if (find->block > 0 && find->block != m->map_start)
   1017 			continue;
   1018 		if (find->size > 0 && find->size != m->map_size)
   1019 			continue;
   1020 
   1021 		i = m->map_index - 1;
   1022 
   1023 		ent = gpt_ent_primary(gpt, i);
   1024 		if (find->label != NULL) {
   1025 			utf16_to_utf8(ent->ent_name, utfbuf, sizeof(utfbuf));
   1026 			if (strcmp((char *)find->label, (char *)utfbuf) == 0)
   1027 				continue;
   1028 		}
   1029 
   1030 		if (!gpt_uuid_is_nil(find->type) &&
   1031 		    !gpt_uuid_equal(find->type, ent->ent_type))
   1032 			continue;
   1033 
   1034 		/* Change the primary entry. */
   1035 		(*cfn)(ent, v);
   1036 
   1037 		if (gpt_write_primary(gpt) == -1)
   1038 			return -1;
   1039 
   1040 		ent = gpt_ent_backup(gpt, i);
   1041 		/* Change the secondary entry. */
   1042 		(*cfn)(ent, v);
   1043 
   1044 		if (gpt_write_backup(gpt) == -1)
   1045 			return -1;
   1046 
   1047 		gpt_msg(gpt, "Partition %d %s", m->map_index, find->msg);
   1048 	}
   1049 	return 0;
   1050 }
   1051 
   1052 int
   1053 gpt_add_ais(gpt_t gpt, off_t *alignment, u_int *entry, off_t *size, int ch)
   1054 {
   1055 	switch (ch) {
   1056 	case 'a':
   1057 		if (gpt_human_get(alignment) == -1)
   1058 			return -1;
   1059 		return 0;
   1060 	case 'i':
   1061 		if (gpt_entry_get(entry) == -1)
   1062 			return -1;
   1063 		return 0;
   1064 	case 's':
   1065 		if (gpt_size_get(gpt, size) == -1)
   1066 			return -1;
   1067 		return 0;
   1068 	default:
   1069 		return -1;
   1070 	}
   1071 }
   1072 
   1073 off_t
   1074 gpt_check_ais(gpt_t gpt, off_t alignment, u_int entry, off_t size)
   1075 {
   1076 	if (entry == 0) {
   1077 		gpt_warnx(gpt, "Entry not specified");
   1078 		return -1;
   1079 	}
   1080 	if (alignment % gpt->secsz != 0) {
   1081 		gpt_warnx(gpt, "Alignment (%#jx) must be a multiple of "
   1082 		    "sector size (%#x)", (uintmax_t)alignment, gpt->secsz);
   1083 		return -1;
   1084 	}
   1085 
   1086 	if (size % gpt->secsz != 0) {
   1087 		gpt_warnx(gpt, "Size (%#jx) must be a multiple of "
   1088 		    "sector size (%#x)", (uintmax_t)size, gpt->secsz);
   1089 		return -1;
   1090 	}
   1091 	if (size > 0)
   1092 		return size / gpt->secsz;
   1093 	return 0;
   1094 }
   1095 int
   1096 gpt_attr_get(uint64_t *attributes)
   1097 {
   1098 	if (strcmp(optarg, "biosboot") == 0)
   1099 		*attributes |= GPT_ENT_ATTR_LEGACY_BIOS_BOOTABLE;
   1100 	else if (strcmp(optarg, "bootme") == 0)
   1101 		*attributes |= GPT_ENT_ATTR_BOOTME;
   1102 	else if (strcmp(optarg, "bootonce") == 0)
   1103 		*attributes |= GPT_ENT_ATTR_BOOTONCE;
   1104 	else if (strcmp(optarg, "bootfailed") == 0)
   1105 		*attributes |= GPT_ENT_ATTR_BOOTFAILED;
   1106 	else
   1107 		return -1;
   1108 	return 0;
   1109 }
   1110 int
   1111 gpt_attr_update(gpt_t gpt, u_int entry, uint64_t set, uint64_t clr)
   1112 {
   1113 	struct gpt_hdr *hdr;
   1114 	struct gpt_ent *ent;
   1115 	unsigned int i;
   1116 
   1117 	if (entry == 0 || (set == 0 && clr == 0))
   1118 		return -1;
   1119 
   1120 	if ((hdr = gpt_hdr(gpt)) == NULL)
   1121 		return -1;
   1122 
   1123 	if (entry > le32toh(hdr->hdr_entries)) {
   1124 		gpt_warnx(gpt, "Index %u out of range (%u max)",
   1125 		    entry, le32toh(hdr->hdr_entries));
   1126 		return -1;
   1127 	}
   1128 
   1129 	i = entry - 1;
   1130 	ent = gpt_ent_primary(gpt, i);
   1131 	if (gpt_uuid_is_nil(ent->ent_type)) {
   1132 		gpt_warnx(gpt, "Entry at index %u is unused", entry);
   1133 		return -1;
   1134 	}
   1135 
   1136 	ent->ent_attr &= ~clr;
   1137 	ent->ent_attr |= set;
   1138 
   1139 	if (gpt_write_primary(gpt) == -1)
   1140 		return -1;
   1141 
   1142 	ent = gpt_ent_backup(gpt, i);
   1143 	ent->ent_attr &= ~clr;
   1144 	ent->ent_attr |= set;
   1145 
   1146 	if (gpt_write_backup(gpt) == -1)
   1147 		return -1;
   1148 	gpt_msg(gpt, "Partition %d attributes updated", entry);
   1149 	return 0;
   1150 }
   1151 
   1152 int
   1153 gpt_entry_get(u_int *entry)
   1154 {
   1155 	char *p;
   1156 	if (*entry > 0)
   1157 		return -1;
   1158 	*entry = strtoul(optarg, &p, 10);
   1159 	if (*p != 0 || *entry < 1)
   1160 		return -1;
   1161 	return 0;
   1162 }
   1163 int
   1164 gpt_uuid_get(gpt_t gpt, gpt_uuid_t *uuid)
   1165 {
   1166 	if (!gpt_uuid_is_nil(*uuid))
   1167 		return -1;
   1168 	if (gpt_uuid_parse(optarg, *uuid) != 0) {
   1169 		gpt_warn(gpt, "Can't parse uuid");
   1170 		return -1;
   1171 	}
   1172 	return 0;
   1173 }
   1174 
   1175 int
   1176 gpt_name_get(gpt_t gpt, void *v)
   1177 {
   1178 	char **name = v;
   1179 	if (*name != NULL)
   1180 		return -1;
   1181 	*name = strdup(optarg);
   1182 	if (*name == NULL) {
   1183 		gpt_warn(gpt, "Can't copy string");
   1184 		return -1;
   1185 	}
   1186 	return 0;
   1187 }
   1188