xref: /openbsd-src/sbin/fdisk/gpt.c (revision 24bb5fcea3ed904bc467217bdaadb5dfc618d5bf)
1 /*	$OpenBSD: gpt.c,v 1.47 2021/07/21 12:22:54 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 <stdint.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <unistd.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			  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 
60 /*
61  * Return the index into dp[] of the EFI GPT (0xEE) partition, or -1 if no such
62  * partition exists.
63  *
64  * Taken from kern/subr_disk.c.
65  *
66  */
67 int
68 gpt_chk_mbr(struct dos_partition *dp, u_int64_t dsize)
69 {
70 	struct dos_partition	*dp2;
71 	int			 efi, eficnt, found, i;
72 	uint32_t		 psize;
73 
74 	found = efi = eficnt = 0;
75 	for (dp2 = dp, i = 0; i < NDOSPART; i++, dp2++) {
76 		if (dp2->dp_typ == DOSPTYP_UNUSED)
77 			continue;
78 		found++;
79 		if (dp2->dp_typ != DOSPTYP_EFI)
80 			continue;
81 		if (letoh32(dp2->dp_start) != GPTSECTOR)
82 			continue;
83 		psize = letoh32(dp2->dp_size);
84 		if (psize <= (dsize - GPTSECTOR) || psize == UINT32_MAX) {
85 			efi = i;
86 			eficnt++;
87 		}
88 	}
89 	if (found == 1 && eficnt == 1)
90 		return efi;
91 
92 	return -1;
93 }
94 
95 int
96 protective_mbr(const struct mbr *mbr)
97 {
98 	struct dos_partition	dp[NDOSPART], dos_partition;
99 	int			i;
100 
101 	if (mbr->mbr_lba_self != 0)
102 		return -1;
103 
104 	for (i = 0; i < NDOSPART; i++) {
105 		PRT_make(&mbr->mbr_prt[i], mbr->mbr_lba_self, mbr->mbr_lba_firstembr,
106 		    &dos_partition);
107 		memcpy(&dp[i], &dos_partition, sizeof(dp[i]));
108 	}
109 
110 	return gpt_chk_mbr(dp, DL_GETDSIZE(&dl));
111 }
112 
113 int
114 get_header(const uint64_t sector)
115 {
116 	char			*secbuf;
117 	uint64_t		 partlastlba, partslen, lba_end;
118 	int			 partspersec;
119 	uint32_t		 orig_gh_csum, new_gh_csum;
120 
121 	secbuf = DISK_readsector(sector);
122 	if (secbuf == NULL)
123 		return -1;
124 
125 	memcpy(&gh, secbuf, sizeof(struct gpt_header));
126 	free(secbuf);
127 
128 	if (letoh64(gh.gh_sig) != GPTSIGNATURE) {
129 		DPRINTF("gpt signature: expected 0x%llx, got 0x%llx\n",
130 		    GPTSIGNATURE, letoh64(gh.gh_sig));
131 		return -1;
132 	}
133 
134 	if (letoh32(gh.gh_rev) != GPTREVISION) {
135 		DPRINTF("gpt revision: expected 0x%x, got 0x%x\n",
136 		    GPTREVISION, letoh32(gh.gh_rev));
137 		return -1;
138 	}
139 
140 	if (letoh64(gh.gh_lba_self) != sector) {
141 		DPRINTF("gpt self lba: expected %llu, got %llu\n",
142 		    sector, letoh64(gh.gh_lba_self));
143 		return -1;
144 	}
145 
146 	if (letoh32(gh.gh_size) != GPTMINHDRSIZE) {
147 		DPRINTF("gpt header size: expected %u, got %u\n",
148 		    GPTMINHDRSIZE, letoh32(gh.gh_size));
149 		return -1;
150 	}
151 
152 	if (letoh32(gh.gh_part_size) != GPTMINPARTSIZE) {
153 		DPRINTF("gpt partition size: expected %u, got %u\n",
154 		    GPTMINPARTSIZE, letoh32(gh.gh_part_size));
155 		return -1;
156 	}
157 
158 	if (letoh32(gh.gh_part_num) > NGPTPARTITIONS) {
159 		DPRINTF("gpt partition count: expected <= %u, got %u\n",
160 		    NGPTPARTITIONS, letoh32(gh.gh_part_num));
161 		return -1;
162 	}
163 
164 	orig_gh_csum = gh.gh_csum;
165 	gh.gh_csum = 0;
166 	new_gh_csum = crc32((unsigned char *)&gh, letoh32(gh.gh_size));
167 	gh.gh_csum = orig_gh_csum;
168 	if (letoh32(orig_gh_csum) != new_gh_csum) {
169 		DPRINTF("gpt header checksum: expected 0x%x, got 0x%x\n",
170 		    orig_gh_csum, new_gh_csum);
171 		return -1;
172 	}
173 
174 	/* XXX Assume part_num * part_size is multiple of secsize. */
175 	partslen = letoh32(gh.gh_part_num) * letoh32(gh.gh_part_size) /
176 	    dl.d_secsize;
177 	lba_end = DL_GETDSIZE(&dl) - partslen - 2;
178 	if (letoh64(gh.gh_lba_end) > lba_end) {
179 		DPRINTF("gpt last usable LBA: reduced from %llu to %llu\n",
180 		    letoh64(gh.gh_lba_end), lba_end);
181 		gh.gh_lba_end = htole64(lba_end);
182 	}
183 
184 	if (letoh64(gh.gh_lba_start) >= letoh64(gh.gh_lba_end)) {
185 		DPRINTF("gpt first usable LBA: expected < %llu, got %llu\n",
186 		    letoh64(gh.gh_lba_end), letoh64(gh.gh_lba_start));
187 		return -1;
188 	}
189 
190 	if (letoh64(gh.gh_part_lba) <= letoh64(gh.gh_lba_end) &&
191 	    letoh64(gh.gh_part_lba) >= letoh64(gh.gh_lba_start)) {
192 		DPRINTF("gpt partition table start lba: expected < %llu or "
193 		    "> %llu, got %llu\n", letoh64(gh.gh_lba_start),
194 		    letoh64(gh.gh_lba_end), letoh64(gh.gh_part_lba));
195 		return -1;
196 	}
197 
198 	partspersec = dl.d_secsize / letoh32(gh.gh_part_size);
199 	partlastlba = letoh64(gh.gh_part_lba) +
200 	    ((letoh32(gh.gh_part_num) + partspersec - 1) / partspersec) - 1;
201 	if (partlastlba <= letoh64(gh.gh_lba_end) &&
202 	    partlastlba >= letoh64(gh.gh_lba_start)) {
203 		DPRINTF("gpt partition table last LBA: expected < %llu or "
204 		    "> %llu, got %llu\n", letoh64(gh.gh_lba_start),
205 		    letoh64(gh.gh_lba_end), partlastlba);
206 		return -1;
207 	}
208 
209 	/*
210 	 * Other possible paranoia checks:
211 	 *	1) partition table starts before primary gpt lba.
212 	 *	2) partition table extends into lowest partition.
213 	 *	3) alt partition table starts before gh_lba_end.
214 	 */
215 	return 0;
216 }
217 
218 int
219 get_partition_table(void)
220 {
221 	ssize_t			len;
222 	off_t			off, where;
223 	int			secs;
224 	uint32_t		checksum, partspersec;
225 
226 	DPRINTF("gpt partition table being read from LBA %llu\n",
227 	    letoh64(gh.gh_part_lba));
228 
229 	partspersec = dl.d_secsize / letoh32(gh.gh_part_size);
230 	if (partspersec * letoh32(gh.gh_part_size) != dl.d_secsize) {
231 		DPRINTF("gpt partition table entry invalid size. %u\n",
232 		    letoh32(gh.gh_part_size));
233 		return -1;
234 	}
235 	secs = (letoh32(gh.gh_part_num) + partspersec - 1) / partspersec;
236 
237 	memset(&gp, 0, sizeof(gp));
238 
239 	where = letoh64(gh.gh_part_lba) * dl.d_secsize;
240 	off = lseek(disk.dk_fd, where, SEEK_SET);
241 	if (off == -1) {
242 		DPRINTF("seek to gpt partition table @ sector %llu failed\n",
243 		    (unsigned long long)where / dl.d_secsize);
244 		return -1;
245 	}
246 	len = read(disk.dk_fd, &gp, secs * dl.d_secsize);
247 	if (len == -1 || len != secs * dl.d_secsize) {
248 		DPRINTF("gpt partition table read failed.\n");
249 		return -1;
250 	}
251 
252 	checksum = crc32((unsigned char *)&gp, letoh32(gh.gh_part_num) *
253 	    letoh32(gh.gh_part_size));
254 	if (checksum != letoh32(gh.gh_part_csum)) {
255 		DPRINTF("gpt partition table checksum: expected %x, got %x\n",
256 		    checksum, letoh32(gh.gh_part_csum));
257 		return -1;
258 	}
259 
260 	return 0;
261 }
262 
263 int
264 GPT_read(const int which)
265 {
266 	int			error;
267 
268 	error = MBR_read(0, 0, &gmbr);
269 	if (error == 0)
270 		error = protective_mbr(&gmbr);
271 	if (error)
272 		goto done;
273 
274 	switch (which) {
275 	case PRIMARYGPT:
276 		error = get_header(GPTSECTOR);
277 		break;
278 	case SECONDARYGPT:
279 		error = get_header(DL_GETDSIZE(&dl) - 1);
280 		break;
281 	case ANYGPT:
282 		error = get_header(GPTSECTOR);
283 		if (error != 0 || get_partition_table() != 0)
284 			error = get_header(DL_GETDSIZE(&dl) - 1);
285 		break;
286 	default:
287 		return -1;
288 	}
289 
290 	if (error == 0)
291 		error = get_partition_table();
292 
293  done:
294 	if (error != 0) {
295 		/* No valid GPT found. Zap any artifacts. */
296 		memset(&gmbr, 0, sizeof(gmbr));
297 		memset(&gh, 0, sizeof(gh));
298 		memset(&gp, 0, sizeof(gp));
299 	}
300 
301 	return error;
302 }
303 
304 void
305 GPT_print(const char *units, const int verbosity)
306 {
307 	const int		 secsize = unit_types[SECTORS].ut_conversion;
308 	struct uuid		 guid;
309 	char			*guidstr = NULL;
310 	double			 size;
311 	int			 i, u, status;
312 
313 	u = unit_lookup(units);
314 	size = ((double)DL_GETDSIZE(&dl) * secsize) / unit_types[u].ut_conversion;
315 	printf("Disk: %s       Usable LBA: %llu to %llu [%.0f ",
316 	    disk.dk_name, letoh64(gh.gh_lba_start), letoh64(gh.gh_lba_end), size);
317 
318 	if (u == SECTORS && secsize != DEV_BSIZE)
319 		printf("%d-byte ", secsize);
320 	printf("%s]\n", unit_types[u].ut_lname);
321 
322 	if (verbosity == VERBOSE) {
323 		printf("GUID: ");
324 		uuid_dec_le(&gh.gh_guid, &guid);
325 		uuid_to_string(&guid, &guidstr, &status);
326 		if (status == uuid_s_ok)
327 			printf("%s\n", guidstr);
328 		else
329 			printf("<invalid header GUID>\n");
330 		free(guidstr);
331 	}
332 
333 	GPT_print_parthdr(verbosity);
334 	for (i = 0; i < letoh32(gh.gh_part_num); i++) {
335 		if (uuid_is_nil(&gp[i].gp_type, NULL))
336 			continue;
337 		GPT_print_part(i, units, verbosity);
338 	}
339 }
340 
341 void
342 GPT_print_parthdr(const int verbosity)
343 {
344 	printf("   #: type                                "
345 	    " [       start:         size ]\n");
346 	if (verbosity == VERBOSE)
347 		printf("      guid                                 name\n");
348 	printf("--------------------------------------------------------"
349 	    "----------------\n");
350 }
351 
352 void
353 GPT_print_part(const int n, const char *units, const int verbosity)
354 {
355 	struct uuid		 guid;
356 	struct gpt_partition	*partn = &gp[n];
357 	char			*guidstr = NULL;
358 	const int		 secsize = unit_types[SECTORS].ut_conversion;
359 	double			 size;
360 	int			 u, status;
361 
362 	uuid_dec_le(&partn->gp_type, &guid);
363 	u = unit_lookup(units);
364 	size = letoh64(partn->gp_lba_end) - letoh64(partn->gp_lba_start) + 1;
365 	size = (size * secsize) / unit_types[u].ut_conversion;
366 	printf("%c%3d: %-36s [%12lld: %12.0f%s]\n",
367 	    (letoh64(partn->gp_attrs) & GPTDOSACTIVE)?'*':' ', n,
368 	    PRT_uuid_to_typename(&guid), letoh64(partn->gp_lba_start),
369 	    size, unit_types[u].ut_abbr);
370 
371 	if (verbosity == VERBOSE) {
372 		uuid_dec_le(&partn->gp_guid, &guid);
373 		uuid_to_string(&guid, &guidstr, &status);
374 		if (status != uuid_s_ok)
375 			printf("      <invalid partition guid>             ");
376 		else
377 			printf("      %-36s ", guidstr);
378 		printf("%-36s\n", utf16le_to_string(partn->gp_name));
379 		free(guidstr);
380 	}
381 }
382 
383 int
384 add_partition(const uint8_t *beuuid, const char *name, uint64_t sectors)
385 {
386 	struct uuid		uuid, gp_type;
387 	int			rslt;
388 	uint64_t		end, freesectors, start;
389 	uint32_t		status, pn, pncnt;
390 
391 	uuid_dec_be(beuuid, &uuid);
392 	uuid_enc_le(&gp_type, &uuid);
393 
394 	pncnt = letoh32(gh.gh_part_num);
395 	for (pn = 0; pn < pncnt; pn++) {
396 		if (uuid_is_nil(&gp[pn].gp_type, NULL))
397 			break;
398 	}
399 	if (pn == pncnt)
400 		goto done;
401 
402 	rslt = lba_free(&start, &end);
403 	if (rslt == -1)
404 		goto done;
405 
406 	if (start % BLOCKALIGNMENT)
407 		start += (BLOCKALIGNMENT - start % BLOCKALIGNMENT);
408 	if (start >= end)
409 		goto done;
410 
411 	freesectors = end - start + 1;
412 
413 	if (sectors == 0)
414 		sectors = freesectors;
415 
416 	if (freesectors < sectors)
417 		goto done;
418 	else if (freesectors > sectors)
419 		end = start + sectors - 1;
420 
421 	gp[pn].gp_type = gp_type;
422 	gp[pn].gp_lba_start = htole64(start);
423 	gp[pn].gp_lba_end = htole64(end);
424 	memcpy(gp[pn].gp_name, string_to_utf16le(name),
425 	    sizeof(gp[pn].gp_name));
426 
427 	uuid_create(&uuid, &status);
428 	if (status != uuid_s_ok)
429 		goto done;
430 
431 	uuid_enc_le(&gp[pn].gp_guid, &uuid);
432 	gh.gh_part_csum = crc32((unsigned char *)&gp, sizeof(gp));
433 	gh.gh_csum = crc32((unsigned char *)&gh, sizeof(gh));
434 
435 	return 0;
436 
437  done:
438 	if (pn != pncnt)
439 		memset(&gp[pn], 0, sizeof(gp[pn]));
440 	printf("unable to add %s\n", name);
441 	return -1;
442 }
443 
444 int
445 init_gh(void)
446 {
447 	struct gpt_header	oldgh;
448 	struct uuid		guid;
449 	const int		secsize = unit_types[SECTORS].ut_conversion;
450 	int			needed;
451 	uint32_t		status;
452 
453 	memcpy(&oldgh, &gh, sizeof(oldgh));
454 	memset(&gh, 0, sizeof(gh));
455 	memset(&gmbr, 0, sizeof(gmbr));
456 
457 	/* XXX Do we need the boot code? UEFI spec & Apple says no. */
458 	memcpy(gmbr.mbr_code, initial_mbr.mbr_code, sizeof(gmbr.mbr_code));
459 	gmbr.mbr_prt[0].prt_id = DOSPTYP_EFI;
460 	gmbr.mbr_prt[0].prt_bs = 1;
461 	gmbr.mbr_prt[0].prt_ns = UINT32_MAX;
462 	PRT_fix_CHS(&gmbr.mbr_prt[0]);
463 	gmbr.mbr_signature = DOSMBR_SIGNATURE;
464 
465 	needed = sizeof(gp) / secsize + 2;
466 
467 	if (needed % BLOCKALIGNMENT)
468 		needed += (needed - (needed % BLOCKALIGNMENT));
469 
470 	gh.gh_sig = htole64(GPTSIGNATURE);
471 	gh.gh_rev = htole32(GPTREVISION);
472 	gh.gh_size = htole32(GPTMINHDRSIZE);
473 	gh.gh_csum = 0;
474 	gh.gh_rsvd = 0;
475 	gh.gh_lba_self = htole64(1);
476 	gh.gh_lba_alt = htole64(DL_GETDSIZE(&dl) - 1);
477 	gh.gh_lba_start = htole64(needed);
478 	gh.gh_lba_end = htole64(DL_GETDSIZE(&dl) - needed);
479 	gh.gh_part_lba = htole64(2);
480 	gh.gh_part_num = htole32(NGPTPARTITIONS);
481 	gh.gh_part_size = htole32(GPTMINPARTSIZE);
482 
483 	uuid_create(&guid, &status);
484 	if (status != uuid_s_ok) {
485 		memcpy(&gh, &oldgh, sizeof(gh));
486 		return -1;
487 	}
488 
489 	uuid_enc_le(&gh.gh_guid, &guid);
490 	return 0;
491 }
492 
493 int
494 init_gp(const int how)
495 {
496 	struct gpt_partition	oldgp[NGPTPARTITIONS];
497 	const uint8_t		gpt_uuid_efi_system[] = GPT_UUID_EFI_SYSTEM;
498 	const uint8_t		gpt_uuid_openbsd[] = GPT_UUID_OPENBSD;
499 	int			pn, rslt;
500 
501 	memcpy(&oldgp, &gp, sizeof(oldgp));
502 	if (how == GHANDGP)
503 		memset(&gp, 0, sizeof(gp));
504 	else {
505 		for (pn = 0; pn < NGPTPARTITIONS; pn++) {
506 			if (PRT_protected_guid(&gp[pn].gp_type))
507 				continue;
508 			memset(&gp[pn], 0, sizeof(gp[pn]));
509 		}
510 	}
511 
512 	rslt = 0;
513 	if (disk.dk_bootprt.prt_ns > 0) {
514 		rslt = add_partition(gpt_uuid_efi_system, "EFI System Area",
515 		    disk.dk_bootprt.prt_ns);
516 	}
517 	if (rslt == 0)
518 		rslt = add_partition(gpt_uuid_openbsd, "OpenBSD Area", 0);
519 
520 	if (rslt != 0)
521 		memcpy(&gp, &oldgp, sizeof(gp));
522 
523 	return rslt;
524 }
525 
526 int
527 GPT_init(const int how)
528 {
529 	int			rslt = 0;
530 
531 	if (how == GHANDGP)
532 		rslt = init_gh();
533 	if (rslt == 0)
534 		rslt = init_gp(how);
535 
536 	return rslt;
537 }
538 
539 void
540 GPT_zap_headers(void)
541 {
542 	char			*secbuf;
543 	uint64_t		 sig;
544 
545 	secbuf = DISK_readsector(GPTSECTOR);
546 	if (secbuf == NULL)
547 		return;
548 
549 	memcpy(&sig, secbuf, sizeof(sig));
550 	if (letoh64(sig) == GPTSIGNATURE) {
551 		memset(secbuf, 0, dl.d_secsize);
552 		DISK_writesector(secbuf, GPTSECTOR);
553 	}
554 	free(secbuf);
555 
556 	secbuf = DISK_readsector(DL_GETDSIZE(&dl) - 1);
557 	if (secbuf == NULL)
558 		return;
559 
560 	memcpy(&sig, secbuf, sizeof(sig));
561 	if (letoh64(sig) == GPTSIGNATURE) {
562 		memset(secbuf, 0, dl.d_secsize);
563 		DISK_writesector(secbuf, DL_GETDSIZE(&dl) - 1);
564 	}
565 	free(secbuf);
566 }
567 
568 int
569 GPT_write(void)
570 {
571 	char			*secbuf;
572 	ssize_t			 len;
573 	off_t			 off;
574 	const int		 secsize = unit_types[SECTORS].ut_conversion;
575 	uint64_t		 altgh, altgp, prigh, prigp, gpbytes;
576 
577 	MBR_write(&gmbr);
578 
579 	/*
580 	 * XXX Assume size of gp is multiple of sector size.
581 	 */
582 	gpbytes = letoh32(gh.gh_part_num) * letoh32(gh.gh_part_size);
583 
584 	prigh = GPTSECTOR;
585 	prigp = prigh + 1;
586 	altgh = DL_GETDSIZE(&dl) - 1;
587 	altgp = DL_GETDSIZE(&dl) - 1 - (gpbytes / secsize);
588 
589 	gh.gh_lba_self = htole64(prigh);
590 	gh.gh_lba_alt = htole64(altgh);
591 	gh.gh_part_lba = htole64(prigp);
592 	gh.gh_part_csum = crc32((unsigned char *)&gp, gpbytes);
593 	gh.gh_csum = 0;
594 	gh.gh_csum = crc32((unsigned char *)&gh, letoh32(gh.gh_size));
595 
596 	secbuf = DISK_readsector(prigh);
597 	if (secbuf == NULL)
598 		return -1;
599 
600 	memcpy(secbuf, &gh, sizeof(gh));
601 	DISK_writesector(secbuf, prigh);
602 	free(secbuf);
603 
604 	gh.gh_lba_self = htole64(altgh);
605 	gh.gh_lba_alt = htole64(prigh);
606 	gh.gh_part_lba = htole64(altgp);
607 	gh.gh_csum = 0;
608 	gh.gh_csum = crc32((unsigned char *)&gh, letoh32(gh.gh_size));
609 
610 	secbuf = DISK_readsector(altgh);
611 	if (secbuf == NULL)
612 		return -1;
613 
614 	memcpy(secbuf, &gh, sizeof(gh));
615 	DISK_writesector(secbuf, altgh);
616 	free(secbuf);
617 
618 	off = lseek(disk.dk_fd, secsize * prigp, SEEK_SET);
619 	if (off == secsize * prigp)
620 		len = write(disk.dk_fd, &gp, gpbytes);
621 	else
622 		len = -1;
623 	if (len == -1 || len != gpbytes) {
624 		errno = EIO;
625 		return -1;
626 	}
627 
628 	off = lseek(disk.dk_fd, secsize * altgp, SEEK_SET);
629 	if (off == secsize * altgp)
630 		len = write(disk.dk_fd, &gp, gpbytes);
631 	else
632 		len = -1;
633 
634 	if (len == -1 || len != gpbytes) {
635 		errno = EIO;
636 		return -1;
637 	}
638 
639 	/* Refresh in-kernel disklabel from the updated disk information. */
640 	ioctl(disk.dk_fd, DIOCRLDINFO, 0);
641 
642 	return 0;
643 }
644 
645 int
646 gp_lba_start_cmp(const void *e1, const void *e2)
647 {
648 	struct gpt_partition	*p1 = *(struct gpt_partition **)e1;
649 	struct gpt_partition	*p2 = *(struct gpt_partition **)e2;
650 	uint64_t		 o1;
651 	uint64_t		 o2;
652 
653 	o1 = letoh64(p1->gp_lba_start);
654 	o2 = letoh64(p2->gp_lba_start);
655 
656 	if (o1 < o2)
657 		return -1;
658 	else if (o1 > o2)
659 		return 1;
660 	else
661 		return 0;
662 }
663 
664 struct gpt_partition **
665 sort_gpt(void)
666 {
667 	static struct gpt_partition	*sgp[NGPTPARTITIONS+2];
668 	unsigned int			 i, j;
669 
670 	memset(sgp, 0, sizeof(sgp));
671 
672 	j = 0;
673 	for (i = 0; i < letoh32(gh.gh_part_num); i++) {
674 		if (letoh64(gp[i].gp_lba_start) >= letoh64(gh.gh_lba_start))
675 			sgp[j++] = &gp[i];
676 	}
677 
678 	if (j > 1) {
679 		if (mergesort(sgp, j, sizeof(sgp[0]), gp_lba_start_cmp) == -1) {
680 			printf("unable to sort gpt by lba start\n");
681 			return NULL;
682 		}
683 	}
684 
685 	return sgp;
686 }
687 
688 int
689 lba_free(uint64_t *start, uint64_t *end)
690 {
691 	struct gpt_partition	**sgp;
692 	uint64_t		  bs, bigbs, nextbs, ns;
693 	unsigned int		  i;
694 
695 	sgp = sort_gpt();
696 	if (sgp == NULL)
697 		return -1;
698 
699 	bs = letoh64(gh.gh_lba_start);
700 	ns = letoh64(gh.gh_lba_end) - bs + 1;
701 
702 	if (sgp[0] != NULL) {
703 		bigbs = bs;
704 		ns = 0;
705 		for (i = 0; sgp[i] != NULL; i++) {
706 			nextbs = letoh64(sgp[i]->gp_lba_start);
707 			if (bs < nextbs && ns < nextbs - bs) {
708 				ns = nextbs - bs;
709 				bigbs = bs;
710 			}
711 			bs = letoh64(sgp[i]->gp_lba_end) + 1;
712 		}
713 		nextbs = letoh64(gh.gh_lba_end) + 1;
714 		if (bs < nextbs && ns < nextbs - bs) {
715 			ns = nextbs - bs;
716 			bigbs = bs;
717 		}
718 		bs = bigbs;
719 	}
720 
721 	if (ns == 0)
722 		return -1;
723 
724 	if (start != NULL)
725 		*start = bs;
726 	if (end != NULL)
727 		*end = bs + ns - 1;
728 
729 	return 0;
730 }
731 
732 int
733 GPT_get_lba_start(const unsigned int pn)
734 {
735 	uint64_t		bs;
736 	unsigned int		i;
737 	int			rslt;
738 
739 	bs = letoh64(gh.gh_lba_start);
740 
741 	if (letoh64(gp[pn].gp_lba_start) >= bs) {
742 		bs = letoh64(gp[pn].gp_lba_start);
743 	} else {
744 		rslt = lba_free(&bs, NULL);
745 		if (rslt == -1) {
746 			printf("no space for partition %u\n", pn);
747 			return -1;
748 		}
749 	}
750 
751 	bs = getuint64("Partition offset", bs, letoh64(gh.gh_lba_start),
752 	    letoh64(gh.gh_lba_end));
753 
754 	for (i = 0; i < letoh32(gh.gh_part_num); i++) {
755 		if (i == pn)
756 			continue;
757 		if (bs >= letoh64(gp[i].gp_lba_start) &&
758 		    bs <= letoh64(gp[i].gp_lba_end)) {
759 			printf("partition %u can't start inside partition %u\n",
760 			    pn, i);
761 			return -1;
762 		}
763 	}
764 
765 	gp[pn].gp_lba_start = htole64(bs);
766 
767 	return 0;
768 }
769 
770 int
771 GPT_get_lba_end(const unsigned int pn)
772 {
773 	struct gpt_partition	**sgp;
774 	uint64_t		  bs, nextbs, ns;
775 	unsigned int		  i;
776 
777 	sgp = sort_gpt();
778 	if (sgp == NULL)
779 		return -1;
780 
781 	bs = letoh64(gp[pn].gp_lba_start);
782 	ns = letoh64(gh.gh_lba_end) - bs + 1;
783 	for (i = 0; sgp[i] != NULL; i++) {
784 		nextbs = letoh64(sgp[i]->gp_lba_start);
785 		if (nextbs > bs) {
786 			ns = nextbs - bs;
787 			break;
788 		}
789 	}
790 	ns = getuint64("Partition size", ns, 1, ns);
791 
792 	gp[pn].gp_lba_end = htole64(bs + ns - 1);
793 
794 	return 0;
795 }
796 
797 /*
798  * Adapted from Hacker's Delight crc32b().
799  *
800  * To quote http://www.hackersdelight.org/permissions.htm :
801  *
802  * "You are free to use, copy, and distribute any of the code on
803  *  this web site, whether modified by you or not. You need not give
804  *  attribution. This includes the algorithms (some of which appear
805  *  in Hacker's Delight), the Hacker's Assistant, and any code submitted
806  *  by readers. Submitters implicitly agree to this."
807  */
808 uint32_t
809 crc32(const u_char *buf, const uint32_t size)
810 {
811 	int			j;
812 	uint32_t		i, byte, crc, mask;
813 
814 	crc = 0xFFFFFFFF;
815 
816 	for (i = 0; i < size; i++) {
817 		byte = buf[i];			/* Get next byte. */
818 		crc = crc ^ byte;
819 		for (j = 7; j >= 0; j--) {	/* Do eight times. */
820 			mask = -(crc & 1);
821 			crc = (crc >> 1) ^ (0xEDB88320 & mask);
822 		}
823 	}
824 
825 	return ~crc;
826 }
827