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