xref: /openbsd-src/sbin/fdisk/gpt.c (revision 4e1ee0786f11cc571bd0be17d38e46f635c719fc)
1 /*	$OpenBSD: gpt.c,v 1.53 2021/09/13 15:07:51 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 struct gpt_partition	**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			  get_header(const uint64_t);
53 int			  get_partition_table(void);
54 int			  init_gh(void);
55 int			  init_gp(const int);
56 uint32_t		  crc32(const u_char *, const uint32_t);
57 int			  protective_mbr(const struct mbr *);
58 int			  gpt_chk_mbr(struct dos_partition *, uint64_t);
59 
60 /*
61  * Return the index into dp[] of the EFI GPT (0xEE) partition, or -1 if no such
62  * partition exists.
63  *
64  * Taken from kern/subr_disk.c.
65  *
66  */
67 int
68 gpt_chk_mbr(struct dos_partition *dp, u_int64_t dsize)
69 {
70 	struct dos_partition	*dp2;
71 	int			 efi, eficnt, found, i;
72 	uint32_t		 psize;
73 
74 	found = efi = eficnt = 0;
75 	for (dp2 = dp, i = 0; i < NDOSPART; i++, dp2++) {
76 		if (dp2->dp_typ == DOSPTYP_UNUSED)
77 			continue;
78 		found++;
79 		if (dp2->dp_typ != DOSPTYP_EFI)
80 			continue;
81 		if (letoh32(dp2->dp_start) != GPTSECTOR)
82 			continue;
83 		psize = letoh32(dp2->dp_size);
84 		if (psize <= (dsize - GPTSECTOR) || psize == UINT32_MAX) {
85 			efi = i;
86 			eficnt++;
87 		}
88 	}
89 	if (found == 1 && eficnt == 1)
90 		return efi;
91 
92 	return -1;
93 }
94 
95 int
96 protective_mbr(const struct mbr *mbr)
97 {
98 	struct dos_partition	dp[NDOSPART], dos_partition;
99 	int			i;
100 
101 	if (mbr->mbr_lba_self != 0)
102 		return -1;
103 
104 	for (i = 0; i < NDOSPART; i++) {
105 		PRT_make(&mbr->mbr_prt[i], mbr->mbr_lba_self, mbr->mbr_lba_firstembr,
106 		    &dos_partition);
107 		memcpy(&dp[i], &dos_partition, sizeof(dp[i]));
108 	}
109 
110 	return gpt_chk_mbr(dp, DL_GETDSIZE(&dl));
111 }
112 
113 int
114 get_header(const uint64_t sector)
115 {
116 	char			*secbuf;
117 	uint64_t		 partlastlba, partslen, lba_end;
118 	int			 partspersec;
119 	uint32_t		 orig_gh_csum, new_gh_csum;
120 
121 	secbuf = DISK_readsectors(sector, 1);
122 	if (secbuf == NULL)
123 		return -1;
124 
125 	memcpy(&gh, secbuf, sizeof(struct gpt_header));
126 	free(secbuf);
127 
128 	if (letoh64(gh.gh_sig) != GPTSIGNATURE) {
129 		DPRINTF("gpt signature: expected 0x%llx, got 0x%llx\n",
130 		    GPTSIGNATURE, letoh64(gh.gh_sig));
131 		return -1;
132 	}
133 
134 	if (letoh32(gh.gh_rev) != GPTREVISION) {
135 		DPRINTF("gpt revision: expected 0x%x, got 0x%x\n",
136 		    GPTREVISION, letoh32(gh.gh_rev));
137 		return -1;
138 	}
139 
140 	if (letoh64(gh.gh_lba_self) != sector) {
141 		DPRINTF("gpt self lba: expected %llu, got %llu\n",
142 		    sector, letoh64(gh.gh_lba_self));
143 		return -1;
144 	}
145 
146 	if (letoh32(gh.gh_size) != GPTMINHDRSIZE) {
147 		DPRINTF("gpt header size: expected %u, got %u\n",
148 		    GPTMINHDRSIZE, letoh32(gh.gh_size));
149 		return -1;
150 	}
151 
152 	if (letoh32(gh.gh_part_size) != GPTMINPARTSIZE) {
153 		DPRINTF("gpt partition size: expected %u, got %u\n",
154 		    GPTMINPARTSIZE, letoh32(gh.gh_part_size));
155 		return -1;
156 	}
157 
158 	if (letoh32(gh.gh_part_num) > NGPTPARTITIONS) {
159 		DPRINTF("gpt partition count: expected <= %u, got %u\n",
160 		    NGPTPARTITIONS, letoh32(gh.gh_part_num));
161 		return -1;
162 	}
163 
164 	orig_gh_csum = gh.gh_csum;
165 	gh.gh_csum = 0;
166 	new_gh_csum = crc32((unsigned char *)&gh, letoh32(gh.gh_size));
167 	gh.gh_csum = orig_gh_csum;
168 	if (letoh32(orig_gh_csum) != new_gh_csum) {
169 		DPRINTF("gpt header checksum: expected 0x%x, got 0x%x\n",
170 		    orig_gh_csum, new_gh_csum);
171 		return -1;
172 	}
173 
174 	/* XXX Assume part_num * part_size is multiple of secsize. */
175 	partslen = letoh32(gh.gh_part_num) * letoh32(gh.gh_part_size) /
176 	    dl.d_secsize;
177 	lba_end = DL_GETDSIZE(&dl) - partslen - 2;
178 	if (letoh64(gh.gh_lba_end) > lba_end) {
179 		DPRINTF("gpt last usable LBA: reduced from %llu to %llu\n",
180 		    letoh64(gh.gh_lba_end), lba_end);
181 		gh.gh_lba_end = htole64(lba_end);
182 	}
183 
184 	if (letoh64(gh.gh_lba_start) >= letoh64(gh.gh_lba_end)) {
185 		DPRINTF("gpt first usable LBA: expected < %llu, got %llu\n",
186 		    letoh64(gh.gh_lba_end), letoh64(gh.gh_lba_start));
187 		return -1;
188 	}
189 
190 	if (letoh64(gh.gh_part_lba) <= letoh64(gh.gh_lba_end) &&
191 	    letoh64(gh.gh_part_lba) >= letoh64(gh.gh_lba_start)) {
192 		DPRINTF("gpt partition table start lba: expected < %llu or "
193 		    "> %llu, got %llu\n", letoh64(gh.gh_lba_start),
194 		    letoh64(gh.gh_lba_end), letoh64(gh.gh_part_lba));
195 		return -1;
196 	}
197 
198 	partspersec = dl.d_secsize / letoh32(gh.gh_part_size);
199 	partlastlba = letoh64(gh.gh_part_lba) +
200 	    ((letoh32(gh.gh_part_num) + partspersec - 1) / partspersec) - 1;
201 	if (partlastlba <= letoh64(gh.gh_lba_end) &&
202 	    partlastlba >= letoh64(gh.gh_lba_start)) {
203 		DPRINTF("gpt partition table last LBA: expected < %llu or "
204 		    "> %llu, got %llu\n", letoh64(gh.gh_lba_start),
205 		    letoh64(gh.gh_lba_end), partlastlba);
206 		return -1;
207 	}
208 
209 	/*
210 	 * Other possible paranoia checks:
211 	 *	1) partition table starts before primary gpt lba.
212 	 *	2) partition table extends into lowest partition.
213 	 *	3) alt partition table starts before gh_lba_end.
214 	 */
215 	return 0;
216 }
217 
218 int
219 get_partition_table(void)
220 {
221 	char			*secbuf;
222 	uint64_t		 gpbytes, gpsectors;
223 	uint32_t		 checksum, partspersec;
224 
225 	DPRINTF("gpt partition table being read from LBA %llu\n",
226 	    letoh64(gh.gh_part_lba));
227 
228 	partspersec = dl.d_secsize / letoh32(gh.gh_part_size);
229 	if (partspersec * letoh32(gh.gh_part_size) != dl.d_secsize) {
230 		DPRINTF("gpt partition table entry invalid size. %u\n",
231 		    letoh32(gh.gh_part_size));
232 		return -1;
233 	}
234 	gpbytes = letoh32(gh.gh_part_num) * letoh32(gh.gh_part_size);
235 	gpsectors = gpbytes / dl.d_secsize;
236 	memset(&gp, 0, sizeof(gp));
237 
238 	secbuf = DISK_readsectors(letoh64(gh.gh_part_lba), gpsectors);
239 	if (secbuf == NULL)
240 		return -1;
241 
242 	memcpy(&gp, secbuf, gpbytes);
243 	free(secbuf);
244 
245 	checksum = crc32((unsigned char *)&gp, gpbytes);
246 	if (checksum != letoh32(gh.gh_part_csum)) {
247 		DPRINTF("gpt partition table checksum: expected %x, got %x\n",
248 		    checksum, letoh32(gh.gh_part_csum));
249 		return -1;
250 	}
251 
252 	return 0;
253 }
254 
255 int
256 GPT_read(const int which)
257 {
258 	int			error;
259 
260 	error = MBR_read(0, 0, &gmbr);
261 	if (error == 0)
262 		error = protective_mbr(&gmbr);
263 	if (error)
264 		goto done;
265 
266 	switch (which) {
267 	case PRIMARYGPT:
268 		error = get_header(GPTSECTOR);
269 		break;
270 	case SECONDARYGPT:
271 		error = get_header(DL_GETDSIZE(&dl) - 1);
272 		break;
273 	case ANYGPT:
274 		error = get_header(GPTSECTOR);
275 		if (error != 0 || get_partition_table() != 0)
276 			error = get_header(DL_GETDSIZE(&dl) - 1);
277 		break;
278 	default:
279 		return -1;
280 	}
281 
282 	if (error == 0)
283 		error = get_partition_table();
284 
285  done:
286 	if (error != 0) {
287 		/* No valid GPT found. Zap any artifacts. */
288 		memset(&gmbr, 0, sizeof(gmbr));
289 		memset(&gh, 0, sizeof(gh));
290 		memset(&gp, 0, sizeof(gp));
291 	}
292 
293 	return error;
294 }
295 
296 void
297 GPT_print(const char *units, const int verbosity)
298 {
299 	const struct unit_type	*ut;
300 	struct uuid		 guid;
301 	const int		 secsize = dl.d_secsize;
302 	char			*guidstr = NULL;
303 	double			 size;
304 	int			 i, status;
305 
306 #ifdef	DEBUG
307 	char			*p;
308 	uint64_t		 gh_sig;
309 
310 	gh_sig = letoh64(gh.gh_sig);
311 	p = (unsigned char *)&gh_sig;
312 
313 	printf("gh_sig         : ");
314 	for (i = 0; i < sizeof(gh_sig); i++)
315 		printf("%c", isprint((unsigned char)p[i]) ? p[i] : '?');
316 	printf(" (");
317 	for (i = 0; i < sizeof(gh_sig); i++) {
318 		printf("%02x", p[i]);
319 		if ((i + 1) < sizeof(uint64_t))
320 			printf(":");
321 	}
322 	printf(")\n");
323 	printf("gh_rev         : %u\n", letoh32(gh.gh_rev));
324 	printf("gh_size        : %u (%zd)\n", letoh32(gh.gh_size), sizeof(gh));
325 	printf("gh_csum        : %u\n", letoh32(gh.gh_csum));
326 	printf("gh_rsvd        : %u\n", letoh32(gh.gh_rsvd));
327 	printf("gh_lba_self    : %llu\n", letoh64(gh.gh_lba_self));
328 	printf("gh_lba_alt     : %llu\n", letoh64(gh.gh_lba_alt));
329 	printf("gh_lba_start   : %llu\n", letoh64(gh.gh_lba_start));
330 	printf("gh_lba_end     : %llu\n", letoh64(gh.gh_lba_end));
331 	p = NULL;
332 	uuid_to_string(&gh.gh_guid, &p, &status);
333 	printf("gh_gh_guid     : %s\n", (status == uuid_s_ok) ? p : "<invalid>");
334 	free(p);
335 	printf("gh_gh_part_lba : %llu\n", letoh64(gh.gh_part_lba));
336 	printf("gh_gh_part_num : %u (%zu)\n", letoh32(gh.gh_part_num), nitems(gp));
337 	printf("gh_gh_part_size: %u (%zu)\n", letoh32(gh.gh_part_size), sizeof(gp[0]));
338 	printf("gh_gh_part_csum: %u\n", letoh32(gh.gh_part_csum));
339 	printf("\n");
340 #endif	/* DEBUG */
341 
342 	size = units_size(units, DL_GETDSIZE(&dl), &ut);
343 	printf("Disk: %s       Usable LBA: %llu to %llu [%.0f ",
344 	    disk.dk_name, letoh64(gh.gh_lba_start), letoh64(gh.gh_lba_end),
345 	    size);
346 	if (ut->ut_conversion == 0 && secsize != DEV_BSIZE)
347 		printf("%d-byte ", secsize);
348 	printf("%s]\n", ut->ut_lname);
349 
350 	if (verbosity == VERBOSE) {
351 		printf("GUID: ");
352 		uuid_dec_le(&gh.gh_guid, &guid);
353 		uuid_to_string(&guid, &guidstr, &status);
354 		if (status == uuid_s_ok)
355 			printf("%s\n", guidstr);
356 		else
357 			printf("<invalid header GUID>\n");
358 		free(guidstr);
359 	}
360 
361 	GPT_print_parthdr(verbosity);
362 	for (i = 0; i < letoh32(gh.gh_part_num); i++) {
363 		if (uuid_is_nil(&gp[i].gp_type, NULL))
364 			continue;
365 		GPT_print_part(i, units, verbosity);
366 	}
367 }
368 
369 void
370 GPT_print_parthdr(const int verbosity)
371 {
372 	printf("   #: type                                "
373 	    " [       start:         size ]\n");
374 	if (verbosity == VERBOSE)
375 		printf("      guid                                 name\n");
376 	printf("--------------------------------------------------------"
377 	    "----------------\n");
378 }
379 
380 void
381 GPT_print_part(const int n, const char *units, const int verbosity)
382 {
383 	const struct unit_type	*ut;
384 	struct uuid		 guid;
385 	struct gpt_partition	*partn = &gp[n];
386 	char			*guidstr = NULL;
387 	double			 size;
388 	uint64_t		 sectors;
389 	int			 status;
390 
391 	uuid_dec_le(&partn->gp_type, &guid);
392 	sectors = letoh64(partn->gp_lba_end) - letoh64(partn->gp_lba_start) + 1;
393 	size = units_size(units, sectors, &ut);
394 	printf("%c%3d: %-36s [%12lld: %12.0f%s]\n",
395 	    (letoh64(partn->gp_attrs) & GPTDOSACTIVE)?'*':' ', n,
396 	    PRT_uuid_to_typename(&guid), letoh64(partn->gp_lba_start),
397 	    size, ut->ut_abbr);
398 
399 	if (verbosity == VERBOSE) {
400 		uuid_dec_le(&partn->gp_guid, &guid);
401 		uuid_to_string(&guid, &guidstr, &status);
402 		if (status != uuid_s_ok)
403 			printf("      <invalid partition guid>             ");
404 		else
405 			printf("      %-36s ", guidstr);
406 		printf("%-36s\n", utf16le_to_string(partn->gp_name));
407 		free(guidstr);
408 	}
409 }
410 
411 int
412 add_partition(const uint8_t *beuuid, const char *name, uint64_t sectors)
413 {
414 	struct uuid		uuid, gp_type;
415 	int			rslt;
416 	uint64_t		end, freesectors, start;
417 	uint32_t		status, pn, pncnt;
418 
419 	uuid_dec_be(beuuid, &uuid);
420 	uuid_enc_le(&gp_type, &uuid);
421 
422 	pncnt = letoh32(gh.gh_part_num);
423 	for (pn = 0; pn < pncnt; pn++) {
424 		if (uuid_is_nil(&gp[pn].gp_type, NULL))
425 			break;
426 	}
427 	if (pn == pncnt)
428 		goto done;
429 
430 	rslt = lba_free(&start, &end);
431 	if (rslt == -1)
432 		goto done;
433 
434 	if (start % BLOCKALIGNMENT)
435 		start += (BLOCKALIGNMENT - start % BLOCKALIGNMENT);
436 	if (start >= end)
437 		goto done;
438 
439 	freesectors = end - start + 1;
440 
441 	if (sectors == 0)
442 		sectors = freesectors;
443 
444 	if (freesectors < sectors)
445 		goto done;
446 	else if (freesectors > sectors)
447 		end = start + sectors - 1;
448 
449 	gp[pn].gp_type = gp_type;
450 	gp[pn].gp_lba_start = htole64(start);
451 	gp[pn].gp_lba_end = htole64(end);
452 	memcpy(gp[pn].gp_name, string_to_utf16le(name),
453 	    sizeof(gp[pn].gp_name));
454 
455 	uuid_create(&uuid, &status);
456 	if (status != uuid_s_ok)
457 		goto done;
458 
459 	uuid_enc_le(&gp[pn].gp_guid, &uuid);
460 	gh.gh_part_csum = crc32((unsigned char *)&gp, sizeof(gp));
461 	gh.gh_csum = crc32((unsigned char *)&gh, sizeof(gh));
462 
463 	return 0;
464 
465  done:
466 	if (pn != pncnt)
467 		memset(&gp[pn], 0, sizeof(gp[pn]));
468 	printf("unable to add %s\n", name);
469 	return -1;
470 }
471 
472 int
473 init_gh(void)
474 {
475 	struct gpt_header	oldgh;
476 	struct uuid		guid;
477 	const int		secsize = dl.d_secsize;
478 	int			needed;
479 	uint32_t		status;
480 
481 	memcpy(&oldgh, &gh, sizeof(oldgh));
482 	memset(&gh, 0, sizeof(gh));
483 	memset(&gmbr, 0, sizeof(gmbr));
484 
485 	/* XXX Do we need the boot code? UEFI spec & Apple says no. */
486 	memcpy(gmbr.mbr_code, default_dmbr.dmbr_boot, sizeof(gmbr.mbr_code));
487 	gmbr.mbr_prt[0].prt_id = DOSPTYP_EFI;
488 	gmbr.mbr_prt[0].prt_bs = 1;
489 	gmbr.mbr_prt[0].prt_ns = UINT32_MAX;
490 	PRT_fix_CHS(&gmbr.mbr_prt[0]);
491 	gmbr.mbr_signature = DOSMBR_SIGNATURE;
492 
493 	needed = sizeof(gp) / secsize + 2;
494 
495 	if (needed % BLOCKALIGNMENT)
496 		needed += (needed - (needed % BLOCKALIGNMENT));
497 
498 	gh.gh_sig = htole64(GPTSIGNATURE);
499 	gh.gh_rev = htole32(GPTREVISION);
500 	gh.gh_size = htole32(GPTMINHDRSIZE);
501 	gh.gh_csum = 0;
502 	gh.gh_rsvd = 0;
503 	gh.gh_lba_self = htole64(1);
504 	gh.gh_lba_alt = htole64(DL_GETDSIZE(&dl) - 1);
505 	gh.gh_lba_start = htole64(needed);
506 	gh.gh_lba_end = htole64(DL_GETDSIZE(&dl) - needed);
507 	gh.gh_part_lba = htole64(2);
508 	gh.gh_part_num = htole32(NGPTPARTITIONS);
509 	gh.gh_part_size = htole32(GPTMINPARTSIZE);
510 
511 	uuid_create(&guid, &status);
512 	if (status != uuid_s_ok) {
513 		memcpy(&gh, &oldgh, sizeof(gh));
514 		return -1;
515 	}
516 
517 	uuid_enc_le(&gh.gh_guid, &guid);
518 	return 0;
519 }
520 
521 int
522 init_gp(const int how)
523 {
524 	struct gpt_partition	oldgp[NGPTPARTITIONS];
525 	const uint8_t		gpt_uuid_efi_system[] = GPT_UUID_EFI_SYSTEM;
526 	const uint8_t		gpt_uuid_openbsd[] = GPT_UUID_OPENBSD;
527 	int			pn, rslt;
528 
529 	memcpy(&oldgp, &gp, sizeof(oldgp));
530 	if (how == GHANDGP)
531 		memset(&gp, 0, sizeof(gp));
532 	else {
533 		for (pn = 0; pn < NGPTPARTITIONS; pn++) {
534 			if (PRT_protected_guid(&gp[pn].gp_type))
535 				continue;
536 			memset(&gp[pn], 0, sizeof(gp[pn]));
537 		}
538 	}
539 
540 	rslt = 0;
541 	if (disk.dk_bootprt.prt_ns > 0) {
542 		rslt = add_partition(gpt_uuid_efi_system, "EFI System Area",
543 		    disk.dk_bootprt.prt_ns);
544 	}
545 	if (rslt == 0)
546 		rslt = add_partition(gpt_uuid_openbsd, "OpenBSD Area", 0);
547 
548 	if (rslt != 0)
549 		memcpy(&gp, &oldgp, sizeof(gp));
550 
551 	return rslt;
552 }
553 
554 int
555 GPT_init(const int how)
556 {
557 	int			rslt = 0;
558 
559 	if (how == GHANDGP)
560 		rslt = init_gh();
561 	if (rslt == 0)
562 		rslt = init_gp(how);
563 
564 	return rslt;
565 }
566 
567 void
568 GPT_zap_headers(void)
569 {
570 	char			*secbuf;
571 	uint64_t		 sig;
572 
573 	secbuf = DISK_readsectors(GPTSECTOR, 1);
574 	if (secbuf == NULL)
575 		return;
576 
577 	memcpy(&sig, secbuf, sizeof(sig));
578 	if (letoh64(sig) == GPTSIGNATURE) {
579 		memset(secbuf, 0, dl.d_secsize);
580 		if (DISK_writesectors(secbuf, GPTSECTOR, 1))
581 			DPRINTF("Unable to zap GPT header @ sector %d",
582 			    GPTSECTOR);
583 	}
584 	free(secbuf);
585 
586 	secbuf = DISK_readsectors(DL_GETDSIZE(&dl) - 1, 1);
587 	if (secbuf == NULL)
588 		return;
589 
590 	memcpy(&sig, secbuf, sizeof(sig));
591 	if (letoh64(sig) == GPTSIGNATURE) {
592 		memset(secbuf, 0, dl.d_secsize);
593 		if (DISK_writesectors(secbuf, DL_GETDSIZE(&dl) - 1, 1))
594 			DPRINTF("Unable to zap GPT header @ sector %llu",
595 			    DL_GETDSIZE(&dl) - 1);
596 	}
597 	free(secbuf);
598 }
599 
600 int
601 GPT_write(void)
602 {
603 	char			*secbuf;
604 	uint64_t		 altgh, altgp, prigh, prigp;
605 	uint64_t		 gpbytes, gpsectors;
606 	int			 rslt;
607 
608 	if (MBR_write(&gmbr))
609 		return -1;
610 
611 	/*
612 	 * XXX Assume size of gp is multiple of sector size.
613 	 */
614 	gpbytes = letoh32(gh.gh_part_num) * letoh32(gh.gh_part_size);
615 	gpsectors = gpbytes / dl.d_secsize;
616 
617 	prigh = GPTSECTOR;
618 	prigp = prigh + 1;
619 	altgh = DL_GETDSIZE(&dl) - 1;
620 	altgp = altgh - gpsectors;
621 
622 	gh.gh_lba_self = htole64(prigh);
623 	gh.gh_lba_alt = htole64(altgh);
624 	gh.gh_part_lba = htole64(prigp);
625 	gh.gh_part_csum = crc32((unsigned char *)&gp, gpbytes);
626 	gh.gh_csum = 0;
627 	gh.gh_csum = crc32((unsigned char *)&gh, letoh32(gh.gh_size));
628 
629 	secbuf = DISK_readsectors(prigh, 1);
630 	if (secbuf == NULL)
631 		return -1;
632 
633 	memcpy(secbuf, &gh, sizeof(gh));
634 	rslt = DISK_writesectors(secbuf, prigh, 1);
635 	free(secbuf);
636 	if (rslt)
637 		return -1;
638 
639 	gh.gh_lba_self = htole64(altgh);
640 	gh.gh_lba_alt = htole64(prigh);
641 	gh.gh_part_lba = htole64(altgp);
642 	gh.gh_csum = 0;
643 	gh.gh_csum = crc32((unsigned char *)&gh, letoh32(gh.gh_size));
644 
645 	secbuf = DISK_readsectors(altgh, 1);
646 	if (secbuf == NULL)
647 		return -1;
648 
649 	memcpy(secbuf, &gh, sizeof(gh));
650 	rslt = DISK_writesectors(secbuf, altgh, 1);
651 	free(secbuf);
652 	if (rslt)
653 		return -1;
654 
655 	if (DISK_writesectors((const char *)&gp, prigp, gpsectors))
656 		return -1;
657 	if (DISK_writesectors((const char *)&gp, altgp, gpsectors))
658 		return -1;
659 
660 	/* Refresh in-kernel disklabel from the updated disk information. */
661 	if (ioctl(disk.dk_fd, DIOCRLDINFO, 0) == -1)
662 		warn("DIOCRLDINFO");
663 
664 	return 0;
665 }
666 
667 int
668 gp_lba_start_cmp(const void *e1, const void *e2)
669 {
670 	struct gpt_partition	*p1 = *(struct gpt_partition **)e1;
671 	struct gpt_partition	*p2 = *(struct gpt_partition **)e2;
672 	uint64_t		 o1;
673 	uint64_t		 o2;
674 
675 	o1 = letoh64(p1->gp_lba_start);
676 	o2 = letoh64(p2->gp_lba_start);
677 
678 	if (o1 < o2)
679 		return -1;
680 	else if (o1 > o2)
681 		return 1;
682 	else
683 		return 0;
684 }
685 
686 struct gpt_partition **
687 sort_gpt(void)
688 {
689 	static struct gpt_partition	*sgp[NGPTPARTITIONS+2];
690 	unsigned int			 i, j;
691 
692 	memset(sgp, 0, sizeof(sgp));
693 
694 	j = 0;
695 	for (i = 0; i < letoh32(gh.gh_part_num); i++) {
696 		if (letoh64(gp[i].gp_lba_start) >= letoh64(gh.gh_lba_start))
697 			sgp[j++] = &gp[i];
698 	}
699 
700 	if (j > 1) {
701 		if (mergesort(sgp, j, sizeof(sgp[0]), gp_lba_start_cmp) == -1) {
702 			printf("unable to sort gpt by lba start\n");
703 			return NULL;
704 		}
705 	}
706 
707 	return sgp;
708 }
709 
710 int
711 lba_free(uint64_t *start, uint64_t *end)
712 {
713 	struct gpt_partition	**sgp;
714 	uint64_t		  bs, bigbs, nextbs, ns;
715 	unsigned int		  i;
716 
717 	sgp = sort_gpt();
718 	if (sgp == NULL)
719 		return -1;
720 
721 	bs = letoh64(gh.gh_lba_start);
722 	ns = letoh64(gh.gh_lba_end) - bs + 1;
723 
724 	if (sgp[0] != NULL) {
725 		bigbs = bs;
726 		ns = 0;
727 		for (i = 0; sgp[i] != NULL; i++) {
728 			nextbs = letoh64(sgp[i]->gp_lba_start);
729 			if (bs < nextbs && ns < nextbs - bs) {
730 				ns = nextbs - bs;
731 				bigbs = bs;
732 			}
733 			bs = letoh64(sgp[i]->gp_lba_end) + 1;
734 		}
735 		nextbs = letoh64(gh.gh_lba_end) + 1;
736 		if (bs < nextbs && ns < nextbs - bs) {
737 			ns = nextbs - bs;
738 			bigbs = bs;
739 		}
740 		bs = bigbs;
741 	}
742 
743 	if (ns == 0)
744 		return -1;
745 
746 	if (start != NULL)
747 		*start = bs;
748 	if (end != NULL)
749 		*end = bs + ns - 1;
750 
751 	return 0;
752 }
753 
754 int
755 GPT_get_lba_start(const unsigned int pn)
756 {
757 	uint64_t		bs;
758 	unsigned int		i;
759 	int			rslt;
760 
761 	bs = letoh64(gh.gh_lba_start);
762 
763 	if (letoh64(gp[pn].gp_lba_start) >= bs) {
764 		bs = letoh64(gp[pn].gp_lba_start);
765 	} else {
766 		rslt = lba_free(&bs, NULL);
767 		if (rslt == -1) {
768 			printf("no space for partition %u\n", pn);
769 			return -1;
770 		}
771 	}
772 
773 	bs = getuint64("Partition offset", bs, letoh64(gh.gh_lba_start),
774 	    letoh64(gh.gh_lba_end));
775 
776 	for (i = 0; i < letoh32(gh.gh_part_num); i++) {
777 		if (i == pn)
778 			continue;
779 		if (bs >= letoh64(gp[i].gp_lba_start) &&
780 		    bs <= letoh64(gp[i].gp_lba_end)) {
781 			printf("partition %u can't start inside partition %u\n",
782 			    pn, i);
783 			return -1;
784 		}
785 	}
786 
787 	gp[pn].gp_lba_start = htole64(bs);
788 
789 	return 0;
790 }
791 
792 int
793 GPT_get_lba_end(const unsigned int pn)
794 {
795 	struct gpt_partition	**sgp;
796 	uint64_t		  bs, nextbs, ns;
797 	unsigned int		  i;
798 
799 	sgp = sort_gpt();
800 	if (sgp == NULL)
801 		return -1;
802 
803 	bs = letoh64(gp[pn].gp_lba_start);
804 	ns = letoh64(gh.gh_lba_end) - bs + 1;
805 	for (i = 0; sgp[i] != NULL; i++) {
806 		nextbs = letoh64(sgp[i]->gp_lba_start);
807 		if (nextbs > bs) {
808 			ns = nextbs - bs;
809 			break;
810 		}
811 	}
812 	ns = getuint64("Partition size", ns, 1, ns);
813 
814 	gp[pn].gp_lba_end = htole64(bs + ns - 1);
815 
816 	return 0;
817 }
818 
819 /*
820  * Adapted from Hacker's Delight crc32b().
821  *
822  * To quote http://www.hackersdelight.org/permissions.htm :
823  *
824  * "You are free to use, copy, and distribute any of the code on
825  *  this web site, whether modified by you or not. You need not give
826  *  attribution. This includes the algorithms (some of which appear
827  *  in Hacker's Delight), the Hacker's Assistant, and any code submitted
828  *  by readers. Submitters implicitly agree to this."
829  */
830 uint32_t
831 crc32(const u_char *buf, const uint32_t size)
832 {
833 	int			j;
834 	uint32_t		i, byte, crc, mask;
835 
836 	crc = 0xFFFFFFFF;
837 
838 	for (i = 0; i < size; i++) {
839 		byte = buf[i];			/* Get next byte. */
840 		crc = crc ^ byte;
841 		for (j = 7; j >= 0; j--) {	/* Do eight times. */
842 			mask = -(crc & 1);
843 			crc = (crc >> 1) ^ (0xEDB88320 & mask);
844 		}
845 	}
846 
847 	return ~crc;
848 }
849