xref: /openbsd-src/sbin/fdisk/gpt.c (revision 25c4e8bd056e974b28f4a0ffd39d76c190a56013)
1 /*	$OpenBSD: gpt.c,v 1.79 2022/06/28 15:35:24 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 struct unit_type	*ut;
438 	char			*guidstr = NULL;
439 	double			 size;
440 	uint64_t		 sectors;
441 	uint32_t		 status;
442 
443 	sectors = gp[pn].gp_lba_end - gp[pn].gp_lba_start + 1;
444 	size = units_size(units, sectors, &ut);
445 	printf("%c%3u: %-36s [%12lld: %12.0f%s]\n",
446 	    gp[pn].gp_attrs & GPTDOSACTIVE ? '*' : ' ', pn,
447 	    PRT_uuid_to_sname(&gp[pn].gp_type), gp[pn].gp_lba_start,
448 	    size, ut->ut_abbr);
449 
450 	if (verbosity == VERBOSE) {
451 		uuid_to_string(&gp[pn].gp_guid, &guidstr, &status);
452 		if (status != uuid_s_ok)
453 			printf("      <invalid partition guid>             ");
454 		else
455 			printf("      %-36s ", guidstr);
456 		printf("%-36s\n", name_to_string(pn));
457 		free(guidstr);
458 	}
459 }
460 
461 int
462 find_partition(const uint8_t *beuuid)
463 {
464 	struct uuid		uuid;
465 	unsigned int		pn;
466 
467 	uuid_dec_be(beuuid, &uuid);
468 
469 	for (pn = 0; pn < gh.gh_part_num; pn++) {
470 		if (uuid_compare(&gp[pn].gp_type, &uuid, NULL) == 0)
471 			return pn;
472 	}
473 	return -1;
474 }
475 
476 int
477 add_partition(const uint8_t *beuuid, const char *name, uint64_t sectors)
478 {
479 	struct uuid		uuid;
480 	int			rslt;
481 	uint64_t		end, freesectors, start;
482 	uint32_t		status, pn;
483 
484 	uuid_dec_be(beuuid, &uuid);
485 
486 	for (pn = 0; pn < gh.gh_part_num; pn++) {
487 		if (uuid_is_nil(&gp[pn].gp_type, NULL))
488 			break;
489 	}
490 	if (pn == gh.gh_part_num)
491 		goto done;
492 
493 	rslt = lba_free(&start, &end);
494 	if (rslt == -1)
495 		goto done;
496 
497 	if (start % BLOCKALIGNMENT)
498 		start += (BLOCKALIGNMENT - start % BLOCKALIGNMENT);
499 	if (start >= end)
500 		goto done;
501 
502 	freesectors = end - start + 1;
503 
504 	if (sectors == 0)
505 		sectors = freesectors;
506 
507 	if (freesectors < sectors)
508 		goto done;
509 	else if (freesectors > sectors)
510 		end = start + sectors - 1;
511 
512 	gp[pn].gp_type = uuid;
513 	gp[pn].gp_lba_start = start;
514 	gp[pn].gp_lba_end = end;
515 	string_to_name(pn, name);
516 
517 	uuid_create(&gp[pn].gp_guid, &status);
518 	if (status == uuid_s_ok)
519 		return 0;
520 
521  done:
522 	if (pn != gh.gh_part_num)
523 		memset(&gp[pn], 0, sizeof(gp[pn]));
524 	printf("unable to add %s\n", name);
525 	return -1;
526 }
527 
528 int
529 init_gh(void)
530 {
531 	struct gpt_header	oldgh;
532 	const int		secsize = dl.d_secsize;
533 	int			needed;
534 	uint32_t		status;
535 
536 	memcpy(&oldgh, &gh, sizeof(oldgh));
537 	memset(&gh, 0, sizeof(gh));
538 	memset(&gmbr, 0, sizeof(gmbr));
539 
540 	/* XXX Do we need the boot code? UEFI spec & Apple says no. */
541 	memcpy(gmbr.mbr_code, default_dmbr.dmbr_boot, sizeof(gmbr.mbr_code));
542 	gmbr.mbr_prt[0].prt_id = DOSPTYP_EFI;
543 	gmbr.mbr_prt[0].prt_bs = 1;
544 	gmbr.mbr_prt[0].prt_ns = UINT32_MAX;
545 	gmbr.mbr_signature = DOSMBR_SIGNATURE;
546 
547 	needed = sizeof(gp) / secsize + 2;
548 
549 	if (needed % BLOCKALIGNMENT)
550 		needed += (needed - (needed % BLOCKALIGNMENT));
551 
552 	gh.gh_sig = GPTSIGNATURE;
553 	gh.gh_rev = GPTREVISION;
554 	gh.gh_size = GPTMINHDRSIZE;
555 	gh.gh_csum = 0;
556 	gh.gh_rsvd = 0;
557 	gh.gh_lba_self = 1;
558 	gh.gh_lba_alt = DL_GETDSIZE(&dl) - 1;
559 	gh.gh_lba_start = needed;
560 	gh.gh_lba_end = DL_GETDSIZE(&dl) - needed;
561 	uuid_create(&gh.gh_guid, &status);
562 	if (status != uuid_s_ok) {
563 		memcpy(&gh, &oldgh, sizeof(gh));
564 		return -1;
565 	}
566 	gh.gh_part_lba = 2;
567 	gh.gh_part_num = NGPTPARTITIONS;
568 	gh.gh_part_size = GPTMINPARTSIZE;
569 	gh.gh_part_csum = 0;
570 
571 	return 0;
572 }
573 
574 int
575 init_gp(const int how)
576 {
577 	struct gpt_partition	oldgp[NGPTPARTITIONS];
578 	const uint8_t		gpt_uuid_efi_system[] = GPT_UUID_EFI_SYSTEM;
579 	const uint8_t		gpt_uuid_openbsd[] = GPT_UUID_OPENBSD;
580 	uint64_t		prt_ns;
581 	int			pn, rslt;
582 
583 	memcpy(&oldgp, &gp, sizeof(oldgp));
584 	if (how == GHANDGP)
585 		memset(&gp, 0, sizeof(gp));
586 	else {
587 		for (pn = 0; pn < gh.gh_part_num; pn++) {
588 			if (PRT_protected_guid(&gp[pn].gp_type))
589 				continue;
590 			memset(&gp[pn], 0, sizeof(gp[pn]));
591 		}
592 	}
593 
594 	rslt = 0;
595 	if (disk.dk_bootprt.prt_ns > 0) {
596 		pn = find_partition(gpt_uuid_efi_system);
597 		if (pn == -1) {
598 			rslt = add_partition(gpt_uuid_efi_system,
599 			    "EFI System Area", disk.dk_bootprt.prt_ns);
600 		} else {
601 			prt_ns = gp[pn].gp_lba_end - gp[pn].gp_lba_start + 1;
602 			if (prt_ns < disk.dk_bootprt.prt_ns) {
603 				printf("EFI System Area < %llu sectors\n",
604 				    disk.dk_bootprt.prt_ns);
605 				rslt = -1;
606 			}
607 		}
608 	}
609 	if (rslt == 0)
610 		rslt = add_partition(gpt_uuid_openbsd, "OpenBSD Area", 0);
611 
612 	if (rslt != 0)
613 		memcpy(&gp, &oldgp, sizeof(gp));
614 
615 	return rslt;
616 }
617 
618 int
619 GPT_init(const int how)
620 {
621 	int			rslt = 0;
622 
623 	if (how == GHANDGP)
624 		rslt = init_gh();
625 	if (rslt == 0)
626 		rslt = init_gp(how);
627 
628 	return rslt;
629 }
630 
631 void
632 GPT_zap_headers(void)
633 {
634 	struct gpt_header	legh;
635 
636 	if (DISK_readbytes(&legh, GPTSECTOR, sizeof(legh)))
637 		return;
638 
639 	if (letoh64(legh.gh_sig) == GPTSIGNATURE) {
640 		memset(&legh, 0, sizeof(legh));
641 		if (DISK_writebytes(&legh, GPTSECTOR, sizeof(legh)))
642 			DPRINTF("Unable to zap GPT header @ sector %d",
643 			    GPTSECTOR);
644 	}
645 
646 	if (DISK_readbytes(&legh, DL_GETDSIZE(&dl) - 1, sizeof(legh)))
647 		return;
648 
649 	if (letoh64(legh.gh_sig) == GPTSIGNATURE) {
650 		memset(&legh, 0, GPTMINHDRSIZE);
651 		if (DISK_writebytes(&legh, DL_GETDSIZE(&dl) - 1, sizeof(legh)))
652 			DPRINTF("Unable to zap GPT header @ sector %llu",
653 			    DL_GETDSIZE(&dl) - 1);
654 	}
655 }
656 
657 int
658 GPT_write(void)
659 {
660 	struct gpt_header	 legh;
661 	struct gpt_partition	*legp;
662 	uint64_t		 altgh, altgp;
663 	uint64_t		 gpbytes, gpsectors;
664 	unsigned int		 pn;
665 	int			 rslt = -1;
666 
667 	if (MBR_write(&gmbr))
668 		return -1;
669 
670 	gpbytes = gh.gh_part_num * gh.gh_part_size;
671 	gpsectors = (gpbytes + dl.d_secsize - 1) / dl.d_secsize;
672 
673 	altgh = DL_GETDSIZE(&dl) - 1;
674 	altgp = altgh - gpsectors;
675 
676 	legh.gh_sig = htole64(GPTSIGNATURE);
677 	legh.gh_rev = htole32(GPTREVISION);
678 	legh.gh_size = htole32(GPTMINHDRSIZE);
679 	legh.gh_rsvd = 0;
680 	legh.gh_lba_self = htole64(GPTSECTOR);
681 	legh.gh_lba_alt = htole64(altgh);
682 	legh.gh_lba_start = htole64(gh.gh_lba_start);
683 	legh.gh_lba_end = htole64(gh.gh_lba_end);
684 	uuid_enc_le(&legh.gh_guid, &gh.gh_guid);
685 	legh.gh_part_lba = htole64(GPTSECTOR + 1);
686 	legh.gh_part_num = htole32(gh.gh_part_num);
687 	legh.gh_part_size = htole32(GPTMINPARTSIZE);
688 
689 	legp = calloc(1, gpbytes);
690 	if (legp == NULL)
691 		err(1, "legp");
692 
693 	for (pn = 0; pn < gh.gh_part_num; pn++) {
694 		uuid_enc_le(&legp[pn].gp_type, &gp[pn].gp_type);
695 		uuid_enc_le(&legp[pn].gp_guid, &gp[pn].gp_guid);
696 		legp[pn].gp_lba_start = htole64(gp[pn].gp_lba_start);
697 		legp[pn].gp_lba_end = htole64(gp[pn].gp_lba_end);
698 		legp[pn].gp_attrs = htole64(gp[pn].gp_attrs);
699 		memcpy(legp[pn].gp_name, gp[pn].gp_name,
700 		    sizeof(legp[pn].gp_name));
701 	}
702 	legh.gh_part_csum = htole32(crc32((unsigned char *)legp, gpbytes));
703 	legh.gh_csum = 0;
704 	legh.gh_csum = htole32(crc32((unsigned char *)&legh, gh.gh_size));
705 
706 	if (DISK_writebytes(&legh, GPTSECTOR, gh.gh_size) ||
707 	    DISK_writebytes(legp, GPTSECTOR + 1, gpbytes))
708 		goto done;
709 
710 	legh.gh_lba_self = htole64(altgh);
711 	legh.gh_lba_alt = htole64(GPTSECTOR);
712 	legh.gh_part_lba = htole64(altgp);
713 	legh.gh_csum = 0;
714 	legh.gh_csum = htole32(crc32((unsigned char *)&legh, gh.gh_size));
715 
716 	if (DISK_writebytes(&legh, altgh, gh.gh_size) ||
717 	    DISK_writebytes(&gp, altgp, gpbytes))
718 		goto done;
719 
720 	/* Refresh in-kernel disklabel from the updated disk information. */
721 	if (ioctl(disk.dk_fd, DIOCRLDINFO, 0) == -1)
722 		warn("DIOCRLDINFO");
723 	rslt = 0;
724 
725  done:
726 	free(legp);
727 	return rslt;
728 }
729 
730 int
731 gp_lba_start_cmp(const void *e1, const void *e2)
732 {
733 	struct gpt_partition	*p1 = *(struct gpt_partition **)e1;
734 	struct gpt_partition	*p2 = *(struct gpt_partition **)e2;
735 	uint64_t		 o1;
736 	uint64_t		 o2;
737 
738 	o1 = p1->gp_lba_start;
739 	o2 = p2->gp_lba_start;
740 
741 	if (o1 < o2)
742 		return -1;
743 	else if (o1 > o2)
744 		return 1;
745 	else
746 		return 0;
747 }
748 
749 struct gpt_partition **
750 sort_gpt(void)
751 {
752 	static struct gpt_partition	*sgp[NGPTPARTITIONS+2];
753 	unsigned int			 i, pn;
754 
755 	memset(sgp, 0, sizeof(sgp));
756 
757 	i = 0;
758 	for (pn = 0; pn < gh.gh_part_num; pn++) {
759 		if (gp[pn].gp_lba_start >= gh.gh_lba_start)
760 			sgp[i++] = &gp[pn];
761 	}
762 
763 	if (i > 1) {
764 		if (mergesort(sgp, i, sizeof(sgp[0]), gp_lba_start_cmp) == -1) {
765 			printf("unable to sort gpt by lba start\n");
766 			return NULL;
767 		}
768 	}
769 
770 	return sgp;
771 }
772 
773 int
774 lba_free(uint64_t *start, uint64_t *end)
775 {
776 	struct gpt_partition	**sgp;
777 	uint64_t		  bs, bigbs, nextbs, ns;
778 	unsigned int		  i;
779 
780 	sgp = sort_gpt();
781 	if (sgp == NULL)
782 		return -1;
783 
784 	bs = gh.gh_lba_start;
785 	ns = gh.gh_lba_end - bs + 1;
786 
787 	if (sgp[0] != NULL) {
788 		bigbs = bs;
789 		ns = 0;
790 		for (i = 0; sgp[i] != NULL; i++) {
791 			nextbs = sgp[i]->gp_lba_start;
792 			if (bs < nextbs && ns < nextbs - bs) {
793 				ns = nextbs - bs;
794 				bigbs = bs;
795 			}
796 			bs = sgp[i]->gp_lba_end + 1;
797 		}
798 		nextbs = gh.gh_lba_end + 1;
799 		if (bs < nextbs && ns < nextbs - bs) {
800 			ns = nextbs - bs;
801 			bigbs = bs;
802 		}
803 		bs = bigbs;
804 	}
805 
806 	if (ns == 0)
807 		return -1;
808 
809 	if (start != NULL)
810 		*start = bs;
811 	if (end != NULL)
812 		*end = bs + ns - 1;
813 
814 	return 0;
815 }
816 
817 int
818 GPT_get_lba_start(const unsigned int pn)
819 {
820 	uint64_t		bs;
821 	unsigned int		i;
822 	int			rslt;
823 
824 	bs = gh.gh_lba_start;
825 
826 	if (gp[pn].gp_lba_start >= bs) {
827 		bs = gp[pn].gp_lba_start;
828 	} else {
829 		rslt = lba_free(&bs, NULL);
830 		if (rslt == -1) {
831 			printf("no space for partition %u\n", pn);
832 			return -1;
833 		}
834 	}
835 
836 	bs = getuint64("Partition offset", bs, gh.gh_lba_start, gh.gh_lba_end);
837 	for (i = 0; i < gh.gh_part_num; i++) {
838 		if (i == pn)
839 			continue;
840 		if (bs >= gp[i].gp_lba_start && bs <= gp[i].gp_lba_end) {
841 			printf("partition %u can't start inside partition %u\n",
842 			    pn, i);
843 			return -1;
844 		}
845 	}
846 
847 	gp[pn].gp_lba_start = bs;
848 
849 	return 0;
850 }
851 
852 int
853 GPT_get_lba_end(const unsigned int pn)
854 {
855 	struct gpt_partition	**sgp;
856 	uint64_t		  bs, nextbs, ns;
857 	unsigned int		  i;
858 
859 	sgp = sort_gpt();
860 	if (sgp == NULL)
861 		return -1;
862 
863 	bs = gp[pn].gp_lba_start;
864 	ns = gh.gh_lba_end - bs + 1;
865 	for (i = 0; sgp[i] != NULL; i++) {
866 		nextbs = sgp[i]->gp_lba_start;
867 		if (nextbs > bs) {
868 			ns = nextbs - bs;
869 			break;
870 		}
871 	}
872 	ns = getuint64("Partition size", ns, 1, ns);
873 
874 	gp[pn].gp_lba_end = bs + ns - 1;
875 
876 	return 0;
877 }
878 
879 int
880 GPT_get_name(const unsigned int pn)
881 {
882 	char			 name[GPTPARTNAMESIZE + 1];
883 
884 	printf("Partition name: [%s] ", name_to_string(pn));
885 	string_from_line(name, sizeof(name), UNTRIMMED);
886 
887 	switch (strlen(name)) {
888 	case 0:
889 		break;
890 	case GPTPARTNAMESIZE:
891 		printf("partition name must be < %d characters\n",
892 		    GPTPARTNAMESIZE);
893 		return -1;
894 	default:
895 		string_to_name(pn, name);
896 		break;
897 	}
898 
899 	return 0;
900 }
901 
902 /*
903  * Adapted from Hacker's Delight crc32b().
904  *
905  * To quote http://www.hackersdelight.org/permissions.htm :
906  *
907  * "You are free to use, copy, and distribute any of the code on
908  *  this web site, whether modified by you or not. You need not give
909  *  attribution. This includes the algorithms (some of which appear
910  *  in Hacker's Delight), the Hacker's Assistant, and any code submitted
911  *  by readers. Submitters implicitly agree to this."
912  */
913 uint32_t
914 crc32(const u_char *buf, const uint32_t size)
915 {
916 	int			j;
917 	uint32_t		i, byte, crc, mask;
918 
919 	crc = 0xFFFFFFFF;
920 
921 	for (i = 0; i < size; i++) {
922 		byte = buf[i];			/* Get next byte. */
923 		crc = crc ^ byte;
924 		for (j = 7; j >= 0; j--) {	/* Do eight times. */
925 			mask = -(crc & 1);
926 			crc = (crc >> 1) ^ (0xEDB88320 & mask);
927 		}
928 	}
929 
930 	return ~crc;
931 }
932