xref: /openbsd/sbin/fdisk/gpt.c (revision ce7279d8)
1 /*	$OpenBSD: gpt.c,v 1.94 2024/05/21 05:00:47 jsg 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_free(uint64_t *, uint64_t *);
50 int			  add_partition(const uint8_t *, const char *, uint64_t);
51 int			  find_partition(const uint8_t *);
52 int			  get_header(const uint64_t);
53 int			  get_partition_table(void);
54 int			  init_gh(void);
55 int			  init_gp(const int);
56 uint32_t		  crc32(const u_char *, const uint32_t);
57 int			  protective_mbr(const struct mbr *);
58 int			  gpt_chk_mbr(struct dos_partition *, uint64_t);
59 void			  string_to_name(const unsigned int, const char *);
60 const char		 *name_to_string(const unsigned int);
61 
62 void
string_to_name(const unsigned int pn,const char * ch)63 string_to_name(const unsigned int pn, const char *ch)
64 {
65 	unsigned int			i;
66 
67 	memset(gp[pn].gp_name, 0, sizeof(gp[pn].gp_name));
68 
69 	for (i = 0; i < sizeof(gp[pn].gp_name) && ch[i] != '\0'; i++)
70 		gp[pn].gp_name[i] = htole16((unsigned int)ch[i]);
71 }
72 
73 const char *
name_to_string(const unsigned int pn)74 name_to_string(const unsigned int pn)
75 {
76 	static char		name[GPTPARTNAMESIZE + 1];
77 	unsigned int		i;
78 
79 	for (i = 0; i < GPTPARTNAMESIZE && gp[pn].gp_name[i] != 0; i++)
80 		name[i] = letoh16(gp[pn].gp_name[i]) & 0x7F;
81 	name[i] = '\0';
82 
83 	return name;
84 }
85 
86 /*
87  * Return the index into dp[] of the EFI GPT (0xEE) partition, or -1 if no such
88  * partition exists.
89  *
90  * Taken from kern/subr_disk.c.
91  *
92  */
93 int
gpt_chk_mbr(struct dos_partition * dp,u_int64_t dsize)94 gpt_chk_mbr(struct dos_partition *dp, u_int64_t dsize)
95 {
96 	struct dos_partition	*dp2;
97 	int			 efi, eficnt, found, i;
98 	uint32_t		 psize;
99 
100 	found = efi = eficnt = 0;
101 	for (dp2 = dp, i = 0; i < NDOSPART; i++, dp2++) {
102 		if (dp2->dp_typ == DOSPTYP_UNUSED)
103 			continue;
104 		found++;
105 		if (dp2->dp_typ != DOSPTYP_EFI)
106 			continue;
107 		if (letoh32(dp2->dp_start) != GPTSECTOR)
108 			continue;
109 		psize = letoh32(dp2->dp_size);
110 		if (psize <= (dsize - GPTSECTOR) || psize == UINT32_MAX) {
111 			efi = i;
112 			eficnt++;
113 		}
114 	}
115 	if (found == 1 && eficnt == 1)
116 		return efi;
117 
118 	return -1;
119 }
120 
121 int
protective_mbr(const struct mbr * mbr)122 protective_mbr(const struct mbr *mbr)
123 {
124 	struct dos_partition	dp[NDOSPART], dos_partition;
125 	unsigned int		i;
126 
127 	if (mbr->mbr_lba_self != 0)
128 		return -1;
129 
130 	for (i = 0; i < nitems(dp); i++) {
131 		memset(&dos_partition, 0, sizeof(dos_partition));
132 		if (i < nitems(mbr->mbr_prt))
133 			PRT_prt_to_dp(&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
get_header(const uint64_t sector)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
get_partition_table(void)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
GPT_read(const int which)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
GPT_print(const char * units,const int verbosity)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
GPT_print_parthdr(const int verbosity)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
GPT_print_part(const unsigned int pn,const char * units,const int verbosity)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		 attrs, end, start;
441 	uint32_t		 status;
442 
443 	start = gp[pn].gp_lba_start;
444 	end = gp[pn].gp_lba_end;
445 	size = units_size(units, (start > end) ? 0 : end - start + 1, &ut);
446 
447 	printf(" %3u: %-36s [%12lld: %12.0f%s]\n", pn,
448 	    PRT_uuid_to_desc(&gp[pn].gp_type), start, 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("%s\n", name_to_string(pn));
457 		free(guidstr);
458 		attrs = gp[pn].gp_attrs;
459 		if (attrs) {
460 			printf("      Attributes: (0x%016llx) ", attrs);
461 			if (attrs & GPTPARTATTR_REQUIRED)
462 				printf("Required " );
463 			if (attrs & GPTPARTATTR_IGNORE)
464 				printf("Ignore ");
465 			if (attrs & GPTPARTATTR_BOOTABLE)
466 				printf("Bootable ");
467 			if (attrs & GPTPARTATTR_MS_READONLY)
468 				printf("MSReadOnly " );
469 			if (attrs & GPTPARTATTR_MS_SHADOW)
470 				printf("MSShadow ");
471 			if (attrs & GPTPARTATTR_MS_HIDDEN)
472 				printf("MSHidden ");
473 			if (attrs & GPTPARTATTR_MS_NOAUTOMOUNT)
474 				printf("MSNoAutoMount ");
475 			printf("\n");
476 		}
477 	}
478 
479 	if (uuid_is_nil(&gp[pn].gp_type, NULL) == 0) {
480 		if (start > end)
481 			printf("partition %u first LBA is > last LBA\n", pn);
482 		if (start < gh.gh_lba_start || end > gh.gh_lba_end)
483 			printf("partition %u extends beyond usable LBA range "
484 			    "of %s\n", pn, disk.dk_name);
485 	}
486 }
487 
488 int
find_partition(const uint8_t * beuuid)489 find_partition(const uint8_t *beuuid)
490 {
491 	struct uuid		uuid;
492 	unsigned int		pn;
493 
494 	uuid_dec_be(beuuid, &uuid);
495 
496 	for (pn = 0; pn < gh.gh_part_num; pn++) {
497 		if (uuid_compare(&gp[pn].gp_type, &uuid, NULL) == 0)
498 			return pn;
499 	}
500 	return -1;
501 }
502 
503 int
add_partition(const uint8_t * beuuid,const char * name,uint64_t sectors)504 add_partition(const uint8_t *beuuid, const char *name, uint64_t sectors)
505 {
506 	struct uuid		uuid;
507 	int			rslt;
508 	uint64_t		end, freesectors, start;
509 	uint32_t		status, pn;
510 
511 	uuid_dec_be(beuuid, &uuid);
512 
513 	for (pn = 0; pn < gh.gh_part_num; pn++) {
514 		if (uuid_is_nil(&gp[pn].gp_type, NULL))
515 			break;
516 	}
517 	if (pn == gh.gh_part_num)
518 		goto done;
519 
520 	rslt = lba_free(&start, &end);
521 	if (rslt == -1)
522 		goto done;
523 
524 	if (start % BLOCKALIGNMENT)
525 		start += (BLOCKALIGNMENT - start % BLOCKALIGNMENT);
526 	if (start >= end)
527 		goto done;
528 
529 	freesectors = end - start + 1;
530 
531 	if (sectors == 0)
532 		sectors = freesectors;
533 
534 	if (freesectors < sectors)
535 		goto done;
536 	else if (freesectors > sectors)
537 		end = start + sectors - 1;
538 
539 	gp[pn].gp_type = uuid;
540 	gp[pn].gp_lba_start = start;
541 	gp[pn].gp_lba_end = end;
542 	string_to_name(pn, name);
543 
544 	uuid_create(&gp[pn].gp_guid, &status);
545 	if (status == uuid_s_ok)
546 		return 0;
547 
548  done:
549 	if (pn != gh.gh_part_num)
550 		memset(&gp[pn], 0, sizeof(gp[pn]));
551 	printf("unable to add %s\n", name);
552 	return -1;
553 }
554 
555 int
init_gh(void)556 init_gh(void)
557 {
558 	struct gpt_header	oldgh;
559 	const int		secsize = dl.d_secsize;
560 	int			needed;
561 	uint32_t		status;
562 
563 	memcpy(&oldgh, &gh, sizeof(oldgh));
564 	memset(&gh, 0, sizeof(gh));
565 	memset(&gmbr, 0, sizeof(gmbr));
566 
567 	/* XXX Do we need the boot code? UEFI spec & Apple says no. */
568 	memcpy(gmbr.mbr_code, default_dmbr.dmbr_boot, sizeof(gmbr.mbr_code));
569 	gmbr.mbr_prt[0].prt_id = DOSPTYP_EFI;
570 	gmbr.mbr_prt[0].prt_bs = 1;
571 	gmbr.mbr_prt[0].prt_ns = UINT32_MAX;
572 	gmbr.mbr_signature = DOSMBR_SIGNATURE;
573 
574 	needed = sizeof(gp) / secsize + 2;
575 
576 	if (needed % BLOCKALIGNMENT)
577 		needed += (needed - (needed % BLOCKALIGNMENT));
578 
579 	gh.gh_sig = GPTSIGNATURE;
580 	gh.gh_rev = GPTREVISION;
581 	gh.gh_size = GPTMINHDRSIZE;
582 	gh.gh_csum = 0;
583 	gh.gh_rsvd = 0;
584 	gh.gh_lba_self = 1;
585 	gh.gh_lba_alt = DL_GETDSIZE(&dl) - 1;
586 	gh.gh_lba_start = needed;
587 	gh.gh_lba_end = DL_GETDSIZE(&dl) - needed;
588 	uuid_create(&gh.gh_guid, &status);
589 	if (status != uuid_s_ok) {
590 		memcpy(&gh, &oldgh, sizeof(gh));
591 		return -1;
592 	}
593 	gh.gh_part_lba = 2;
594 	gh.gh_part_num = NGPTPARTITIONS;
595 	gh.gh_part_size = GPTMINPARTSIZE;
596 	gh.gh_part_csum = 0;
597 
598 	return 0;
599 }
600 
601 int
init_gp(const int how)602 init_gp(const int how)
603 {
604 	struct gpt_partition	oldgp[NGPTPARTITIONS];
605 	const uint8_t		gpt_uuid_efi_system[] = GPT_UUID_EFI_SYSTEM;
606 	const uint8_t		gpt_uuid_openbsd[] = GPT_UUID_OPENBSD;
607 	uint64_t		prt_ns;
608 	int			pn, rslt;
609 
610 	memcpy(&oldgp, &gp, sizeof(oldgp));
611 	if (how == GHANDGP)
612 		memset(&gp, 0, sizeof(gp));
613 	else {
614 		for (pn = 0; pn < gh.gh_part_num; pn++) {
615 			if (PRT_protected_uuid(&gp[pn].gp_type) ||
616 			    (gp[pn].gp_attrs & GPTPARTATTR_REQUIRED))
617 				continue;
618 			memset(&gp[pn], 0, sizeof(gp[pn]));
619 		}
620 	}
621 
622 	rslt = 0;
623 	if (disk.dk_bootprt.prt_ns > 0) {
624 		pn = find_partition(gpt_uuid_efi_system);
625 		if (pn == -1) {
626 			rslt = add_partition(gpt_uuid_efi_system,
627 			    "EFI System Area", disk.dk_bootprt.prt_ns);
628 		} else {
629 			prt_ns = gp[pn].gp_lba_end - gp[pn].gp_lba_start + 1;
630 			if (prt_ns < disk.dk_bootprt.prt_ns) {
631 				printf("EFI System Area < %llu sectors\n",
632 				    disk.dk_bootprt.prt_ns);
633 				rslt = -1;
634 			}
635 		}
636 	}
637 	if (rslt == 0)
638 		rslt = add_partition(gpt_uuid_openbsd, "OpenBSD Area", 0);
639 
640 	if (rslt != 0)
641 		memcpy(&gp, &oldgp, sizeof(gp));
642 
643 	return rslt;
644 }
645 
646 int
GPT_init(const int how)647 GPT_init(const int how)
648 {
649 	int			rslt = 0;
650 
651 	if (how == GHANDGP)
652 		rslt = init_gh();
653 	if (rslt == 0)
654 		rslt = init_gp(how);
655 
656 	return rslt;
657 }
658 
659 void
GPT_zap_headers(void)660 GPT_zap_headers(void)
661 {
662 	struct gpt_header	legh;
663 
664 	if (DISK_readbytes(&legh, GPTSECTOR, sizeof(legh)))
665 		return;
666 
667 	if (letoh64(legh.gh_sig) == GPTSIGNATURE) {
668 		memset(&legh, 0, sizeof(legh));
669 		if (DISK_writebytes(&legh, GPTSECTOR, sizeof(legh)))
670 			DPRINTF("Unable to zap GPT header @ sector %d",
671 			    GPTSECTOR);
672 	}
673 
674 	if (DISK_readbytes(&legh, DL_GETDSIZE(&dl) - 1, sizeof(legh)))
675 		return;
676 
677 	if (letoh64(legh.gh_sig) == GPTSIGNATURE) {
678 		memset(&legh, 0, GPTMINHDRSIZE);
679 		if (DISK_writebytes(&legh, DL_GETDSIZE(&dl) - 1, sizeof(legh)))
680 			DPRINTF("Unable to zap GPT header @ sector %llu",
681 			    DL_GETDSIZE(&dl) - 1);
682 	}
683 }
684 
685 int
GPT_write(void)686 GPT_write(void)
687 {
688 	struct gpt_header	 legh;
689 	struct gpt_partition	*legp;
690 	uint64_t		 altgh, altgp;
691 	uint64_t		 gpbytes, gpsectors;
692 	unsigned int		 pn;
693 	int			 rslt = -1;
694 
695 	if (MBR_write(&gmbr))
696 		return -1;
697 
698 	gpbytes = gh.gh_part_num * gh.gh_part_size;
699 	gpsectors = (gpbytes + dl.d_secsize - 1) / dl.d_secsize;
700 
701 	altgh = DL_GETDSIZE(&dl) - 1;
702 	altgp = altgh - gpsectors;
703 
704 	legh.gh_sig = htole64(GPTSIGNATURE);
705 	legh.gh_rev = htole32(GPTREVISION);
706 	legh.gh_size = htole32(GPTMINHDRSIZE);
707 	legh.gh_rsvd = 0;
708 	legh.gh_lba_self = htole64(GPTSECTOR);
709 	legh.gh_lba_alt = htole64(altgh);
710 	legh.gh_lba_start = htole64(gh.gh_lba_start);
711 	legh.gh_lba_end = htole64(gh.gh_lba_end);
712 	uuid_enc_le(&legh.gh_guid, &gh.gh_guid);
713 	legh.gh_part_lba = htole64(GPTSECTOR + 1);
714 	legh.gh_part_num = htole32(gh.gh_part_num);
715 	legh.gh_part_size = htole32(GPTMINPARTSIZE);
716 
717 	legp = calloc(1, gpbytes);
718 	if (legp == NULL)
719 		err(1, "legp");
720 
721 	for (pn = 0; pn < gh.gh_part_num; pn++) {
722 		uuid_enc_le(&legp[pn].gp_type, &gp[pn].gp_type);
723 		uuid_enc_le(&legp[pn].gp_guid, &gp[pn].gp_guid);
724 		legp[pn].gp_lba_start = htole64(gp[pn].gp_lba_start);
725 		legp[pn].gp_lba_end = htole64(gp[pn].gp_lba_end);
726 		legp[pn].gp_attrs = htole64(gp[pn].gp_attrs);
727 		memcpy(legp[pn].gp_name, gp[pn].gp_name,
728 		    sizeof(legp[pn].gp_name));
729 	}
730 	legh.gh_part_csum = htole32(crc32((unsigned char *)legp, gpbytes));
731 	legh.gh_csum = 0;
732 	legh.gh_csum = htole32(crc32((unsigned char *)&legh, gh.gh_size));
733 
734 	if (DISK_writebytes(&legh, GPTSECTOR, gh.gh_size) ||
735 	    DISK_writebytes(legp, GPTSECTOR + 1, gpbytes))
736 		goto done;
737 
738 	legh.gh_lba_self = htole64(altgh);
739 	legh.gh_lba_alt = htole64(GPTSECTOR);
740 	legh.gh_part_lba = htole64(altgp);
741 	legh.gh_csum = 0;
742 	legh.gh_csum = htole32(crc32((unsigned char *)&legh, gh.gh_size));
743 
744 	if (DISK_writebytes(&legh, altgh, gh.gh_size) ||
745 	    DISK_writebytes(&gp, altgp, gpbytes))
746 		goto done;
747 
748 	/* Refresh in-kernel disklabel from the updated disk information. */
749 	if (ioctl(disk.dk_fd, DIOCRLDINFO, 0) == -1)
750 		warn("DIOCRLDINFO");
751 	rslt = 0;
752 
753  done:
754 	free(legp);
755 	return rslt;
756 }
757 
758 int
gp_lba_start_cmp(const void * e1,const void * e2)759 gp_lba_start_cmp(const void *e1, const void *e2)
760 {
761 	struct gpt_partition	*p1 = *(struct gpt_partition **)e1;
762 	struct gpt_partition	*p2 = *(struct gpt_partition **)e2;
763 	uint64_t		 o1;
764 	uint64_t		 o2;
765 
766 	o1 = p1->gp_lba_start;
767 	o2 = p2->gp_lba_start;
768 
769 	if (o1 < o2)
770 		return -1;
771 	else if (o1 > o2)
772 		return 1;
773 	else
774 		return 0;
775 }
776 
777 const struct gpt_partition * const *
sort_gpt(void)778 sort_gpt(void)
779 {
780 	static const struct gpt_partition	*sgp[NGPTPARTITIONS+2];
781 	unsigned int				 i, pn;
782 
783 	memset(sgp, 0, sizeof(sgp));
784 
785 	i = 0;
786 	for (pn = 0; pn < gh.gh_part_num; pn++) {
787 		if (gp[pn].gp_lba_start >= gh.gh_lba_start)
788 			sgp[i++] = &gp[pn];
789 	}
790 
791 	if (i > 1) {
792 		if (mergesort(sgp, i, sizeof(sgp[0]), gp_lba_start_cmp) == -1) {
793 			printf("unable to sort gpt by lba start\n");
794 			return NULL;
795 		}
796 	}
797 
798 	return sgp;
799 }
800 
801 int
lba_free(uint64_t * start,uint64_t * end)802 lba_free(uint64_t *start, uint64_t *end)
803 {
804 	const struct gpt_partition * const *sgp;
805 	uint64_t			  bs, bigbs, nextbs, ns;
806 	unsigned int			  i;
807 
808 	sgp = sort_gpt();
809 	if (sgp == NULL)
810 		return -1;
811 
812 	bs = gh.gh_lba_start;
813 	ns = gh.gh_lba_end - bs + 1;
814 
815 	if (sgp[0] != NULL) {
816 		bigbs = bs;
817 		ns = 0;
818 		for (i = 0; sgp[i] != NULL; i++) {
819 			nextbs = sgp[i]->gp_lba_start;
820 			if (bs < nextbs && ns < nextbs - bs) {
821 				ns = nextbs - bs;
822 				bigbs = bs;
823 			}
824 			bs = sgp[i]->gp_lba_end + 1;
825 		}
826 		nextbs = gh.gh_lba_end + 1;
827 		if (bs < nextbs && ns < nextbs - bs) {
828 			ns = nextbs - bs;
829 			bigbs = bs;
830 		}
831 		bs = bigbs;
832 	}
833 
834 	if (ns == 0)
835 		return -1;
836 
837 	if (start != NULL)
838 		*start = bs;
839 	if (end != NULL)
840 		*end = bs + ns - 1;
841 
842 	return 0;
843 }
844 
845 int
GPT_get_lba_start(const unsigned int pn)846 GPT_get_lba_start(const unsigned int pn)
847 {
848 	uint64_t		bs;
849 	unsigned int		i;
850 	int			rslt;
851 
852 	bs = gh.gh_lba_start;
853 
854 	if (gp[pn].gp_lba_start >= bs) {
855 		bs = gp[pn].gp_lba_start;
856 	} else {
857 		rslt = lba_free(&bs, NULL);
858 		if (rslt == -1) {
859 			printf("no space for partition %u\n", pn);
860 			return -1;
861 		}
862 	}
863 
864 	bs = getuint64("Partition offset", bs, gh.gh_lba_start, gh.gh_lba_end);
865 	for (i = 0; i < gh.gh_part_num; i++) {
866 		if (i == pn)
867 			continue;
868 		if (bs >= gp[i].gp_lba_start && bs <= gp[i].gp_lba_end) {
869 			printf("partition %u can't start inside partition %u\n",
870 			    pn, i);
871 			return -1;
872 		}
873 	}
874 
875 	gp[pn].gp_lba_start = bs;
876 
877 	return 0;
878 }
879 
880 int
GPT_get_lba_end(const unsigned int pn)881 GPT_get_lba_end(const unsigned int pn)
882 {
883 	const struct gpt_partition	* const *sgp;
884 	uint64_t			  bs, nextbs, ns;
885 	unsigned int			  i;
886 
887 	sgp = sort_gpt();
888 	if (sgp == NULL)
889 		return -1;
890 
891 	bs = gp[pn].gp_lba_start;
892 	ns = gh.gh_lba_end - bs + 1;
893 	for (i = 0; sgp[i] != NULL; i++) {
894 		nextbs = sgp[i]->gp_lba_start;
895 		if (nextbs > bs) {
896 			ns = nextbs - bs;
897 			break;
898 		}
899 	}
900 	ns = getuint64("Partition size", ns, 1, ns);
901 
902 	gp[pn].gp_lba_end = bs + ns - 1;
903 
904 	return 0;
905 }
906 
907 int
GPT_get_name(const unsigned int pn)908 GPT_get_name(const unsigned int pn)
909 {
910 	char			 name[GPTPARTNAMESIZE + 1];
911 
912 	printf("Partition name: [%s] ", name_to_string(pn));
913 	string_from_line(name, sizeof(name), UNTRIMMED);
914 
915 	switch (strlen(name)) {
916 	case 0:
917 		break;
918 	case GPTPARTNAMESIZE:
919 		printf("partition name must be < %d characters\n",
920 		    GPTPARTNAMESIZE);
921 		return -1;
922 	default:
923 		string_to_name(pn, name);
924 		break;
925 	}
926 
927 	return 0;
928 }
929 
930 /*
931  * Adapted from Hacker's Delight crc32b().
932  *
933  * To quote http://www.hackersdelight.org/permissions.htm :
934  *
935  * "You are free to use, copy, and distribute any of the code on
936  *  this web site, whether modified by you or not. You need not give
937  *  attribution. This includes the algorithms (some of which appear
938  *  in Hacker's Delight), the Hacker's Assistant, and any code submitted
939  *  by readers. Submitters implicitly agree to this."
940  */
941 uint32_t
crc32(const u_char * buf,const uint32_t size)942 crc32(const u_char *buf, const uint32_t size)
943 {
944 	int			j;
945 	uint32_t		i, byte, crc, mask;
946 
947 	crc = 0xFFFFFFFF;
948 
949 	for (i = 0; i < size; i++) {
950 		byte = buf[i];			/* Get next byte. */
951 		crc = crc ^ byte;
952 		for (j = 7; j >= 0; j--) {	/* Do eight times. */
953 			mask = -(crc & 1);
954 			crc = (crc >> 1) ^ (0xEDB88320 & mask);
955 		}
956 	}
957 
958 	return ~crc;
959 }
960