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