xref: /openbsd-src/sbin/fdisk/gpt.c (revision f1dd7b858388b4a23f4f67a4957ec5ff656ebbe8)
1 /*	$OpenBSD: gpt.c,v 1.16 2021/05/15 22:06:43 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 <errno.h>
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include <unistd.h>
29 #include <uuid.h>
30 
31 #include "disk.h"
32 #include "misc.h"
33 #include "part.h"
34 #include "gpt.h"
35 
36 #ifdef DEBUG
37 #define DPRINTF(x...)	printf(x)
38 #else
39 #define DPRINTF(x...)
40 #endif
41 
42 struct gpt_header gh;
43 struct gpt_partition gp[NGPTPARTITIONS];
44 
45 struct gpt_partition	**sort_gpt(void);
46 int			  lba_start_cmp(const void *e1, const void *e2);
47 
48 int
49 GPT_get_header(off_t where)
50 {
51 	char *secbuf;
52 	uint64_t partlastlba;
53 	int partspersec;
54 	uint32_t orig_gh_csum, new_gh_csum;
55 
56 	secbuf = DISK_readsector(where);
57 	if (secbuf == 0)
58 		return (1);
59 
60 	memcpy(&gh, secbuf, sizeof(struct gpt_header));
61 	free(secbuf);
62 
63 	if (letoh64(gh.gh_sig) != GPTSIGNATURE) {
64 		DPRINTF("gpt signature: expected 0x%llx, got 0x%llx\n",
65 		    GPTSIGNATURE, letoh64(gh.gh_sig));
66 		return (1);
67 	}
68 
69 	if (letoh32(gh.gh_rev) != GPTREVISION) {
70 		DPRINTF("gpt revision: expected 0x%x, got 0x%x\n",
71 		    GPTREVISION, letoh32(gh.gh_rev));
72 		return (1);
73 	}
74 
75 	if (letoh64(gh.gh_lba_self) != where) {
76 		DPRINTF("gpt self lba: expected %lld, got %llu\n",
77 		    (long long)where, letoh64(gh.gh_lba_self));
78 		return (1);
79 	}
80 
81 	if (letoh32(gh.gh_size) != GPTMINHDRSIZE) {
82 		DPRINTF("gpt header size: expected %u, got %u\n",
83 		    GPTMINHDRSIZE, letoh32(gh.gh_size));
84 		return (1);
85 	}
86 
87 	if (letoh32(gh.gh_part_size) != GPTMINPARTSIZE) {
88 		DPRINTF("gpt partition size: expected %u, got %u\n",
89 		    GPTMINPARTSIZE, letoh32(gh.gh_part_size));
90 		return (1);
91 	}
92 
93 	if (letoh32(gh.gh_part_num) > NGPTPARTITIONS) {
94 		DPRINTF("gpt partition count: expected <= %u, got %u\n",
95 		    NGPTPARTITIONS, letoh32(gh.gh_part_num));
96 		return (1);
97 	}
98 
99 	orig_gh_csum = gh.gh_csum;
100 	gh.gh_csum = 0;
101 	new_gh_csum = crc32((unsigned char *)&gh, letoh32(gh.gh_size));
102 	gh.gh_csum = orig_gh_csum;
103 	if (letoh32(orig_gh_csum) != new_gh_csum) {
104 		DPRINTF("gpt header checksum: expected 0x%x, got 0x%x\n",
105 		    orig_gh_csum, new_gh_csum);
106 		return (1);
107 	}
108 
109 	if (letoh64(gh.gh_lba_end) >= DL_GETDSIZE(&dl)) {
110 		DPRINTF("gpt last usable LBA: expected < %lld, got %llu\n",
111 		    DL_GETDSIZE(&dl), letoh64(gh.gh_lba_end));
112 		return (1);
113 	}
114 
115 	if (letoh64(gh.gh_lba_start) >= letoh64(gh.gh_lba_end)) {
116 		DPRINTF("gpt first usable LBA: expected < %llu, got %llu\n",
117 		    letoh64(gh.gh_lba_start), letoh64(gh.gh_lba_start));
118 		return (1);
119 	}
120 
121 	if (letoh64(gh.gh_part_lba) <= letoh64(gh.gh_lba_end) &&
122 	    letoh64(gh.gh_part_lba) >= letoh64(gh.gh_lba_start)) {
123 		DPRINTF("gpt partition table start lba: expected < %llu or "
124 		    "> %llu, got %llu\n", letoh64(gh.gh_lba_start),
125 		    letoh64(gh.gh_lba_end), letoh64(gh.gh_part_lba));
126 		return (1);
127 	}
128 
129 	partspersec = dl.d_secsize / letoh32(gh.gh_part_size);
130 	partlastlba = letoh64(gh.gh_part_lba) +
131 	    ((letoh32(gh.gh_part_num) + partspersec - 1) / partspersec) - 1;
132 	if (partlastlba <= letoh64(gh.gh_lba_end) &&
133 	    partlastlba >= letoh64(gh.gh_lba_start)) {
134 		DPRINTF("gpt partition table last LBA: expected < %llu or "
135 		    "> %llu, got %llu\n", letoh64(gh.gh_lba_start),
136 		    letoh64(gh.gh_lba_end), partlastlba);
137 		return (1);
138 	}
139 
140 	/*
141 	 * Other possible paranoia checks:
142 	 *	1) partition table starts before primary gpt lba.
143 	 *	2) partition table extends into lowest partition.
144 	 *	3) alt partition table starts before gh_lba_end.
145 	 */
146 	return (0);
147 }
148 
149 int
150 GPT_get_partition_table(off_t where)
151 {
152 	ssize_t len;
153 	off_t off;
154 	int secs;
155 	uint32_t checksum, partspersec;
156 
157 	DPRINTF("gpt partition table being read from LBA %llu\n",
158 	    letoh64(gh.gh_part_lba));
159 
160 	partspersec = dl.d_secsize / letoh32(gh.gh_part_size);
161 	if (partspersec * letoh32(gh.gh_part_size) != dl.d_secsize) {
162 		DPRINTF("gpt partition table entry invalid size. %u\n",
163 		    letoh32(gh.gh_part_size));
164 		return (1);
165 	}
166 	secs = (letoh32(gh.gh_part_num) + partspersec - 1) / partspersec;
167 
168 	memset(&gp, 0, sizeof(gp));
169 
170 	where *= dl.d_secsize;
171 	off = lseek(disk.fd, where, SEEK_SET);
172 	if (off == -1) {
173 		DPRINTF("seek to gpt partition table @ sector %llu failed\n",
174 		    (unsigned long long)where / dl.d_secsize);
175 		return (1);
176 	}
177 	len = read(disk.fd, &gp, secs * dl.d_secsize);
178 	if (len == -1 || len != secs * dl.d_secsize) {
179 		DPRINTF("gpt partition table read failed.\n");
180 		return (1);
181 	}
182 
183 	checksum = crc32((unsigned char *)&gp, letoh32(gh.gh_part_num) *
184 	    letoh32(gh.gh_part_size));
185 	if (checksum != letoh32(gh.gh_part_csum)) {
186 		DPRINTF("gpt partition table checksum: expected %x, got %x\n",
187 		    checksum, letoh32(gh.gh_part_csum));
188 		return (1);
189 	}
190 
191 	return (0);
192 }
193 
194 void
195 GPT_read(int which)
196 {
197 	int valid;
198 
199 	switch (which) {
200 	case PRIMARYGPT:
201 		valid = GPT_get_header(GPTSECTOR);
202 		break;
203 	case SECONDARYGPT:
204 		valid = GPT_get_header(DL_GETDSIZE(&dl) - 1);
205 		break;
206 	case ANYGPT:
207 		valid = GPT_get_header(GPTSECTOR);
208 		if (valid != 0 || GPT_get_partition_table(gh.gh_part_lba) != 0)
209 			valid = GPT_get_header(DL_GETDSIZE(&dl) - 1);
210 		break;
211 	default:
212 		return;
213 	}
214 
215 	if (valid == 0)
216 		valid = GPT_get_partition_table(gh.gh_part_lba);
217 
218 	if (valid != 0) {
219 		/* No valid GPT found. Zap any artifacts. */
220 		memset(&gh, 0, sizeof(gh));
221 		memset(&gp, 0, sizeof(gp));
222 	}
223 }
224 
225 void
226 GPT_print(char *units, int verbosity)
227 {
228 	const int secsize = unit_types[SECTORS].conversion;
229 	struct uuid guid;
230 	char *guidstr = NULL;
231 	double size;
232 	int i, u, status;
233 
234 	u = unit_lookup(units);
235 	size = ((double)DL_GETDSIZE(&dl) * secsize) / unit_types[u].conversion;
236 	printf("Disk: %s       Usable LBA: %llu to %llu [%.0f ",
237 	    disk.name, letoh64(gh.gh_lba_start), letoh64(gh.gh_lba_end), size);
238 
239 	if (u == SECTORS && secsize != DEV_BSIZE)
240 		printf("%d-byte ", secsize);
241 	printf("%s]\n", unit_types[u].lname);
242 
243 	if (verbosity == VERBOSE) {
244 		printf("GUID: ");
245 		uuid_dec_le(&gh.gh_guid, &guid);
246 		uuid_to_string(&guid, &guidstr, &status);
247 		if (status == uuid_s_ok)
248 			printf("%s\n", guidstr);
249 		else
250 			printf("<invalid header GUID>\n");
251 		free(guidstr);
252 	}
253 
254 	GPT_print_parthdr(verbosity);
255 	for (i = 0; i < letoh32(gh.gh_part_num); i++) {
256 		if (uuid_is_nil(&gp[i].gp_type, NULL))
257 			continue;
258 		GPT_print_part(i, units, verbosity);
259 	}
260 
261 }
262 
263 void
264 GPT_print_parthdr(int verbosity)
265 {
266 	printf("   #: type                                "
267 	    " [       start:         size ]\n");
268 	if (verbosity == VERBOSE)
269 		printf("      guid                                 name\n");
270 	printf("--------------------------------------------------------"
271 	    "----------------\n");
272 }
273 
274 void
275 GPT_print_part(int n, char *units, int verbosity)
276 {
277 	struct uuid guid;
278 	const int secsize = unit_types[SECTORS].conversion;
279 	struct gpt_partition *partn = &gp[n];
280 	char *guidstr = NULL;
281 	double size;
282 	int u, status;
283 
284 	uuid_dec_le(&partn->gp_type, &guid);
285 	u = unit_lookup(units);
286 	size = letoh64(partn->gp_lba_end) - letoh64(partn->gp_lba_start) + 1;
287 	size = (size * secsize) / unit_types[u].conversion;
288 	printf("%c%3d: %-36s [%12lld: %12.0f%s]\n",
289 	    (letoh64(partn->gp_attrs) & GPTDOSACTIVE)?'*':' ', n,
290 	    PRT_uuid_to_typename(&guid), letoh64(partn->gp_lba_start),
291 	    size, unit_types[u].abbr);
292 
293 	if (verbosity == VERBOSE) {
294 		uuid_dec_le(&partn->gp_guid, &guid);
295 		uuid_to_string(&guid, &guidstr, &status);
296 		if (status != uuid_s_ok)
297 			printf("      <invalid partition guid>             ");
298 		else
299 			printf("      %-36s ", guidstr);
300 		printf("%-36s\n", utf16le_to_string(partn->gp_name));
301 		free(guidstr);
302 	}
303 }
304 
305 int
306 GPT_init(void)
307 {
308 	extern uint32_t b_arg;
309 	const int secsize = unit_types[SECTORS].conversion;
310 	struct uuid guid;
311 	int needed;
312 	uint32_t status;
313 	const uint8_t gpt_uuid_efi_system[] = GPT_UUID_EFI_SYSTEM;
314 	const uint8_t gpt_uuid_openbsd[] = GPT_UUID_OPENBSD;
315 
316 	memset(&gh, 0, sizeof(gh));
317 	memset(&gp, 0, sizeof(gp));
318 
319 	needed = sizeof(gp) / secsize + 2;
320 	/* Start on 64 sector boundary */
321 	if (needed % 64)
322 		needed += (64 - (needed % 64));
323 
324 	gh.gh_sig = htole64(GPTSIGNATURE);
325 	gh.gh_rev = htole32(GPTREVISION);
326 	gh.gh_size = htole32(GPTMINHDRSIZE);
327 	gh.gh_csum = 0;
328 	gh.gh_rsvd = 0;
329 	gh.gh_lba_self = htole64(1);
330 	gh.gh_lba_alt = htole64(DL_GETDSIZE(&dl) - 1);
331 	gh.gh_lba_start = htole64(needed);
332 	gh.gh_lba_end = htole64(DL_GETDSIZE(&dl) - needed);
333 	gh.gh_part_lba = htole64(2);
334 	gh.gh_part_num = htole32(NGPTPARTITIONS);
335 	gh.gh_part_size = htole32(GPTMINPARTSIZE);
336 
337 	uuid_create(&guid, &status);
338 	if (status != uuid_s_ok)
339 		return (1);
340 	uuid_enc_le(&gh.gh_guid, &guid);
341 
342 #if defined(__i386__) || defined(__amd64__)
343 	if (b_arg > 0) {
344 		/* Add an EFI system partition on i386/amd64. */
345 		uuid_dec_be(gpt_uuid_efi_system, &guid);
346 		uuid_enc_le(&gp[1].gp_type, &guid);
347 		uuid_create(&guid, &status);
348 		if (status != uuid_s_ok)
349 			return (1);
350 		uuid_enc_le(&gp[1].gp_guid, &guid);
351 		gp[1].gp_lba_start = gh.gh_lba_start;
352 		gp[1].gp_lba_end = htole64(letoh64(gh.gh_lba_start)+b_arg - 1);
353 		memcpy(gp[1].gp_name, string_to_utf16le("EFI System Area"),
354 		    sizeof(gp[1].gp_name));
355 	}
356 #endif
357 	uuid_dec_be(gpt_uuid_openbsd, &guid);
358 	uuid_enc_le(&gp[3].gp_type, &guid);
359 	uuid_create(&guid, &status);
360 	if (status != uuid_s_ok)
361 		return (1);
362 	uuid_enc_le(&gp[3].gp_guid, &guid);
363 	gp[3].gp_lba_start = gh.gh_lba_start;
364 #if defined(__i386__) || defined(__amd64__)
365 	if (b_arg > 0) {
366 		gp[3].gp_lba_start = htole64(letoh64(gp[3].gp_lba_start) +
367 		    b_arg);
368 		if (letoh64(gp[3].gp_lba_start) % 64)
369 			gp[3].gp_lba_start =
370 			    htole64(letoh64(gp[3].gp_lba_start) +
371 			    (64 - letoh64(gp[3].gp_lba_start) % 64));
372 	}
373 #endif
374 	gp[3].gp_lba_end = gh.gh_lba_end;
375 	memcpy(gp[3].gp_name, string_to_utf16le("OpenBSD Area"),
376 	    sizeof(gp[3].gp_name));
377 
378 	gh.gh_part_csum = crc32((unsigned char *)&gp, sizeof(gp));
379 	gh.gh_csum = crc32((unsigned char *)&gh, sizeof(gh));
380 
381 	return 0;
382 }
383 
384 int
385 GPT_write(void)
386 {
387 	char *secbuf;
388 	const int secsize = unit_types[SECTORS].conversion;
389 	ssize_t len;
390 	off_t off;
391 	uint64_t altgh, altgp, prigh, prigp;
392 
393 	/* Assume we always write full-size partition table. XXX */
394 	prigh = GPTSECTOR;
395 	prigp = prigh + 1;
396 	altgh = DL_GETDSIZE(&dl) - 1;
397 	altgp = DL_GETDSIZE(&dl) - 1 - (sizeof(gp) / secsize);
398 
399 	gh.gh_lba_self = htole64(prigh);
400 	gh.gh_lba_alt = htole64(altgh);
401 	gh.gh_part_lba = htole64(prigp);
402 	gh.gh_part_csum = crc32((unsigned char *)&gp, sizeof(gp));
403 	gh.gh_csum = 0;
404 	gh.gh_csum = crc32((unsigned char *)&gh, letoh32(gh.gh_size));
405 
406 	secbuf = DISK_readsector(prigh);
407 	if (secbuf == NULL)
408 		return (-1);
409 
410 	memcpy(secbuf, &gh, sizeof(gh));
411 	DISK_writesector(secbuf, prigh);
412 	free(secbuf);
413 
414 	gh.gh_lba_self = htole64(altgh);
415 	gh.gh_lba_alt = htole64(prigh);
416 	gh.gh_part_lba = htole64(altgp);
417 	gh.gh_csum = 0;
418 	gh.gh_csum = crc32((unsigned char *)&gh, letoh32(gh.gh_size));
419 
420 	secbuf = DISK_readsector(altgh);
421 	if (secbuf == NULL)
422 		return (-1);
423 
424 	memcpy(secbuf, &gh, sizeof(gh));
425 	DISK_writesector(secbuf, altgh);
426 	free(secbuf);
427 
428 	/*
429 	 * XXX ALWAYS NGPTPARTITIONS!
430 	 * XXX ASSUME gp is multiple of sector size!
431 	 */
432 	off = lseek(disk.fd, secsize * prigp, SEEK_SET);
433 	if (off == secsize * prigp)
434 		len = write(disk.fd, &gp, sizeof(gp));
435 	else
436 		len = -1;
437 	if (len == -1 || len != sizeof(gp)) {
438 		errno = EIO;
439 		return (-1);
440 	}
441 
442 	off = lseek(disk.fd, secsize * altgp, SEEK_SET);
443 	if (off == secsize * altgp)
444 		len = write(disk.fd, &gp, sizeof(gp));
445 	else
446 		len = -1;
447 
448 	if (len == -1 || len != sizeof(gp)) {
449 		errno = EIO;
450 		return (-1);
451 	}
452 
453 	/* Refresh in-kernel disklabel from the updated disk information. */
454 	ioctl(disk.fd, DIOCRLDINFO, 0);
455 
456 	return (0);
457 }
458 
459 int
460 gp_lba_start_cmp(const void *e1, const void *e2)
461 {
462 	struct gpt_partition *p1 = *(struct gpt_partition **)e1;
463 	struct gpt_partition *p2 = *(struct gpt_partition **)e2;
464 	uint64_t o1;
465 	uint64_t o2;
466 
467 	o1 = letoh64(p1->gp_lba_start);
468 	o2 = letoh64(p2->gp_lba_start);
469 
470 	if (o1 < o2)
471 		return -1;
472 	else if (o1 > o2)
473 		return 1;
474 	else
475 		return 0;
476 }
477 
478 struct gpt_partition **
479 sort_gpt(void)
480 {
481 	static struct gpt_partition *sgp[NGPTPARTITIONS+2];
482 	unsigned int i, j;
483 
484 	memset(sgp, 0, sizeof(sgp));
485 
486 	j = 0;
487 	for (i = 0; i < letoh32(gh.gh_part_num); i++) {
488 		if (letoh64(gp[i].gp_lba_start) >= letoh64(gh.gh_lba_start))
489 			sgp[j++] = &gp[i];
490 	}
491 
492 	if (j > 1) {
493 		if (mergesort(sgp, j, sizeof(sgp[0]), gp_lba_start_cmp) == -1) {
494 			printf("unable to sort gpt by lba start\n");
495 			return NULL;
496 		}
497 	}
498 
499 	return (sgp);
500 }
501 
502 int
503 GPT_get_lba_start(unsigned int pn)
504 {
505 	struct gpt_partition	**sgp;
506 	uint64_t		  bs, bigbs, nextbs, ns;
507 	unsigned int		  i;
508 
509 	bs = letoh64(gh.gh_lba_start);
510 
511 	if (letoh64(gp[pn].gp_lba_start) >= bs) {
512 		bs = letoh64(gp[pn].gp_lba_start);
513 	} else {
514 		sgp = sort_gpt();
515 		if (sgp == NULL)
516 			return -1;
517 		if (sgp[0] != NULL) {
518 			bigbs = bs;
519 			ns = 0;
520 			for (i = 0; sgp[i] != NULL; i++) {
521 				nextbs = letoh64(sgp[i]->gp_lba_start);
522 				if (bs < nextbs && ns < nextbs - bs) {
523 					ns = nextbs - bs;
524 					bigbs = bs;
525 				}
526 				bs = letoh64(sgp[i]->gp_lba_end) + 1;
527 			}
528 			nextbs = letoh64(gh.gh_lba_end) + 1;
529 			if (bs < nextbs && ns < nextbs - bs) {
530 				ns = nextbs - bs;
531 				bigbs = bs;
532 			}
533 			if (ns == 0) {
534 				printf("no space for partition %u\n", pn);
535 				return -1;
536 			}
537 			bs = bigbs;
538 		}
539 	}
540 
541 	bs = getuint64("Partition offset", bs, letoh64(gh.gh_lba_start),
542 	    letoh64(gh.gh_lba_end));
543 
544 	for (i = 0; i < letoh32(gh.gh_part_num); i++) {
545 		if (i == pn)
546 			continue;
547 		if (bs >= letoh64(gp[i].gp_lba_start) &&
548 		    bs <= letoh64(gp[i].gp_lba_end)) {
549 			printf("partition %u can't start inside partition %u\n",
550 			    pn, i);
551 			return -1;
552 		}
553 	}
554 
555 	gp[pn].gp_lba_start = htole64(bs);
556 
557 	return 0;
558 }
559 
560 int
561 GPT_get_lba_end(unsigned int pn)
562 {
563 	struct gpt_partition	**sgp;
564 	uint64_t		  bs, nextbs, ns;
565 	unsigned int		  i;
566 
567 	sgp = sort_gpt();
568 	if (sgp == NULL)
569 		return -1;
570 
571 	bs = letoh64(gp[pn].gp_lba_start);
572 	ns = letoh64(gh.gh_lba_end) - bs + 1;
573 	for (i = 0; sgp[i] != NULL; i++) {
574 		nextbs = letoh64(sgp[i]->gp_lba_start);
575 		if (nextbs > bs) {
576 			ns = nextbs - bs;
577 			break;
578 		}
579 	}
580 	ns = getuint64("Partition size", ns, 1, ns);
581 
582 	gp[pn].gp_lba_end = htole64(bs + ns - 1);
583 
584 	return 0;
585 }
586