Home | History | Annotate | Line # | Download | only in gpt
gpt.c revision 1.70
      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.70 2017/02/16 03:32:17 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: flag=%#x type=%d, start=%ju, "
    340 			    "size=%ju", mbr->mbr_part[i].part_flag,
    341 			    mbr->mbr_part[i].part_typ,
    342 			    (uintmax_t)start, (uintmax_t)size);
    343 		if (mbr->mbr_part[i].part_typ != MBR_PTYPE_EXT_LBA) {
    344 			m = map_add(gpt, start, size, MAP_TYPE_MBR_PART, p, 0);
    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, (off_t)le64toh((uint64_t)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, 1);
    419 	if (m == NULL)
    420 		return (-1);
    421 
    422 	m = map_add(gpt, (off_t)le64toh((uint64_t)hdr->hdr_lba_table),
    423 	    (off_t)blocks,
    424 	    lba == 1 ? MAP_TYPE_PRI_GPT_TBL : MAP_TYPE_SEC_GPT_TBL, p, 1);
    425 	if (m == NULL)
    426 		return (-1);
    427 
    428 	if (lba != 1)
    429 		return (1);
    430 
    431 	for (i = 0; i < le32toh(hdr->hdr_entries); i++) {
    432 		ent = (void*)(p + i * le32toh(hdr->hdr_entsz));
    433 		if (gpt_uuid_is_nil(ent->ent_type))
    434 			continue;
    435 
    436 		size = (off_t)(le64toh((uint64_t)ent->ent_lba_end) -
    437 		    le64toh((uint64_t)ent->ent_lba_start) + 1LL);
    438 		if (gpt->verbose > 2) {
    439 			char buf[128];
    440 			gpt_uuid_snprintf(buf, sizeof(buf), "%s",
    441 			    ent->ent_type);
    442 			gpt_msg(gpt, "GPT partition: type=%s, start=%ju, "
    443 			    "size=%ju", buf,
    444 			    (uintmax_t)le64toh(ent->ent_lba_start),
    445 			    (uintmax_t)size);
    446 		}
    447 		m = map_add(gpt, (off_t)le64toh((uint64_t)ent->ent_lba_start),
    448 		    size, MAP_TYPE_GPT_PART, ent, 0);
    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     time_t timestamp)
    466 {
    467 	int mode, found;
    468 	off_t devsz;
    469 	gpt_t gpt;
    470 
    471 
    472 	if ((gpt = calloc(1, sizeof(*gpt))) == NULL) {
    473 		if (!(flags & GPT_QUIET))
    474 			warn("Cannot allocate `%s'", dev);
    475 		return NULL;
    476 	}
    477 	gpt->flags = flags;
    478 	gpt->verbose = verbose;
    479 	gpt->mediasz = mediasz;
    480 	gpt->secsz = secsz;
    481 	gpt->timestamp = timestamp;
    482 
    483 	mode = (gpt->flags & GPT_READONLY) ? O_RDONLY : O_RDWR|O_EXCL;
    484 
    485 	gpt->fd = opendisk(dev, mode, gpt->device_name,
    486 	    sizeof(gpt->device_name), 0);
    487 	if (gpt->fd == -1) {
    488 		strlcpy(gpt->device_name, dev, sizeof(gpt->device_name));
    489 		gpt_warn(gpt, "Cannot open");
    490 		goto close;
    491 	}
    492 
    493 	if (fstat(gpt->fd, &gpt->sb) == -1) {
    494 		gpt_warn(gpt, "Cannot stat");
    495 		goto close;
    496 	}
    497 
    498 	if ((gpt->sb.st_mode & S_IFMT) != S_IFREG) {
    499 		if (gpt->secsz == 0) {
    500 #ifdef DIOCGSECTORSIZE
    501 			if (ioctl(gpt->fd, DIOCGSECTORSIZE, &gpt->secsz) == -1) {
    502 				gpt_warn(gpt, "Cannot get sector size");
    503 				goto close;
    504 			}
    505 #endif
    506 			if (gpt->secsz == 0) {
    507 				gpt_warnx(gpt, "Sector size can't be 0");
    508 				goto close;
    509 			}
    510 		}
    511 		if (gpt->mediasz == 0) {
    512 #ifdef DIOCGMEDIASIZE
    513 			if (ioctl(gpt->fd, DIOCGMEDIASIZE, &gpt->mediasz) == -1) {
    514 				gpt_warn(gpt, "Cannot get media size");
    515 				goto close;
    516 			}
    517 #endif
    518 			if (gpt->mediasz == 0) {
    519 				gpt_warnx(gpt, "Media size can't be 0");
    520 				goto close;
    521 			}
    522 		}
    523 	} else {
    524 		gpt->flags |= GPT_FILE;
    525 		if (gpt->secsz == 0)
    526 			gpt->secsz = 512;	/* Fixed size for files. */
    527 		if (gpt->mediasz == 0) {
    528 			if (gpt->sb.st_size % gpt->secsz) {
    529 				errno = EINVAL;
    530 				goto close;
    531 			}
    532 			gpt->mediasz = gpt->sb.st_size;
    533 		}
    534 		gpt->flags |= GPT_NOSYNC;
    535 	}
    536 
    537 	/*
    538 	 * We require an absolute minimum of 6 sectors. One for the MBR,
    539 	 * 2 for the GPT header, 2 for the GPT table and one to hold some
    540 	 * user data. Let's catch this extreme border case here so that
    541 	 * we don't have to worry about it later.
    542 	 */
    543 	devsz = gpt->mediasz / gpt->secsz;
    544 	if (devsz < 6) {
    545 		gpt_warnx(gpt, "Need 6 sectors, we have %ju",
    546 		    (uintmax_t)devsz);
    547 		goto close;
    548 	}
    549 
    550 	if (gpt->verbose) {
    551 		gpt_msg(gpt, "mediasize=%ju; sectorsize=%u; blocks=%ju",
    552 		    (uintmax_t)gpt->mediasz, gpt->secsz, (uintmax_t)devsz);
    553 	}
    554 
    555 	if (map_init(gpt, devsz) == -1)
    556 		goto close;
    557 
    558 	if (gpt_mbr(gpt, 0LL) == -1)
    559 		goto close;
    560 	if ((found = gpt_gpt(gpt, 1LL, 1)) == -1)
    561 		goto close;
    562 	if (gpt_gpt(gpt, devsz - 1LL, found) == -1)
    563 		goto close;
    564 
    565 	return gpt;
    566 
    567  close:
    568 	if (gpt->fd != -1)
    569 		close(gpt->fd);
    570 	free(gpt);
    571 	return NULL;
    572 }
    573 
    574 void
    575 gpt_close(gpt_t gpt)
    576 {
    577 
    578 	if (!(gpt->flags & GPT_MODIFIED))
    579 		goto out;
    580 
    581 	if (!(gpt->flags & GPT_NOSYNC)) {
    582 #ifdef DIOCMWEDGES
    583 		int bits;
    584 		if (ioctl(gpt->fd, DIOCMWEDGES, &bits) == -1)
    585 			gpt_warn(gpt, "Can't update wedge information");
    586 		else
    587 			goto out;
    588 #endif
    589 	}
    590 	if (!(gpt->flags & GPT_FILE))
    591 		gpt_msg(gpt, "You need to run \"dkctl %s makewedges\""
    592 		    " for the changes to take effect\n", gpt->device_name);
    593 
    594 out:
    595 	close(gpt->fd);
    596 }
    597 
    598 __printflike(2, 0)
    599 static void
    600 gpt_vwarnx(gpt_t gpt, const char *fmt, va_list ap, const char *e)
    601 {
    602 	if (gpt && (gpt->flags & GPT_QUIET))
    603 		return;
    604 	fprintf(stderr, "%s: ", getprogname());
    605 	if (gpt)
    606 		fprintf(stderr, "%s: ", gpt->device_name);
    607 	vfprintf(stderr, fmt, ap);
    608 	if (e)
    609 		fprintf(stderr, " (%s)\n", e);
    610 	else
    611 		fputc('\n', stderr);
    612 }
    613 
    614 void
    615 gpt_warnx(gpt_t gpt, const char *fmt, ...)
    616 {
    617 	va_list ap;
    618 
    619 	va_start(ap, fmt);
    620 	gpt_vwarnx(gpt, fmt, ap, NULL);
    621 	va_end(ap);
    622 }
    623 
    624 void
    625 gpt_warn(gpt_t gpt, const char *fmt, ...)
    626 {
    627 	va_list ap;
    628 
    629 	va_start(ap, fmt);
    630 	gpt_vwarnx(gpt, fmt, ap, strerror(errno));
    631 	va_end(ap);
    632 }
    633 
    634 void
    635 gpt_msg(gpt_t gpt, const char *fmt, ...)
    636 {
    637 	va_list ap;
    638 
    639 	if (gpt && (gpt->flags & GPT_QUIET))
    640 		return;
    641 	if (gpt)
    642 		printf("%s: ", gpt->device_name);
    643 	va_start(ap, fmt);
    644 	vprintf(fmt, ap);
    645 	va_end(ap);
    646 	printf("\n");
    647 }
    648 
    649 struct gpt_hdr *
    650 gpt_hdr(gpt_t gpt)
    651 {
    652 	gpt->gpt = map_find(gpt, MAP_TYPE_PRI_GPT_HDR);
    653 	if (gpt->gpt == NULL) {
    654 		gpt_warnx(gpt, "No primary GPT header; run create or recover");
    655 		return NULL;
    656 	}
    657 
    658 	gpt->tpg = map_find(gpt, MAP_TYPE_SEC_GPT_HDR);
    659 	if (gpt->tpg == NULL) {
    660 		gpt_warnx(gpt, "No secondary GPT header; run recover");
    661 		return NULL;
    662 	}
    663 
    664 	gpt->tbl = map_find(gpt, MAP_TYPE_PRI_GPT_TBL);
    665 	gpt->lbt = map_find(gpt, MAP_TYPE_SEC_GPT_TBL);
    666 	if (gpt->tbl == NULL || gpt->lbt == NULL) {
    667 		gpt_warnx(gpt, "Corrupt maps, run recover");
    668 		return NULL;
    669 	}
    670 
    671 	return gpt->gpt->map_data;
    672 }
    673 
    674 int
    675 gpt_write_crc(gpt_t gpt, map_t map, map_t tbl)
    676 {
    677 	struct gpt_hdr *hdr = map->map_data;
    678 
    679 	hdr->hdr_crc_table = htole32(crc32(tbl->map_data,
    680 	    le32toh(hdr->hdr_entries) * le32toh(hdr->hdr_entsz)));
    681 	hdr->hdr_crc_self = 0;
    682 	hdr->hdr_crc_self = htole32(crc32(hdr, le32toh(hdr->hdr_size)));
    683 
    684 	if (gpt_write(gpt, map) == -1) {
    685 		gpt_warn(gpt, "Error writing crc map");
    686 		return -1;
    687 	}
    688 
    689 	if (gpt_write(gpt, tbl) == -1) {
    690 		gpt_warn(gpt, "Error writing crc table");
    691 		return -1;
    692 	}
    693 
    694 	return 0;
    695 }
    696 
    697 int
    698 gpt_write_primary(gpt_t gpt)
    699 {
    700 	return gpt_write_crc(gpt, gpt->gpt, gpt->tbl);
    701 }
    702 
    703 
    704 int
    705 gpt_write_backup(gpt_t gpt)
    706 {
    707 	return gpt_write_crc(gpt, gpt->tpg, gpt->lbt);
    708 }
    709 
    710 void
    711 gpt_create_pmbr_part(struct mbr_part *part, off_t last, int active)
    712 {
    713 	part->part_flag = active ? 0x80 : 0;
    714 	part->part_shd = 0x00;
    715 	part->part_ssect = 0x02;
    716 	part->part_scyl = 0x00;
    717 	part->part_typ = MBR_PTYPE_PMBR;
    718 	part->part_ehd = 0xfe;
    719 	part->part_esect = 0xff;
    720 	part->part_ecyl = 0xff;
    721 	part->part_start_lo = htole16(1);
    722 	if (last > 0xffffffff) {
    723 		part->part_size_lo = htole16(0xffff);
    724 		part->part_size_hi = htole16(0xffff);
    725 	} else {
    726 		part->part_size_lo = htole16((uint16_t)last);
    727 		part->part_size_hi = htole16((uint16_t)(last >> 16));
    728 	}
    729 }
    730 
    731 struct gpt_ent *
    732 gpt_ent(map_t map, map_t tbl, unsigned int i)
    733 {
    734 	struct gpt_hdr *hdr = map->map_data;
    735 	return (void *)((char *)tbl->map_data + i * le32toh(hdr->hdr_entsz));
    736 }
    737 
    738 struct gpt_ent *
    739 gpt_ent_primary(gpt_t gpt, unsigned int i)
    740 {
    741 	return gpt_ent(gpt->gpt, gpt->tbl, i);
    742 }
    743 
    744 struct gpt_ent *
    745 gpt_ent_backup(gpt_t gpt, unsigned int i)
    746 {
    747 	return gpt_ent(gpt->tpg, gpt->lbt, i);
    748 }
    749 
    750 int
    751 gpt_usage(const char *prefix, const struct gpt_cmd *cmd)
    752 {
    753 	const char **a = cmd->help;
    754 	size_t hlen = cmd->hlen;
    755 	size_t i;
    756 
    757 	if (prefix == NULL) {
    758 		const char *pname = getprogname();
    759 		const char *d1, *d2, *d = " <device>";
    760 		int len = (int)strlen(pname);
    761 		if (strcmp(pname, "gpt") == 0) {
    762 			d1 = "";
    763 			d2 = d;
    764 		} else {
    765 			d2 = "";
    766 			d1 = d;
    767 		}
    768 		fprintf(stderr, "Usage: %s%s %s %s%s\n", pname,
    769 		    d1, cmd->name, a[0], d2);
    770 		for (i = 1; i < hlen; i++) {
    771 			fprintf(stderr,
    772 			    "       %*s%s %s %s%s\n", len, "",
    773 			    d1, cmd->name, a[i], d2);
    774 		}
    775 	} else {
    776 		for (i = 0; i < hlen; i++)
    777 		    fprintf(stderr, "%s%s %s\n", prefix, cmd->name, a[i]);
    778 	}
    779 	return -1;
    780 }
    781 
    782 off_t
    783 gpt_last(gpt_t gpt)
    784 {
    785 	return gpt->mediasz / gpt->secsz - 1LL;
    786 }
    787 
    788 off_t
    789 gpt_create(gpt_t gpt, off_t last, u_int parts, int primary_only)
    790 {
    791 	off_t blocks;
    792 	map_t map;
    793 	struct gpt_hdr *hdr;
    794 	struct gpt_ent *ent;
    795 	unsigned int i;
    796 	void *p;
    797 
    798 	if (map_find(gpt, MAP_TYPE_PRI_GPT_HDR) != NULL ||
    799 	    map_find(gpt, MAP_TYPE_SEC_GPT_HDR) != NULL) {
    800 		gpt_warnx(gpt, "Device already contains a GPT, "
    801 		    "destroy it first");
    802 		return -1;
    803 	}
    804 
    805 	/* Get the amount of free space after the MBR */
    806 	blocks = map_free(gpt, 1LL, 0LL);
    807 	if (blocks == 0LL) {
    808 		gpt_warnx(gpt, "No room for the GPT header");
    809 		return -1;
    810 	}
    811 
    812 	/* Don't create more than parts entries. */
    813 	if ((uint64_t)(blocks - 1) * gpt->secsz >
    814 	    parts * sizeof(struct gpt_ent)) {
    815 		blocks = (off_t)((parts * sizeof(struct gpt_ent)) / gpt->secsz);
    816 		if ((parts * sizeof(struct gpt_ent)) % gpt->secsz)
    817 			blocks++;
    818 		blocks++;		/* Don't forget the header itself */
    819 	}
    820 
    821 	/* Never cross the median of the device. */
    822 	if ((blocks + 1LL) > ((last + 1LL) >> 1))
    823 		blocks = ((last + 1LL) >> 1) - 1LL;
    824 
    825 	/*
    826 	 * Get the amount of free space at the end of the device and
    827 	 * calculate the size for the GPT structures.
    828 	 */
    829 	map = map_last(gpt);
    830 	if (map->map_type != MAP_TYPE_UNUSED) {
    831 		gpt_warnx(gpt, "No room for the backup header");
    832 		return -1;
    833 	}
    834 
    835 	if (map->map_size < blocks)
    836 		blocks = map->map_size;
    837 	if (blocks == 1LL) {
    838 		gpt_warnx(gpt, "No room for the GPT table");
    839 		return -1;
    840 	}
    841 
    842 	blocks--;		/* Number of blocks in the GPT table. */
    843 
    844 	if (gpt_add_hdr(gpt, MAP_TYPE_PRI_GPT_HDR, 1) == -1)
    845 		return -1;
    846 
    847 	if ((p = calloc((size_t)blocks, gpt->secsz)) == NULL) {
    848 		gpt_warnx(gpt, "Can't allocate the primary GPT table");
    849 		return -1;
    850 	}
    851 	if ((gpt->tbl = map_add(gpt, 2LL, blocks,
    852 	    MAP_TYPE_PRI_GPT_TBL, p, 1)) == NULL) {
    853 		free(p);
    854 		gpt_warnx(gpt, "Can't add the primary GPT table");
    855 		return -1;
    856 	}
    857 
    858 	hdr = gpt->gpt->map_data;
    859 	memcpy(hdr->hdr_sig, GPT_HDR_SIG, sizeof(hdr->hdr_sig));
    860 
    861 	/*
    862 	 * XXX struct gpt_hdr is not a multiple of 8 bytes in size and thus
    863 	 * contains padding we must not include in the size.
    864 	 */
    865 	hdr->hdr_revision = htole32(GPT_HDR_REVISION);
    866 	hdr->hdr_size = htole32(GPT_HDR_SIZE);
    867 	hdr->hdr_lba_self = htole64((uint64_t)gpt->gpt->map_start);
    868 	hdr->hdr_lba_alt = htole64((uint64_t)last);
    869 	hdr->hdr_lba_start = htole64((uint64_t)(gpt->tbl->map_start + blocks));
    870 	hdr->hdr_lba_end = htole64((uint64_t)(last - blocks - 1LL));
    871 	if (gpt_uuid_generate(gpt, hdr->hdr_guid) == -1)
    872 		return -1;
    873 	hdr->hdr_lba_table = htole64((uint64_t)(gpt->tbl->map_start));
    874 	hdr->hdr_entries = htole32((uint32_t)(((uint64_t)blocks * gpt->secsz) /
    875 	    sizeof(struct gpt_ent)));
    876 	if (le32toh(hdr->hdr_entries) > parts)
    877 		hdr->hdr_entries = htole32(parts);
    878 	hdr->hdr_entsz = htole32(sizeof(struct gpt_ent));
    879 
    880 	ent = gpt->tbl->map_data;
    881 	for (i = 0; i < le32toh(hdr->hdr_entries); i++) {
    882 		if (gpt_uuid_generate(gpt, ent[i].ent_guid) == -1)
    883 			return -1;
    884 	}
    885 
    886 	/*
    887 	 * Create backup GPT if the user didn't suppress it.
    888 	 */
    889 	if (primary_only)
    890 		return last;
    891 
    892 	if (gpt_add_hdr(gpt, MAP_TYPE_SEC_GPT_HDR, last) == -1)
    893 		return -1;
    894 
    895 	if ((gpt->lbt = map_add(gpt, last - blocks, blocks,
    896 	    MAP_TYPE_SEC_GPT_TBL, gpt->tbl->map_data, 0)) == NULL) {
    897 		gpt_warnx(gpt, "Can't add the secondary GPT table");
    898 		return -1;
    899 	}
    900 
    901 	memcpy(gpt->tpg->map_data, gpt->gpt->map_data, gpt->secsz);
    902 
    903 	hdr = gpt->tpg->map_data;
    904 	hdr->hdr_lba_self = htole64((uint64_t)gpt->tpg->map_start);
    905 	hdr->hdr_lba_alt = htole64((uint64_t)gpt->gpt->map_start);
    906 	hdr->hdr_lba_table = htole64((uint64_t)gpt->lbt->map_start);
    907 	return last;
    908 }
    909 
    910 static int
    911 gpt_size_get(gpt_t gpt, off_t *size)
    912 {
    913 	off_t sectors;
    914 	int64_t human_num;
    915 	char *p;
    916 
    917 	if (*size > 0)
    918 		return -1;
    919 	sectors = strtoll(optarg, &p, 10);
    920 	if (sectors < 1)
    921 		return -1;
    922 	if (*p == '\0' || ((*p == 's' || *p == 'S') && p[1] == '\0')) {
    923 		*size = sectors * gpt->secsz;
    924 		return 0;
    925 	}
    926 	if ((*p == 'b' || *p == 'B') && p[1] == '\0') {
    927 		*size = sectors;
    928 		return 0;
    929 	}
    930 	if (dehumanize_number(optarg, &human_num) < 0)
    931 		return -1;
    932 	*size = human_num;
    933 	return 0;
    934 }
    935 
    936 int
    937 gpt_human_get(gpt_t gpt, off_t *human)
    938 {
    939 	int64_t human_num;
    940 
    941 	if (*human > 0) {
    942 		gpt_warn(gpt, "Already set to %jd new `%s'", (intmax_t)*human,
    943 		    optarg);
    944 		return -1;
    945 	}
    946 	if (dehumanize_number(optarg, &human_num) < 0) {
    947 		gpt_warn(gpt, "Bad number `%s'", optarg);
    948 		return -1;
    949 	}
    950 	*human = human_num;
    951 	if (*human < 1) {
    952 		gpt_warn(gpt, "Number `%s' < 1", optarg);
    953 		return -1;
    954 	}
    955 	return 0;
    956 }
    957 
    958 int
    959 gpt_add_find(gpt_t gpt, struct gpt_find *find, int ch)
    960 {
    961 	switch (ch) {
    962 	case 'a':
    963 		if (find->all > 0) {
    964 			gpt_warn(gpt, "-a is already set");
    965 			return -1;
    966 		}
    967 		find->all = 1;
    968 		break;
    969 	case 'b':
    970 		if (gpt_human_get(gpt, &find->block) == -1)
    971 			return -1;
    972 		break;
    973 	case 'i':
    974 		if (gpt_uint_get(gpt, &find->entry) == -1)
    975 			return -1;
    976 		break;
    977 	case 'L':
    978 		if (gpt_name_get(gpt, &find->label) == -1)
    979 			return -1;
    980 		break;
    981 	case 's':
    982 		if (gpt_size_get(gpt, &find->size) == -1)
    983 			return -1;
    984 		break;
    985 	case 't':
    986 		if (!gpt_uuid_is_nil(find->type))
    987 			return -1;
    988 		if (gpt_uuid_parse(optarg, find->type) != 0)
    989 			return -1;
    990 		break;
    991 	default:
    992 		gpt_warn(gpt, "Unknown find option `%c'", ch);
    993 		return -1;
    994 	}
    995 	return 0;
    996 }
    997 
    998 int
    999 gpt_change_ent(gpt_t gpt, const struct gpt_find *find,
   1000     void (*cfn)(struct gpt_ent *, void *), void *v)
   1001 {
   1002 	map_t m;
   1003 	struct gpt_hdr *hdr;
   1004 	struct gpt_ent *ent;
   1005 	unsigned int i;
   1006 	uint8_t utfbuf[__arraycount(ent->ent_name) * 3 + 1];
   1007 
   1008 	if (!find->all ^
   1009 	    (find->block > 0 || find->entry > 0 || find->label != NULL
   1010 	    || find->size > 0 || !gpt_uuid_is_nil(find->type)))
   1011 		return -1;
   1012 
   1013 	if ((hdr = gpt_hdr(gpt)) == NULL)
   1014 		return -1;
   1015 
   1016 	/* Relabel all matching entries in the map. */
   1017 	for (m = map_first(gpt); m != NULL; m = m->map_next) {
   1018 		if (m->map_type != MAP_TYPE_GPT_PART || m->map_index < 1)
   1019 			continue;
   1020 		if (find->entry > 0 && find->entry != m->map_index)
   1021 			continue;
   1022 		if (find->block > 0 && find->block != m->map_start)
   1023 			continue;
   1024 		if (find->size > 0 && find->size != m->map_size)
   1025 			continue;
   1026 
   1027 		i = m->map_index - 1;
   1028 
   1029 		ent = gpt_ent_primary(gpt, i);
   1030 		if (find->label != NULL) {
   1031 			utf16_to_utf8(ent->ent_name, utfbuf, sizeof(utfbuf));
   1032 			if (strcmp((char *)find->label, (char *)utfbuf) == 0)
   1033 				continue;
   1034 		}
   1035 
   1036 		if (!gpt_uuid_is_nil(find->type) &&
   1037 		    !gpt_uuid_equal(find->type, ent->ent_type))
   1038 			continue;
   1039 
   1040 		/* Change the primary entry. */
   1041 		(*cfn)(ent, v);
   1042 
   1043 		if (gpt_write_primary(gpt) == -1)
   1044 			return -1;
   1045 
   1046 		ent = gpt_ent_backup(gpt, i);
   1047 		/* Change the secondary entry. */
   1048 		(*cfn)(ent, v);
   1049 
   1050 		if (gpt_write_backup(gpt) == -1)
   1051 			return -1;
   1052 
   1053 		gpt_msg(gpt, "Partition %d %s", m->map_index, find->msg);
   1054 	}
   1055 	return 0;
   1056 }
   1057 
   1058 int
   1059 gpt_add_ais(gpt_t gpt, off_t *alignment, u_int *entry, off_t *size, int ch)
   1060 {
   1061 	switch (ch) {
   1062 	case 'a':
   1063 		if (gpt_human_get(gpt, alignment) == -1)
   1064 			return -1;
   1065 		return 0;
   1066 	case 'i':
   1067 		if (gpt_uint_get(gpt, entry) == -1)
   1068 			return -1;
   1069 		return 0;
   1070 	case 's':
   1071 		if (gpt_size_get(gpt, size) == -1)
   1072 			return -1;
   1073 		return 0;
   1074 	default:
   1075 		gpt_warn(gpt, "Unknown alignment/index/size option `%c'", ch);
   1076 		return -1;
   1077 	}
   1078 }
   1079 
   1080 off_t
   1081 gpt_check_ais(gpt_t gpt, off_t alignment, u_int entry, off_t size)
   1082 {
   1083 	if (entry == 0) {
   1084 		gpt_warnx(gpt, "Entry not specified");
   1085 		return -1;
   1086 	}
   1087 	if (alignment % gpt->secsz != 0) {
   1088 		gpt_warnx(gpt, "Alignment (%#jx) must be a multiple of "
   1089 		    "sector size (%#x)", (uintmax_t)alignment, gpt->secsz);
   1090 		return -1;
   1091 	}
   1092 
   1093 	if (size % gpt->secsz != 0) {
   1094 		gpt_warnx(gpt, "Size (%#jx) must be a multiple of "
   1095 		    "sector size (%#x)", (uintmax_t)size, gpt->secsz);
   1096 		return -1;
   1097 	}
   1098 	if (size > 0)
   1099 		return size / gpt->secsz;
   1100 	return 0;
   1101 }
   1102 
   1103 static const struct nvd {
   1104 	const char *name;
   1105 	uint64_t mask;
   1106 	const char *description;
   1107 } gpt_attr[] = {
   1108 	{
   1109 		"biosboot",
   1110 		GPT_ENT_ATTR_LEGACY_BIOS_BOOTABLE,
   1111 		"Legacy BIOS boot partition",
   1112 	},
   1113 	{
   1114 		"bootme",
   1115 		GPT_ENT_ATTR_BOOTME,
   1116 		"Bootable partition",
   1117 	},
   1118 	{
   1119 		"bootfailed",
   1120 		GPT_ENT_ATTR_BOOTFAILED,
   1121 		"Partition that marked bootonce failed to boot",
   1122 	},
   1123 	{
   1124 		"bootonce",
   1125 		GPT_ENT_ATTR_BOOTONCE,
   1126 		"Attempt to boot this partition only once",
   1127 	},
   1128 	{
   1129 		"noblockio",
   1130 		GPT_ENT_ATTR_NO_BLOCK_IO_PROTOCOL,
   1131 		"UEFI won't recognize file system for block I/O",
   1132 	},
   1133 	{
   1134 		"required",
   1135 		GPT_ENT_ATTR_REQUIRED_PARTITION,
   1136 		"Partition required for platform to function",
   1137 	},
   1138 };
   1139 
   1140 int
   1141 gpt_attr_get(gpt_t gpt, uint64_t *attributes)
   1142 {
   1143 	size_t i;
   1144 	int rv = 0;
   1145 	char *ptr;
   1146 
   1147 	*attributes = 0;
   1148 
   1149 	for (ptr = strtok(optarg, ","); ptr; ptr = strtok(NULL, ",")) {
   1150 		for (i = 0; i < __arraycount(gpt_attr); i++)
   1151 			if (strcmp(gpt_attr[i].name, ptr) == 0)
   1152 				break;
   1153 		if (i == __arraycount(gpt_attr)) {
   1154 			gpt_warnx(gpt, "Unregognized attribute `%s'", ptr);
   1155 			rv = -1;
   1156 		} else
   1157 			*attributes |= gpt_attr[i].mask;
   1158 	}
   1159 	return rv;
   1160 }
   1161 
   1162 void
   1163 gpt_attr_help(const char *prefix)
   1164 {
   1165 	size_t i;
   1166 
   1167 	for (i = 0; i < __arraycount(gpt_attr); i++)
   1168 		printf("%s%10.10s\t%s\n", prefix, gpt_attr[i].name,
   1169 		    gpt_attr[i].description);
   1170 }
   1171 
   1172 const char *
   1173 gpt_attr_list(char *buf, size_t len, uint64_t attributes)
   1174 {
   1175 	size_t i;
   1176 	strlcpy(buf, "", len);
   1177 
   1178 	for (i = 0; i < __arraycount(gpt_attr); i++)
   1179 		if (attributes & gpt_attr[i].mask) {
   1180 			strlcat(buf, buf[0] ? ", " : "", len);
   1181 			strlcat(buf, gpt_attr[i].name, len);
   1182 		}
   1183 	return buf;
   1184 }
   1185 
   1186 int
   1187 gpt_attr_update(gpt_t gpt, u_int entry, uint64_t set, uint64_t clr)
   1188 {
   1189 	struct gpt_hdr *hdr;
   1190 	struct gpt_ent *ent;
   1191 	unsigned int i;
   1192 
   1193 	if (entry == 0 || (set == 0 && clr == 0)) {
   1194 		gpt_warnx(gpt, "Nothing to set");
   1195 		return -1;
   1196 	}
   1197 
   1198 	if ((hdr = gpt_hdr(gpt)) == NULL)
   1199 		return -1;
   1200 
   1201 	if (entry > le32toh(hdr->hdr_entries)) {
   1202 		gpt_warnx(gpt, "Index %u out of range (%u max)",
   1203 		    entry, le32toh(hdr->hdr_entries));
   1204 		return -1;
   1205 	}
   1206 
   1207 	i = entry - 1;
   1208 	ent = gpt_ent_primary(gpt, i);
   1209 	if (gpt_uuid_is_nil(ent->ent_type)) {
   1210 		gpt_warnx(gpt, "Entry at index %u is unused", entry);
   1211 		return -1;
   1212 	}
   1213 
   1214 	ent->ent_attr &= ~clr;
   1215 	ent->ent_attr |= set;
   1216 
   1217 	if (gpt_write_primary(gpt) == -1)
   1218 		return -1;
   1219 
   1220 	ent = gpt_ent_backup(gpt, i);
   1221 	ent->ent_attr &= ~clr;
   1222 	ent->ent_attr |= set;
   1223 
   1224 	if (gpt_write_backup(gpt) == -1)
   1225 		return -1;
   1226 	gpt_msg(gpt, "Partition %d attributes updated", entry);
   1227 	return 0;
   1228 }
   1229 
   1230 int
   1231 gpt_uint_get(gpt_t gpt, u_int *entry)
   1232 {
   1233 	char *p;
   1234 	if (*entry > 0)
   1235 		return -1;
   1236 	*entry = (u_int)strtoul(optarg, &p, 10);
   1237 	if (*p != 0 || *entry < 1) {
   1238 		gpt_warn(gpt, "Bad number `%s'", optarg);
   1239 		return -1;
   1240 	}
   1241 	return 0;
   1242 }
   1243 int
   1244 gpt_uuid_get(gpt_t gpt, gpt_uuid_t *uuid)
   1245 {
   1246 	if (!gpt_uuid_is_nil(*uuid))
   1247 		return -1;
   1248 	if (gpt_uuid_parse(optarg, *uuid) != 0) {
   1249 		gpt_warn(gpt, "Can't parse uuid");
   1250 		return -1;
   1251 	}
   1252 	return 0;
   1253 }
   1254 
   1255 int
   1256 gpt_name_get(gpt_t gpt, void *v)
   1257 {
   1258 	char **name = v;
   1259 	if (*name != NULL)
   1260 		return -1;
   1261 	*name = strdup(optarg);
   1262 	if (*name == NULL) {
   1263 		gpt_warn(gpt, "Can't copy string");
   1264 		return -1;
   1265 	}
   1266 	return 0;
   1267 }
   1268 
   1269 void
   1270 gpt_show_num(const char *prompt, uintmax_t num)
   1271 {
   1272 #ifdef HN_AUTOSCALE
   1273 	char human_num[5];
   1274 	if (humanize_number(human_num, 5, (int64_t)num ,
   1275 	    "", HN_AUTOSCALE, HN_NOSPACE|HN_B) < 0)
   1276 		human_num[0] = '\0';
   1277 #endif
   1278 	printf("%s: %ju", prompt, num);
   1279 #ifdef HN_AUTOSCALE
   1280 	if (human_num[0] != '\0')
   1281 		printf(" (%s)", human_num);
   1282 #endif
   1283 	printf("\n");
   1284 }
   1285 
   1286 int
   1287 gpt_add_hdr(gpt_t gpt, int type, off_t loc)
   1288 {
   1289 	void *p;
   1290 	map_t *t;
   1291 	const char *msg;
   1292 
   1293 	switch (type) {
   1294 	case MAP_TYPE_PRI_GPT_HDR:
   1295 		t = &gpt->gpt;
   1296 		msg = "primary";
   1297 		break;
   1298 	case MAP_TYPE_SEC_GPT_HDR:
   1299 		t = &gpt->tpg;
   1300 		msg = "secondary";
   1301 		break;
   1302 	default:
   1303 		gpt_warnx(gpt, "Unknown GPT header type %d", type);
   1304 		return -1;
   1305 	}
   1306 
   1307 	if ((p = calloc(1, gpt->secsz)) == NULL) {
   1308 		gpt_warn(gpt, "Error allocating %s GPT header", msg);
   1309 		return -1;
   1310 	}
   1311 
   1312 	*t = map_add(gpt, loc, 1LL, type, p, 1);
   1313 	if (*t == NULL) {
   1314 		gpt_warn(gpt, "Error adding %s GPT header", msg);
   1315 		free(p);
   1316 		return -1;
   1317 	}
   1318 	return 0;
   1319 }
   1320