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