xref: /openbsd-src/sbin/fdisk/gpt.c (revision b7c9a52ed4f75fcfedd11a409658b69ac06a7120)
1*b7c9a52eSkrw /*	$OpenBSD: gpt.c,v 1.95 2024/12/24 21:34:23 krw Exp $	*/
21cd24417Skrw /*
31cd24417Skrw  * Copyright (c) 2015 Markus Muller <mmu@grummel.net>
41cd24417Skrw  * Copyright (c) 2015 Kenneth R Westerback <krw@openbsd.org>
51cd24417Skrw  *
61cd24417Skrw  * Permission to use, copy, modify, and distribute this software for any
71cd24417Skrw  * purpose with or without fee is hereby granted, provided that the above
81cd24417Skrw  * copyright notice and this permission notice appear in all copies.
91cd24417Skrw  *
101cd24417Skrw  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
111cd24417Skrw  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
121cd24417Skrw  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
131cd24417Skrw  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
141cd24417Skrw  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
151cd24417Skrw  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
161cd24417Skrw  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
171cd24417Skrw  */
181cd24417Skrw 
19729290c0Skrw #include <sys/param.h>	/* DEV_BSIZE */
201cd24417Skrw #include <sys/disklabel.h>
211cd24417Skrw #include <sys/dkio.h>
221cd24417Skrw #include <sys/ioctl.h>
23729290c0Skrw 
242cdacd27Skrw #include <ctype.h>
25605b5690Skrw #include <err.h>
26fba7235cSkrw #include <stdio.h>
27dfcac45eSkrw #include <stdint.h>
281cd24417Skrw #include <stdlib.h>
291cd24417Skrw #include <string.h>
301cd24417Skrw #include <uuid.h>
311cd24417Skrw 
32199eafeaSkrw #include "part.h"
331cd24417Skrw #include "disk.h"
34afd1db78Skrw #include "mbr.h"
351cd24417Skrw #include "misc.h"
361cd24417Skrw #include "gpt.h"
371cd24417Skrw 
38fba7235cSkrw #ifdef DEBUG
39fba7235cSkrw #define DPRINTF(x...)	printf(x)
40fba7235cSkrw #else
41fba7235cSkrw #define DPRINTF(x...)
42fba7235cSkrw #endif
43fba7235cSkrw 
44dfcac45eSkrw struct mbr		gmbr;
451cd24417Skrw struct gpt_header	gh;
461cd24417Skrw struct gpt_partition	gp[NGPTPARTITIONS];
471cd24417Skrw 
488c6d22aeSkrw const struct gpt_partition * const *sort_gpt(void);
4938678f29Skrw int			  lba_free(uint64_t *, uint64_t *);
50534ba684Skrw int			  add_partition(const uint8_t *, const char *, uint64_t);
5165deb39bSkrw int			  find_partition(const uint8_t *);
520cd9e2afSkrw int			  get_header(const uint64_t);
53032115fcSkrw int			  get_partition_table(void);
544b0ba6ccSkrw int			  init_gh(void);
55199eafeaSkrw int			  init_gp(const int);
56543185b3Skrw uint32_t		  crc32(const u_char *, const uint32_t);
57dfcac45eSkrw int			  protective_mbr(const struct mbr *);
58dfcac45eSkrw int			  gpt_chk_mbr(struct dos_partition *, uint64_t);
59a66d82b0Skrw void			  string_to_name(const unsigned int, const char *);
60a66d82b0Skrw const char		 *name_to_string(const unsigned int);
61a66d82b0Skrw 
62a66d82b0Skrw void
63a66d82b0Skrw string_to_name(const unsigned int pn, const char *ch)
64a66d82b0Skrw {
65a66d82b0Skrw 	unsigned int			i;
66a66d82b0Skrw 
67a66d82b0Skrw 	memset(gp[pn].gp_name, 0, sizeof(gp[pn].gp_name));
68a66d82b0Skrw 
69*b7c9a52eSkrw 	for (i = 0; i < nitems(gp[pn].gp_name) && ch[i] != '\0'; i++)
70a66d82b0Skrw 		gp[pn].gp_name[i] = htole16((unsigned int)ch[i]);
71a66d82b0Skrw }
72a66d82b0Skrw 
73a66d82b0Skrw const char *
74a66d82b0Skrw name_to_string(const unsigned int pn)
75a66d82b0Skrw {
76a66d82b0Skrw 	static char		name[GPTPARTNAMESIZE + 1];
77a66d82b0Skrw 	unsigned int		i;
78a66d82b0Skrw 
7944612db3Skrw 	for (i = 0; i < GPTPARTNAMESIZE && gp[pn].gp_name[i] != 0; i++)
80a66d82b0Skrw 		name[i] = letoh16(gp[pn].gp_name[i]) & 0x7F;
8144612db3Skrw 	name[i] = '\0';
82a66d82b0Skrw 
83a66d82b0Skrw 	return name;
84a66d82b0Skrw }
85dfcac45eSkrw 
86dfcac45eSkrw /*
87dfcac45eSkrw  * Return the index into dp[] of the EFI GPT (0xEE) partition, or -1 if no such
88dfcac45eSkrw  * partition exists.
89dfcac45eSkrw  *
90dfcac45eSkrw  * Taken from kern/subr_disk.c.
91dfcac45eSkrw  *
92dfcac45eSkrw  */
93dfcac45eSkrw int
94dfcac45eSkrw gpt_chk_mbr(struct dos_partition *dp, u_int64_t dsize)
95dfcac45eSkrw {
96dfcac45eSkrw 	struct dos_partition	*dp2;
97dfcac45eSkrw 	int			 efi, eficnt, found, i;
98dfcac45eSkrw 	uint32_t		 psize;
99dfcac45eSkrw 
100dfcac45eSkrw 	found = efi = eficnt = 0;
101dfcac45eSkrw 	for (dp2 = dp, i = 0; i < NDOSPART; i++, dp2++) {
102dfcac45eSkrw 		if (dp2->dp_typ == DOSPTYP_UNUSED)
103dfcac45eSkrw 			continue;
104dfcac45eSkrw 		found++;
105dfcac45eSkrw 		if (dp2->dp_typ != DOSPTYP_EFI)
106dfcac45eSkrw 			continue;
107dfcac45eSkrw 		if (letoh32(dp2->dp_start) != GPTSECTOR)
108dfcac45eSkrw 			continue;
109dfcac45eSkrw 		psize = letoh32(dp2->dp_size);
110dfcac45eSkrw 		if (psize <= (dsize - GPTSECTOR) || psize == UINT32_MAX) {
111dfcac45eSkrw 			efi = i;
112dfcac45eSkrw 			eficnt++;
113dfcac45eSkrw 		}
114dfcac45eSkrw 	}
115dfcac45eSkrw 	if (found == 1 && eficnt == 1)
116dfcac45eSkrw 		return efi;
117dfcac45eSkrw 
118dfcac45eSkrw 	return -1;
119dfcac45eSkrw }
120dfcac45eSkrw 
121dfcac45eSkrw int
122dfcac45eSkrw protective_mbr(const struct mbr *mbr)
123dfcac45eSkrw {
124dfcac45eSkrw 	struct dos_partition	dp[NDOSPART], dos_partition;
125c5431474Skrw 	unsigned int		i;
126dfcac45eSkrw 
127dfcac45eSkrw 	if (mbr->mbr_lba_self != 0)
128dfcac45eSkrw 		return -1;
129dfcac45eSkrw 
130c5431474Skrw 	for (i = 0; i < nitems(dp); i++) {
131c5431474Skrw 		memset(&dos_partition, 0, sizeof(dos_partition));
132c5431474Skrw 		if (i < nitems(mbr->mbr_prt))
133614d0f20Skrw 			PRT_prt_to_dp(&mbr->mbr_prt[i], mbr->mbr_lba_self,
13499e0469cSkrw 			    mbr->mbr_lba_firstembr, &dos_partition);
135dfcac45eSkrw 		memcpy(&dp[i], &dos_partition, sizeof(dp[i]));
136dfcac45eSkrw 	}
137dfcac45eSkrw 
138dfcac45eSkrw 	return gpt_chk_mbr(dp, DL_GETDSIZE(&dl));
139dfcac45eSkrw }
140c2d03168Skrw 
1411cd24417Skrw int
1420cd9e2afSkrw get_header(const uint64_t sector)
143fba7235cSkrw {
144ad6c5e72Skrw 	struct gpt_header	 legh;
145b7307f09Skrw 	uint64_t		 gpbytes, gpsectors, lba_end;
146fba7235cSkrw 
147adf35c80Skrw 	if (DISK_readbytes(&legh, sector, sizeof(legh)))
14839351f7aSkrw 		return -1;
149fba7235cSkrw 
150ad6c5e72Skrw 	gh.gh_sig = letoh64(legh.gh_sig);
151ad6c5e72Skrw 	if (gh.gh_sig != GPTSIGNATURE) {
152fba7235cSkrw 		DPRINTF("gpt signature: expected 0x%llx, got 0x%llx\n",
153ad6c5e72Skrw 		    GPTSIGNATURE, gh.gh_sig);
15439351f7aSkrw 		return -1;
155fba7235cSkrw 	}
156fba7235cSkrw 
157ad6c5e72Skrw 	gh.gh_rev = letoh32(legh.gh_rev);
158ad6c5e72Skrw 	if (gh.gh_rev != GPTREVISION) {
159fba7235cSkrw 		DPRINTF("gpt revision: expected 0x%x, got 0x%x\n",
160ad6c5e72Skrw 		    GPTREVISION, gh.gh_rev);
16139351f7aSkrw 		return -1;
162fba7235cSkrw 	}
163fba7235cSkrw 
164ad6c5e72Skrw 	gh.gh_lba_self = letoh64(legh.gh_lba_self);
165ad6c5e72Skrw 	if (gh.gh_lba_self != sector) {
1660cd9e2afSkrw 		DPRINTF("gpt self lba: expected %llu, got %llu\n",
167ad6c5e72Skrw 		    sector, gh.gh_lba_self);
16839351f7aSkrw 		return -1;
169fba7235cSkrw 	}
170fba7235cSkrw 
171ad6c5e72Skrw 	gh.gh_size = letoh32(legh.gh_size);
172ad6c5e72Skrw 	if (gh.gh_size != GPTMINHDRSIZE) {
173fba7235cSkrw 		DPRINTF("gpt header size: expected %u, got %u\n",
174ad6c5e72Skrw 		    GPTMINHDRSIZE, gh.gh_size);
17539351f7aSkrw 		return -1;
176fba7235cSkrw 	}
177fba7235cSkrw 
178ad6c5e72Skrw 	gh.gh_part_size = letoh32(legh.gh_part_size);
179ad6c5e72Skrw 	if (gh.gh_part_size != GPTMINPARTSIZE) {
180fba7235cSkrw 		DPRINTF("gpt partition size: expected %u, got %u\n",
181ad6c5e72Skrw 		    GPTMINPARTSIZE, gh.gh_part_size);
18239351f7aSkrw 		return -1;
183fba7235cSkrw 	}
184fba7235cSkrw 
185ad6c5e72Skrw 	if ((dl.d_secsize % gh.gh_part_size) != 0) {
1866c525197Skrw 		DPRINTF("gpt sector size %% partition size (%u %% %u) != 0\n",
187ad6c5e72Skrw 		    dl.d_secsize, gh.gh_part_size);
1886c525197Skrw 		return -1;
1896c525197Skrw 	}
1906c525197Skrw 
191ad6c5e72Skrw 	gh.gh_part_num = letoh32(legh.gh_part_num);
192ad6c5e72Skrw 	if (gh.gh_part_num > NGPTPARTITIONS) {
193fba7235cSkrw 		DPRINTF("gpt partition count: expected <= %u, got %u\n",
194ad6c5e72Skrw 		    NGPTPARTITIONS, gh.gh_part_num);
19539351f7aSkrw 		return -1;
196fba7235cSkrw 	}
197fba7235cSkrw 
198ad6c5e72Skrw 	gh.gh_csum = letoh32(legh.gh_csum);
199ad6c5e72Skrw 	legh.gh_csum = 0;
200ad6c5e72Skrw 	legh.gh_csum = crc32((unsigned char *)&legh, gh.gh_size);
201ad6c5e72Skrw 	if (legh.gh_csum != gh.gh_csum) {
202fba7235cSkrw 		DPRINTF("gpt header checksum: expected 0x%x, got 0x%x\n",
203ad6c5e72Skrw 		    legh.gh_csum, gh.gh_csum);
2044b0a6a04Skrw 		/* Accept wrong-endian checksum. */
205ad6c5e72Skrw 		if (swap32(legh.gh_csum) != gh.gh_csum)
20639351f7aSkrw 			return -1;
207fba7235cSkrw 	}
208fba7235cSkrw 
209ad6c5e72Skrw 	gpbytes = gh.gh_part_num * gh.gh_part_size;
210b7307f09Skrw 	gpsectors = (gpbytes + dl.d_secsize - 1) / dl.d_secsize;
211b7307f09Skrw 	lba_end = DL_GETDSIZE(&dl) - gpsectors - 2;
212ad6c5e72Skrw 
213ad6c5e72Skrw 	gh.gh_lba_end = letoh64(legh.gh_lba_end);
214ad6c5e72Skrw 	if (gh.gh_lba_end > lba_end) {
2157345db1eSkrw 		DPRINTF("gpt last usable LBA: reduced from %llu to %llu\n",
216ad6c5e72Skrw 		    gh.gh_lba_end, lba_end);
217ad6c5e72Skrw 		gh.gh_lba_end = lba_end;
218fba7235cSkrw 	}
219fba7235cSkrw 
220ad6c5e72Skrw 	gh.gh_lba_start = letoh64(legh.gh_lba_start);
221ad6c5e72Skrw 	if (gh.gh_lba_start >= gh.gh_lba_end) {
222fba7235cSkrw 		DPRINTF("gpt first usable LBA: expected < %llu, got %llu\n",
223ad6c5e72Skrw 		    gh.gh_lba_end, gh.gh_lba_start);
22439351f7aSkrw 		return -1;
225fba7235cSkrw 	}
226fba7235cSkrw 
227ad6c5e72Skrw 	gh.gh_part_lba = letoh64(legh.gh_part_lba);
228ef761e99Skrw 	if (gh.gh_lba_self == GPTSECTOR) {
229ef761e99Skrw 		if (gh.gh_part_lba <= GPTSECTOR) {
230ef761e99Skrw 			DPRINTF("gpt partition entries start: expected > %u, "
231ef761e99Skrw 			    "got %llu\n", GPTSECTOR, gh.gh_part_lba);
23239351f7aSkrw 			return -1;
233fba7235cSkrw 		}
234ef761e99Skrw 		if (gh.gh_part_lba + gpsectors > gh.gh_lba_start) {
235ef761e99Skrw 			DPRINTF("gpt partition entries end: expected < %llu, "
236ef761e99Skrw 			    "got %llu\n", gh.gh_lba_start,
237ef761e99Skrw 			    gh.gh_part_lba + gpsectors);
23839351f7aSkrw 			return -1;
239fba7235cSkrw 		}
240ef761e99Skrw 	} else {
241ef761e99Skrw 		if (gh.gh_part_lba <= gh.gh_lba_end) {
242ef761e99Skrw 			DPRINTF("gpt partition entries start: expected > %llu, "
243ef761e99Skrw 			    "got %llu\n", gh.gh_lba_end, gh.gh_part_lba);
244ef761e99Skrw 			return -1;
245ef761e99Skrw 		}
246ef761e99Skrw 		if (gh.gh_part_lba + gpsectors > gh.gh_lba_self) {
247ef761e99Skrw 			DPRINTF("gpt partition entries end: expected < %llu, "
248ef761e99Skrw 			    "got %llu\n", gh.gh_lba_self,
249ef761e99Skrw 			    gh.gh_part_lba + gpsectors);
250ef761e99Skrw 			return -1;
251ef761e99Skrw 		}
252ef761e99Skrw 	}
253ad6c5e72Skrw 
254ad6c5e72Skrw 	gh.gh_lba_alt = letoh32(legh.gh_lba_alt);
255ad6c5e72Skrw 	gh.gh_part_csum = letoh32(legh.gh_part_csum);
256ad6c5e72Skrw 	gh.gh_rsvd = letoh32(legh.gh_rsvd);	/* Should always be 0. */
257ad6c5e72Skrw 	uuid_dec_le(&legh.gh_guid, &gh.gh_guid);
258ad6c5e72Skrw 
2592a536aa2Skrw 	return 0;
260fba7235cSkrw }
261fba7235cSkrw 
262fba7235cSkrw int
263032115fcSkrw get_partition_table(void)
264fba7235cSkrw {
2652de77560Skrw 	struct gpt_partition	*legp;
266adf35c80Skrw 	uint64_t		 gpbytes;
2672de77560Skrw 	unsigned int		 pn;
2682de77560Skrw 	int			 rslt = -1;
2696c525197Skrw 	uint32_t		 gh_part_csum;
270fba7235cSkrw 
271fba7235cSkrw 	DPRINTF("gpt partition table being read from LBA %llu\n",
272ad6c5e72Skrw 	    gh.gh_part_lba);
273fba7235cSkrw 
274ad6c5e72Skrw 	gpbytes = gh.gh_part_num * gh.gh_part_size;
275fba7235cSkrw 
2762de77560Skrw 	legp = calloc(1, gpbytes);
2772de77560Skrw 	if (legp == NULL)
2782de77560Skrw 		err(1, "legp");
279fba7235cSkrw 
2802de77560Skrw 	if (DISK_readbytes(legp, gh.gh_part_lba, gpbytes))
2812de77560Skrw 		goto done;
2822de77560Skrw 	gh_part_csum = crc32((unsigned char *)legp, gpbytes);
2832de77560Skrw 
2844b0a6a04Skrw 	if (gh_part_csum != gh.gh_part_csum) {
285461ffcadSkrw 		DPRINTF("gpt partition table checksum: expected 0x%x, "
286ad6c5e72Skrw 		    "got 0x%x\n", gh.gh_part_csum, gh_part_csum);
2874b0a6a04Skrw 		/* Accept wrong-endian checksum. */
2884b0a6a04Skrw 		if (swap32(gh_part_csum) != gh.gh_part_csum)
2892de77560Skrw 			goto done;
290fba7235cSkrw 	}
291fba7235cSkrw 
2922de77560Skrw 	memset(&gp, 0, sizeof(gp));
2932de77560Skrw 	for (pn = 0; pn < gh.gh_part_num; pn++) {
2942de77560Skrw 		uuid_dec_le(&legp[pn].gp_type, &gp[pn].gp_type);
2952de77560Skrw 		uuid_dec_le(&legp[pn].gp_guid, &gp[pn].gp_guid);
2962de77560Skrw 		gp[pn].gp_lba_start = letoh64(legp[pn].gp_lba_start);
2972de77560Skrw 		gp[pn].gp_lba_end = letoh64(legp[pn].gp_lba_end);
2982de77560Skrw 		gp[pn].gp_attrs = letoh64(legp[pn].gp_attrs);
2992de77560Skrw 		memcpy(gp[pn].gp_name, legp[pn].gp_name,
3002de77560Skrw 		    sizeof(gp[pn].gp_name));
3012de77560Skrw 	}
3022de77560Skrw 	rslt = 0;
3032de77560Skrw 
3042de77560Skrw  done:
3052de77560Skrw 	free(legp);
3062de77560Skrw 	return rslt;
307fba7235cSkrw }
308fba7235cSkrw 
309afd1db78Skrw int
310859be6c9Skrw GPT_read(const int which)
311fba7235cSkrw {
312afd1db78Skrw 	int			error;
313afd1db78Skrw 
314dfcac45eSkrw 	error = MBR_read(0, 0, &gmbr);
315afd1db78Skrw 	if (error)
316afd1db78Skrw 		goto done;
31742cf3a9bSkrw 	error = protective_mbr(&gmbr);
31842cf3a9bSkrw 	if (error == -1)
31942cf3a9bSkrw 		goto done;
320fba7235cSkrw 
321dcd4cc8aSkrw 	switch (which) {
322dcd4cc8aSkrw 	case PRIMARYGPT:
323afd1db78Skrw 		error = get_header(GPTSECTOR);
324dcd4cc8aSkrw 		break;
325dcd4cc8aSkrw 	case SECONDARYGPT:
326afd1db78Skrw 		error = get_header(DL_GETDSIZE(&dl) - 1);
327dcd4cc8aSkrw 		break;
328dcd4cc8aSkrw 	case ANYGPT:
329afd1db78Skrw 		error = get_header(GPTSECTOR);
330afd1db78Skrw 		if (error != 0 || get_partition_table() != 0)
331afd1db78Skrw 			error = get_header(DL_GETDSIZE(&dl) - 1);
332dcd4cc8aSkrw 		break;
333dcd4cc8aSkrw 	default:
334afd1db78Skrw 		return -1;
335dcd4cc8aSkrw 	}
336dcd4cc8aSkrw 
337afd1db78Skrw 	if (error == 0)
338afd1db78Skrw 		error = get_partition_table();
339fba7235cSkrw 
340afd1db78Skrw  done:
341afd1db78Skrw 	if (error != 0) {
3420df3379fSkrw 		/* No valid GPT found. Zap any artifacts. */
343dfcac45eSkrw 		memset(&gmbr, 0, sizeof(gmbr));
3440df3379fSkrw 		memset(&gh, 0, sizeof(gh));
3450df3379fSkrw 		memset(&gp, 0, sizeof(gp));
346fba7235cSkrw 	}
347afd1db78Skrw 
348afd1db78Skrw 	return error;
349dcd4cc8aSkrw }
350fba7235cSkrw 
351fba7235cSkrw void
352859be6c9Skrw GPT_print(const char *units, const int verbosity)
353fba7235cSkrw {
3543e9b7d6bSkrw 	const struct unit_type	*ut;
3553e9b7d6bSkrw 	const int		 secsize = dl.d_secsize;
356fba7235cSkrw 	char			*guidstr = NULL;
357fba7235cSkrw 	double			 size;
358db53e521Skrw 	unsigned int		 pn;
359351f0068Skrw 	uint32_t		 status;
360fba7235cSkrw 
3612cdacd27Skrw #ifdef	DEBUG
3622cdacd27Skrw 	char			*p;
363e42a96d8Skrw 	uint64_t		 sig;
364db53e521Skrw 	unsigned int		 i;
3652cdacd27Skrw 
366ad6c5e72Skrw 	sig = htole64(gh.gh_sig);
367ad6c5e72Skrw 	p = (char *)&sig;
3682cdacd27Skrw 
3692cdacd27Skrw 	printf("gh_sig         : ");
370ad6c5e72Skrw 	for (i = 0; i < sizeof(sig); i++)
3712cdacd27Skrw 		printf("%c", isprint((unsigned char)p[i]) ? p[i] : '?');
3722cdacd27Skrw 	printf(" (");
373ad6c5e72Skrw 	for (i = 0; i < sizeof(sig); i++) {
3742cdacd27Skrw 		printf("%02x", p[i]);
375ad6c5e72Skrw 		if ((i + 1) < sizeof(sig))
3762cdacd27Skrw 			printf(":");
3772cdacd27Skrw 	}
3782cdacd27Skrw 	printf(")\n");
379ad6c5e72Skrw 	printf("gh_rev         : %u\n", gh.gh_rev);
380ad6c5e72Skrw 	printf("gh_size        : %u (%zd)\n", gh.gh_size, sizeof(gh));
381ad6c5e72Skrw 	printf("gh_csum        : 0x%x\n", gh.gh_csum);
382ad6c5e72Skrw 	printf("gh_rsvd        : %u\n", gh.gh_rsvd);
383ad6c5e72Skrw 	printf("gh_lba_self    : %llu\n", gh.gh_lba_self);
384ad6c5e72Skrw 	printf("gh_lba_alt     : %llu\n", gh.gh_lba_alt);
385ad6c5e72Skrw 	printf("gh_lba_start   : %llu\n", gh.gh_lba_start);
386ad6c5e72Skrw 	printf("gh_lba_end     : %llu\n", gh.gh_lba_end);
3872cdacd27Skrw 	p = NULL;
3882cdacd27Skrw 	uuid_to_string(&gh.gh_guid, &p, &status);
3892cdacd27Skrw 	printf("gh_gh_guid     : %s\n", (status == uuid_s_ok) ? p : "<invalid>");
3902cdacd27Skrw 	free(p);
391ad6c5e72Skrw 	printf("gh_gh_part_lba : %llu\n", gh.gh_part_lba);
392ad6c5e72Skrw 	printf("gh_gh_part_num : %u (%zu)\n", gh.gh_part_num, nitems(gp));
393ad6c5e72Skrw 	printf("gh_gh_part_size: %u (%zu)\n", gh.gh_part_size, sizeof(gp[0]));
394ad6c5e72Skrw 	printf("gh_gh_part_csum: 0x%x\n", gh.gh_part_csum);
3952cdacd27Skrw 	printf("\n");
3962cdacd27Skrw #endif	/* DEBUG */
3972cdacd27Skrw 
3983e9b7d6bSkrw 	size = units_size(units, DL_GETDSIZE(&dl), &ut);
39977db81deSkrw 	printf("Disk: %s       Usable LBA: %llu to %llu [%.0f ",
400ad6c5e72Skrw 	    disk.dk_name, gh.gh_lba_start, gh.gh_lba_end, size);
4013e9b7d6bSkrw 	if (ut->ut_conversion == 0 && secsize != DEV_BSIZE)
402fba7235cSkrw 		printf("%d-byte ", secsize);
4033e9b7d6bSkrw 	printf("%s]\n", ut->ut_lname);
404fba7235cSkrw 
4053bbc645fSkrw 	if (verbosity == VERBOSE) {
40677db81deSkrw 		printf("GUID: ");
407ad6c5e72Skrw 		uuid_to_string(&gh.gh_guid, &guidstr, &status);
40877db81deSkrw 		if (status == uuid_s_ok)
40977db81deSkrw 			printf("%s\n", guidstr);
41077db81deSkrw 		else
41177db81deSkrw 			printf("<invalid header GUID>\n");
41277db81deSkrw 		free(guidstr);
41377db81deSkrw 	}
414fba7235cSkrw 
41577db81deSkrw 	GPT_print_parthdr(verbosity);
4162de77560Skrw 	for (pn = 0; pn < gh.gh_part_num; pn++) {
4172de77560Skrw 		if (uuid_is_nil(&gp[pn].gp_type, NULL))
418fba7235cSkrw 			continue;
4192de77560Skrw 		GPT_print_part(pn, units, verbosity);
420fba7235cSkrw 	}
421fba7235cSkrw }
422fba7235cSkrw 
423fba7235cSkrw void
424859be6c9Skrw GPT_print_parthdr(const int verbosity)
425fba7235cSkrw {
42677db81deSkrw 	printf("   #: type                                "
42777db81deSkrw 	    " [       start:         size ]\n");
4283bbc645fSkrw 	if (verbosity == VERBOSE)
42977db81deSkrw 		printf("      guid                                 name\n");
43077db81deSkrw 	printf("--------------------------------------------------------"
43177db81deSkrw 	    "----------------\n");
432fba7235cSkrw }
433fba7235cSkrw 
434fba7235cSkrw void
435eea9397aSkrw GPT_print_part(const unsigned int pn, const char *units, const int verbosity)
436fba7235cSkrw {
4373e9b7d6bSkrw 	const struct unit_type	*ut;
438fba7235cSkrw 	char			*guidstr = NULL;
439fba7235cSkrw 	double			 size;
44095a0034fSkrw 	uint64_t		 attrs, end, start;
441351f0068Skrw 	uint32_t		 status;
442fba7235cSkrw 
443ccf52da1Skrw 	start = gp[pn].gp_lba_start;
444ccf52da1Skrw 	end = gp[pn].gp_lba_end;
445ccf52da1Skrw 	size = units_size(units, (start > end) ? 0 : end - start + 1, &ut);
446ccf52da1Skrw 
44795a0034fSkrw 	printf(" %3u: %-36s [%12lld: %12.0f%s]\n", pn,
448d8f6db85Skrw 	    PRT_uuid_to_desc(&gp[pn].gp_type), start, size, ut->ut_abbr);
449fba7235cSkrw 
4503bbc645fSkrw 	if (verbosity == VERBOSE) {
4512de77560Skrw 		uuid_to_string(&gp[pn].gp_guid, &guidstr, &status);
452fba7235cSkrw 		if (status != uuid_s_ok)
453fba7235cSkrw 			printf("      <invalid partition guid>             ");
454fba7235cSkrw 		else
45577db81deSkrw 			printf("      %-36s ", guidstr);
4568f546ed6Skrw 		printf("%s\n", name_to_string(pn));
457fba7235cSkrw 		free(guidstr);
45895a0034fSkrw 		attrs = gp[pn].gp_attrs;
45995a0034fSkrw 		if (attrs) {
46095a0034fSkrw 			printf("      Attributes: (0x%016llx) ", attrs);
46195a0034fSkrw 			if (attrs & GPTPARTATTR_REQUIRED)
46295a0034fSkrw 				printf("Required " );
46395a0034fSkrw 			if (attrs & GPTPARTATTR_IGNORE)
46495a0034fSkrw 				printf("Ignore ");
46595a0034fSkrw 			if (attrs & GPTPARTATTR_BOOTABLE)
46695a0034fSkrw 				printf("Bootable ");
4675f095273Skrw 			if (attrs & GPTPARTATTR_MS_READONLY)
468d0f0c904Skrw 				printf("MSReadOnly " );
4695f095273Skrw 			if (attrs & GPTPARTATTR_MS_SHADOW)
470d0f0c904Skrw 				printf("MSShadow ");
4715f095273Skrw 			if (attrs & GPTPARTATTR_MS_HIDDEN)
472d0f0c904Skrw 				printf("MSHidden ");
4735f095273Skrw 			if (attrs & GPTPARTATTR_MS_NOAUTOMOUNT)
474d0f0c904Skrw 				printf("MSNoAutoMount ");
47595a0034fSkrw 			printf("\n");
47695a0034fSkrw 		}
47777db81deSkrw 	}
478ccf52da1Skrw 
479c4840cb2Skrw 	if (uuid_is_nil(&gp[pn].gp_type, NULL) == 0) {
480ccf52da1Skrw 		if (start > end)
481ccf52da1Skrw 			printf("partition %u first LBA is > last LBA\n", pn);
482ccf52da1Skrw 		if (start < gh.gh_lba_start || end > gh.gh_lba_end)
483c4840cb2Skrw 			printf("partition %u extends beyond usable LBA range "
484c4840cb2Skrw 			    "of %s\n", pn, disk.dk_name);
485c4840cb2Skrw 	}
486fba7235cSkrw }
487fba7235cSkrw 
488fba7235cSkrw int
48965deb39bSkrw find_partition(const uint8_t *beuuid)
49065deb39bSkrw {
4912de77560Skrw 	struct uuid		uuid;
4926c98a3deSkrw 	unsigned int		pn;
49365deb39bSkrw 
49465deb39bSkrw 	uuid_dec_be(beuuid, &uuid);
49565deb39bSkrw 
4966c98a3deSkrw 	for (pn = 0; pn < gh.gh_part_num; pn++) {
4972de77560Skrw 		if (uuid_compare(&gp[pn].gp_type, &uuid, NULL) == 0)
49865deb39bSkrw 			return pn;
49965deb39bSkrw 	}
50065deb39bSkrw 	return -1;
50165deb39bSkrw }
50265deb39bSkrw 
50365deb39bSkrw int
504534ba684Skrw add_partition(const uint8_t *beuuid, const char *name, uint64_t sectors)
505534ba684Skrw {
5062de77560Skrw 	struct uuid		uuid;
507534ba684Skrw 	int			rslt;
5085ef2e3e1Skrw 	uint64_t		end, freesectors, start;
5096c98a3deSkrw 	uint32_t		status, pn;
510534ba684Skrw 
511534ba684Skrw 	uuid_dec_be(beuuid, &uuid);
512534ba684Skrw 
5136c98a3deSkrw 	for (pn = 0; pn < gh.gh_part_num; pn++) {
514534ba684Skrw 		if (uuid_is_nil(&gp[pn].gp_type, NULL))
515534ba684Skrw 			break;
516534ba684Skrw 	}
5176c98a3deSkrw 	if (pn == gh.gh_part_num)
518534ba684Skrw 		goto done;
519534ba684Skrw 
520534ba684Skrw 	rslt = lba_free(&start, &end);
521534ba684Skrw 	if (rslt == -1)
522534ba684Skrw 		goto done;
523534ba684Skrw 
524893a2455Skrw 	if (start % BLOCKALIGNMENT)
525893a2455Skrw 		start += (BLOCKALIGNMENT - start % BLOCKALIGNMENT);
526534ba684Skrw 	if (start >= end)
527534ba684Skrw 		goto done;
528534ba684Skrw 
529534ba684Skrw 	freesectors = end - start + 1;
530534ba684Skrw 
531534ba684Skrw 	if (sectors == 0)
532534ba684Skrw 		sectors = freesectors;
533534ba684Skrw 
534534ba684Skrw 	if (freesectors < sectors)
535534ba684Skrw 		goto done;
536534ba684Skrw 	else if (freesectors > sectors)
537534ba684Skrw 		end = start + sectors - 1;
538534ba684Skrw 
5392de77560Skrw 	gp[pn].gp_type = uuid;
5402de77560Skrw 	gp[pn].gp_lba_start = start;
5412de77560Skrw 	gp[pn].gp_lba_end = end;
542a66d82b0Skrw 	string_to_name(pn, name);
543534ba684Skrw 
5442de77560Skrw 	uuid_create(&gp[pn].gp_guid, &status);
5452de77560Skrw 	if (status == uuid_s_ok)
546534ba684Skrw 		return 0;
547534ba684Skrw 
548534ba684Skrw  done:
5496c98a3deSkrw 	if (pn != gh.gh_part_num)
550534ba684Skrw 		memset(&gp[pn], 0, sizeof(gp[pn]));
551534ba684Skrw 	printf("unable to add %s\n", name);
55239351f7aSkrw 	return -1;
553534ba684Skrw }
554534ba684Skrw 
555534ba684Skrw int
5564b0ba6ccSkrw init_gh(void)
5571cd24417Skrw {
5584b0ba6ccSkrw 	struct gpt_header	oldgh;
5591629ab0bSkrw 	const int		secsize = dl.d_secsize;
5604b0ba6ccSkrw 	int			needed;
5611cd24417Skrw 	uint32_t		status;
5621cd24417Skrw 
5634b0ba6ccSkrw 	memcpy(&oldgh, &gh, sizeof(oldgh));
564f0d03845Skrw 	memset(&gh, 0, sizeof(gh));
565dfcac45eSkrw 	memset(&gmbr, 0, sizeof(gmbr));
566dfcac45eSkrw 
567dfcac45eSkrw 	/* XXX Do we need the boot code? UEFI spec & Apple says no. */
568f43a9f23Skrw 	memcpy(gmbr.mbr_code, default_dmbr.dmbr_boot, sizeof(gmbr.mbr_code));
569dfcac45eSkrw 	gmbr.mbr_prt[0].prt_id = DOSPTYP_EFI;
570dfcac45eSkrw 	gmbr.mbr_prt[0].prt_bs = 1;
571dfcac45eSkrw 	gmbr.mbr_prt[0].prt_ns = UINT32_MAX;
572dfcac45eSkrw 	gmbr.mbr_signature = DOSMBR_SIGNATURE;
5731cd24417Skrw 
5746bbd8ae9Skrw 	needed = sizeof(gp) / secsize + 2;
5754b0ba6ccSkrw 
576893a2455Skrw 	if (needed % BLOCKALIGNMENT)
577893a2455Skrw 		needed += (needed - (needed % BLOCKALIGNMENT));
5781cd24417Skrw 
579ad6c5e72Skrw 	gh.gh_sig = GPTSIGNATURE;
580ad6c5e72Skrw 	gh.gh_rev = GPTREVISION;
581ad6c5e72Skrw 	gh.gh_size = GPTMINHDRSIZE;
5821cd24417Skrw 	gh.gh_csum = 0;
5831cd24417Skrw 	gh.gh_rsvd = 0;
584ad6c5e72Skrw 	gh.gh_lba_self = 1;
585ad6c5e72Skrw 	gh.gh_lba_alt = DL_GETDSIZE(&dl) - 1;
586ad6c5e72Skrw 	gh.gh_lba_start = needed;
587ad6c5e72Skrw 	gh.gh_lba_end = DL_GETDSIZE(&dl) - needed;
588ad6c5e72Skrw 	uuid_create(&gh.gh_guid, &status);
5894b0ba6ccSkrw 	if (status != uuid_s_ok) {
5904b0ba6ccSkrw 		memcpy(&gh, &oldgh, sizeof(gh));
59139351f7aSkrw 		return -1;
5924b0ba6ccSkrw 	}
593ad6c5e72Skrw 	gh.gh_part_lba = 2;
594ad6c5e72Skrw 	gh.gh_part_num = NGPTPARTITIONS;
595ad6c5e72Skrw 	gh.gh_part_size = GPTMINPARTSIZE;
596ad6c5e72Skrw 	gh.gh_part_csum = 0;
5974b0ba6ccSkrw 
5984b0ba6ccSkrw 	return 0;
5994b0ba6ccSkrw }
6004b0ba6ccSkrw 
6014b0ba6ccSkrw int
602199eafeaSkrw init_gp(const int how)
6034b0ba6ccSkrw {
6048c10a749Skrw 	struct gpt_partition	oldgp[NGPTPARTITIONS];
6054b0ba6ccSkrw 	const uint8_t		gpt_uuid_efi_system[] = GPT_UUID_EFI_SYSTEM;
6064b0ba6ccSkrw 	const uint8_t		gpt_uuid_openbsd[] = GPT_UUID_OPENBSD;
60765deb39bSkrw 	uint64_t		prt_ns;
60895e8765cSkrw 	int			pn, rslt;
6094b0ba6ccSkrw 
6104b0ba6ccSkrw 	memcpy(&oldgp, &gp, sizeof(oldgp));
611c2fbfd6dSkrw 	if (how == GHANDGP)
612f0d03845Skrw 		memset(&gp, 0, sizeof(gp));
61395e8765cSkrw 	else {
614ad6c5e72Skrw 		for (pn = 0; pn < gh.gh_part_num; pn++) {
615490259bdSkrw 			if (PRT_protected_uuid(&gp[pn].gp_type) ||
61695a0034fSkrw 			    (gp[pn].gp_attrs & GPTPARTATTR_REQUIRED))
61795e8765cSkrw 				continue;
61895e8765cSkrw 			memset(&gp[pn], 0, sizeof(gp[pn]));
61995e8765cSkrw 		}
62095e8765cSkrw 	}
6211cd24417Skrw 
622534ba684Skrw 	rslt = 0;
623199eafeaSkrw 	if (disk.dk_bootprt.prt_ns > 0) {
62465deb39bSkrw 		pn = find_partition(gpt_uuid_efi_system);
62565deb39bSkrw 		if (pn == -1) {
62665deb39bSkrw 			rslt = add_partition(gpt_uuid_efi_system,
62765deb39bSkrw 			    "EFI System Area", disk.dk_bootprt.prt_ns);
62865deb39bSkrw 		} else {
62965deb39bSkrw 			prt_ns = gp[pn].gp_lba_end - gp[pn].gp_lba_start + 1;
63065deb39bSkrw 			if (prt_ns < disk.dk_bootprt.prt_ns) {
63165deb39bSkrw 				printf("EFI System Area < %llu sectors\n",
632199eafeaSkrw 				    disk.dk_bootprt.prt_ns);
63365deb39bSkrw 				rslt = -1;
63465deb39bSkrw 			}
63565deb39bSkrw 		}
6361cd24417Skrw 	}
637534ba684Skrw 	if (rslt == 0)
638534ba684Skrw 		rslt = add_partition(gpt_uuid_openbsd, "OpenBSD Area", 0);
639fba7235cSkrw 
6404b0ba6ccSkrw 	if (rslt != 0)
6414b0ba6ccSkrw 		memcpy(&gp, &oldgp, sizeof(gp));
6424b0ba6ccSkrw 
6434b0ba6ccSkrw 	return rslt;
6444b0ba6ccSkrw }
6454b0ba6ccSkrw 
6464b0ba6ccSkrw int
647199eafeaSkrw GPT_init(const int how)
6484b0ba6ccSkrw {
649c2fbfd6dSkrw 	int			rslt = 0;
6504b0ba6ccSkrw 
651c2fbfd6dSkrw 	if (how == GHANDGP)
6524b0ba6ccSkrw 		rslt = init_gh();
6534b0ba6ccSkrw 	if (rslt == 0)
654199eafeaSkrw 		rslt = init_gp(how);
6554b0ba6ccSkrw 
656534ba684Skrw 	return rslt;
6571cd24417Skrw }
6581cd24417Skrw 
659c94b0391Skrw void
660c94b0391Skrw GPT_zap_headers(void)
661c94b0391Skrw {
662adf35c80Skrw 	struct gpt_header	legh;
663c94b0391Skrw 
664adf35c80Skrw 	if (DISK_readbytes(&legh, GPTSECTOR, sizeof(legh)))
665c94b0391Skrw 		return;
666c94b0391Skrw 
667adf35c80Skrw 	if (letoh64(legh.gh_sig) == GPTSIGNATURE) {
668adf35c80Skrw 		memset(&legh, 0, sizeof(legh));
669adf35c80Skrw 		if (DISK_writebytes(&legh, GPTSECTOR, sizeof(legh)))
670605b5690Skrw 			DPRINTF("Unable to zap GPT header @ sector %d",
671605b5690Skrw 			    GPTSECTOR);
672c94b0391Skrw 	}
673c94b0391Skrw 
674adf35c80Skrw 	if (DISK_readbytes(&legh, DL_GETDSIZE(&dl) - 1, sizeof(legh)))
675c94b0391Skrw 		return;
676c94b0391Skrw 
677adf35c80Skrw 	if (letoh64(legh.gh_sig) == GPTSIGNATURE) {
678adf35c80Skrw 		memset(&legh, 0, GPTMINHDRSIZE);
679adf35c80Skrw 		if (DISK_writebytes(&legh, DL_GETDSIZE(&dl) - 1, sizeof(legh)))
680605b5690Skrw 			DPRINTF("Unable to zap GPT header @ sector %llu",
681605b5690Skrw 			    DL_GETDSIZE(&dl) - 1);
682c94b0391Skrw 	}
683c94b0391Skrw }
684c94b0391Skrw 
6851cd24417Skrw int
6860468c08fSkrw GPT_write(void)
6871cd24417Skrw {
688ad6c5e72Skrw 	struct gpt_header	 legh;
6892de77560Skrw 	struct gpt_partition	*legp;
690ad6c5e72Skrw 	uint64_t		 altgh, altgp;
691605b5690Skrw 	uint64_t		 gpbytes, gpsectors;
6922de77560Skrw 	unsigned int		 pn;
6932de77560Skrw 	int			 rslt = -1;
6941cd24417Skrw 
695605b5690Skrw 	if (MBR_write(&gmbr))
696605b5690Skrw 		return -1;
697dfcac45eSkrw 
698ad6c5e72Skrw 	gpbytes = gh.gh_part_num * gh.gh_part_size;
699b7307f09Skrw 	gpsectors = (gpbytes + dl.d_secsize - 1) / dl.d_secsize;
700153ceb9cSkrw 
7011cd24417Skrw 	altgh = DL_GETDSIZE(&dl) - 1;
702605b5690Skrw 	altgp = altgh - gpsectors;
7031cd24417Skrw 
704ad6c5e72Skrw 	legh.gh_sig = htole64(GPTSIGNATURE);
705ad6c5e72Skrw 	legh.gh_rev = htole32(GPTREVISION);
706ad6c5e72Skrw 	legh.gh_size = htole32(GPTMINHDRSIZE);
707ad6c5e72Skrw 	legh.gh_rsvd = 0;
708ad6c5e72Skrw 	legh.gh_lba_self = htole64(GPTSECTOR);
709ad6c5e72Skrw 	legh.gh_lba_alt = htole64(altgh);
710ad6c5e72Skrw 	legh.gh_lba_start = htole64(gh.gh_lba_start);
711ad6c5e72Skrw 	legh.gh_lba_end = htole64(gh.gh_lba_end);
712ad6c5e72Skrw 	uuid_enc_le(&legh.gh_guid, &gh.gh_guid);
713ad6c5e72Skrw 	legh.gh_part_lba = htole64(GPTSECTOR + 1);
714ad6c5e72Skrw 	legh.gh_part_num = htole32(gh.gh_part_num);
715ad6c5e72Skrw 	legh.gh_part_size = htole32(GPTMINPARTSIZE);
7162de77560Skrw 
7172de77560Skrw 	legp = calloc(1, gpbytes);
7182de77560Skrw 	if (legp == NULL)
7192de77560Skrw 		err(1, "legp");
7202de77560Skrw 
7212de77560Skrw 	for (pn = 0; pn < gh.gh_part_num; pn++) {
7222de77560Skrw 		uuid_enc_le(&legp[pn].gp_type, &gp[pn].gp_type);
7232de77560Skrw 		uuid_enc_le(&legp[pn].gp_guid, &gp[pn].gp_guid);
7242de77560Skrw 		legp[pn].gp_lba_start = htole64(gp[pn].gp_lba_start);
7252de77560Skrw 		legp[pn].gp_lba_end = htole64(gp[pn].gp_lba_end);
7262de77560Skrw 		legp[pn].gp_attrs = htole64(gp[pn].gp_attrs);
7272de77560Skrw 		memcpy(legp[pn].gp_name, gp[pn].gp_name,
7282de77560Skrw 		    sizeof(legp[pn].gp_name));
7292de77560Skrw 	}
7302de77560Skrw 	legh.gh_part_csum = htole32(crc32((unsigned char *)legp, gpbytes));
7312de77560Skrw 	legh.gh_csum = 0;
732ad6c5e72Skrw 	legh.gh_csum = htole32(crc32((unsigned char *)&legh, gh.gh_size));
733adf35c80Skrw 
734adf35c80Skrw 	if (DISK_writebytes(&legh, GPTSECTOR, gh.gh_size) ||
7352de77560Skrw 	    DISK_writebytes(legp, GPTSECTOR + 1, gpbytes))
7362de77560Skrw 		goto done;
7371cd24417Skrw 
738ad6c5e72Skrw 	legh.gh_lba_self = htole64(altgh);
739ad6c5e72Skrw 	legh.gh_lba_alt = htole64(GPTSECTOR);
740ad6c5e72Skrw 	legh.gh_part_lba = htole64(altgp);
741ad6c5e72Skrw 	legh.gh_csum = 0;
742ad6c5e72Skrw 	legh.gh_csum = htole32(crc32((unsigned char *)&legh, gh.gh_size));
7431cd24417Skrw 
744adf35c80Skrw 	if (DISK_writebytes(&legh, altgh, gh.gh_size) ||
745adf35c80Skrw 	    DISK_writebytes(&gp, altgp, gpbytes))
7462de77560Skrw 		goto done;
7471cd24417Skrw 
74834be3939Skrw 	/* Refresh in-kernel disklabel from the updated disk information. */
749605b5690Skrw 	if (ioctl(disk.dk_fd, DIOCRLDINFO, 0) == -1)
750605b5690Skrw 		warn("DIOCRLDINFO");
7512de77560Skrw 	rslt = 0;
75234be3939Skrw 
7532de77560Skrw  done:
7542de77560Skrw 	free(legp);
7552de77560Skrw 	return rslt;
7561cd24417Skrw }
757c2d03168Skrw 
758c2d03168Skrw int
759c2d03168Skrw gp_lba_start_cmp(const void *e1, const void *e2)
760c2d03168Skrw {
761c2d03168Skrw 	struct gpt_partition	*p1 = *(struct gpt_partition **)e1;
762c2d03168Skrw 	struct gpt_partition	*p2 = *(struct gpt_partition **)e2;
763b87d3542Skrw 	uint64_t		 o1;
764b87d3542Skrw 	uint64_t		 o2;
765c2d03168Skrw 
7662de77560Skrw 	o1 = p1->gp_lba_start;
7672de77560Skrw 	o2 = p2->gp_lba_start;
768c2d03168Skrw 
769c2d03168Skrw 	if (o1 < o2)
770c2d03168Skrw 		return -1;
771c2d03168Skrw 	else if (o1 > o2)
772c2d03168Skrw 		return 1;
773c2d03168Skrw 	else
774c2d03168Skrw 		return 0;
775c2d03168Skrw }
776c2d03168Skrw 
7778c6d22aeSkrw const struct gpt_partition * const *
778c2d03168Skrw sort_gpt(void)
779c2d03168Skrw {
7808c6d22aeSkrw 	static const struct gpt_partition	*sgp[NGPTPARTITIONS+2];
7812de77560Skrw 	unsigned int				 i, pn;
782c2d03168Skrw 
783c2d03168Skrw 	memset(sgp, 0, sizeof(sgp));
784c2d03168Skrw 
7852de77560Skrw 	i = 0;
7862de77560Skrw 	for (pn = 0; pn < gh.gh_part_num; pn++) {
7872de77560Skrw 		if (gp[pn].gp_lba_start >= gh.gh_lba_start)
7882de77560Skrw 			sgp[i++] = &gp[pn];
789c2d03168Skrw 	}
790c2d03168Skrw 
7912de77560Skrw 	if (i > 1) {
7922de77560Skrw 		if (mergesort(sgp, i, sizeof(sgp[0]), gp_lba_start_cmp) == -1) {
793c2d03168Skrw 			printf("unable to sort gpt by lba start\n");
794c2d03168Skrw 			return NULL;
795c2d03168Skrw 		}
796c2d03168Skrw 	}
797c2d03168Skrw 
7982a536aa2Skrw 	return sgp;
799c2d03168Skrw }
800c2d03168Skrw 
801c2d03168Skrw int
80238678f29Skrw lba_free(uint64_t *start, uint64_t *end)
803c2d03168Skrw {
8048c6d22aeSkrw 	const struct gpt_partition * const *sgp;
805c2d03168Skrw 	uint64_t			  bs, bigbs, nextbs, ns;
806c2d03168Skrw 	unsigned int			  i;
807c2d03168Skrw 
808c2d03168Skrw 	sgp = sort_gpt();
809c2d03168Skrw 	if (sgp == NULL)
810c2d03168Skrw 		return -1;
81138678f29Skrw 
812ad6c5e72Skrw 	bs = gh.gh_lba_start;
813ad6c5e72Skrw 	ns = gh.gh_lba_end - bs + 1;
81438678f29Skrw 
815c2d03168Skrw 	if (sgp[0] != NULL) {
816c2d03168Skrw 		bigbs = bs;
817c2d03168Skrw 		ns = 0;
818c2d03168Skrw 		for (i = 0; sgp[i] != NULL; i++) {
8192de77560Skrw 			nextbs = sgp[i]->gp_lba_start;
820c2d03168Skrw 			if (bs < nextbs && ns < nextbs - bs) {
821c2d03168Skrw 				ns = nextbs - bs;
822c2d03168Skrw 				bigbs = bs;
823c2d03168Skrw 			}
8242de77560Skrw 			bs = sgp[i]->gp_lba_end + 1;
825c2d03168Skrw 		}
826ad6c5e72Skrw 		nextbs = gh.gh_lba_end + 1;
827c2d03168Skrw 		if (bs < nextbs && ns < nextbs - bs) {
828c2d03168Skrw 			ns = nextbs - bs;
829c2d03168Skrw 			bigbs = bs;
830c2d03168Skrw 		}
831c2d03168Skrw 		bs = bigbs;
832c2d03168Skrw 	}
83338678f29Skrw 
83438678f29Skrw 	if (ns == 0)
83538678f29Skrw 		return -1;
83638678f29Skrw 
83738678f29Skrw 	if (start != NULL)
83838678f29Skrw 		*start = bs;
83938678f29Skrw 	if (end != NULL)
84038678f29Skrw 		*end = bs + ns - 1;
84138678f29Skrw 
84238678f29Skrw 	return 0;
84338678f29Skrw }
84438678f29Skrw 
84538678f29Skrw int
846859be6c9Skrw GPT_get_lba_start(const unsigned int pn)
84738678f29Skrw {
84838678f29Skrw 	uint64_t		bs;
84938678f29Skrw 	unsigned int		i;
85038678f29Skrw 	int			rslt;
85138678f29Skrw 
852ad6c5e72Skrw 	bs = gh.gh_lba_start;
85338678f29Skrw 
8542de77560Skrw 	if (gp[pn].gp_lba_start >= bs) {
8552de77560Skrw 		bs = gp[pn].gp_lba_start;
85638678f29Skrw 	} else {
85738678f29Skrw 		rslt = lba_free(&bs, NULL);
858965f1a17Skrw 		if (rslt == -1) {
859965f1a17Skrw 			printf("no space for partition %u\n", pn);
86038678f29Skrw 			return -1;
861c2d03168Skrw 		}
862965f1a17Skrw 	}
863c2d03168Skrw 
864ad6c5e72Skrw 	bs = getuint64("Partition offset", bs, gh.gh_lba_start, gh.gh_lba_end);
865ad6c5e72Skrw 	for (i = 0; i < gh.gh_part_num; i++) {
866c2d03168Skrw 		if (i == pn)
867c2d03168Skrw 			continue;
8682de77560Skrw 		if (bs >= gp[i].gp_lba_start && bs <= gp[i].gp_lba_end) {
869c2d03168Skrw 			printf("partition %u can't start inside partition %u\n",
870c2d03168Skrw 			    pn, i);
871c2d03168Skrw 			return -1;
872c2d03168Skrw 		}
873c2d03168Skrw 	}
874c2d03168Skrw 
8752de77560Skrw 	gp[pn].gp_lba_start = bs;
876c2d03168Skrw 
877c2d03168Skrw 	return 0;
878c2d03168Skrw }
879c2d03168Skrw 
880c2d03168Skrw int
881859be6c9Skrw GPT_get_lba_end(const unsigned int pn)
882c2d03168Skrw {
8838c6d22aeSkrw 	const struct gpt_partition	* const *sgp;
884c2d03168Skrw 	uint64_t			  bs, nextbs, ns;
885c2d03168Skrw 	unsigned int			  i;
886c2d03168Skrw 
887c2d03168Skrw 	sgp = sort_gpt();
888c2d03168Skrw 	if (sgp == NULL)
889c2d03168Skrw 		return -1;
890c2d03168Skrw 
8912de77560Skrw 	bs = gp[pn].gp_lba_start;
892ad6c5e72Skrw 	ns = gh.gh_lba_end - bs + 1;
893c2d03168Skrw 	for (i = 0; sgp[i] != NULL; i++) {
8942de77560Skrw 		nextbs = sgp[i]->gp_lba_start;
895c2d03168Skrw 		if (nextbs > bs) {
896c2d03168Skrw 			ns = nextbs - bs;
897c2d03168Skrw 			break;
898c2d03168Skrw 		}
899c2d03168Skrw 	}
900c2d03168Skrw 	ns = getuint64("Partition size", ns, 1, ns);
901c2d03168Skrw 
9022de77560Skrw 	gp[pn].gp_lba_end = bs + ns - 1;
903c2d03168Skrw 
904c2d03168Skrw 	return 0;
905c2d03168Skrw }
906543185b3Skrw 
907a66d82b0Skrw int
908a66d82b0Skrw GPT_get_name(const unsigned int pn)
909a66d82b0Skrw {
910a66d82b0Skrw 	char			 name[GPTPARTNAMESIZE + 1];
911a66d82b0Skrw 
912a66d82b0Skrw 	printf("Partition name: [%s] ", name_to_string(pn));
913a66d82b0Skrw 	string_from_line(name, sizeof(name), UNTRIMMED);
914a66d82b0Skrw 
915a66d82b0Skrw 	switch (strlen(name)) {
916a66d82b0Skrw 	case 0:
917a66d82b0Skrw 		break;
918a66d82b0Skrw 	case GPTPARTNAMESIZE:
919a66d82b0Skrw 		printf("partition name must be < %d characters\n",
920a66d82b0Skrw 		    GPTPARTNAMESIZE);
921a66d82b0Skrw 		return -1;
922a66d82b0Skrw 	default:
923a66d82b0Skrw 		string_to_name(pn, name);
924a66d82b0Skrw 		break;
925a66d82b0Skrw 	}
926a66d82b0Skrw 
927a66d82b0Skrw 	return 0;
928a66d82b0Skrw }
929a66d82b0Skrw 
930543185b3Skrw /*
931543185b3Skrw  * Adapted from Hacker's Delight crc32b().
932543185b3Skrw  *
933543185b3Skrw  * To quote http://www.hackersdelight.org/permissions.htm :
934543185b3Skrw  *
935543185b3Skrw  * "You are free to use, copy, and distribute any of the code on
936543185b3Skrw  *  this web site, whether modified by you or not. You need not give
937543185b3Skrw  *  attribution. This includes the algorithms (some of which appear
938543185b3Skrw  *  in Hacker's Delight), the Hacker's Assistant, and any code submitted
939543185b3Skrw  *  by readers. Submitters implicitly agree to this."
940543185b3Skrw  */
941543185b3Skrw uint32_t
942543185b3Skrw crc32(const u_char *buf, const uint32_t size)
943543185b3Skrw {
944543185b3Skrw 	int			j;
945543185b3Skrw 	uint32_t		i, byte, crc, mask;
946543185b3Skrw 
947543185b3Skrw 	crc = 0xFFFFFFFF;
948543185b3Skrw 
949543185b3Skrw 	for (i = 0; i < size; i++) {
950543185b3Skrw 		byte = buf[i];			/* Get next byte. */
951543185b3Skrw 		crc = crc ^ byte;
952543185b3Skrw 		for (j = 7; j >= 0; j--) {	/* Do eight times. */
953543185b3Skrw 			mask = -(crc & 1);
954543185b3Skrw 			crc = (crc >> 1) ^ (0xEDB88320 & mask);
955543185b3Skrw 		}
956543185b3Skrw 	}
957543185b3Skrw 
958543185b3Skrw 	return ~crc;
959543185b3Skrw }
960