xref: /openbsd-src/sbin/fdisk/gpt.c (revision ff0e7be1ebbcc809ea8ad2b6dafe215824da9e46)
1 /*	$OpenBSD: gpt.c,v 1.91 2023/05/17 12:59:37 krw Exp $	*/
2 /*
3  * Copyright (c) 2015 Markus Muller <mmu@grummel.net>
4  * Copyright (c) 2015 Kenneth R Westerback <krw@openbsd.org>
5  *
6  * Permission to use, copy, modify, and distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  */
18 
19 #include <sys/param.h>	/* DEV_BSIZE */
20 #include <sys/disklabel.h>
21 #include <sys/dkio.h>
22 #include <sys/ioctl.h>
23 
24 #include <ctype.h>
25 #include <err.h>
26 #include <stdio.h>
27 #include <stdint.h>
28 #include <stdlib.h>
29 #include <string.h>
30 #include <uuid.h>
31 
32 #include "part.h"
33 #include "disk.h"
34 #include "mbr.h"
35 #include "misc.h"
36 #include "gpt.h"
37 
38 #ifdef DEBUG
39 #define DPRINTF(x...)	printf(x)
40 #else
41 #define DPRINTF(x...)
42 #endif
43 
44 struct mbr		gmbr;
45 struct gpt_header	gh;
46 struct gpt_partition	gp[NGPTPARTITIONS];
47 
48 const struct gpt_partition * const *sort_gpt(void);
49 int			  lba_start_cmp(const void *e1, const void *e2);
50 int			  lba_free(uint64_t *, uint64_t *);
51 int			  add_partition(const uint8_t *, const char *, uint64_t);
52 int			  find_partition(const uint8_t *);
53 int			  get_header(const uint64_t);
54 int			  get_partition_table(void);
55 int			  init_gh(void);
56 int			  init_gp(const int);
57 uint32_t		  crc32(const u_char *, const uint32_t);
58 int			  protective_mbr(const struct mbr *);
59 int			  gpt_chk_mbr(struct dos_partition *, uint64_t);
60 void			  string_to_name(const unsigned int, const char *);
61 const char		 *name_to_string(const unsigned int);
62 
63 void
64 string_to_name(const unsigned int pn, const char *ch)
65 {
66 	unsigned int			i;
67 
68 	memset(gp[pn].gp_name, 0, sizeof(gp[pn].gp_name));
69 
70 	for (i = 0; i < sizeof(gp[pn].gp_name) && ch[i] != '\0'; i++)
71 		gp[pn].gp_name[i] = htole16((unsigned int)ch[i]);
72 }
73 
74 const char *
75 name_to_string(const unsigned int pn)
76 {
77 	static char		name[GPTPARTNAMESIZE + 1];
78 	unsigned int		i;
79 
80 	memset(name, 0, sizeof(name));
81 
82 	for (i = 0; i < sizeof(name) && gp[pn].gp_name[i] != 0; i++)
83 		name[i] = letoh16(gp[pn].gp_name[i]) & 0x7F;
84 
85 	return name;
86 }
87 
88 /*
89  * Return the index into dp[] of the EFI GPT (0xEE) partition, or -1 if no such
90  * partition exists.
91  *
92  * Taken from kern/subr_disk.c.
93  *
94  */
95 int
96 gpt_chk_mbr(struct dos_partition *dp, u_int64_t dsize)
97 {
98 	struct dos_partition	*dp2;
99 	int			 efi, eficnt, found, i;
100 	uint32_t		 psize;
101 
102 	found = efi = eficnt = 0;
103 	for (dp2 = dp, i = 0; i < NDOSPART; i++, dp2++) {
104 		if (dp2->dp_typ == DOSPTYP_UNUSED)
105 			continue;
106 		found++;
107 		if (dp2->dp_typ != DOSPTYP_EFI)
108 			continue;
109 		if (letoh32(dp2->dp_start) != GPTSECTOR)
110 			continue;
111 		psize = letoh32(dp2->dp_size);
112 		if (psize <= (dsize - GPTSECTOR) || psize == UINT32_MAX) {
113 			efi = i;
114 			eficnt++;
115 		}
116 	}
117 	if (found == 1 && eficnt == 1)
118 		return efi;
119 
120 	return -1;
121 }
122 
123 int
124 protective_mbr(const struct mbr *mbr)
125 {
126 	struct dos_partition	dp[NDOSPART], dos_partition;
127 	unsigned int		i;
128 
129 	if (mbr->mbr_lba_self != 0)
130 		return -1;
131 
132 	for (i = 0; i < nitems(dp); i++) {
133 		memset(&dos_partition, 0, sizeof(dos_partition));
134 		if (i < nitems(mbr->mbr_prt))
135 			PRT_prt_to_dp(&mbr->mbr_prt[i], mbr->mbr_lba_self,
136 			    mbr->mbr_lba_firstembr, &dos_partition);
137 		memcpy(&dp[i], &dos_partition, sizeof(dp[i]));
138 	}
139 
140 	return gpt_chk_mbr(dp, DL_GETDSIZE(&dl));
141 }
142 
143 int
144 get_header(const uint64_t sector)
145 {
146 	struct gpt_header	 legh;
147 	uint64_t		 gpbytes, gpsectors, lba_end;
148 
149 	if (DISK_readbytes(&legh, sector, sizeof(legh)))
150 		return -1;
151 
152 	gh.gh_sig = letoh64(legh.gh_sig);
153 	if (gh.gh_sig != GPTSIGNATURE) {
154 		DPRINTF("gpt signature: expected 0x%llx, got 0x%llx\n",
155 		    GPTSIGNATURE, gh.gh_sig);
156 		return -1;
157 	}
158 
159 	gh.gh_rev = letoh32(legh.gh_rev);
160 	if (gh.gh_rev != GPTREVISION) {
161 		DPRINTF("gpt revision: expected 0x%x, got 0x%x\n",
162 		    GPTREVISION, gh.gh_rev);
163 		return -1;
164 	}
165 
166 	gh.gh_lba_self = letoh64(legh.gh_lba_self);
167 	if (gh.gh_lba_self != sector) {
168 		DPRINTF("gpt self lba: expected %llu, got %llu\n",
169 		    sector, gh.gh_lba_self);
170 		return -1;
171 	}
172 
173 	gh.gh_size = letoh32(legh.gh_size);
174 	if (gh.gh_size != GPTMINHDRSIZE) {
175 		DPRINTF("gpt header size: expected %u, got %u\n",
176 		    GPTMINHDRSIZE, gh.gh_size);
177 		return -1;
178 	}
179 
180 	gh.gh_part_size = letoh32(legh.gh_part_size);
181 	if (gh.gh_part_size != GPTMINPARTSIZE) {
182 		DPRINTF("gpt partition size: expected %u, got %u\n",
183 		    GPTMINPARTSIZE, gh.gh_part_size);
184 		return -1;
185 	}
186 
187 	if ((dl.d_secsize % gh.gh_part_size) != 0) {
188 		DPRINTF("gpt sector size %% partition size (%u %% %u) != 0\n",
189 		    dl.d_secsize, gh.gh_part_size);
190 		return -1;
191 	}
192 
193 	gh.gh_part_num = letoh32(legh.gh_part_num);
194 	if (gh.gh_part_num > NGPTPARTITIONS) {
195 		DPRINTF("gpt partition count: expected <= %u, got %u\n",
196 		    NGPTPARTITIONS, gh.gh_part_num);
197 		return -1;
198 	}
199 
200 	gh.gh_csum = letoh32(legh.gh_csum);
201 	legh.gh_csum = 0;
202 	legh.gh_csum = crc32((unsigned char *)&legh, gh.gh_size);
203 	if (legh.gh_csum != gh.gh_csum) {
204 		DPRINTF("gpt header checksum: expected 0x%x, got 0x%x\n",
205 		    legh.gh_csum, gh.gh_csum);
206 		/* Accept wrong-endian checksum. */
207 		if (swap32(legh.gh_csum) != gh.gh_csum)
208 			return -1;
209 	}
210 
211 	gpbytes = gh.gh_part_num * gh.gh_part_size;
212 	gpsectors = (gpbytes + dl.d_secsize - 1) / dl.d_secsize;
213 	lba_end = DL_GETDSIZE(&dl) - gpsectors - 2;
214 
215 	gh.gh_lba_end = letoh64(legh.gh_lba_end);
216 	if (gh.gh_lba_end > lba_end) {
217 		DPRINTF("gpt last usable LBA: reduced from %llu to %llu\n",
218 		    gh.gh_lba_end, lba_end);
219 		gh.gh_lba_end = lba_end;
220 	}
221 
222 	gh.gh_lba_start = letoh64(legh.gh_lba_start);
223 	if (gh.gh_lba_start >= gh.gh_lba_end) {
224 		DPRINTF("gpt first usable LBA: expected < %llu, got %llu\n",
225 		    gh.gh_lba_end, gh.gh_lba_start);
226 		return -1;
227 	}
228 
229 	gh.gh_part_lba = letoh64(legh.gh_part_lba);
230 	if (gh.gh_lba_self == GPTSECTOR) {
231 		if (gh.gh_part_lba <= GPTSECTOR) {
232 			DPRINTF("gpt partition entries start: expected > %u, "
233 			    "got %llu\n", GPTSECTOR, gh.gh_part_lba);
234 			return -1;
235 		}
236 		if (gh.gh_part_lba + gpsectors > gh.gh_lba_start) {
237 			DPRINTF("gpt partition entries end: expected < %llu, "
238 			    "got %llu\n", gh.gh_lba_start,
239 			    gh.gh_part_lba + gpsectors);
240 			return -1;
241 		}
242 	} else {
243 		if (gh.gh_part_lba <= gh.gh_lba_end) {
244 			DPRINTF("gpt partition entries start: expected > %llu, "
245 			    "got %llu\n", gh.gh_lba_end, gh.gh_part_lba);
246 			return -1;
247 		}
248 		if (gh.gh_part_lba + gpsectors > gh.gh_lba_self) {
249 			DPRINTF("gpt partition entries end: expected < %llu, "
250 			    "got %llu\n", gh.gh_lba_self,
251 			    gh.gh_part_lba + gpsectors);
252 			return -1;
253 		}
254 	}
255 
256 	gh.gh_lba_alt = letoh32(legh.gh_lba_alt);
257 	gh.gh_part_csum = letoh32(legh.gh_part_csum);
258 	gh.gh_rsvd = letoh32(legh.gh_rsvd);	/* Should always be 0. */
259 	uuid_dec_le(&legh.gh_guid, &gh.gh_guid);
260 
261 	return 0;
262 }
263 
264 int
265 get_partition_table(void)
266 {
267 	struct gpt_partition	*legp;
268 	uint64_t		 gpbytes;
269 	unsigned int		 pn;
270 	int			 rslt = -1;
271 	uint32_t		 gh_part_csum;
272 
273 	DPRINTF("gpt partition table being read from LBA %llu\n",
274 	    gh.gh_part_lba);
275 
276 	gpbytes = gh.gh_part_num * gh.gh_part_size;
277 
278 	legp = calloc(1, gpbytes);
279 	if (legp == NULL)
280 		err(1, "legp");
281 
282 	if (DISK_readbytes(legp, gh.gh_part_lba, gpbytes))
283 		goto done;
284 	gh_part_csum = crc32((unsigned char *)legp, gpbytes);
285 
286 	if (gh_part_csum != gh.gh_part_csum) {
287 		DPRINTF("gpt partition table checksum: expected 0x%x, "
288 		    "got 0x%x\n", gh.gh_part_csum, gh_part_csum);
289 		/* Accept wrong-endian checksum. */
290 		if (swap32(gh_part_csum) != gh.gh_part_csum)
291 			goto done;
292 	}
293 
294 	memset(&gp, 0, sizeof(gp));
295 	for (pn = 0; pn < gh.gh_part_num; pn++) {
296 		uuid_dec_le(&legp[pn].gp_type, &gp[pn].gp_type);
297 		uuid_dec_le(&legp[pn].gp_guid, &gp[pn].gp_guid);
298 		gp[pn].gp_lba_start = letoh64(legp[pn].gp_lba_start);
299 		gp[pn].gp_lba_end = letoh64(legp[pn].gp_lba_end);
300 		gp[pn].gp_attrs = letoh64(legp[pn].gp_attrs);
301 		memcpy(gp[pn].gp_name, legp[pn].gp_name,
302 		    sizeof(gp[pn].gp_name));
303 	}
304 	rslt = 0;
305 
306  done:
307 	free(legp);
308 	return rslt;
309 }
310 
311 int
312 GPT_read(const int which)
313 {
314 	int			error;
315 
316 	error = MBR_read(0, 0, &gmbr);
317 	if (error)
318 		goto done;
319 	error = protective_mbr(&gmbr);
320 	if (error == -1)
321 		goto done;
322 
323 	switch (which) {
324 	case PRIMARYGPT:
325 		error = get_header(GPTSECTOR);
326 		break;
327 	case SECONDARYGPT:
328 		error = get_header(DL_GETDSIZE(&dl) - 1);
329 		break;
330 	case ANYGPT:
331 		error = get_header(GPTSECTOR);
332 		if (error != 0 || get_partition_table() != 0)
333 			error = get_header(DL_GETDSIZE(&dl) - 1);
334 		break;
335 	default:
336 		return -1;
337 	}
338 
339 	if (error == 0)
340 		error = get_partition_table();
341 
342  done:
343 	if (error != 0) {
344 		/* No valid GPT found. Zap any artifacts. */
345 		memset(&gmbr, 0, sizeof(gmbr));
346 		memset(&gh, 0, sizeof(gh));
347 		memset(&gp, 0, sizeof(gp));
348 	}
349 
350 	return error;
351 }
352 
353 void
354 GPT_print(const char *units, const int verbosity)
355 {
356 	const struct unit_type	*ut;
357 	const int		 secsize = dl.d_secsize;
358 	char			*guidstr = NULL;
359 	double			 size;
360 	unsigned int		 pn;
361 	uint32_t		 status;
362 
363 #ifdef	DEBUG
364 	char			*p;
365 	uint64_t		 sig;
366 	unsigned int		 i;
367 
368 	sig = htole64(gh.gh_sig);
369 	p = (char *)&sig;
370 
371 	printf("gh_sig         : ");
372 	for (i = 0; i < sizeof(sig); i++)
373 		printf("%c", isprint((unsigned char)p[i]) ? p[i] : '?');
374 	printf(" (");
375 	for (i = 0; i < sizeof(sig); i++) {
376 		printf("%02x", p[i]);
377 		if ((i + 1) < sizeof(sig))
378 			printf(":");
379 	}
380 	printf(")\n");
381 	printf("gh_rev         : %u\n", gh.gh_rev);
382 	printf("gh_size        : %u (%zd)\n", gh.gh_size, sizeof(gh));
383 	printf("gh_csum        : 0x%x\n", gh.gh_csum);
384 	printf("gh_rsvd        : %u\n", gh.gh_rsvd);
385 	printf("gh_lba_self    : %llu\n", gh.gh_lba_self);
386 	printf("gh_lba_alt     : %llu\n", gh.gh_lba_alt);
387 	printf("gh_lba_start   : %llu\n", gh.gh_lba_start);
388 	printf("gh_lba_end     : %llu\n", gh.gh_lba_end);
389 	p = NULL;
390 	uuid_to_string(&gh.gh_guid, &p, &status);
391 	printf("gh_gh_guid     : %s\n", (status == uuid_s_ok) ? p : "<invalid>");
392 	free(p);
393 	printf("gh_gh_part_lba : %llu\n", gh.gh_part_lba);
394 	printf("gh_gh_part_num : %u (%zu)\n", gh.gh_part_num, nitems(gp));
395 	printf("gh_gh_part_size: %u (%zu)\n", gh.gh_part_size, sizeof(gp[0]));
396 	printf("gh_gh_part_csum: 0x%x\n", gh.gh_part_csum);
397 	printf("\n");
398 #endif	/* DEBUG */
399 
400 	size = units_size(units, DL_GETDSIZE(&dl), &ut);
401 	printf("Disk: %s       Usable LBA: %llu to %llu [%.0f ",
402 	    disk.dk_name, gh.gh_lba_start, gh.gh_lba_end, size);
403 	if (ut->ut_conversion == 0 && secsize != DEV_BSIZE)
404 		printf("%d-byte ", secsize);
405 	printf("%s]\n", ut->ut_lname);
406 
407 	if (verbosity == VERBOSE) {
408 		printf("GUID: ");
409 		uuid_to_string(&gh.gh_guid, &guidstr, &status);
410 		if (status == uuid_s_ok)
411 			printf("%s\n", guidstr);
412 		else
413 			printf("<invalid header GUID>\n");
414 		free(guidstr);
415 	}
416 
417 	GPT_print_parthdr(verbosity);
418 	for (pn = 0; pn < gh.gh_part_num; pn++) {
419 		if (uuid_is_nil(&gp[pn].gp_type, NULL))
420 			continue;
421 		GPT_print_part(pn, units, verbosity);
422 	}
423 }
424 
425 void
426 GPT_print_parthdr(const int verbosity)
427 {
428 	printf("   #: type                                "
429 	    " [       start:         size ]\n");
430 	if (verbosity == VERBOSE)
431 		printf("      guid                                 name\n");
432 	printf("--------------------------------------------------------"
433 	    "----------------\n");
434 }
435 
436 void
437 GPT_print_part(const unsigned int pn, const char *units, const int verbosity)
438 {
439 	const struct unit_type	*ut;
440 	char			*guidstr = NULL;
441 	double			 size;
442 	uint64_t		 attrs, end, start;
443 	uint32_t		 status;
444 
445 	start = gp[pn].gp_lba_start;
446 	end = gp[pn].gp_lba_end;
447 	size = units_size(units, (start > end) ? 0 : end - start + 1, &ut);
448 
449 	printf(" %3u: %-36s [%12lld: %12.0f%s]\n", pn,
450 	    PRT_uuid_to_desc(&gp[pn].gp_type), start, size, ut->ut_abbr);
451 
452 	if (verbosity == VERBOSE) {
453 		uuid_to_string(&gp[pn].gp_guid, &guidstr, &status);
454 		if (status != uuid_s_ok)
455 			printf("      <invalid partition guid>             ");
456 		else
457 			printf("      %-36s ", guidstr);
458 		printf("%-36s\n", name_to_string(pn));
459 		free(guidstr);
460 		attrs = gp[pn].gp_attrs;
461 		if (attrs) {
462 			printf("      Attributes: (0x%016llx) ", attrs);
463 			if (attrs & GPTPARTATTR_REQUIRED)
464 				printf("Required " );
465 			if (attrs & GPTPARTATTR_IGNORE)
466 				printf("Ignore ");
467 			if (attrs & GPTPARTATTR_BOOTABLE)
468 				printf("Bootable ");
469 			if (attrs & GPTPARTATTR_MS_READONLY)
470 				printf("MSReadOnly " );
471 			if (attrs & GPTPARTATTR_MS_SHADOW)
472 				printf("MSShadow ");
473 			if (attrs & GPTPARTATTR_MS_HIDDEN)
474 				printf("MSHidden ");
475 			if (attrs & GPTPARTATTR_MS_NOAUTOMOUNT)
476 				printf("MSNoAutoMount ");
477 			printf("\n");
478 		}
479 	}
480 
481 	if (uuid_is_nil(&gp[pn].gp_type, NULL) == 0) {
482 		if (start > end)
483 			printf("partition %u first LBA is > last LBA\n", pn);
484 		if (start < gh.gh_lba_start || end > gh.gh_lba_end)
485 			printf("partition %u extends beyond usable LBA range "
486 			    "of %s\n", pn, disk.dk_name);
487 	}
488 }
489 
490 int
491 find_partition(const uint8_t *beuuid)
492 {
493 	struct uuid		uuid;
494 	unsigned int		pn;
495 
496 	uuid_dec_be(beuuid, &uuid);
497 
498 	for (pn = 0; pn < gh.gh_part_num; pn++) {
499 		if (uuid_compare(&gp[pn].gp_type, &uuid, NULL) == 0)
500 			return pn;
501 	}
502 	return -1;
503 }
504 
505 int
506 add_partition(const uint8_t *beuuid, const char *name, uint64_t sectors)
507 {
508 	struct uuid		uuid;
509 	int			rslt;
510 	uint64_t		end, freesectors, start;
511 	uint32_t		status, pn;
512 
513 	uuid_dec_be(beuuid, &uuid);
514 
515 	for (pn = 0; pn < gh.gh_part_num; pn++) {
516 		if (uuid_is_nil(&gp[pn].gp_type, NULL))
517 			break;
518 	}
519 	if (pn == gh.gh_part_num)
520 		goto done;
521 
522 	rslt = lba_free(&start, &end);
523 	if (rslt == -1)
524 		goto done;
525 
526 	if (start % BLOCKALIGNMENT)
527 		start += (BLOCKALIGNMENT - start % BLOCKALIGNMENT);
528 	if (start >= end)
529 		goto done;
530 
531 	freesectors = end - start + 1;
532 
533 	if (sectors == 0)
534 		sectors = freesectors;
535 
536 	if (freesectors < sectors)
537 		goto done;
538 	else if (freesectors > sectors)
539 		end = start + sectors - 1;
540 
541 	gp[pn].gp_type = uuid;
542 	gp[pn].gp_lba_start = start;
543 	gp[pn].gp_lba_end = end;
544 	string_to_name(pn, name);
545 
546 	uuid_create(&gp[pn].gp_guid, &status);
547 	if (status == uuid_s_ok)
548 		return 0;
549 
550  done:
551 	if (pn != gh.gh_part_num)
552 		memset(&gp[pn], 0, sizeof(gp[pn]));
553 	printf("unable to add %s\n", name);
554 	return -1;
555 }
556 
557 int
558 init_gh(void)
559 {
560 	struct gpt_header	oldgh;
561 	const int		secsize = dl.d_secsize;
562 	int			needed;
563 	uint32_t		status;
564 
565 	memcpy(&oldgh, &gh, sizeof(oldgh));
566 	memset(&gh, 0, sizeof(gh));
567 	memset(&gmbr, 0, sizeof(gmbr));
568 
569 	/* XXX Do we need the boot code? UEFI spec & Apple says no. */
570 	memcpy(gmbr.mbr_code, default_dmbr.dmbr_boot, sizeof(gmbr.mbr_code));
571 	gmbr.mbr_prt[0].prt_id = DOSPTYP_EFI;
572 	gmbr.mbr_prt[0].prt_bs = 1;
573 	gmbr.mbr_prt[0].prt_ns = UINT32_MAX;
574 	gmbr.mbr_signature = DOSMBR_SIGNATURE;
575 
576 	needed = sizeof(gp) / secsize + 2;
577 
578 	if (needed % BLOCKALIGNMENT)
579 		needed += (needed - (needed % BLOCKALIGNMENT));
580 
581 	gh.gh_sig = GPTSIGNATURE;
582 	gh.gh_rev = GPTREVISION;
583 	gh.gh_size = GPTMINHDRSIZE;
584 	gh.gh_csum = 0;
585 	gh.gh_rsvd = 0;
586 	gh.gh_lba_self = 1;
587 	gh.gh_lba_alt = DL_GETDSIZE(&dl) - 1;
588 	gh.gh_lba_start = needed;
589 	gh.gh_lba_end = DL_GETDSIZE(&dl) - needed;
590 	uuid_create(&gh.gh_guid, &status);
591 	if (status != uuid_s_ok) {
592 		memcpy(&gh, &oldgh, sizeof(gh));
593 		return -1;
594 	}
595 	gh.gh_part_lba = 2;
596 	gh.gh_part_num = NGPTPARTITIONS;
597 	gh.gh_part_size = GPTMINPARTSIZE;
598 	gh.gh_part_csum = 0;
599 
600 	return 0;
601 }
602 
603 int
604 init_gp(const int how)
605 {
606 	struct gpt_partition	oldgp[NGPTPARTITIONS];
607 	const uint8_t		gpt_uuid_efi_system[] = GPT_UUID_EFI_SYSTEM;
608 	const uint8_t		gpt_uuid_openbsd[] = GPT_UUID_OPENBSD;
609 	uint64_t		prt_ns;
610 	int			pn, rslt;
611 
612 	memcpy(&oldgp, &gp, sizeof(oldgp));
613 	if (how == GHANDGP)
614 		memset(&gp, 0, sizeof(gp));
615 	else {
616 		for (pn = 0; pn < gh.gh_part_num; pn++) {
617 			if (PRT_protected_uuid(&gp[pn].gp_type) ||
618 			    (gp[pn].gp_attrs & GPTPARTATTR_REQUIRED))
619 				continue;
620 			memset(&gp[pn], 0, sizeof(gp[pn]));
621 		}
622 	}
623 
624 	rslt = 0;
625 	if (disk.dk_bootprt.prt_ns > 0) {
626 		pn = find_partition(gpt_uuid_efi_system);
627 		if (pn == -1) {
628 			rslt = add_partition(gpt_uuid_efi_system,
629 			    "EFI System Area", disk.dk_bootprt.prt_ns);
630 		} else {
631 			prt_ns = gp[pn].gp_lba_end - gp[pn].gp_lba_start + 1;
632 			if (prt_ns < disk.dk_bootprt.prt_ns) {
633 				printf("EFI System Area < %llu sectors\n",
634 				    disk.dk_bootprt.prt_ns);
635 				rslt = -1;
636 			}
637 		}
638 	}
639 	if (rslt == 0)
640 		rslt = add_partition(gpt_uuid_openbsd, "OpenBSD Area", 0);
641 
642 	if (rslt != 0)
643 		memcpy(&gp, &oldgp, sizeof(gp));
644 
645 	return rslt;
646 }
647 
648 int
649 GPT_init(const int how)
650 {
651 	int			rslt = 0;
652 
653 	if (how == GHANDGP)
654 		rslt = init_gh();
655 	if (rslt == 0)
656 		rslt = init_gp(how);
657 
658 	return rslt;
659 }
660 
661 void
662 GPT_zap_headers(void)
663 {
664 	struct gpt_header	legh;
665 
666 	if (DISK_readbytes(&legh, GPTSECTOR, sizeof(legh)))
667 		return;
668 
669 	if (letoh64(legh.gh_sig) == GPTSIGNATURE) {
670 		memset(&legh, 0, sizeof(legh));
671 		if (DISK_writebytes(&legh, GPTSECTOR, sizeof(legh)))
672 			DPRINTF("Unable to zap GPT header @ sector %d",
673 			    GPTSECTOR);
674 	}
675 
676 	if (DISK_readbytes(&legh, DL_GETDSIZE(&dl) - 1, sizeof(legh)))
677 		return;
678 
679 	if (letoh64(legh.gh_sig) == GPTSIGNATURE) {
680 		memset(&legh, 0, GPTMINHDRSIZE);
681 		if (DISK_writebytes(&legh, DL_GETDSIZE(&dl) - 1, sizeof(legh)))
682 			DPRINTF("Unable to zap GPT header @ sector %llu",
683 			    DL_GETDSIZE(&dl) - 1);
684 	}
685 }
686 
687 int
688 GPT_write(void)
689 {
690 	struct gpt_header	 legh;
691 	struct gpt_partition	*legp;
692 	uint64_t		 altgh, altgp;
693 	uint64_t		 gpbytes, gpsectors;
694 	unsigned int		 pn;
695 	int			 rslt = -1;
696 
697 	if (MBR_write(&gmbr))
698 		return -1;
699 
700 	gpbytes = gh.gh_part_num * gh.gh_part_size;
701 	gpsectors = (gpbytes + dl.d_secsize - 1) / dl.d_secsize;
702 
703 	altgh = DL_GETDSIZE(&dl) - 1;
704 	altgp = altgh - gpsectors;
705 
706 	legh.gh_sig = htole64(GPTSIGNATURE);
707 	legh.gh_rev = htole32(GPTREVISION);
708 	legh.gh_size = htole32(GPTMINHDRSIZE);
709 	legh.gh_rsvd = 0;
710 	legh.gh_lba_self = htole64(GPTSECTOR);
711 	legh.gh_lba_alt = htole64(altgh);
712 	legh.gh_lba_start = htole64(gh.gh_lba_start);
713 	legh.gh_lba_end = htole64(gh.gh_lba_end);
714 	uuid_enc_le(&legh.gh_guid, &gh.gh_guid);
715 	legh.gh_part_lba = htole64(GPTSECTOR + 1);
716 	legh.gh_part_num = htole32(gh.gh_part_num);
717 	legh.gh_part_size = htole32(GPTMINPARTSIZE);
718 
719 	legp = calloc(1, gpbytes);
720 	if (legp == NULL)
721 		err(1, "legp");
722 
723 	for (pn = 0; pn < gh.gh_part_num; pn++) {
724 		uuid_enc_le(&legp[pn].gp_type, &gp[pn].gp_type);
725 		uuid_enc_le(&legp[pn].gp_guid, &gp[pn].gp_guid);
726 		legp[pn].gp_lba_start = htole64(gp[pn].gp_lba_start);
727 		legp[pn].gp_lba_end = htole64(gp[pn].gp_lba_end);
728 		legp[pn].gp_attrs = htole64(gp[pn].gp_attrs);
729 		memcpy(legp[pn].gp_name, gp[pn].gp_name,
730 		    sizeof(legp[pn].gp_name));
731 	}
732 	legh.gh_part_csum = htole32(crc32((unsigned char *)legp, gpbytes));
733 	legh.gh_csum = 0;
734 	legh.gh_csum = htole32(crc32((unsigned char *)&legh, gh.gh_size));
735 
736 	if (DISK_writebytes(&legh, GPTSECTOR, gh.gh_size) ||
737 	    DISK_writebytes(legp, GPTSECTOR + 1, gpbytes))
738 		goto done;
739 
740 	legh.gh_lba_self = htole64(altgh);
741 	legh.gh_lba_alt = htole64(GPTSECTOR);
742 	legh.gh_part_lba = htole64(altgp);
743 	legh.gh_csum = 0;
744 	legh.gh_csum = htole32(crc32((unsigned char *)&legh, gh.gh_size));
745 
746 	if (DISK_writebytes(&legh, altgh, gh.gh_size) ||
747 	    DISK_writebytes(&gp, altgp, gpbytes))
748 		goto done;
749 
750 	/* Refresh in-kernel disklabel from the updated disk information. */
751 	if (ioctl(disk.dk_fd, DIOCRLDINFO, 0) == -1)
752 		warn("DIOCRLDINFO");
753 	rslt = 0;
754 
755  done:
756 	free(legp);
757 	return rslt;
758 }
759 
760 int
761 gp_lba_start_cmp(const void *e1, const void *e2)
762 {
763 	struct gpt_partition	*p1 = *(struct gpt_partition **)e1;
764 	struct gpt_partition	*p2 = *(struct gpt_partition **)e2;
765 	uint64_t		 o1;
766 	uint64_t		 o2;
767 
768 	o1 = p1->gp_lba_start;
769 	o2 = p2->gp_lba_start;
770 
771 	if (o1 < o2)
772 		return -1;
773 	else if (o1 > o2)
774 		return 1;
775 	else
776 		return 0;
777 }
778 
779 const struct gpt_partition * const *
780 sort_gpt(void)
781 {
782 	static const struct gpt_partition	*sgp[NGPTPARTITIONS+2];
783 	unsigned int				 i, pn;
784 
785 	memset(sgp, 0, sizeof(sgp));
786 
787 	i = 0;
788 	for (pn = 0; pn < gh.gh_part_num; pn++) {
789 		if (gp[pn].gp_lba_start >= gh.gh_lba_start)
790 			sgp[i++] = &gp[pn];
791 	}
792 
793 	if (i > 1) {
794 		if (mergesort(sgp, i, sizeof(sgp[0]), gp_lba_start_cmp) == -1) {
795 			printf("unable to sort gpt by lba start\n");
796 			return NULL;
797 		}
798 	}
799 
800 	return sgp;
801 }
802 
803 int
804 lba_free(uint64_t *start, uint64_t *end)
805 {
806 	const struct gpt_partition * const *sgp;
807 	uint64_t			  bs, bigbs, nextbs, ns;
808 	unsigned int			  i;
809 
810 	sgp = sort_gpt();
811 	if (sgp == NULL)
812 		return -1;
813 
814 	bs = gh.gh_lba_start;
815 	ns = gh.gh_lba_end - bs + 1;
816 
817 	if (sgp[0] != NULL) {
818 		bigbs = bs;
819 		ns = 0;
820 		for (i = 0; sgp[i] != NULL; i++) {
821 			nextbs = sgp[i]->gp_lba_start;
822 			if (bs < nextbs && ns < nextbs - bs) {
823 				ns = nextbs - bs;
824 				bigbs = bs;
825 			}
826 			bs = sgp[i]->gp_lba_end + 1;
827 		}
828 		nextbs = gh.gh_lba_end + 1;
829 		if (bs < nextbs && ns < nextbs - bs) {
830 			ns = nextbs - bs;
831 			bigbs = bs;
832 		}
833 		bs = bigbs;
834 	}
835 
836 	if (ns == 0)
837 		return -1;
838 
839 	if (start != NULL)
840 		*start = bs;
841 	if (end != NULL)
842 		*end = bs + ns - 1;
843 
844 	return 0;
845 }
846 
847 int
848 GPT_get_lba_start(const unsigned int pn)
849 {
850 	uint64_t		bs;
851 	unsigned int		i;
852 	int			rslt;
853 
854 	bs = gh.gh_lba_start;
855 
856 	if (gp[pn].gp_lba_start >= bs) {
857 		bs = gp[pn].gp_lba_start;
858 	} else {
859 		rslt = lba_free(&bs, NULL);
860 		if (rslt == -1) {
861 			printf("no space for partition %u\n", pn);
862 			return -1;
863 		}
864 	}
865 
866 	bs = getuint64("Partition offset", bs, gh.gh_lba_start, gh.gh_lba_end);
867 	for (i = 0; i < gh.gh_part_num; i++) {
868 		if (i == pn)
869 			continue;
870 		if (bs >= gp[i].gp_lba_start && bs <= gp[i].gp_lba_end) {
871 			printf("partition %u can't start inside partition %u\n",
872 			    pn, i);
873 			return -1;
874 		}
875 	}
876 
877 	gp[pn].gp_lba_start = bs;
878 
879 	return 0;
880 }
881 
882 int
883 GPT_get_lba_end(const unsigned int pn)
884 {
885 	const struct gpt_partition	* const *sgp;
886 	uint64_t			  bs, nextbs, ns;
887 	unsigned int			  i;
888 
889 	sgp = sort_gpt();
890 	if (sgp == NULL)
891 		return -1;
892 
893 	bs = gp[pn].gp_lba_start;
894 	ns = gh.gh_lba_end - bs + 1;
895 	for (i = 0; sgp[i] != NULL; i++) {
896 		nextbs = sgp[i]->gp_lba_start;
897 		if (nextbs > bs) {
898 			ns = nextbs - bs;
899 			break;
900 		}
901 	}
902 	ns = getuint64("Partition size", ns, 1, ns);
903 
904 	gp[pn].gp_lba_end = bs + ns - 1;
905 
906 	return 0;
907 }
908 
909 int
910 GPT_get_name(const unsigned int pn)
911 {
912 	char			 name[GPTPARTNAMESIZE + 1];
913 
914 	printf("Partition name: [%s] ", name_to_string(pn));
915 	string_from_line(name, sizeof(name), UNTRIMMED);
916 
917 	switch (strlen(name)) {
918 	case 0:
919 		break;
920 	case GPTPARTNAMESIZE:
921 		printf("partition name must be < %d characters\n",
922 		    GPTPARTNAMESIZE);
923 		return -1;
924 	default:
925 		string_to_name(pn, name);
926 		break;
927 	}
928 
929 	return 0;
930 }
931 
932 /*
933  * Adapted from Hacker's Delight crc32b().
934  *
935  * To quote http://www.hackersdelight.org/permissions.htm :
936  *
937  * "You are free to use, copy, and distribute any of the code on
938  *  this web site, whether modified by you or not. You need not give
939  *  attribution. This includes the algorithms (some of which appear
940  *  in Hacker's Delight), the Hacker's Assistant, and any code submitted
941  *  by readers. Submitters implicitly agree to this."
942  */
943 uint32_t
944 crc32(const u_char *buf, const uint32_t size)
945 {
946 	int			j;
947 	uint32_t		i, byte, crc, mask;
948 
949 	crc = 0xFFFFFFFF;
950 
951 	for (i = 0; i < size; i++) {
952 		byte = buf[i];			/* Get next byte. */
953 		crc = crc ^ byte;
954 		for (j = 7; j >= 0; j--) {	/* Do eight times. */
955 			mask = -(crc & 1);
956 			crc = (crc >> 1) ^ (0xEDB88320 & mask);
957 		}
958 	}
959 
960 	return ~crc;
961 }
962