xref: /netbsd-src/sbin/gpt/gpt.c (revision f21b7d7f2cbdd5c14b3882c4e8a3d43580d460a6)
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.69 2016/09/24 13:40:55 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 		    "destroy it first");
800 		return -1;
801 	}
802 
803 	/* Get the amount of free space after the MBR */
804 	blocks = map_free(gpt, 1LL, 0LL);
805 	if (blocks == 0LL) {
806 		gpt_warnx(gpt, "No room for the GPT header");
807 		return -1;
808 	}
809 
810 	/* Don't create more than parts entries. */
811 	if ((uint64_t)(blocks - 1) * gpt->secsz >
812 	    parts * sizeof(struct gpt_ent)) {
813 		blocks = (off_t)((parts * sizeof(struct gpt_ent)) / gpt->secsz);
814 		if ((parts * sizeof(struct gpt_ent)) % gpt->secsz)
815 			blocks++;
816 		blocks++;		/* Don't forget the header itself */
817 	}
818 
819 	/* Never cross the median of the device. */
820 	if ((blocks + 1LL) > ((last + 1LL) >> 1))
821 		blocks = ((last + 1LL) >> 1) - 1LL;
822 
823 	/*
824 	 * Get the amount of free space at the end of the device and
825 	 * calculate the size for the GPT structures.
826 	 */
827 	map = map_last(gpt);
828 	if (map->map_type != MAP_TYPE_UNUSED) {
829 		gpt_warnx(gpt, "No room for the backup header");
830 		return -1;
831 	}
832 
833 	if (map->map_size < blocks)
834 		blocks = map->map_size;
835 	if (blocks == 1LL) {
836 		gpt_warnx(gpt, "No room for the GPT table");
837 		return -1;
838 	}
839 
840 	blocks--;		/* Number of blocks in the GPT table. */
841 
842 	if (gpt_add_hdr(gpt, MAP_TYPE_PRI_GPT_HDR, 1) == -1)
843 		return -1;
844 
845 	if ((p = calloc((size_t)blocks, gpt->secsz)) == NULL) {
846 		gpt_warnx(gpt, "Can't allocate the primary GPT table");
847 		return -1;
848 	}
849 	if ((gpt->tbl = map_add(gpt, 2LL, blocks,
850 	    MAP_TYPE_PRI_GPT_TBL, p, 1)) == NULL) {
851 		free(p);
852 		gpt_warnx(gpt, "Can't add the primary GPT table");
853 		return -1;
854 	}
855 
856 	hdr = gpt->gpt->map_data;
857 	memcpy(hdr->hdr_sig, GPT_HDR_SIG, sizeof(hdr->hdr_sig));
858 
859 	/*
860 	 * XXX struct gpt_hdr is not a multiple of 8 bytes in size and thus
861 	 * contains padding we must not include in the size.
862 	 */
863 	hdr->hdr_revision = htole32(GPT_HDR_REVISION);
864 	hdr->hdr_size = htole32(GPT_HDR_SIZE);
865 	hdr->hdr_lba_self = htole64((uint64_t)gpt->gpt->map_start);
866 	hdr->hdr_lba_alt = htole64((uint64_t)last);
867 	hdr->hdr_lba_start = htole64((uint64_t)(gpt->tbl->map_start + blocks));
868 	hdr->hdr_lba_end = htole64((uint64_t)(last - blocks - 1LL));
869 	if (gpt_uuid_generate(gpt, hdr->hdr_guid) == -1)
870 		return -1;
871 	hdr->hdr_lba_table = htole64((uint64_t)(gpt->tbl->map_start));
872 	hdr->hdr_entries = htole32((uint32_t)(((uint64_t)blocks * gpt->secsz) /
873 	    sizeof(struct gpt_ent)));
874 	if (le32toh(hdr->hdr_entries) > parts)
875 		hdr->hdr_entries = htole32(parts);
876 	hdr->hdr_entsz = htole32(sizeof(struct gpt_ent));
877 
878 	ent = gpt->tbl->map_data;
879 	for (i = 0; i < le32toh(hdr->hdr_entries); i++) {
880 		if (gpt_uuid_generate(gpt, ent[i].ent_guid) == -1)
881 			return -1;
882 	}
883 
884 	/*
885 	 * Create backup GPT if the user didn't suppress it.
886 	 */
887 	if (primary_only)
888 		return last;
889 
890 	if (gpt_add_hdr(gpt, MAP_TYPE_SEC_GPT_HDR, last) == -1)
891 		return -1;
892 
893 	if ((gpt->lbt = map_add(gpt, last - blocks, blocks,
894 	    MAP_TYPE_SEC_GPT_TBL, gpt->tbl->map_data, 0)) == NULL) {
895 		gpt_warnx(gpt, "Can't add the secondary GPT table");
896 		return -1;
897 	}
898 
899 	memcpy(gpt->tpg->map_data, gpt->gpt->map_data, gpt->secsz);
900 
901 	hdr = gpt->tpg->map_data;
902 	hdr->hdr_lba_self = htole64((uint64_t)gpt->tpg->map_start);
903 	hdr->hdr_lba_alt = htole64((uint64_t)gpt->gpt->map_start);
904 	hdr->hdr_lba_table = htole64((uint64_t)gpt->lbt->map_start);
905 	return last;
906 }
907 
908 static int
909 gpt_size_get(gpt_t gpt, off_t *size)
910 {
911 	off_t sectors;
912 	int64_t human_num;
913 	char *p;
914 
915 	if (*size > 0)
916 		return -1;
917 	sectors = strtoll(optarg, &p, 10);
918 	if (sectors < 1)
919 		return -1;
920 	if (*p == '\0' || ((*p == 's' || *p == 'S') && p[1] == '\0')) {
921 		*size = sectors * gpt->secsz;
922 		return 0;
923 	}
924 	if ((*p == 'b' || *p == 'B') && p[1] == '\0') {
925 		*size = sectors;
926 		return 0;
927 	}
928 	if (dehumanize_number(optarg, &human_num) < 0)
929 		return -1;
930 	*size = human_num;
931 	return 0;
932 }
933 
934 int
935 gpt_human_get(gpt_t gpt, off_t *human)
936 {
937 	int64_t human_num;
938 
939 	if (*human > 0) {
940 		gpt_warn(gpt, "Already set to %jd new `%s'", (intmax_t)*human,
941 		    optarg);
942 		return -1;
943 	}
944 	if (dehumanize_number(optarg, &human_num) < 0) {
945 		gpt_warn(gpt, "Bad number `%s'", optarg);
946 		return -1;
947 	}
948 	*human = human_num;
949 	if (*human < 1) {
950 		gpt_warn(gpt, "Number `%s' < 1", optarg);
951 		return -1;
952 	}
953 	return 0;
954 }
955 
956 int
957 gpt_add_find(gpt_t gpt, struct gpt_find *find, int ch)
958 {
959 	switch (ch) {
960 	case 'a':
961 		if (find->all > 0) {
962 			gpt_warn(gpt, "-a is already set");
963 			return -1;
964 		}
965 		find->all = 1;
966 		break;
967 	case 'b':
968 		if (gpt_human_get(gpt, &find->block) == -1)
969 			return -1;
970 		break;
971 	case 'i':
972 		if (gpt_uint_get(gpt, &find->entry) == -1)
973 			return -1;
974 		break;
975 	case 'L':
976 		if (gpt_name_get(gpt, &find->label) == -1)
977 			return -1;
978 		break;
979 	case 's':
980 		if (gpt_size_get(gpt, &find->size) == -1)
981 			return -1;
982 		break;
983 	case 't':
984 		if (!gpt_uuid_is_nil(find->type))
985 			return -1;
986 		if (gpt_uuid_parse(optarg, find->type) != 0)
987 			return -1;
988 		break;
989 	default:
990 		gpt_warn(gpt, "Unknown find option `%c'", ch);
991 		return -1;
992 	}
993 	return 0;
994 }
995 
996 int
997 gpt_change_ent(gpt_t gpt, const struct gpt_find *find,
998     void (*cfn)(struct gpt_ent *, void *), void *v)
999 {
1000 	map_t m;
1001 	struct gpt_hdr *hdr;
1002 	struct gpt_ent *ent;
1003 	unsigned int i;
1004 	uint8_t utfbuf[__arraycount(ent->ent_name) * 3 + 1];
1005 
1006 	if (!find->all ^
1007 	    (find->block > 0 || find->entry > 0 || find->label != NULL
1008 	    || find->size > 0 || !gpt_uuid_is_nil(find->type)))
1009 		return -1;
1010 
1011 	if ((hdr = gpt_hdr(gpt)) == NULL)
1012 		return -1;
1013 
1014 	/* Relabel all matching entries in the map. */
1015 	for (m = map_first(gpt); m != NULL; m = m->map_next) {
1016 		if (m->map_type != MAP_TYPE_GPT_PART || m->map_index < 1)
1017 			continue;
1018 		if (find->entry > 0 && find->entry != m->map_index)
1019 			continue;
1020 		if (find->block > 0 && find->block != m->map_start)
1021 			continue;
1022 		if (find->size > 0 && find->size != m->map_size)
1023 			continue;
1024 
1025 		i = m->map_index - 1;
1026 
1027 		ent = gpt_ent_primary(gpt, i);
1028 		if (find->label != NULL) {
1029 			utf16_to_utf8(ent->ent_name, utfbuf, sizeof(utfbuf));
1030 			if (strcmp((char *)find->label, (char *)utfbuf) == 0)
1031 				continue;
1032 		}
1033 
1034 		if (!gpt_uuid_is_nil(find->type) &&
1035 		    !gpt_uuid_equal(find->type, ent->ent_type))
1036 			continue;
1037 
1038 		/* Change the primary entry. */
1039 		(*cfn)(ent, v);
1040 
1041 		if (gpt_write_primary(gpt) == -1)
1042 			return -1;
1043 
1044 		ent = gpt_ent_backup(gpt, i);
1045 		/* Change the secondary entry. */
1046 		(*cfn)(ent, v);
1047 
1048 		if (gpt_write_backup(gpt) == -1)
1049 			return -1;
1050 
1051 		gpt_msg(gpt, "Partition %d %s", m->map_index, find->msg);
1052 	}
1053 	return 0;
1054 }
1055 
1056 int
1057 gpt_add_ais(gpt_t gpt, off_t *alignment, u_int *entry, off_t *size, int ch)
1058 {
1059 	switch (ch) {
1060 	case 'a':
1061 		if (gpt_human_get(gpt, alignment) == -1)
1062 			return -1;
1063 		return 0;
1064 	case 'i':
1065 		if (gpt_uint_get(gpt, entry) == -1)
1066 			return -1;
1067 		return 0;
1068 	case 's':
1069 		if (gpt_size_get(gpt, size) == -1)
1070 			return -1;
1071 		return 0;
1072 	default:
1073 		gpt_warn(gpt, "Unknown alignment/index/size option `%c'", ch);
1074 		return -1;
1075 	}
1076 }
1077 
1078 off_t
1079 gpt_check_ais(gpt_t gpt, off_t alignment, u_int entry, off_t size)
1080 {
1081 	if (entry == 0) {
1082 		gpt_warnx(gpt, "Entry not specified");
1083 		return -1;
1084 	}
1085 	if (alignment % gpt->secsz != 0) {
1086 		gpt_warnx(gpt, "Alignment (%#jx) must be a multiple of "
1087 		    "sector size (%#x)", (uintmax_t)alignment, gpt->secsz);
1088 		return -1;
1089 	}
1090 
1091 	if (size % gpt->secsz != 0) {
1092 		gpt_warnx(gpt, "Size (%#jx) must be a multiple of "
1093 		    "sector size (%#x)", (uintmax_t)size, gpt->secsz);
1094 		return -1;
1095 	}
1096 	if (size > 0)
1097 		return size / gpt->secsz;
1098 	return 0;
1099 }
1100 
1101 static const struct nvd {
1102 	const char *name;
1103 	uint64_t mask;
1104 	const char *description;
1105 } gpt_attr[] = {
1106 	{
1107 		"biosboot",
1108 		GPT_ENT_ATTR_LEGACY_BIOS_BOOTABLE,
1109 		"Legacy BIOS boot partition",
1110 	},
1111 	{
1112 		"bootme",
1113 		GPT_ENT_ATTR_BOOTME,
1114 		"Bootable partition",
1115 	},
1116 	{
1117 		"bootfailed",
1118 		GPT_ENT_ATTR_BOOTFAILED,
1119 		"Partition that marked bootonce failed to boot",
1120 	},
1121 	{
1122 		"bootonce",
1123 		GPT_ENT_ATTR_BOOTONCE,
1124 		"Attempt to boot this partition only once",
1125 	},
1126 	{
1127 		"noblockio",
1128 		GPT_ENT_ATTR_NO_BLOCK_IO_PROTOCOL,
1129 		"UEFI won't recognize file system for block I/O",
1130 	},
1131 	{
1132 		"required",
1133 		GPT_ENT_ATTR_REQUIRED_PARTITION,
1134 		"Partition required for platform to function",
1135 	},
1136 };
1137 
1138 int
1139 gpt_attr_get(gpt_t gpt, uint64_t *attributes)
1140 {
1141 	size_t i;
1142 	int rv = 0;
1143 	char *ptr;
1144 
1145 	*attributes = 0;
1146 
1147 	for (ptr = strtok(optarg, ","); ptr; ptr = strtok(NULL, ",")) {
1148 		for (i = 0; i < __arraycount(gpt_attr); i++)
1149 			if (strcmp(gpt_attr[i].name, ptr) == 0)
1150 				break;
1151 		if (i == __arraycount(gpt_attr)) {
1152 			gpt_warnx(gpt, "Unregognized attribute `%s'", ptr);
1153 			rv = -1;
1154 		} else
1155 			*attributes |= gpt_attr[i].mask;
1156 	}
1157 	return rv;
1158 }
1159 
1160 void
1161 gpt_attr_help(const char *prefix)
1162 {
1163 	size_t i;
1164 
1165 	for (i = 0; i < __arraycount(gpt_attr); i++)
1166 		printf("%s%10.10s\t%s\n", prefix, gpt_attr[i].name,
1167 		    gpt_attr[i].description);
1168 }
1169 
1170 const char *
1171 gpt_attr_list(char *buf, size_t len, uint64_t attributes)
1172 {
1173 	size_t i;
1174 	strlcpy(buf, "", len);
1175 
1176 	for (i = 0; i < __arraycount(gpt_attr); i++)
1177 		if (attributes & gpt_attr[i].mask) {
1178 			strlcat(buf, buf[0] ? ", " : "", len);
1179 			strlcat(buf, gpt_attr[i].name, len);
1180 		}
1181 	return buf;
1182 }
1183 
1184 int
1185 gpt_attr_update(gpt_t gpt, u_int entry, uint64_t set, uint64_t clr)
1186 {
1187 	struct gpt_hdr *hdr;
1188 	struct gpt_ent *ent;
1189 	unsigned int i;
1190 
1191 	if (entry == 0 || (set == 0 && clr == 0)) {
1192 		gpt_warnx(gpt, "Nothing to set");
1193 		return -1;
1194 	}
1195 
1196 	if ((hdr = gpt_hdr(gpt)) == NULL)
1197 		return -1;
1198 
1199 	if (entry > le32toh(hdr->hdr_entries)) {
1200 		gpt_warnx(gpt, "Index %u out of range (%u max)",
1201 		    entry, le32toh(hdr->hdr_entries));
1202 		return -1;
1203 	}
1204 
1205 	i = entry - 1;
1206 	ent = gpt_ent_primary(gpt, i);
1207 	if (gpt_uuid_is_nil(ent->ent_type)) {
1208 		gpt_warnx(gpt, "Entry at index %u is unused", entry);
1209 		return -1;
1210 	}
1211 
1212 	ent->ent_attr &= ~clr;
1213 	ent->ent_attr |= set;
1214 
1215 	if (gpt_write_primary(gpt) == -1)
1216 		return -1;
1217 
1218 	ent = gpt_ent_backup(gpt, i);
1219 	ent->ent_attr &= ~clr;
1220 	ent->ent_attr |= set;
1221 
1222 	if (gpt_write_backup(gpt) == -1)
1223 		return -1;
1224 	gpt_msg(gpt, "Partition %d attributes updated", entry);
1225 	return 0;
1226 }
1227 
1228 int
1229 gpt_uint_get(gpt_t gpt, u_int *entry)
1230 {
1231 	char *p;
1232 	if (*entry > 0)
1233 		return -1;
1234 	*entry = (u_int)strtoul(optarg, &p, 10);
1235 	if (*p != 0 || *entry < 1) {
1236 		gpt_warn(gpt, "Bad number `%s'", optarg);
1237 		return -1;
1238 	}
1239 	return 0;
1240 }
1241 int
1242 gpt_uuid_get(gpt_t gpt, gpt_uuid_t *uuid)
1243 {
1244 	if (!gpt_uuid_is_nil(*uuid))
1245 		return -1;
1246 	if (gpt_uuid_parse(optarg, *uuid) != 0) {
1247 		gpt_warn(gpt, "Can't parse uuid");
1248 		return -1;
1249 	}
1250 	return 0;
1251 }
1252 
1253 int
1254 gpt_name_get(gpt_t gpt, void *v)
1255 {
1256 	char **name = v;
1257 	if (*name != NULL)
1258 		return -1;
1259 	*name = strdup(optarg);
1260 	if (*name == NULL) {
1261 		gpt_warn(gpt, "Can't copy string");
1262 		return -1;
1263 	}
1264 	return 0;
1265 }
1266 
1267 void
1268 gpt_show_num(const char *prompt, uintmax_t num)
1269 {
1270 #ifdef HN_AUTOSCALE
1271 	char human_num[5];
1272 	if (humanize_number(human_num, 5, (int64_t)num ,
1273 	    "", HN_AUTOSCALE, HN_NOSPACE|HN_B) < 0)
1274 		human_num[0] = '\0';
1275 #endif
1276 	printf("%s: %ju", prompt, num);
1277 #ifdef HN_AUTOSCALE
1278 	if (human_num[0] != '\0')
1279 		printf(" (%s)", human_num);
1280 #endif
1281 	printf("\n");
1282 }
1283 
1284 int
1285 gpt_add_hdr(gpt_t gpt, int type, off_t loc)
1286 {
1287 	void *p;
1288 	map_t *t;
1289 	const char *msg;
1290 
1291 	switch (type) {
1292 	case MAP_TYPE_PRI_GPT_HDR:
1293 		t = &gpt->gpt;
1294 		msg = "primary";
1295 		break;
1296 	case MAP_TYPE_SEC_GPT_HDR:
1297 		t = &gpt->tpg;
1298 		msg = "secondary";
1299 		break;
1300 	default:
1301 		gpt_warnx(gpt, "Unknown GPT header type %d", type);
1302 		return -1;
1303 	}
1304 
1305 	if ((p = calloc(1, gpt->secsz)) == NULL) {
1306 		gpt_warn(gpt, "Error allocating %s GPT header", msg);
1307 		return -1;
1308 	}
1309 
1310 	*t = map_add(gpt, loc, 1LL, type, p, 1);
1311 	if (*t == NULL) {
1312 		gpt_warn(gpt, "Error adding %s GPT header", msg);
1313 		free(p);
1314 		return -1;
1315 	}
1316 	return 0;
1317 }
1318