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