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