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