xref: /netbsd-src/sbin/gpt/gpt.c (revision 10edc71f1e2c4c397da6dde358b825df1121851c)
15819a8c0Schristos /*-
25819a8c0Schristos  * Copyright (c) 2002 Marcel Moolenaar
35819a8c0Schristos  * All rights reserved.
45819a8c0Schristos  *
55819a8c0Schristos  * Redistribution and use in source and binary forms, with or without
65819a8c0Schristos  * modification, are permitted provided that the following conditions
75819a8c0Schristos  * are met:
85819a8c0Schristos  *
95819a8c0Schristos  * 1. Redistributions of source code must retain the above copyright
105819a8c0Schristos  *    notice, this list of conditions and the following disclaimer.
115819a8c0Schristos  * 2. Redistributions in binary form must reproduce the above copyright
125819a8c0Schristos  *    notice, this list of conditions and the following disclaimer in the
135819a8c0Schristos  *    documentation and/or other materials provided with the distribution.
145819a8c0Schristos  *
155819a8c0Schristos  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
165819a8c0Schristos  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
175819a8c0Schristos  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
185819a8c0Schristos  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
195819a8c0Schristos  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
205819a8c0Schristos  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
215819a8c0Schristos  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
225819a8c0Schristos  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
235819a8c0Schristos  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
245819a8c0Schristos  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
255819a8c0Schristos  *
265819a8c0Schristos  * CRC32 code derived from work by Gary S. Brown.
275819a8c0Schristos  */
285819a8c0Schristos 
29a50708a1Schristos #if HAVE_NBTOOL_CONFIG_H
30a50708a1Schristos #include "nbtool_config.h"
31a50708a1Schristos #endif
32a50708a1Schristos 
335819a8c0Schristos #include <sys/cdefs.h>
349b522365Schristos #ifdef __FBSDID
355819a8c0Schristos __FBSDID("$FreeBSD: src/sbin/gpt/gpt.c,v 1.16 2006/07/07 02:44:23 marcel Exp $");
369b522365Schristos #endif
379b522365Schristos #ifdef __RCSID
38*10edc71fSmlelstv __RCSID("$NetBSD: gpt.c,v 1.90 2024/10/20 08:21:30 mlelstv Exp $");
399b522365Schristos #endif
405819a8c0Schristos 
415819a8c0Schristos #include <sys/param.h>
425819a8c0Schristos #include <sys/types.h>
435819a8c0Schristos #include <sys/stat.h>
449b522365Schristos #include <sys/ioctl.h>
4516550bc8Sjakllsch #include <sys/bootblock.h>
465819a8c0Schristos 
475819a8c0Schristos #include <err.h>
485819a8c0Schristos #include <errno.h>
495819a8c0Schristos #include <fcntl.h>
505819a8c0Schristos #include <paths.h>
515819a8c0Schristos #include <stddef.h>
52ca4e0dcdSchristos #include <stdarg.h>
535819a8c0Schristos #include <stdio.h>
545819a8c0Schristos #include <stdlib.h>
555819a8c0Schristos #include <string.h>
565819a8c0Schristos #include <unistd.h>
579b522365Schristos #include <ctype.h>
585819a8c0Schristos 
595819a8c0Schristos #include "map.h"
605819a8c0Schristos #include "gpt.h"
610b43d398Schristos #include "gpt_private.h"
625819a8c0Schristos 
635819a8c0Schristos static uint32_t crc32_tab[] = {
645819a8c0Schristos 	0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f,
655819a8c0Schristos 	0xe963a535, 0x9e6495a3,	0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
665819a8c0Schristos 	0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2,
675819a8c0Schristos 	0xf3b97148, 0x84be41de,	0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
685819a8c0Schristos 	0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec,	0x14015c4f, 0x63066cd9,
695819a8c0Schristos 	0xfa0f3d63, 0x8d080df5,	0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,
705819a8c0Schristos 	0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b,	0x35b5a8fa, 0x42b2986c,
715819a8c0Schristos 	0xdbbbc9d6, 0xacbcf940,	0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
725819a8c0Schristos 	0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423,
735819a8c0Schristos 	0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
745819a8c0Schristos 	0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d,	0x76dc4190, 0x01db7106,
755819a8c0Schristos 	0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
765819a8c0Schristos 	0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d,
775819a8c0Schristos 	0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,
785819a8c0Schristos 	0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950,
795819a8c0Schristos 	0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
805819a8c0Schristos 	0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7,
815819a8c0Schristos 	0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,
825819a8c0Schristos 	0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa,
835819a8c0Schristos 	0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
845819a8c0Schristos 	0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81,
855819a8c0Schristos 	0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a,
865819a8c0Schristos 	0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84,
875819a8c0Schristos 	0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
885819a8c0Schristos 	0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb,
895819a8c0Schristos 	0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,
905819a8c0Schristos 	0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e,
915819a8c0Schristos 	0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
925819a8c0Schristos 	0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55,
935819a8c0Schristos 	0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
945819a8c0Schristos 	0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28,
955819a8c0Schristos 	0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
965819a8c0Schristos 	0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f,
975819a8c0Schristos 	0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38,
985819a8c0Schristos 	0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242,
995819a8c0Schristos 	0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
1005819a8c0Schristos 	0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69,
1015819a8c0Schristos 	0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2,
1025819a8c0Schristos 	0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc,
1035819a8c0Schristos 	0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
1045819a8c0Schristos 	0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693,
1055819a8c0Schristos 	0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
1065819a8c0Schristos 	0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d
1075819a8c0Schristos };
1085819a8c0Schristos 
1095819a8c0Schristos uint32_t
1105819a8c0Schristos crc32(const void *buf, size_t size)
1115819a8c0Schristos {
1125819a8c0Schristos 	const uint8_t *p;
1135819a8c0Schristos 	uint32_t crc;
1145819a8c0Schristos 
1155819a8c0Schristos 	p = buf;
1165819a8c0Schristos 	crc = ~0U;
1175819a8c0Schristos 
1185819a8c0Schristos 	while (size--)
1195819a8c0Schristos 		crc = crc32_tab[(crc ^ *p++) & 0xFF] ^ (crc >> 8);
1205819a8c0Schristos 
1215819a8c0Schristos 	return crc ^ ~0U;
1225819a8c0Schristos }
1235819a8c0Schristos 
124d6862190Schristos /*
125d6862190Schristos  * Produce a NUL-terminated utf-8 string from the non-NUL-terminated
126d6862190Schristos  * utf16 string.
127d6862190Schristos  */
128cdf86847Schristos void
129d6862190Schristos utf16_to_utf8(const uint16_t *s16, size_t s16len, uint8_t *s8, size_t s8len)
1305819a8c0Schristos {
131d6862190Schristos 	size_t s8idx, s16idx;
1325819a8c0Schristos 	uint32_t utfchar;
1335819a8c0Schristos 	unsigned int c;
1345819a8c0Schristos 
135d6862190Schristos 	for (s16idx = 0; s16idx < s16len; s16idx++)
136d6862190Schristos 		if (s16[s16idx] == 0)
137d6862190Schristos 			break;
138d6862190Schristos 
139d6862190Schristos 	s16len = s16idx;
1405819a8c0Schristos 	s8idx = s16idx = 0;
1415819a8c0Schristos 	while (s16idx < s16len) {
1425819a8c0Schristos 		utfchar = le16toh(s16[s16idx++]);
1435819a8c0Schristos 		if ((utfchar & 0xf800) == 0xd800) {
1445819a8c0Schristos 			c = le16toh(s16[s16idx]);
1455819a8c0Schristos 			if ((utfchar & 0x400) != 0 || (c & 0xfc00) != 0xdc00)
1465819a8c0Schristos 				utfchar = 0xfffd;
1475819a8c0Schristos 			else
1485819a8c0Schristos 				s16idx++;
1495819a8c0Schristos 		}
1505819a8c0Schristos 		if (utfchar < 0x80) {
151cdf86847Schristos 			if (s8idx + 1 >= s8len)
152cdf86847Schristos 				break;
1535c1ccc6eSchristos 			s8[s8idx++] = (uint8_t)utfchar;
1545819a8c0Schristos 		} else if (utfchar < 0x800) {
155cdf86847Schristos 			if (s8idx + 2 >= s8len)
156cdf86847Schristos 				break;
1575c1ccc6eSchristos 			s8[s8idx++] = (uint8_t)(0xc0 | (utfchar >> 6));
1585c1ccc6eSchristos 			s8[s8idx++] = (uint8_t)(0x80 | (utfchar & 0x3f));
1595819a8c0Schristos 		} else if (utfchar < 0x10000) {
160cdf86847Schristos 			if (s8idx + 3 >= s8len)
161cdf86847Schristos 				break;
1625c1ccc6eSchristos 			s8[s8idx++] = (uint8_t)(0xe0 | (utfchar >> 12));
1635c1ccc6eSchristos 			s8[s8idx++] = (uint8_t)(0x80 | ((utfchar >> 6) & 0x3f));
1645c1ccc6eSchristos 			s8[s8idx++] = (uint8_t)(0x80 | (utfchar & 0x3f));
1655819a8c0Schristos 		} else if (utfchar < 0x200000) {
166cdf86847Schristos 			if (s8idx + 4 >= s8len)
167cdf86847Schristos 				break;
1685c1ccc6eSchristos 			s8[s8idx++] = (uint8_t)(0xf0 | (utfchar >> 18));
1695c1ccc6eSchristos 			s8[s8idx++] = (uint8_t)(0x80 | ((utfchar >> 12) & 0x3f));
1705c1ccc6eSchristos 			s8[s8idx++] = (uint8_t)(0x80 | ((utfchar >> 6) & 0x3f));
1715c1ccc6eSchristos 			s8[s8idx++] = (uint8_t)(0x80 | (utfchar & 0x3f));
1725819a8c0Schristos 		}
1735819a8c0Schristos 	}
174cdf86847Schristos 	s8[s8idx] = 0;
1755819a8c0Schristos }
1765819a8c0Schristos 
177d6862190Schristos /*
178d6862190Schristos  * Produce a non-NUL-terminated utf-16 string from the NUL-terminated
179d6862190Schristos  * utf8 string.
180d6862190Schristos  */
1815819a8c0Schristos void
1825819a8c0Schristos utf8_to_utf16(const uint8_t *s8, uint16_t *s16, size_t s16len)
1835819a8c0Schristos {
1845819a8c0Schristos 	size_t s16idx, s8idx, s8len;
1859b522365Schristos 	uint32_t utfchar = 0;
1865819a8c0Schristos 	unsigned int c, utfbytes;
1875819a8c0Schristos 
1885819a8c0Schristos 	s8len = 0;
1895819a8c0Schristos 	while (s8[s8len++] != 0)
1905819a8c0Schristos 		;
1915819a8c0Schristos 	s8idx = s16idx = 0;
1925819a8c0Schristos 	utfbytes = 0;
1935819a8c0Schristos 	do {
1945819a8c0Schristos 		c = s8[s8idx++];
1955819a8c0Schristos 		if ((c & 0xc0) != 0x80) {
1965819a8c0Schristos 			/* Initial characters. */
1975819a8c0Schristos 			if (utfbytes != 0) {
1985819a8c0Schristos 				/* Incomplete encoding. */
19911ded42cSmatt 				s16[s16idx++] = htole16(0xfffd);
2005819a8c0Schristos 				if (s16idx == s16len) {
2015819a8c0Schristos 					s16[--s16idx] = 0;
2025819a8c0Schristos 					return;
2035819a8c0Schristos 				}
2045819a8c0Schristos 			}
2055819a8c0Schristos 			if ((c & 0xf8) == 0xf0) {
2065819a8c0Schristos 				utfchar = c & 0x07;
2075819a8c0Schristos 				utfbytes = 3;
2085819a8c0Schristos 			} else if ((c & 0xf0) == 0xe0) {
2095819a8c0Schristos 				utfchar = c & 0x0f;
2105819a8c0Schristos 				utfbytes = 2;
2115819a8c0Schristos 			} else if ((c & 0xe0) == 0xc0) {
2125819a8c0Schristos 				utfchar = c & 0x1f;
2135819a8c0Schristos 				utfbytes = 1;
2145819a8c0Schristos 			} else {
2155819a8c0Schristos 				utfchar = c & 0x7f;
2165819a8c0Schristos 				utfbytes = 0;
2175819a8c0Schristos 			}
2185819a8c0Schristos 		} else {
2195819a8c0Schristos 			/* Followup characters. */
2205819a8c0Schristos 			if (utfbytes > 0) {
2215819a8c0Schristos 				utfchar = (utfchar << 6) + (c & 0x3f);
2225819a8c0Schristos 				utfbytes--;
2235819a8c0Schristos 			} else if (utfbytes == 0)
2245c1ccc6eSchristos 				utfbytes = (u_int)~0;
2255819a8c0Schristos 		}
2265819a8c0Schristos 		if (utfbytes == 0) {
2275819a8c0Schristos 			if (utfchar >= 0x10000 && s16idx + 2 >= s16len)
22811ded42cSmatt 				utfchar = 0xfffd;
2295819a8c0Schristos 			if (utfchar >= 0x10000) {
2305c1ccc6eSchristos 				s16[s16idx++] = htole16((uint16_t)
2315c1ccc6eSchristos 				    (0xd800 | ((utfchar>>10) - 0x40)));
2325c1ccc6eSchristos 				s16[s16idx++] = htole16((uint16_t)
2335c1ccc6eSchristos 				    (0xdc00 | (utfchar & 0x3ff)));
2345819a8c0Schristos 			} else
2355c1ccc6eSchristos 				s16[s16idx++] = htole16((uint16_t)utfchar);
2365819a8c0Schristos 			if (s16idx == s16len) {
2375819a8c0Schristos 				return;
2385819a8c0Schristos 			}
2395819a8c0Schristos 		}
2405819a8c0Schristos 	} while (c != 0);
241d6862190Schristos 
242d6862190Schristos 	while (s16idx < s16len)
243d6862190Schristos 		s16[s16idx++] = 0;
2445819a8c0Schristos }
2455819a8c0Schristos 
2465819a8c0Schristos void *
2470b43d398Schristos gpt_read(gpt_t gpt, off_t lba, size_t count)
2485819a8c0Schristos {
2495819a8c0Schristos 	off_t ofs;
2505819a8c0Schristos 	void *buf;
2515819a8c0Schristos 
2520b43d398Schristos 	count *= gpt->secsz;
2535819a8c0Schristos 	buf = malloc(count);
2545819a8c0Schristos 	if (buf == NULL)
2550b43d398Schristos 		return NULL;
2565819a8c0Schristos 
2570b43d398Schristos 	ofs = lba * gpt->secsz;
2580b43d398Schristos 	if (lseek(gpt->fd, ofs, SEEK_SET) == ofs &&
2590b43d398Schristos 	    read(gpt->fd, buf, count) == (ssize_t)count)
2600b43d398Schristos 		return buf;
2615819a8c0Schristos 
2625819a8c0Schristos 	free(buf);
2630b43d398Schristos 	return NULL;
2645819a8c0Schristos }
2655819a8c0Schristos 
2665819a8c0Schristos int
2670b43d398Schristos gpt_write(gpt_t gpt, map_t map)
2685819a8c0Schristos {
2695819a8c0Schristos 	off_t ofs;
2705819a8c0Schristos 	size_t count;
2715819a8c0Schristos 
2725c1ccc6eSchristos 	count = (size_t)(map->map_size * gpt->secsz);
2730b43d398Schristos 	ofs = map->map_start * gpt->secsz;
2740b43d398Schristos 	if (lseek(gpt->fd, ofs, SEEK_SET) != ofs ||
2750b43d398Schristos 	    write(gpt->fd, map->map_data, count) != (ssize_t)count)
276e4ed2565Schristos 		return -1;
2770b43d398Schristos 	gpt->flags |= GPT_MODIFIED;
278e4ed2565Schristos 	return 0;
2795819a8c0Schristos }
2805819a8c0Schristos 
2815819a8c0Schristos static int
282be31ceadSmartin gpt_mbr(gpt_t gpt, off_t lba, unsigned int *next_index, off_t ext_offset)
2835819a8c0Schristos {
2845819a8c0Schristos 	struct mbr *mbr;
2850b43d398Schristos 	map_t m, p;
2865819a8c0Schristos 	off_t size, start;
2875819a8c0Schristos 	unsigned int i, pmbr;
2885819a8c0Schristos 
2890b43d398Schristos 	mbr = gpt_read(gpt, lba, 1);
290ca4e0dcdSchristos 	if (mbr == NULL) {
2910b43d398Schristos 		gpt_warn(gpt, "Read failed");
2920b43d398Schristos 		return -1;
293ca4e0dcdSchristos 	}
2945819a8c0Schristos 
2955819a8c0Schristos 	if (mbr->mbr_sig != htole16(MBR_SIG)) {
2960b43d398Schristos 		if (gpt->verbose)
2970b43d398Schristos 			gpt_msg(gpt,
2980b43d398Schristos 			    "MBR not found at sector %ju", (uintmax_t)lba);
2995819a8c0Schristos 		free(mbr);
3000b43d398Schristos 		return 0;
3015819a8c0Schristos 	}
3025819a8c0Schristos 
3035819a8c0Schristos 	/*
3045819a8c0Schristos 	 * Differentiate between a regular MBR and a PMBR. This is more
3055819a8c0Schristos 	 * convenient in general. A PMBR is one with a single partition
3065819a8c0Schristos 	 * of type 0xee.
3075819a8c0Schristos 	 */
3085819a8c0Schristos 	pmbr = 0;
3095819a8c0Schristos 	for (i = 0; i < 4; i++) {
31016550bc8Sjakllsch 		if (mbr->mbr_part[i].part_typ == MBR_PTYPE_UNUSED)
3115819a8c0Schristos 			continue;
31216550bc8Sjakllsch 		if (mbr->mbr_part[i].part_typ == MBR_PTYPE_PMBR)
3135819a8c0Schristos 			pmbr++;
314e5906adeSjmcneill 		else if ((gpt->flags & GPT_HYBRID) == 0)
3155819a8c0Schristos 			break;
3165819a8c0Schristos 	}
3175819a8c0Schristos 	if (pmbr && i == 4 && lba == 0) {
3180b43d398Schristos 		if (pmbr != 1)
3190b43d398Schristos 			gpt_warnx(gpt, "Suspicious PMBR at sector %ju",
320ca4e0dcdSchristos 			    (uintmax_t)lba);
3210b43d398Schristos 		else if (gpt->verbose > 1)
3220b43d398Schristos 			gpt_msg(gpt, "PMBR at sector %ju", (uintmax_t)lba);
3230f110115Schristos 		p = map_add(gpt, lba, 1LL, MAP_TYPE_PMBR, mbr, 1);
3240b43d398Schristos 		goto out;
3250b43d398Schristos 	}
3260b43d398Schristos 	if (pmbr)
3270b43d398Schristos 		gpt_warnx(gpt, "Suspicious MBR at sector %ju", (uintmax_t)lba);
3280b43d398Schristos 	else if (gpt->verbose > 1)
3290b43d398Schristos 		gpt_msg(gpt, "MBR at sector %ju", (uintmax_t)lba);
3305819a8c0Schristos 
3310f110115Schristos 	p = map_add(gpt, lba, 1LL, MAP_TYPE_MBR, mbr, 1);
3325819a8c0Schristos 	if (p == NULL)
3330b43d398Schristos 		goto out;
3340b43d398Schristos 
3355819a8c0Schristos 	for (i = 0; i < 4; i++) {
33616550bc8Sjakllsch 		if (mbr->mbr_part[i].part_typ == MBR_PTYPE_UNUSED ||
33716550bc8Sjakllsch 		    mbr->mbr_part[i].part_typ == MBR_PTYPE_PMBR)
3385819a8c0Schristos 			continue;
3395819a8c0Schristos 		start = le16toh(mbr->mbr_part[i].part_start_hi);
3405819a8c0Schristos 		start = (start << 16) + le16toh(mbr->mbr_part[i].part_start_lo);
3415819a8c0Schristos 		size = le16toh(mbr->mbr_part[i].part_size_hi);
3425819a8c0Schristos 		size = (size << 16) + le16toh(mbr->mbr_part[i].part_size_lo);
3435819a8c0Schristos 		if (start == 0 && size == 0) {
3440b43d398Schristos 			gpt_warnx(gpt, "Malformed MBR at sector %ju",
3450b43d398Schristos 			    (uintmax_t)lba);
3465819a8c0Schristos 			continue;
3475819a8c0Schristos 		}
3480b43d398Schristos 		if (gpt->verbose > 2)
3493017a7a3Schristos 			gpt_msg(gpt, "MBR part: flag=%#x type=%d, start=%ju, "
3503017a7a3Schristos 			    "size=%ju", mbr->mbr_part[i].part_flag,
351ca4e0dcdSchristos 			    mbr->mbr_part[i].part_typ,
352ca4e0dcdSchristos 			    (uintmax_t)start, (uintmax_t)size);
353be31ceadSmartin 		if (!MBR_IS_EXTENDED(mbr->mbr_part[i].part_typ)) {
354be31ceadSmartin 			start += lba;
3550f110115Schristos 			m = map_add(gpt, start, size, MAP_TYPE_MBR_PART, p, 0);
3565819a8c0Schristos 			if (m == NULL)
3570b43d398Schristos 				return -1;
358be31ceadSmartin 			m->map_index = *next_index;
359be31ceadSmartin 			(*next_index)++;
3605819a8c0Schristos 		} else {
361be31ceadSmartin 			start += ext_offset;
362be31ceadSmartin 			if (gpt_mbr(gpt, start, next_index,
363be31ceadSmartin 			    ext_offset ? ext_offset : start) == -1)
3640b43d398Schristos 				return -1;
3655819a8c0Schristos 		}
3665819a8c0Schristos 	}
3670b43d398Schristos 	return 0;
3680b43d398Schristos out:
3690b43d398Schristos 	if (p == NULL) {
3700b43d398Schristos 		free(mbr);
3710b43d398Schristos 		return -1;
3720b43d398Schristos 	}
3730b43d398Schristos 	return 0;
3745819a8c0Schristos }
3755819a8c0Schristos 
3762ed5cc26Sjnemeth int
3770b43d398Schristos gpt_gpt(gpt_t gpt, off_t lba, int found)
3785819a8c0Schristos {
3795819a8c0Schristos 	off_t size;
3805819a8c0Schristos 	struct gpt_ent *ent;
3815819a8c0Schristos 	struct gpt_hdr *hdr;
38221c34dbbSchristos 	char *p;
3830b43d398Schristos 	map_t m;
3845819a8c0Schristos 	size_t blocks, tblsz;
3855819a8c0Schristos 	unsigned int i;
3865819a8c0Schristos 	uint32_t crc;
3875819a8c0Schristos 
3880b43d398Schristos 	hdr = gpt_read(gpt, lba, 1);
389f169dbc4Smlelstv 	if (hdr == NULL) {
390f169dbc4Smlelstv 		gpt_warn(gpt, "Read failed");
3910b43d398Schristos 		return -1;
392f169dbc4Smlelstv 	}
3935819a8c0Schristos 
3945819a8c0Schristos 	if (memcmp(hdr->hdr_sig, GPT_HDR_SIG, sizeof(hdr->hdr_sig)))
3955819a8c0Schristos 		goto fail_hdr;
3965819a8c0Schristos 
3975819a8c0Schristos 	crc = le32toh(hdr->hdr_crc_self);
3985819a8c0Schristos 	hdr->hdr_crc_self = 0;
3995819a8c0Schristos 	if (crc32(hdr, le32toh(hdr->hdr_size)) != crc) {
4000b43d398Schristos 		if (gpt->verbose)
4010b43d398Schristos 			gpt_msg(gpt, "Bad CRC in GPT header at sector %ju",
4020b43d398Schristos 			    (uintmax_t)lba);
4035819a8c0Schristos 		goto fail_hdr;
4045819a8c0Schristos 	}
4055819a8c0Schristos 
4065819a8c0Schristos 	tblsz = le32toh(hdr->hdr_entries) * le32toh(hdr->hdr_entsz);
4070b43d398Schristos 	blocks = tblsz / gpt->secsz + ((tblsz % gpt->secsz) ? 1 : 0);
4085819a8c0Schristos 
4095819a8c0Schristos 	/* Use generic pointer to deal with hdr->hdr_entsz != sizeof(*ent). */
4105c1ccc6eSchristos 	p = gpt_read(gpt, (off_t)le64toh((uint64_t)hdr->hdr_lba_table), blocks);
41179bdd20aSchristos 	if (p == NULL) {
41279bdd20aSchristos 		if (found) {
4130b43d398Schristos 			if (gpt->verbose)
4140b43d398Schristos 				gpt_msg(gpt,
4150b43d398Schristos 				    "Cannot read LBA table at sector %ju",
4160b43d398Schristos 				    (uintmax_t)le64toh(hdr->hdr_lba_table));
4170b43d398Schristos 			return -1;
41879bdd20aSchristos 		}
41979bdd20aSchristos 		goto fail_hdr;
42079bdd20aSchristos 	}
4215819a8c0Schristos 
4225819a8c0Schristos 	if (crc32(p, tblsz) != le32toh(hdr->hdr_crc_table)) {
4230b43d398Schristos 		if (gpt->verbose)
4240b43d398Schristos 			gpt_msg(gpt, "Bad CRC in GPT table at sector %ju",
4250b43d398Schristos 			    (uintmax_t)le64toh(hdr->hdr_lba_table));
4265819a8c0Schristos 		goto fail_ent;
4275819a8c0Schristos 	}
4285819a8c0Schristos 
4290b43d398Schristos 	if (gpt->verbose > 1)
4300b43d398Schristos 		gpt_msg(gpt, "%s GPT at sector %ju",
4310b43d398Schristos 		    (lba == 1) ? "Pri" : "Sec", (uintmax_t)lba);
4325819a8c0Schristos 
4330b43d398Schristos 	m = map_add(gpt, lba, 1, (lba == 1)
4340f110115Schristos 	    ? MAP_TYPE_PRI_GPT_HDR : MAP_TYPE_SEC_GPT_HDR, hdr, 1);
4355819a8c0Schristos 	if (m == NULL)
4365819a8c0Schristos 		return (-1);
4375819a8c0Schristos 
4385c1ccc6eSchristos 	m = map_add(gpt, (off_t)le64toh((uint64_t)hdr->hdr_lba_table),
4395c1ccc6eSchristos 	    (off_t)blocks,
4400f110115Schristos 	    lba == 1 ? MAP_TYPE_PRI_GPT_TBL : MAP_TYPE_SEC_GPT_TBL, p, 1);
4415819a8c0Schristos 	if (m == NULL)
4425819a8c0Schristos 		return (-1);
4435819a8c0Schristos 
4445819a8c0Schristos 	if (lba != 1)
44579bdd20aSchristos 		return (1);
4465819a8c0Schristos 
4475819a8c0Schristos 	for (i = 0; i < le32toh(hdr->hdr_entries); i++) {
4485819a8c0Schristos 		ent = (void*)(p + i * le32toh(hdr->hdr_entsz));
44921c34dbbSchristos 		if (gpt_uuid_is_nil(ent->ent_type))
4505819a8c0Schristos 			continue;
4515819a8c0Schristos 
4525c1ccc6eSchristos 		size = (off_t)(le64toh((uint64_t)ent->ent_lba_end) -
4535c1ccc6eSchristos 		    le64toh((uint64_t)ent->ent_lba_start) + 1LL);
4540b43d398Schristos 		if (gpt->verbose > 2) {
45521c34dbbSchristos 			char buf[128];
45621c34dbbSchristos 			gpt_uuid_snprintf(buf, sizeof(buf), "%s",
45721c34dbbSchristos 			    ent->ent_type);
4580b43d398Schristos 			gpt_msg(gpt, "GPT partition: type=%s, start=%ju, "
4590b43d398Schristos 			    "size=%ju", buf,
4600b43d398Schristos 			    (uintmax_t)le64toh(ent->ent_lba_start),
4610b43d398Schristos 			    (uintmax_t)size);
4625819a8c0Schristos 		}
4635c1ccc6eSchristos 		m = map_add(gpt, (off_t)le64toh((uint64_t)ent->ent_lba_start),
4640f110115Schristos 		    size, MAP_TYPE_GPT_PART, ent, 0);
4655819a8c0Schristos 		if (m == NULL)
4665819a8c0Schristos 			return (-1);
4675819a8c0Schristos 		m->map_index = i + 1;
4685819a8c0Schristos 	}
46979bdd20aSchristos 	return (1);
4705819a8c0Schristos 
4715819a8c0Schristos  fail_ent:
4725819a8c0Schristos 	free(p);
4735819a8c0Schristos 
4745819a8c0Schristos  fail_hdr:
4755819a8c0Schristos 	free(hdr);
4765819a8c0Schristos 	return (0);
4775819a8c0Schristos }
4785819a8c0Schristos 
4790b43d398Schristos gpt_t
480ef9cffabSchristos gpt_open(const char *dev, int flags, int verbose, off_t mediasz, u_int secsz,
481ef9cffabSchristos     time_t timestamp)
4825819a8c0Schristos {
4830b43d398Schristos 	int mode, found;
484c3132810Schristos 	off_t devsz;
4850b43d398Schristos 	gpt_t gpt;
486be31ceadSmartin 	unsigned int index;
4875819a8c0Schristos 
4880b43d398Schristos 	if ((gpt = calloc(1, sizeof(*gpt))) == NULL) {
489777094fdSjnemeth 		if (!(flags & GPT_QUIET))
4900b43d398Schristos 			warn("Cannot allocate `%s'", dev);
4910b43d398Schristos 		return NULL;
492ca4e0dcdSchristos 	}
4930b43d398Schristos 	gpt->flags = flags;
4940b43d398Schristos 	gpt->verbose = verbose;
4950b43d398Schristos 	gpt->mediasz = mediasz;
4960b43d398Schristos 	gpt->secsz = secsz;
497ef9cffabSchristos 	gpt->timestamp = timestamp;
498*10edc71fSmlelstv 	gpt->uuidgen = 0;
4999b522365Schristos 
5000b43d398Schristos 	mode = (gpt->flags & GPT_READONLY) ? O_RDONLY : O_RDWR|O_EXCL;
5010b43d398Schristos 
5020b43d398Schristos 	gpt->fd = opendisk(dev, mode, gpt->device_name,
5030b43d398Schristos 	    sizeof(gpt->device_name), 0);
5040b43d398Schristos 	if (gpt->fd == -1) {
5050b43d398Schristos 		strlcpy(gpt->device_name, dev, sizeof(gpt->device_name));
5060b43d398Schristos 		gpt_warn(gpt, "Cannot open");
5075819a8c0Schristos 		goto close;
508ca4e0dcdSchristos 	}
5095819a8c0Schristos 
5100b43d398Schristos 	if (fstat(gpt->fd, &gpt->sb) == -1) {
5110b43d398Schristos 		gpt_warn(gpt, "Cannot stat");
5120b43d398Schristos 		goto close;
5130b43d398Schristos 	}
5140b43d398Schristos 
5150b43d398Schristos 	if ((gpt->sb.st_mode & S_IFMT) != S_IFREG) {
5160b43d398Schristos 		if (gpt->secsz == 0) {
5171ca10bc7Sjnemeth #ifdef DIOCGSECTORSIZE
5180b43d398Schristos 			if (ioctl(gpt->fd, DIOCGSECTORSIZE, &gpt->secsz) == -1) {
5190b43d398Schristos 				gpt_warn(gpt, "Cannot get sector size");
5209b522365Schristos 				goto close;
521ca4e0dcdSchristos 			}
5221ca10bc7Sjnemeth #endif
5230b43d398Schristos 			if (gpt->secsz == 0) {
5240b43d398Schristos 				gpt_warnx(gpt, "Sector size can't be 0");
525f8906ab6Schristos 				goto close;
526f8906ab6Schristos 			}
527f8906ab6Schristos 		}
5280b43d398Schristos 		if (gpt->mediasz == 0) {
529f8906ab6Schristos #ifdef DIOCGMEDIASIZE
5300b43d398Schristos 			if (ioctl(gpt->fd, DIOCGMEDIASIZE, &gpt->mediasz) == -1) {
5310b43d398Schristos 				gpt_warn(gpt, "Cannot get media size");
532f8906ab6Schristos 				goto close;
533f8906ab6Schristos 			}
534f8906ab6Schristos #endif
5350b43d398Schristos 			if (gpt->mediasz == 0) {
5360b43d398Schristos 				gpt_warnx(gpt, "Media size can't be 0");
537f8906ab6Schristos 				goto close;
538f8906ab6Schristos 			}
539f8906ab6Schristos 		}
5405819a8c0Schristos 	} else {
54166710644Schristos 		gpt->flags |= GPT_FILE;
5420b43d398Schristos 		if (gpt->secsz == 0)
5430b43d398Schristos 			gpt->secsz = 512;	/* Fixed size for files. */
5440b43d398Schristos 		if (gpt->mediasz == 0) {
5450b43d398Schristos 			if (gpt->sb.st_size % gpt->secsz) {
546f169dbc4Smlelstv 				gpt_warn(gpt, "Media size not a multiple of sector size (%u)\n", gpt->secsz);
5475819a8c0Schristos 				errno = EINVAL;
5485819a8c0Schristos 				goto close;
5495819a8c0Schristos 			}
5500b43d398Schristos 			gpt->mediasz = gpt->sb.st_size;
5515819a8c0Schristos 		}
552ce4e8c44Schristos 		gpt->flags |= GPT_NOSYNC;
553279da393Schristos 	}
5545819a8c0Schristos 
5555819a8c0Schristos 	/*
5565819a8c0Schristos 	 * We require an absolute minimum of 6 sectors. One for the MBR,
5575819a8c0Schristos 	 * 2 for the GPT header, 2 for the GPT table and one to hold some
5585819a8c0Schristos 	 * user data. Let's catch this extreme border case here so that
5595819a8c0Schristos 	 * we don't have to worry about it later.
5605819a8c0Schristos 	 */
5610b43d398Schristos 	devsz = gpt->mediasz / gpt->secsz;
562c3132810Schristos 	if (devsz < 6) {
56366710644Schristos 		gpt_warnx(gpt, "Need 6 sectors, we have %ju",
5640b43d398Schristos 		    (uintmax_t)devsz);
5655819a8c0Schristos 		goto close;
5665819a8c0Schristos 	}
5675819a8c0Schristos 
5680b43d398Schristos 	if (gpt->verbose) {
5690b43d398Schristos 		gpt_msg(gpt, "mediasize=%ju; sectorsize=%u; blocks=%ju",
5700b43d398Schristos 		    (uintmax_t)gpt->mediasz, gpt->secsz, (uintmax_t)devsz);
571ca4e0dcdSchristos 	}
5725819a8c0Schristos 
573c458b37cSchristos 	if (map_init(gpt, devsz) == -1)
574c458b37cSchristos 		goto close;
5755819a8c0Schristos 
576be31ceadSmartin 	index = 1;
577be31ceadSmartin 	if (gpt_mbr(gpt, 0LL, &index, 0U) == -1)
5785819a8c0Schristos 		goto close;
5790b43d398Schristos 	if ((found = gpt_gpt(gpt, 1LL, 1)) == -1)
5805819a8c0Schristos 		goto close;
5810bf6d488Smlelstv 
5820bf6d488Smlelstv 	if (found) {
5830bf6d488Smlelstv 		struct map *map;
5840bf6d488Smlelstv 		struct gpt_hdr *hdr;
58527a040a8Smlelstv 		uint64_t lba;
5860bf6d488Smlelstv 
5870bf6d488Smlelstv 		/*
5880bf6d488Smlelstv 		 * read secondary GPT from position stored in primary header
5890bf6d488Smlelstv 		 * when possible
5900bf6d488Smlelstv 		 */
5910bf6d488Smlelstv 		map = map_find(gpt, MAP_TYPE_PRI_GPT_HDR);
5920bf6d488Smlelstv 		hdr = map ? map->map_data : NULL;
59327a040a8Smlelstv 		lba = le64toh(hdr->hdr_lba_alt);
59427a040a8Smlelstv 		if (hdr && lba > 0 && lba < (uint64_t)devsz) {
59527a040a8Smlelstv 			if (gpt_gpt(gpt, (off_t)lba, found) == -1)
5960bf6d488Smlelstv 				goto close;
5970bf6d488Smlelstv 		}
5980bf6d488Smlelstv 	} else {
5990b43d398Schristos 		if (gpt_gpt(gpt, devsz - 1LL, found) == -1)
6005819a8c0Schristos 			goto close;
6010bf6d488Smlelstv 	}
6025819a8c0Schristos 
6030b43d398Schristos 	return gpt;
6045819a8c0Schristos 
6055819a8c0Schristos  close:
6060b43d398Schristos 	if (gpt->fd != -1)
6070b43d398Schristos 		close(gpt->fd);
608f169dbc4Smlelstv 	gpt_warn(gpt, "No GPT found");
6095fcb5009Smrg 	free(gpt);
6100b43d398Schristos 	return NULL;
6115819a8c0Schristos }
6125819a8c0Schristos 
6135819a8c0Schristos void
6140b43d398Schristos gpt_close(gpt_t gpt)
6155819a8c0Schristos {
616e4ed2565Schristos 
61708878476Smartin 	if (gpt == NULL)
61808878476Smartin 		return;
61908878476Smartin 
620aa3b5bb2Sjnemeth 	if (!(gpt->flags & GPT_MODIFIED) || !(gpt->flags & GPT_SYNC))
62168bc3825Schristos 		goto out;
62268bc3825Schristos 
6230b43d398Schristos 	if (!(gpt->flags & GPT_NOSYNC)) {
624835e6be8Schristos #ifdef DIOCMWEDGES
625835e6be8Schristos 		int bits;
6260b43d398Schristos 		if (ioctl(gpt->fd, DIOCMWEDGES, &bits) == -1)
6270b43d398Schristos 			gpt_warn(gpt, "Can't update wedge information");
62868bc3825Schristos 		else
62968bc3825Schristos 			goto out;
630835e6be8Schristos #endif
631835e6be8Schristos 	}
63266710644Schristos 	if (!(gpt->flags & GPT_FILE))
6330b43d398Schristos 		gpt_msg(gpt, "You need to run \"dkctl %s makewedges\""
6340b43d398Schristos 		    " for the changes to take effect\n", gpt->device_name);
635e4ed2565Schristos 
63668bc3825Schristos out:
6370b43d398Schristos 	close(gpt->fd);
6385819a8c0Schristos }
6395819a8c0Schristos 
640459b255bSjoerg __printflike(2, 0)
641f9db7548Schristos static void
642f9db7548Schristos gpt_vwarnx(gpt_t gpt, const char *fmt, va_list ap, const char *e)
643f9db7548Schristos {
644f9db7548Schristos 	if (gpt && (gpt->flags & GPT_QUIET))
645f9db7548Schristos 		return;
646f9db7548Schristos 	fprintf(stderr, "%s: ", getprogname());
647f9db7548Schristos 	if (gpt)
648f9db7548Schristos 		fprintf(stderr, "%s: ", gpt->device_name);
649f9db7548Schristos 	vfprintf(stderr, fmt, ap);
650f9db7548Schristos 	if (e)
651f9db7548Schristos 		fprintf(stderr, " (%s)\n", e);
652f9db7548Schristos 	else
653f9db7548Schristos 		fputc('\n', stderr);
654f9db7548Schristos }
655f9db7548Schristos 
656ca4e0dcdSchristos void
6570b43d398Schristos gpt_warnx(gpt_t gpt, const char *fmt, ...)
658ca4e0dcdSchristos {
659ca4e0dcdSchristos 	va_list ap;
66068bc3825Schristos 
6610b43d398Schristos 	va_start(ap, fmt);
662f9db7548Schristos 	gpt_vwarnx(gpt, fmt, ap, NULL);
6630b43d398Schristos 	va_end(ap);
6640b43d398Schristos }
6650b43d398Schristos 
6660b43d398Schristos void
6670b43d398Schristos gpt_warn(gpt_t gpt, const char *fmt, ...)
6680b43d398Schristos {
6690b43d398Schristos 	va_list ap;
6700b43d398Schristos 
6710b43d398Schristos 	va_start(ap, fmt);
672f9db7548Schristos 	gpt_vwarnx(gpt, fmt, ap, strerror(errno));
6730b43d398Schristos 	va_end(ap);
6740b43d398Schristos }
6750b43d398Schristos 
6760b43d398Schristos void
6770b43d398Schristos gpt_msg(gpt_t gpt, const char *fmt, ...)
6780b43d398Schristos {
6790b43d398Schristos 	va_list ap;
6800b43d398Schristos 
681f9db7548Schristos 	if (gpt && (gpt->flags & GPT_QUIET))
6820b43d398Schristos 		return;
683f9db7548Schristos 	if (gpt)
6840b43d398Schristos 		printf("%s: ", gpt->device_name);
685ca4e0dcdSchristos 	va_start(ap, fmt);
686ca4e0dcdSchristos 	vprintf(fmt, ap);
687ca4e0dcdSchristos 	va_end(ap);
688ca4e0dcdSchristos 	printf("\n");
689ca4e0dcdSchristos }
6900b43d398Schristos 
6910b43d398Schristos struct gpt_hdr *
6920b43d398Schristos gpt_hdr(gpt_t gpt)
6930b43d398Schristos {
6940b43d398Schristos 	gpt->gpt = map_find(gpt, MAP_TYPE_PRI_GPT_HDR);
6950b43d398Schristos 	if (gpt->gpt == NULL) {
6960b43d398Schristos 		gpt_warnx(gpt, "No primary GPT header; run create or recover");
6970b43d398Schristos 		return NULL;
6980b43d398Schristos 	}
6990b43d398Schristos 
7000b43d398Schristos 	gpt->tpg = map_find(gpt, MAP_TYPE_SEC_GPT_HDR);
7010b43d398Schristos 	if (gpt->tpg == NULL) {
7020b43d398Schristos 		gpt_warnx(gpt, "No secondary GPT header; run recover");
7030b43d398Schristos 		return NULL;
7040b43d398Schristos 	}
7050b43d398Schristos 
7060b43d398Schristos 	gpt->tbl = map_find(gpt, MAP_TYPE_PRI_GPT_TBL);
7070b43d398Schristos 	gpt->lbt = map_find(gpt, MAP_TYPE_SEC_GPT_TBL);
7080b43d398Schristos 	if (gpt->tbl == NULL || gpt->lbt == NULL) {
7090b43d398Schristos 		gpt_warnx(gpt, "Corrupt maps, run recover");
7100b43d398Schristos 		return NULL;
7110b43d398Schristos 	}
7120b43d398Schristos 
7130b43d398Schristos 	return gpt->gpt->map_data;
7140b43d398Schristos }
7150b43d398Schristos 
7160b43d398Schristos int
7170b43d398Schristos gpt_write_crc(gpt_t gpt, map_t map, map_t tbl)
7180b43d398Schristos {
7190b43d398Schristos 	struct gpt_hdr *hdr = map->map_data;
7200b43d398Schristos 
7210b43d398Schristos 	hdr->hdr_crc_table = htole32(crc32(tbl->map_data,
7220b43d398Schristos 	    le32toh(hdr->hdr_entries) * le32toh(hdr->hdr_entsz)));
7230b43d398Schristos 	hdr->hdr_crc_self = 0;
7240b43d398Schristos 	hdr->hdr_crc_self = htole32(crc32(hdr, le32toh(hdr->hdr_size)));
7250b43d398Schristos 
7260b43d398Schristos 	if (gpt_write(gpt, map) == -1) {
7270b43d398Schristos 		gpt_warn(gpt, "Error writing crc map");
7280b43d398Schristos 		return -1;
7290b43d398Schristos 	}
7300b43d398Schristos 
7310b43d398Schristos 	if (gpt_write(gpt, tbl) == -1) {
7320b43d398Schristos 		gpt_warn(gpt, "Error writing crc table");
7330b43d398Schristos 		return -1;
7340b43d398Schristos 	}
7350b43d398Schristos 
7360b43d398Schristos 	return 0;
7370b43d398Schristos }
7380b43d398Schristos 
7390b43d398Schristos int
7400b43d398Schristos gpt_write_primary(gpt_t gpt)
7410b43d398Schristos {
7420b43d398Schristos 	return gpt_write_crc(gpt, gpt->gpt, gpt->tbl);
7430b43d398Schristos }
7440b43d398Schristos 
7450b43d398Schristos 
7460b43d398Schristos int
7470b43d398Schristos gpt_write_backup(gpt_t gpt)
7480b43d398Schristos {
7490b43d398Schristos 	return gpt_write_crc(gpt, gpt->tpg, gpt->lbt);
7500b43d398Schristos }
7510b43d398Schristos 
7520b43d398Schristos void
7533017a7a3Schristos gpt_create_pmbr_part(struct mbr_part *part, off_t last, int active)
7540b43d398Schristos {
7553017a7a3Schristos 	part->part_flag = active ? 0x80 : 0;
7560b43d398Schristos 	part->part_shd = 0x00;
7570b43d398Schristos 	part->part_ssect = 0x02;
7580b43d398Schristos 	part->part_scyl = 0x00;
7590b43d398Schristos 	part->part_typ = MBR_PTYPE_PMBR;
7600b43d398Schristos 	part->part_ehd = 0xfe;
7610b43d398Schristos 	part->part_esect = 0xff;
7620b43d398Schristos 	part->part_ecyl = 0xff;
7630b43d398Schristos 	part->part_start_lo = htole16(1);
7640b43d398Schristos 	if (last > 0xffffffff) {
7650b43d398Schristos 		part->part_size_lo = htole16(0xffff);
7660b43d398Schristos 		part->part_size_hi = htole16(0xffff);
7670b43d398Schristos 	} else {
7685c1ccc6eSchristos 		part->part_size_lo = htole16((uint16_t)last);
7695c1ccc6eSchristos 		part->part_size_hi = htole16((uint16_t)(last >> 16));
7700b43d398Schristos 	}
7710b43d398Schristos }
7720b43d398Schristos 
7730b43d398Schristos struct gpt_ent *
7740b43d398Schristos gpt_ent(map_t map, map_t tbl, unsigned int i)
7750b43d398Schristos {
7760b43d398Schristos 	struct gpt_hdr *hdr = map->map_data;
7770b43d398Schristos 	return (void *)((char *)tbl->map_data + i * le32toh(hdr->hdr_entsz));
7780b43d398Schristos }
7790b43d398Schristos 
7800b43d398Schristos struct gpt_ent *
7810b43d398Schristos gpt_ent_primary(gpt_t gpt, unsigned int i)
7820b43d398Schristos {
7830b43d398Schristos 	return gpt_ent(gpt->gpt, gpt->tbl, i);
7840b43d398Schristos }
7850b43d398Schristos 
7860b43d398Schristos struct gpt_ent *
7870b43d398Schristos gpt_ent_backup(gpt_t gpt, unsigned int i)
7880b43d398Schristos {
7890b43d398Schristos 	return gpt_ent(gpt->tpg, gpt->lbt, i);
7900b43d398Schristos }
7918ca93e46Schristos 
7928ca93e46Schristos int
7938ca93e46Schristos gpt_usage(const char *prefix, const struct gpt_cmd *cmd)
7948ca93e46Schristos {
7958ca93e46Schristos 	const char **a = cmd->help;
7968ca93e46Schristos 	size_t hlen = cmd->hlen;
7978ca93e46Schristos 	size_t i;
7988ca93e46Schristos 
7998ca93e46Schristos 	if (prefix == NULL) {
8008ca93e46Schristos 		const char *pname = getprogname();
801272fa829Schristos 		const char *d1, *d2, *d = " <device>";
8028ca93e46Schristos 		int len = (int)strlen(pname);
803272fa829Schristos 		if (strcmp(pname, "gpt") == 0) {
804272fa829Schristos 			d1 = "";
805272fa829Schristos 			d2 = d;
806272fa829Schristos 		} else {
807272fa829Schristos 			d2 = "";
808272fa829Schristos 			d1 = d;
809272fa829Schristos 		}
810272fa829Schristos 		fprintf(stderr, "Usage: %s%s %s %s%s\n", pname,
811272fa829Schristos 		    d1, cmd->name, a[0], d2);
8128ca93e46Schristos 		for (i = 1; i < hlen; i++) {
8138ca93e46Schristos 			fprintf(stderr,
814272fa829Schristos 			    "       %*s%s %s %s%s\n", len, "",
815272fa829Schristos 			    d1, cmd->name, a[i], d2);
8168ca93e46Schristos 		}
8178ca93e46Schristos 	} else {
8188ca93e46Schristos 		for (i = 0; i < hlen; i++)
8198ca93e46Schristos 		    fprintf(stderr, "%s%s %s\n", prefix, cmd->name, a[i]);
8208ca93e46Schristos 	}
8218ca93e46Schristos 	return -1;
8228ca93e46Schristos }
823bbb4a8abSchristos 
824bbb4a8abSchristos off_t
825bbb4a8abSchristos gpt_last(gpt_t gpt)
826bbb4a8abSchristos {
827bbb4a8abSchristos 	return gpt->mediasz / gpt->secsz - 1LL;
828bbb4a8abSchristos }
829bbb4a8abSchristos 
8305c1ccc6eSchristos off_t
831bbb4a8abSchristos gpt_create(gpt_t gpt, off_t last, u_int parts, int primary_only)
832bbb4a8abSchristos {
833bbb4a8abSchristos 	off_t blocks;
834bbb4a8abSchristos 	map_t map;
835bbb4a8abSchristos 	struct gpt_hdr *hdr;
836bbb4a8abSchristos 	struct gpt_ent *ent;
837bbb4a8abSchristos 	unsigned int i;
838bbb4a8abSchristos 	void *p;
839bbb4a8abSchristos 
840bbb4a8abSchristos 	if (map_find(gpt, MAP_TYPE_PRI_GPT_HDR) != NULL ||
841bbb4a8abSchristos 	    map_find(gpt, MAP_TYPE_SEC_GPT_HDR) != NULL) {
842f3439cf7Schristos 		gpt_warnx(gpt, "Device already contains a GPT, "
843f3439cf7Schristos 		    "destroy it first");
844bbb4a8abSchristos 		return -1;
845bbb4a8abSchristos 	}
846bbb4a8abSchristos 
847bbb4a8abSchristos 	/* Get the amount of free space after the MBR */
848bbb4a8abSchristos 	blocks = map_free(gpt, 1LL, 0LL);
849bbb4a8abSchristos 	if (blocks == 0LL) {
850bbb4a8abSchristos 		gpt_warnx(gpt, "No room for the GPT header");
851bbb4a8abSchristos 		return -1;
852bbb4a8abSchristos 	}
853bbb4a8abSchristos 
854bbb4a8abSchristos 	/* Don't create more than parts entries. */
855bbb4a8abSchristos 	if ((uint64_t)(blocks - 1) * gpt->secsz >
856bbb4a8abSchristos 	    parts * sizeof(struct gpt_ent)) {
8575c1ccc6eSchristos 		blocks = (off_t)((parts * sizeof(struct gpt_ent)) / gpt->secsz);
858bbb4a8abSchristos 		if ((parts * sizeof(struct gpt_ent)) % gpt->secsz)
859bbb4a8abSchristos 			blocks++;
860bbb4a8abSchristos 		blocks++;		/* Don't forget the header itself */
861bbb4a8abSchristos 	}
862bbb4a8abSchristos 
863bbb4a8abSchristos 	/* Never cross the median of the device. */
864bbb4a8abSchristos 	if ((blocks + 1LL) > ((last + 1LL) >> 1))
865bbb4a8abSchristos 		blocks = ((last + 1LL) >> 1) - 1LL;
866bbb4a8abSchristos 
867bbb4a8abSchristos 	/*
868bbb4a8abSchristos 	 * Get the amount of free space at the end of the device and
869bbb4a8abSchristos 	 * calculate the size for the GPT structures.
870bbb4a8abSchristos 	 */
871bbb4a8abSchristos 	map = map_last(gpt);
872bbb4a8abSchristos 	if (map->map_type != MAP_TYPE_UNUSED) {
873bbb4a8abSchristos 		gpt_warnx(gpt, "No room for the backup header");
874bbb4a8abSchristos 		return -1;
875bbb4a8abSchristos 	}
876bbb4a8abSchristos 
877bbb4a8abSchristos 	if (map->map_size < blocks)
878bbb4a8abSchristos 		blocks = map->map_size;
879bbb4a8abSchristos 	if (blocks == 1LL) {
880bbb4a8abSchristos 		gpt_warnx(gpt, "No room for the GPT table");
881bbb4a8abSchristos 		return -1;
882bbb4a8abSchristos 	}
883bbb4a8abSchristos 
884bbb4a8abSchristos 	blocks--;		/* Number of blocks in the GPT table. */
885bbb4a8abSchristos 
8865b47b594Schristos 	if (gpt_add_hdr(gpt, MAP_TYPE_PRI_GPT_HDR, 1) == -1)
887bbb4a8abSchristos 		return -1;
888bbb4a8abSchristos 
8895c1ccc6eSchristos 	if ((p = calloc((size_t)blocks, gpt->secsz)) == NULL) {
890bbb4a8abSchristos 		gpt_warnx(gpt, "Can't allocate the primary GPT table");
891bbb4a8abSchristos 		return -1;
892bbb4a8abSchristos 	}
893bbb4a8abSchristos 	if ((gpt->tbl = map_add(gpt, 2LL, blocks,
8940f110115Schristos 	    MAP_TYPE_PRI_GPT_TBL, p, 1)) == NULL) {
895bbb4a8abSchristos 		free(p);
896bbb4a8abSchristos 		gpt_warnx(gpt, "Can't add the primary GPT table");
897bbb4a8abSchristos 		return -1;
898bbb4a8abSchristos 	}
899bbb4a8abSchristos 
900bbb4a8abSchristos 	hdr = gpt->gpt->map_data;
901bbb4a8abSchristos 	memcpy(hdr->hdr_sig, GPT_HDR_SIG, sizeof(hdr->hdr_sig));
902bbb4a8abSchristos 
903bbb4a8abSchristos 	/*
904bbb4a8abSchristos 	 * XXX struct gpt_hdr is not a multiple of 8 bytes in size and thus
905bbb4a8abSchristos 	 * contains padding we must not include in the size.
906bbb4a8abSchristos 	 */
907bbb4a8abSchristos 	hdr->hdr_revision = htole32(GPT_HDR_REVISION);
908bbb4a8abSchristos 	hdr->hdr_size = htole32(GPT_HDR_SIZE);
9095c1ccc6eSchristos 	hdr->hdr_lba_self = htole64((uint64_t)gpt->gpt->map_start);
9105c1ccc6eSchristos 	hdr->hdr_lba_alt = htole64((uint64_t)last);
9115c1ccc6eSchristos 	hdr->hdr_lba_start = htole64((uint64_t)(gpt->tbl->map_start + blocks));
9125c1ccc6eSchristos 	hdr->hdr_lba_end = htole64((uint64_t)(last - blocks - 1LL));
9130f004afeSchristos 	if (gpt_uuid_generate(gpt, hdr->hdr_guid) == -1)
9140f004afeSchristos 		return -1;
9155c1ccc6eSchristos 	hdr->hdr_lba_table = htole64((uint64_t)(gpt->tbl->map_start));
9165c1ccc6eSchristos 	hdr->hdr_entries = htole32((uint32_t)(((uint64_t)blocks * gpt->secsz) /
9175c1ccc6eSchristos 	    sizeof(struct gpt_ent)));
918bbb4a8abSchristos 	if (le32toh(hdr->hdr_entries) > parts)
919bbb4a8abSchristos 		hdr->hdr_entries = htole32(parts);
920bbb4a8abSchristos 	hdr->hdr_entsz = htole32(sizeof(struct gpt_ent));
921bbb4a8abSchristos 
922bbb4a8abSchristos 	ent = gpt->tbl->map_data;
923bbb4a8abSchristos 	for (i = 0; i < le32toh(hdr->hdr_entries); i++) {
9240f004afeSchristos 		if (gpt_uuid_generate(gpt, ent[i].ent_guid) == -1)
9250f004afeSchristos 			return -1;
926bbb4a8abSchristos 	}
927bbb4a8abSchristos 
928bbb4a8abSchristos 	/*
929bbb4a8abSchristos 	 * Create backup GPT if the user didn't suppress it.
930bbb4a8abSchristos 	 */
931bbb4a8abSchristos 	if (primary_only)
932bbb4a8abSchristos 		return last;
933bbb4a8abSchristos 
9345b47b594Schristos 	if (gpt_add_hdr(gpt, MAP_TYPE_SEC_GPT_HDR, last) == -1)
935bbb4a8abSchristos 		return -1;
936bbb4a8abSchristos 
937bbb4a8abSchristos 	if ((gpt->lbt = map_add(gpt, last - blocks, blocks,
9380f110115Schristos 	    MAP_TYPE_SEC_GPT_TBL, gpt->tbl->map_data, 0)) == NULL) {
939bbb4a8abSchristos 		gpt_warnx(gpt, "Can't add the secondary GPT table");
940bbb4a8abSchristos 		return -1;
941bbb4a8abSchristos 	}
942bbb4a8abSchristos 
943bbb4a8abSchristos 	memcpy(gpt->tpg->map_data, gpt->gpt->map_data, gpt->secsz);
944bbb4a8abSchristos 
945bbb4a8abSchristos 	hdr = gpt->tpg->map_data;
9465c1ccc6eSchristos 	hdr->hdr_lba_self = htole64((uint64_t)gpt->tpg->map_start);
9475c1ccc6eSchristos 	hdr->hdr_lba_alt = htole64((uint64_t)gpt->gpt->map_start);
9485c1ccc6eSchristos 	hdr->hdr_lba_table = htole64((uint64_t)gpt->lbt->map_start);
949bbb4a8abSchristos 	return last;
950bbb4a8abSchristos }
951bbb4a8abSchristos 
9520f004afeSchristos static int
9530f004afeSchristos gpt_size_get(gpt_t gpt, off_t *size)
954bbb4a8abSchristos {
9550f004afeSchristos 	off_t sectors;
956bbb4a8abSchristos 	int64_t human_num;
957bbb4a8abSchristos 	char *p;
958bbb4a8abSchristos 
9590f004afeSchristos 	if (*size > 0)
9600f004afeSchristos 		return -1;
9610f004afeSchristos 	sectors = strtoll(optarg, &p, 10);
9620f004afeSchristos 	if (sectors < 1)
9630f004afeSchristos 		return -1;
9640f004afeSchristos 	if (*p == '\0' || ((*p == 's' || *p == 'S') && p[1] == '\0')) {
9650f004afeSchristos 		*size = sectors * gpt->secsz;
9660f004afeSchristos 		return 0;
9670f004afeSchristos 	}
9680f004afeSchristos 	if ((*p == 'b' || *p == 'B') && p[1] == '\0') {
9690f004afeSchristos 		*size = sectors;
9700f004afeSchristos 		return 0;
9710f004afeSchristos 	}
9720f004afeSchristos 	if (dehumanize_number(optarg, &human_num) < 0)
9730f004afeSchristos 		return -1;
9740f004afeSchristos 	*size = human_num;
9750f004afeSchristos 	return 0;
9760f004afeSchristos }
9770f004afeSchristos 
9780f004afeSchristos int
979f9db7548Schristos gpt_human_get(gpt_t gpt, off_t *human)
9800f004afeSchristos {
9810f004afeSchristos 	int64_t human_num;
9820f004afeSchristos 
983f9db7548Schristos 	if (*human > 0) {
984f9db7548Schristos 		gpt_warn(gpt, "Already set to %jd new `%s'", (intmax_t)*human,
985f9db7548Schristos 		    optarg);
9860f004afeSchristos 		return -1;
987f9db7548Schristos 	}
988f9db7548Schristos 	if (dehumanize_number(optarg, &human_num) < 0) {
989f9db7548Schristos 		gpt_warn(gpt, "Bad number `%s'", optarg);
9900f004afeSchristos 		return -1;
991f9db7548Schristos 	}
9920f004afeSchristos 	*human = human_num;
993f9db7548Schristos 	if (*human < 1) {
994f9db7548Schristos 		gpt_warn(gpt, "Number `%s' < 1", optarg);
9950f004afeSchristos 		return -1;
996f9db7548Schristos 	}
9970f004afeSchristos 	return 0;
9980f004afeSchristos }
9990f004afeSchristos 
10000f004afeSchristos int
10010f004afeSchristos gpt_add_find(gpt_t gpt, struct gpt_find *find, int ch)
10020f004afeSchristos {
1003bbb4a8abSchristos 	switch (ch) {
1004bbb4a8abSchristos 	case 'a':
1005f9db7548Schristos 		if (find->all > 0) {
1006f9db7548Schristos 			gpt_warn(gpt, "-a is already set");
1007bbb4a8abSchristos 			return -1;
1008f9db7548Schristos 		}
1009bbb4a8abSchristos 		find->all = 1;
1010bbb4a8abSchristos 		break;
1011bbb4a8abSchristos 	case 'b':
1012f9db7548Schristos 		if (gpt_human_get(gpt, &find->block) == -1)
1013bbb4a8abSchristos 			return -1;
1014bbb4a8abSchristos 		break;
1015bbb4a8abSchristos 	case 'i':
1016f9db7548Schristos 		if (gpt_uint_get(gpt, &find->entry) == -1)
1017bbb4a8abSchristos 			return -1;
1018bbb4a8abSchristos 		break;
1019bbb4a8abSchristos 	case 'L':
10200f004afeSchristos 		if (gpt_name_get(gpt, &find->label) == -1)
1021bbb4a8abSchristos 			return -1;
1022bbb4a8abSchristos 		break;
1023bbb4a8abSchristos 	case 's':
10240f004afeSchristos 		if (gpt_size_get(gpt, &find->size) == -1)
1025bbb4a8abSchristos 			return -1;
1026bbb4a8abSchristos 		break;
1027bbb4a8abSchristos 	case 't':
1028bbb4a8abSchristos 		if (!gpt_uuid_is_nil(find->type))
1029bbb4a8abSchristos 			return -1;
1030bbb4a8abSchristos 		if (gpt_uuid_parse(optarg, find->type) != 0)
1031bbb4a8abSchristos 			return -1;
1032bbb4a8abSchristos 		break;
1033bbb4a8abSchristos 	default:
1034f9db7548Schristos 		gpt_warn(gpt, "Unknown find option `%c'", ch);
1035bbb4a8abSchristos 		return -1;
1036bbb4a8abSchristos 	}
1037bbb4a8abSchristos 	return 0;
1038bbb4a8abSchristos }
1039bbb4a8abSchristos 
1040bbb4a8abSchristos int
1041bbb4a8abSchristos gpt_change_ent(gpt_t gpt, const struct gpt_find *find,
1042e003a26fSjnemeth     void (*cfn)(struct gpt_ent *, void *, int), void *v)
1043bbb4a8abSchristos {
1044bbb4a8abSchristos 	map_t m;
1045bbb4a8abSchristos 	struct gpt_hdr *hdr;
1046bbb4a8abSchristos 	struct gpt_ent *ent;
1047bbb4a8abSchristos 	unsigned int i;
1048cdf86847Schristos 	uint8_t utfbuf[__arraycount(ent->ent_name) * 3 + 1];
1049bbb4a8abSchristos 
1050bbb4a8abSchristos 	if (!find->all ^
1051bbb4a8abSchristos 	    (find->block > 0 || find->entry > 0 || find->label != NULL
1052bbb4a8abSchristos 	    || find->size > 0 || !gpt_uuid_is_nil(find->type)))
1053bbb4a8abSchristos 		return -1;
1054bbb4a8abSchristos 
1055bbb4a8abSchristos 	if ((hdr = gpt_hdr(gpt)) == NULL)
1056bbb4a8abSchristos 		return -1;
1057bbb4a8abSchristos 
1058bbb4a8abSchristos 	/* Relabel all matching entries in the map. */
1059bbb4a8abSchristos 	for (m = map_first(gpt); m != NULL; m = m->map_next) {
1060bbb4a8abSchristos 		if (m->map_type != MAP_TYPE_GPT_PART || m->map_index < 1)
1061bbb4a8abSchristos 			continue;
1062bbb4a8abSchristos 		if (find->entry > 0 && find->entry != m->map_index)
1063bbb4a8abSchristos 			continue;
1064bbb4a8abSchristos 		if (find->block > 0 && find->block != m->map_start)
1065bbb4a8abSchristos 			continue;
1066bbb4a8abSchristos 		if (find->size > 0 && find->size != m->map_size)
1067bbb4a8abSchristos 			continue;
1068bbb4a8abSchristos 
1069bbb4a8abSchristos 		i = m->map_index - 1;
1070bbb4a8abSchristos 
1071bbb4a8abSchristos 		ent = gpt_ent_primary(gpt, i);
1072cdf86847Schristos 		if (find->label != NULL) {
107347a40809Schristos 			utf16_to_utf8(ent->ent_name,
107447a40809Schristos 			    __arraycount(ent->ent_name),
107547a40809Schristos 			    utfbuf, __arraycount(utfbuf));
1076a66cbab4Smlelstv 			if (strcmp((char *)find->label, (char *)utfbuf) != 0)
1077bbb4a8abSchristos 				continue;
1078cdf86847Schristos 		}
1079bbb4a8abSchristos 
1080bbb4a8abSchristos 		if (!gpt_uuid_is_nil(find->type) &&
1081bbb4a8abSchristos 		    !gpt_uuid_equal(find->type, ent->ent_type))
1082bbb4a8abSchristos 			continue;
1083bbb4a8abSchristos 
1084bbb4a8abSchristos 		/* Change the primary entry. */
1085e003a26fSjnemeth 		(*cfn)(ent, v, 0);
1086bbb4a8abSchristos 
1087bbb4a8abSchristos 		if (gpt_write_primary(gpt) == -1)
1088bbb4a8abSchristos 			return -1;
1089bbb4a8abSchristos 
1090bbb4a8abSchristos 		ent = gpt_ent_backup(gpt, i);
1091bbb4a8abSchristos 		/* Change the secondary entry. */
1092e003a26fSjnemeth 		(*cfn)(ent, v, 1);
1093bbb4a8abSchristos 
1094bbb4a8abSchristos 		if (gpt_write_backup(gpt) == -1)
1095bbb4a8abSchristos 			return -1;
1096bbb4a8abSchristos 
1097bbb4a8abSchristos 		gpt_msg(gpt, "Partition %d %s", m->map_index, find->msg);
1098bbb4a8abSchristos 	}
1099bbb4a8abSchristos 	return 0;
1100bbb4a8abSchristos }
1101bbb4a8abSchristos 
1102bbb4a8abSchristos int
110346e73491Sjnemeth gpt_change_hdr(gpt_t gpt, const struct gpt_find *find,
110446e73491Sjnemeth     void (*cfn)(struct gpt_hdr *, void *, int), void *v)
110546e73491Sjnemeth {
110646e73491Sjnemeth 	struct gpt_hdr *hdr;
110746e73491Sjnemeth 
110846e73491Sjnemeth 	if ((hdr = gpt_hdr(gpt)) == NULL)
110946e73491Sjnemeth 		return -1;
111046e73491Sjnemeth 
111146e73491Sjnemeth 	/* Change the primary header. */
111246e73491Sjnemeth 	(*cfn)(hdr, v, 0);
111346e73491Sjnemeth 
111446e73491Sjnemeth 	if (gpt_write_primary(gpt) == -1)
111546e73491Sjnemeth 		return -1;
111646e73491Sjnemeth 
111746e73491Sjnemeth 	hdr = gpt->tpg->map_data;
111846e73491Sjnemeth 	/* Change the secondary header. */
111946e73491Sjnemeth 	(*cfn)(hdr, v, 1);
112046e73491Sjnemeth 
112146e73491Sjnemeth 	if (gpt_write_backup(gpt) == -1)
112246e73491Sjnemeth 		return -1;
112346e73491Sjnemeth 
112446e73491Sjnemeth 	gpt_msg(gpt, "Header %s", find->msg);
112546e73491Sjnemeth 
112646e73491Sjnemeth 	return 0;
112746e73491Sjnemeth }
112846e73491Sjnemeth 
112946e73491Sjnemeth int
1130bbb4a8abSchristos gpt_add_ais(gpt_t gpt, off_t *alignment, u_int *entry, off_t *size, int ch)
1131bbb4a8abSchristos {
1132bbb4a8abSchristos 	switch (ch) {
1133bbb4a8abSchristos 	case 'a':
1134f9db7548Schristos 		if (gpt_human_get(gpt, alignment) == -1)
1135bbb4a8abSchristos 			return -1;
1136bbb4a8abSchristos 		return 0;
1137bbb4a8abSchristos 	case 'i':
1138f9db7548Schristos 		if (gpt_uint_get(gpt, entry) == -1)
1139bbb4a8abSchristos 			return -1;
1140bbb4a8abSchristos 		return 0;
1141bbb4a8abSchristos 	case 's':
11420f004afeSchristos 		if (gpt_size_get(gpt, size) == -1)
1143bbb4a8abSchristos 			return -1;
1144bbb4a8abSchristos 		return 0;
1145bbb4a8abSchristos 	default:
1146f9db7548Schristos 		gpt_warn(gpt, "Unknown alignment/index/size option `%c'", ch);
1147bbb4a8abSchristos 		return -1;
1148bbb4a8abSchristos 	}
1149bbb4a8abSchristos }
1150bbb4a8abSchristos 
1151bbb4a8abSchristos off_t
1152bbb4a8abSchristos gpt_check_ais(gpt_t gpt, off_t alignment, u_int entry, off_t size)
1153bbb4a8abSchristos {
1154bbb4a8abSchristos 	if (entry == 0) {
1155bbb4a8abSchristos 		gpt_warnx(gpt, "Entry not specified");
1156bbb4a8abSchristos 		return -1;
1157bbb4a8abSchristos 	}
1158bbb4a8abSchristos 	if (alignment % gpt->secsz != 0) {
1159bbb4a8abSchristos 		gpt_warnx(gpt, "Alignment (%#jx) must be a multiple of "
1160bbb4a8abSchristos 		    "sector size (%#x)", (uintmax_t)alignment, gpt->secsz);
1161bbb4a8abSchristos 		return -1;
1162bbb4a8abSchristos 	}
1163bbb4a8abSchristos 
1164bbb4a8abSchristos 	if (size % gpt->secsz != 0) {
1165bbb4a8abSchristos 		gpt_warnx(gpt, "Size (%#jx) must be a multiple of "
1166bbb4a8abSchristos 		    "sector size (%#x)", (uintmax_t)size, gpt->secsz);
1167bbb4a8abSchristos 		return -1;
1168bbb4a8abSchristos 	}
1169bbb4a8abSchristos 	if (size > 0)
1170bbb4a8abSchristos 		return size / gpt->secsz;
1171bbb4a8abSchristos 	return 0;
1172bbb4a8abSchristos }
11734d523900Schristos 
11744d523900Schristos static const struct nvd {
11754d523900Schristos 	const char *name;
11764d523900Schristos 	uint64_t mask;
11774d523900Schristos 	const char *description;
11784d523900Schristos } gpt_attr[] = {
1179bbb4a8abSchristos 	{
11804d523900Schristos 		"biosboot",
11814d523900Schristos 		GPT_ENT_ATTR_LEGACY_BIOS_BOOTABLE,
11824d523900Schristos 		"Legacy BIOS boot partition",
11834d523900Schristos 	},
11844d523900Schristos 	{
11854d523900Schristos 		"bootme",
11864d523900Schristos 		GPT_ENT_ATTR_BOOTME,
11874d523900Schristos 		"Bootable partition",
11884d523900Schristos 	},
11894d523900Schristos 	{
11904d523900Schristos 		"bootfailed",
11914d523900Schristos 		GPT_ENT_ATTR_BOOTFAILED,
11924d523900Schristos 		"Partition that marked bootonce failed to boot",
11934d523900Schristos 	},
11944d523900Schristos 	{
11954d523900Schristos 		"bootonce",
11964d523900Schristos 		GPT_ENT_ATTR_BOOTONCE,
11974d523900Schristos 		"Attempt to boot this partition only once",
11984d523900Schristos 	},
11994d523900Schristos 	{
12004d523900Schristos 		"noblockio",
12014d523900Schristos 		GPT_ENT_ATTR_NO_BLOCK_IO_PROTOCOL,
12024d523900Schristos 		"UEFI won't recognize file system for block I/O",
12034d523900Schristos 	},
12044d523900Schristos 	{
12054d523900Schristos 		"required",
12064d523900Schristos 		GPT_ENT_ATTR_REQUIRED_PARTITION,
12074d523900Schristos 		"Partition required for platform to function",
12084d523900Schristos 	},
12094d523900Schristos };
12104d523900Schristos 
12114d523900Schristos int
12124d523900Schristos gpt_attr_get(gpt_t gpt, uint64_t *attributes)
12134d523900Schristos {
12144d523900Schristos 	size_t i;
12154d523900Schristos 	int rv = 0;
12164d523900Schristos 	char *ptr;
12174d523900Schristos 
12184d523900Schristos 	*attributes = 0;
12194d523900Schristos 
12204d523900Schristos 	for (ptr = strtok(optarg, ","); ptr; ptr = strtok(NULL, ",")) {
12214d523900Schristos 		for (i = 0; i < __arraycount(gpt_attr); i++)
12224d523900Schristos 			if (strcmp(gpt_attr[i].name, ptr) == 0)
12234d523900Schristos 				break;
12244d523900Schristos 		if (i == __arraycount(gpt_attr)) {
1225331de78cSkre 			gpt_warnx(gpt, "Unrecognized attribute `%s'", ptr);
12264d523900Schristos 			rv = -1;
12274d523900Schristos 		} else
12284d523900Schristos 			*attributes |= gpt_attr[i].mask;
1229bbb4a8abSchristos 	}
12304d523900Schristos 	return rv;
12314d523900Schristos }
12324d523900Schristos 
12334d523900Schristos void
12344d523900Schristos gpt_attr_help(const char *prefix)
12354d523900Schristos {
12364d523900Schristos 	size_t i;
12374d523900Schristos 
12384d523900Schristos 	for (i = 0; i < __arraycount(gpt_attr); i++)
12394d523900Schristos 		printf("%s%10.10s\t%s\n", prefix, gpt_attr[i].name,
12404d523900Schristos 		    gpt_attr[i].description);
12414d523900Schristos }
12424d523900Schristos 
12434d523900Schristos const char *
12444d523900Schristos gpt_attr_list(char *buf, size_t len, uint64_t attributes)
12454d523900Schristos {
12464d523900Schristos 	size_t i;
12476468c262Skre 	/*
12486468c262Skre 	 * a uint64_t (attributes) has at most 16 hex digits
12496468c262Skre 	 * in its representation, add 2 for "0x", and 2 more
12506468c262Skre 	 * for surrounding [ ], plus one for a trailing \0,
12516468c262Skre 	 * and we need 21 bytes, round that up to 24
12526468c262Skre 	 */
12536468c262Skre 	char xbuf[24];
12546468c262Skre 
12554d523900Schristos 	strlcpy(buf, "", len);
12564d523900Schristos 
12576468c262Skre 	for (i = 0; i < __arraycount(gpt_attr); i++) {
12586468c262Skre 		/*
12596468c262Skre 		 * if the attribute is specified in one of bits
12606468c262Skre 		 * 48..63, it should depend upon the defining
12616468c262Skre 		 * partition type for that attribute.   Currently
12626468c262Skre 		 * we have no idea what that is, so...
12636468c262Skre 		 *
12646468c262Skre 		 * Also note that for some partition types, these
12656468c262Skre 		 * fields are not a single bit boolean, but several
12666468c262Skre 		 * bits to form a numeric value.  That we could handle.
12676468c262Skre 		 */
12686468c262Skre 
12694d523900Schristos 		if (attributes & gpt_attr[i].mask) {
12704d523900Schristos 			strlcat(buf, buf[0] ? ", " : "", len);
12714d523900Schristos 			strlcat(buf, gpt_attr[i].name, len);
12726468c262Skre #if 0
12736468c262Skre 	/*
12746468c262Skre 	 * there are none currently defined, so this is untestable
12756468c262Skre 	 * (it does build however).
12766468c262Skre 	 */
12776468c262Skre 			if (gpt_attr[i].mask & (gpt_attr[i].mask - 1)) {
1278331de78cSkre 				/* This only happens in bits 46..63 */
12796468c262Skre 
12806468c262Skre 				/*
12816468c262Skre 				 * xbuf is big enough for "=65535\0"
12826468c262Skre 				 * which is the biggest possible value
12836468c262Skre 				 */
12846468c262Skre 				snprintf(xbuf, sizeof xbuf, "=%ju",
12856468c262Skre 				    (uintmax_t) (
12866468c262Skre 				      (attributes & gpt_attr[i].mask) >>
12876468c262Skre 				      (ffs((int)(gpt_attr[i].mask >> 48)) + 47)
12886468c262Skre 				    ));
12896468c262Skre 
12906468c262Skre 				strlcat(buf, xbuf, len);
12914d523900Schristos 			}
12926468c262Skre #endif
12936468c262Skre 			attributes &=~ gpt_attr[i].mask;
12946468c262Skre 		}
12956468c262Skre 	}
12966468c262Skre 
12976468c262Skre 	if (attributes != 0) {
12986468c262Skre 		snprintf(xbuf, sizeof xbuf, "[%#jx]", (uintmax_t)attributes);
12996468c262Skre 		strlcat(buf, buf[0] ? ", " : "", len);
13006468c262Skre 		strlcat(buf, xbuf, len);
13016468c262Skre 	}
13026468c262Skre 
13034d523900Schristos 	return buf;
13044d523900Schristos }
13054d523900Schristos 
1306bbb4a8abSchristos int
1307bbb4a8abSchristos gpt_attr_update(gpt_t gpt, u_int entry, uint64_t set, uint64_t clr)
1308bbb4a8abSchristos {
1309bbb4a8abSchristos 	struct gpt_hdr *hdr;
1310bbb4a8abSchristos 	struct gpt_ent *ent;
1311bbb4a8abSchristos 	unsigned int i;
1312bbb4a8abSchristos 
1313f9db7548Schristos 	if (entry == 0 || (set == 0 && clr == 0)) {
1314f9db7548Schristos 		gpt_warnx(gpt, "Nothing to set");
1315bbb4a8abSchristos 		return -1;
1316f9db7548Schristos 	}
1317bbb4a8abSchristos 
1318bbb4a8abSchristos 	if ((hdr = gpt_hdr(gpt)) == NULL)
1319bbb4a8abSchristos 		return -1;
1320bbb4a8abSchristos 
1321bbb4a8abSchristos 	if (entry > le32toh(hdr->hdr_entries)) {
1322bbb4a8abSchristos 		gpt_warnx(gpt, "Index %u out of range (%u max)",
1323bbb4a8abSchristos 		    entry, le32toh(hdr->hdr_entries));
1324bbb4a8abSchristos 		return -1;
1325bbb4a8abSchristos 	}
1326bbb4a8abSchristos 
1327bbb4a8abSchristos 	i = entry - 1;
1328bbb4a8abSchristos 	ent = gpt_ent_primary(gpt, i);
1329bbb4a8abSchristos 	if (gpt_uuid_is_nil(ent->ent_type)) {
1330bbb4a8abSchristos 		gpt_warnx(gpt, "Entry at index %u is unused", entry);
1331bbb4a8abSchristos 		return -1;
1332bbb4a8abSchristos 	}
1333bbb4a8abSchristos 
1334bbb4a8abSchristos 	ent->ent_attr &= ~clr;
1335bbb4a8abSchristos 	ent->ent_attr |= set;
1336bbb4a8abSchristos 
1337bbb4a8abSchristos 	if (gpt_write_primary(gpt) == -1)
1338bbb4a8abSchristos 		return -1;
1339bbb4a8abSchristos 
1340bbb4a8abSchristos 	ent = gpt_ent_backup(gpt, i);
1341bbb4a8abSchristos 	ent->ent_attr &= ~clr;
1342bbb4a8abSchristos 	ent->ent_attr |= set;
1343bbb4a8abSchristos 
1344bbb4a8abSchristos 	if (gpt_write_backup(gpt) == -1)
1345bbb4a8abSchristos 		return -1;
1346bbb4a8abSchristos 	gpt_msg(gpt, "Partition %d attributes updated", entry);
1347bbb4a8abSchristos 	return 0;
1348bbb4a8abSchristos }
1349bbb4a8abSchristos 
1350bbb4a8abSchristos int
1351f9db7548Schristos gpt_uint_get(gpt_t gpt, u_int *entry)
1352bbb4a8abSchristos {
1353bbb4a8abSchristos 	char *p;
1354bbb4a8abSchristos 	if (*entry > 0)
1355bbb4a8abSchristos 		return -1;
13565c1ccc6eSchristos 	*entry = (u_int)strtoul(optarg, &p, 10);
1357f9db7548Schristos 	if (*p != 0 || *entry < 1) {
1358f9db7548Schristos 		gpt_warn(gpt, "Bad number `%s'", optarg);
1359bbb4a8abSchristos 		return -1;
1360f9db7548Schristos 	}
1361bbb4a8abSchristos 	return 0;
1362bbb4a8abSchristos }
13630f004afeSchristos int
13640f004afeSchristos gpt_uuid_get(gpt_t gpt, gpt_uuid_t *uuid)
13650f004afeSchristos {
13660f004afeSchristos 	if (!gpt_uuid_is_nil(*uuid))
13670f004afeSchristos 		return -1;
13680f004afeSchristos 	if (gpt_uuid_parse(optarg, *uuid) != 0) {
1369683824ccSchristos 		gpt_warnx(gpt, "Can't parse uuid/type `%s'", optarg);
13700f004afeSchristos 		return -1;
13710f004afeSchristos 	}
13720f004afeSchristos 	return 0;
13730f004afeSchristos }
13740f004afeSchristos 
13750f004afeSchristos int
13760f004afeSchristos gpt_name_get(gpt_t gpt, void *v)
13770f004afeSchristos {
13780f004afeSchristos 	char **name = v;
13790f004afeSchristos 	if (*name != NULL)
13800f004afeSchristos 		return -1;
13810f004afeSchristos 	*name = strdup(optarg);
13820f004afeSchristos 	if (*name == NULL) {
13830f004afeSchristos 		gpt_warn(gpt, "Can't copy string");
13840f004afeSchristos 		return -1;
13850f004afeSchristos 	}
13860f004afeSchristos 	return 0;
13870f004afeSchristos }
13885c1ccc6eSchristos 
13895c1ccc6eSchristos void
13905c1ccc6eSchristos gpt_show_num(const char *prompt, uintmax_t num)
13915c1ccc6eSchristos {
13925c1ccc6eSchristos #ifdef HN_AUTOSCALE
13935c1ccc6eSchristos 	char human_num[5];
13945c1ccc6eSchristos 	if (humanize_number(human_num, 5, (int64_t)num ,
13955c1ccc6eSchristos 	    "", HN_AUTOSCALE, HN_NOSPACE|HN_B) < 0)
13965c1ccc6eSchristos 		human_num[0] = '\0';
13975c1ccc6eSchristos #endif
13985c1ccc6eSchristos 	printf("%s: %ju", prompt, num);
13995c1ccc6eSchristos #ifdef HN_AUTOSCALE
14005c1ccc6eSchristos 	if (human_num[0] != '\0')
14015c1ccc6eSchristos 		printf(" (%s)", human_num);
14025c1ccc6eSchristos #endif
14035c1ccc6eSchristos 	printf("\n");
14045c1ccc6eSchristos }
14055b47b594Schristos 
14065b47b594Schristos int
14075b47b594Schristos gpt_add_hdr(gpt_t gpt, int type, off_t loc)
14085b47b594Schristos {
14095b47b594Schristos 	void *p;
14105b47b594Schristos 	map_t *t;
14115b47b594Schristos 	const char *msg;
14125b47b594Schristos 
14135b47b594Schristos 	switch (type) {
14145b47b594Schristos 	case MAP_TYPE_PRI_GPT_HDR:
14155b47b594Schristos 		t = &gpt->gpt;
14165b47b594Schristos 		msg = "primary";
14175b47b594Schristos 		break;
14185b47b594Schristos 	case MAP_TYPE_SEC_GPT_HDR:
14195b47b594Schristos 		t = &gpt->tpg;
14205b47b594Schristos 		msg = "secondary";
14215b47b594Schristos 		break;
14225b47b594Schristos 	default:
14235b47b594Schristos 		gpt_warnx(gpt, "Unknown GPT header type %d", type);
14245b47b594Schristos 		return -1;
14255b47b594Schristos 	}
14265b47b594Schristos 
14275b47b594Schristos 	if ((p = calloc(1, gpt->secsz)) == NULL) {
14285b47b594Schristos 		gpt_warn(gpt, "Error allocating %s GPT header", msg);
14295b47b594Schristos 		return -1;
14305b47b594Schristos 	}
14315b47b594Schristos 
14325b47b594Schristos 	*t = map_add(gpt, loc, 1LL, type, p, 1);
14335b47b594Schristos 	if (*t == NULL) {
14345b47b594Schristos 		gpt_warn(gpt, "Error adding %s GPT header", msg);
14355b47b594Schristos 		free(p);
14365b47b594Schristos 		return -1;
14375b47b594Schristos 	}
14385b47b594Schristos 	return 0;
14395b47b594Schristos }
1440