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