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