g_part_gpt.c revision 169389
1/*-
2 * Copyright (c) 2002, 2005, 2006, 2007 Marcel Moolenaar
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * 1. Redistributions of source code must retain the above copyright
10 *    notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 *    notice, this list of conditions and the following disclaimer in the
13 *    documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 */
26
27#include <sys/cdefs.h>
28__FBSDID("$FreeBSD: head/sys/geom/part/g_part_gpt.c 169389 2007-05-08 20:18:17Z marcel $");
29
30#include <sys/param.h>
31#include <sys/bio.h>
32#include <sys/diskmbr.h>
33#include <sys/endian.h>
34#include <sys/gpt.h>
35#include <sys/kernel.h>
36#include <sys/kobj.h>
37#include <sys/limits.h>
38#include <sys/lock.h>
39#include <sys/malloc.h>
40#include <sys/mutex.h>
41#include <sys/queue.h>
42#include <sys/sbuf.h>
43#include <sys/systm.h>
44#include <sys/uuid.h>
45#include <geom/geom.h>
46#include <geom/part/g_part.h>
47
48#include "g_part_if.h"
49
50CTASSERT(offsetof(struct gpt_hdr, padding) == 92);
51CTASSERT(sizeof(struct gpt_ent) == 128);
52
53#define	EQUUID(a,b)	(memcmp(a, b, sizeof(struct uuid)) == 0)
54
55enum gpt_elt {
56	GPT_ELT_PRIHDR,
57	GPT_ELT_PRITBL,
58	GPT_ELT_SECHDR,
59	GPT_ELT_SECTBL,
60	GPT_ELT_COUNT
61};
62
63enum gpt_state {
64	GPT_STATE_UNKNOWN,	/* Not determined. */
65	GPT_STATE_MISSING,	/* No signature found. */
66	GPT_STATE_CORRUPT,	/* Checksum mismatch. */
67	GPT_STATE_INVALID,	/* Nonconformant/invalid. */
68	GPT_STATE_OK		/* Perfectly fine. */
69};
70
71struct g_part_gpt_table {
72	struct g_part_table	base;
73	struct gpt_hdr		hdr;
74	quad_t			lba[GPT_ELT_COUNT];
75	enum gpt_state		state[GPT_ELT_COUNT];
76};
77
78struct g_part_gpt_entry {
79	struct g_part_entry	base;
80	struct gpt_ent		ent;
81};
82
83static int g_part_gpt_add(struct g_part_table *, struct g_part_entry *,
84    struct g_part_parms *);
85static int g_part_gpt_create(struct g_part_table *, struct g_part_parms *);
86static int g_part_gpt_destroy(struct g_part_table *, struct g_part_parms *);
87static int g_part_gpt_dumpto(struct g_part_table *, struct g_part_entry *);
88static int g_part_gpt_modify(struct g_part_table *, struct g_part_entry *,
89    struct g_part_parms *);
90static char *g_part_gpt_name(struct g_part_table *, struct g_part_entry *,
91    char *, size_t);
92static int g_part_gpt_probe(struct g_part_table *, struct g_consumer *);
93static int g_part_gpt_read(struct g_part_table *, struct g_consumer *);
94static const char *g_part_gpt_type(struct g_part_table *, struct g_part_entry *,
95    char *, size_t);
96static int g_part_gpt_write(struct g_part_table *, struct g_consumer *);
97
98static kobj_method_t g_part_gpt_methods[] = {
99	KOBJMETHOD(g_part_add,		g_part_gpt_add),
100	KOBJMETHOD(g_part_create,	g_part_gpt_create),
101	KOBJMETHOD(g_part_destroy,	g_part_gpt_destroy),
102	KOBJMETHOD(g_part_dumpto,	g_part_gpt_dumpto),
103	KOBJMETHOD(g_part_modify,	g_part_gpt_modify),
104	KOBJMETHOD(g_part_name,		g_part_gpt_name),
105	KOBJMETHOD(g_part_probe,	g_part_gpt_probe),
106	KOBJMETHOD(g_part_read,		g_part_gpt_read),
107	KOBJMETHOD(g_part_type,		g_part_gpt_type),
108	KOBJMETHOD(g_part_write,	g_part_gpt_write),
109	{ 0, 0 }
110};
111
112static struct g_part_scheme g_part_gpt_scheme = {
113	"GPT",
114	g_part_gpt_methods,
115	sizeof(struct g_part_gpt_table),
116	.gps_entrysz = sizeof(struct g_part_gpt_entry),
117	.gps_minent = 128,
118	.gps_maxent = INT_MAX,
119};
120G_PART_SCHEME_DECLARE(g_part_gpt_scheme);
121
122static struct uuid gpt_uuid_efi = GPT_ENT_TYPE_EFI;
123static struct uuid gpt_uuid_freebsd = GPT_ENT_TYPE_FREEBSD;
124static struct uuid gpt_uuid_freebsd_swap = GPT_ENT_TYPE_FREEBSD_SWAP;
125static struct uuid gpt_uuid_freebsd_ufs = GPT_ENT_TYPE_FREEBSD_UFS;
126static struct uuid gpt_uuid_freebsd_vinum = GPT_ENT_TYPE_FREEBSD_VINUM;
127static struct uuid gpt_uuid_linux_swap = GPT_ENT_TYPE_LINUX_SWAP;
128static struct uuid gpt_uuid_mbr = GPT_ENT_TYPE_MBR;
129static struct uuid gpt_uuid_unused = GPT_ENT_TYPE_UNUSED;
130
131static void
132gpt_read_hdr(struct g_part_gpt_table *table, struct g_consumer *cp,
133    enum gpt_elt elt, struct gpt_hdr *hdr)
134{
135	struct uuid uuid;
136	struct g_provider *pp;
137	char *buf;
138	quad_t lba, last;
139	int error;
140	uint32_t crc, sz;
141
142	pp = cp->provider;
143	last = (pp->mediasize / pp->sectorsize) - 1;
144	table->lba[elt] = (elt == GPT_ELT_PRIHDR) ? 1 : last;
145	table->state[elt] = GPT_STATE_MISSING;
146	buf = g_read_data(cp, table->lba[elt] * pp->sectorsize, pp->sectorsize,
147	    &error);
148	if (buf == NULL)
149		return;
150	bcopy(buf, hdr, sizeof(*hdr));
151	if (memcmp(hdr->hdr_sig, GPT_HDR_SIG, sizeof(hdr->hdr_sig)) != 0)
152		return;
153
154	table->state[elt] = GPT_STATE_CORRUPT;
155	sz = le32toh(hdr->hdr_size);
156	if (sz < 92 || sz > pp->sectorsize)
157		return;
158	crc = le32toh(hdr->hdr_crc_self);
159	hdr->hdr_crc_self = 0;
160	if (crc32(hdr, sz) != crc)
161		return;
162	hdr->hdr_size = sz;
163	hdr->hdr_crc_self = crc;
164
165	table->state[elt] = GPT_STATE_INVALID;
166	hdr->hdr_revision = le32toh(hdr->hdr_revision);
167	if (hdr->hdr_revision < 0x00010000)
168		return;
169	hdr->hdr_lba_self = le64toh(hdr->hdr_lba_self);
170	if (hdr->hdr_lba_self != table->lba[elt])
171		return;
172	hdr->hdr_lba_alt = le64toh(hdr->hdr_lba_alt);
173
174	/* Check the managed area. */
175	hdr->hdr_lba_start = le64toh(hdr->hdr_lba_start);
176	if (hdr->hdr_lba_start < 2 || hdr->hdr_lba_start >= last)
177		return;
178	hdr->hdr_lba_end = le64toh(hdr->hdr_lba_end);
179	if (hdr->hdr_lba_end < hdr->hdr_lba_start || hdr->hdr_lba_end >= last)
180		return;
181
182	/* Check the table location and size of the table. */
183	hdr->hdr_entries = le32toh(hdr->hdr_entries);
184	hdr->hdr_entsz = le32toh(hdr->hdr_entsz);
185	if (hdr->hdr_entries == 0 || hdr->hdr_entsz < 128 ||
186	    (hdr->hdr_entsz & 7) != 0)
187		return;
188	hdr->hdr_lba_table = le64toh(hdr->hdr_lba_table);
189	if (hdr->hdr_lba_table < 2 || hdr->hdr_lba_table >= last)
190		return;
191	if (hdr->hdr_lba_table >= hdr->hdr_lba_start &&
192	    hdr->hdr_lba_table <= hdr->hdr_lba_end)
193		return;
194	lba = hdr->hdr_lba_table +
195	    (hdr->hdr_entries * hdr->hdr_entsz + pp->sectorsize - 1) /
196	    pp->sectorsize - 1;
197	if (lba >= last)
198		return;
199	if (lba >= hdr->hdr_lba_start && lba <= hdr->hdr_lba_end)
200		return;
201
202	table->state[elt] = GPT_STATE_OK;
203	le_uuid_dec(&hdr->hdr_uuid, &uuid);
204	hdr->hdr_uuid = uuid;
205	hdr->hdr_crc_table = le32toh(hdr->hdr_crc_table);
206}
207
208static struct gpt_ent *
209gpt_read_tbl(struct g_part_gpt_table *table, struct g_consumer *cp,
210    enum gpt_elt elt, struct gpt_hdr *hdr)
211{
212	struct g_provider *pp;
213	struct gpt_ent *ent, *tbl;
214	char *buf, *p;
215	unsigned int idx, sectors, tblsz;
216	int error;
217	uint16_t ch;
218
219	pp = cp->provider;
220	table->lba[elt] = hdr->hdr_lba_table;
221
222	table->state[elt] = GPT_STATE_MISSING;
223	tblsz = hdr->hdr_entries * hdr->hdr_entsz;
224	sectors = (tblsz + pp->sectorsize - 1) / pp->sectorsize;
225	buf = g_read_data(cp, table->lba[elt] * pp->sectorsize,
226	    sectors * pp->sectorsize, &error);
227	if (buf == NULL)
228		return (NULL);
229
230	table->state[elt] = GPT_STATE_CORRUPT;
231	if (crc32(buf, tblsz) != hdr->hdr_crc_table) {
232		g_free(buf);
233		return (NULL);
234	}
235
236	table->state[elt] = GPT_STATE_OK;
237	tbl = g_malloc(hdr->hdr_entries * sizeof(struct gpt_ent),
238	    M_WAITOK | M_ZERO);
239
240	for (idx = 0, ent = tbl, p = buf;
241	     idx < hdr->hdr_entries;
242	     idx++, ent++, p += hdr->hdr_entsz) {
243		le_uuid_dec(p, &ent->ent_type);
244		le_uuid_dec(p + 16, &ent->ent_uuid);
245		ent->ent_lba_start = le64dec(p + 32);
246		ent->ent_lba_end = le64dec(p + 40);
247		ent->ent_attr = le64dec(p + 48);
248		for (ch = 0; ch < sizeof(ent->ent_name)/2; ch++)
249			ent->ent_name[ch] = le16dec(p + 56 + ch * 2);
250	}
251
252	g_free(buf);
253	return (tbl);
254}
255
256static int
257gpt_matched_hdrs(struct gpt_hdr *pri, struct gpt_hdr *sec)
258{
259
260	if (!EQUUID(&pri->hdr_uuid, &sec->hdr_uuid))
261		return (0);
262	return ((pri->hdr_revision == sec->hdr_revision &&
263	    pri->hdr_size == sec->hdr_size &&
264	    pri->hdr_lba_start == sec->hdr_lba_start &&
265	    pri->hdr_lba_end == sec->hdr_lba_end &&
266	    pri->hdr_entries == sec->hdr_entries &&
267	    pri->hdr_entsz == sec->hdr_entsz &&
268	    pri->hdr_crc_table == sec->hdr_crc_table) ? 1 : 0);
269}
270
271static int
272gpt_parse_type(const char *type, struct uuid *uuid)
273{
274	struct uuid tmp;
275	const char *alias;
276	int error;
277
278	if (type[0] == '!') {
279		error = parse_uuid(type + 1, &tmp);
280		if (error)
281			return (error);
282		if (EQUUID(&tmp, &gpt_uuid_unused))
283			return (EINVAL);
284		*uuid = tmp;
285		return (0);
286	}
287	alias = g_part_alias_name(G_PART_ALIAS_EFI);
288	if (!strcasecmp(type, alias)) {
289		*uuid = gpt_uuid_efi;
290		return (0);
291	}
292	alias = g_part_alias_name(G_PART_ALIAS_FREEBSD);
293	if (!strcasecmp(type, alias)) {
294		*uuid = gpt_uuid_freebsd;
295		return (0);
296	}
297	alias = g_part_alias_name(G_PART_ALIAS_FREEBSD_SWAP);
298	if (!strcasecmp(type, alias)) {
299		*uuid = gpt_uuid_freebsd_swap;
300		return (0);
301	}
302	alias = g_part_alias_name(G_PART_ALIAS_FREEBSD_UFS);
303	if (!strcasecmp(type, alias)) {
304		*uuid = gpt_uuid_freebsd_ufs;
305		return (0);
306	}
307	alias = g_part_alias_name(G_PART_ALIAS_FREEBSD_VINUM);
308	if (!strcasecmp(type, alias)) {
309		*uuid = gpt_uuid_freebsd_vinum;
310		return (0);
311	}
312	alias = g_part_alias_name(G_PART_ALIAS_MBR);
313	if (!strcasecmp(type, alias)) {
314		*uuid = gpt_uuid_mbr;
315		return (0);
316	}
317	return (EINVAL);
318}
319
320static int
321g_part_gpt_add(struct g_part_table *basetable, struct g_part_entry *baseentry,
322    struct g_part_parms *gpp)
323{
324	struct g_part_gpt_entry *entry;
325	int error;
326
327	entry = (struct g_part_gpt_entry *)baseentry;
328	error = gpt_parse_type(gpp->gpp_type, &entry->ent.ent_type);
329	if (error)
330		return (error);
331	kern_uuidgen(&entry->ent.ent_uuid, 1);
332	entry->ent.ent_lba_start = baseentry->gpe_start;
333	entry->ent.ent_lba_end = baseentry->gpe_end;
334	if (baseentry->gpe_deleted) {
335		entry->ent.ent_attr = 0;
336		bzero(entry->ent.ent_name, sizeof(entry->ent.ent_name));
337	}
338	/* XXX label */
339	return (0);
340}
341
342static int
343g_part_gpt_create(struct g_part_table *basetable, struct g_part_parms *gpp)
344{
345	struct g_provider *pp;
346	struct g_part_gpt_table *table;
347	quad_t last;
348	size_t tblsz;
349
350	table = (struct g_part_gpt_table *)basetable;
351	pp = gpp->gpp_provider;
352	tblsz = (basetable->gpt_entries * sizeof(struct gpt_ent) +
353	    pp->sectorsize - 1) / pp->sectorsize;
354	if (pp->sectorsize < 512 ||
355	    pp->mediasize < (3 + 2 * tblsz + basetable->gpt_entries) *
356	    pp->sectorsize)
357		return (ENOSPC);
358
359	last = (pp->mediasize / pp->sectorsize) - 1;
360
361	table->lba[GPT_ELT_PRIHDR] = 1;
362	table->lba[GPT_ELT_PRITBL] = 2;
363	table->lba[GPT_ELT_SECHDR] = last;
364	table->lba[GPT_ELT_SECTBL] = last - tblsz;
365
366	bcopy(GPT_HDR_SIG, table->hdr.hdr_sig, sizeof(table->hdr.hdr_sig));
367	table->hdr.hdr_revision = GPT_HDR_REVISION;
368	table->hdr.hdr_size = offsetof(struct gpt_hdr, padding);
369	table->hdr.hdr_lba_start = 2 + tblsz;
370	table->hdr.hdr_lba_end = last - tblsz - 1;
371	kern_uuidgen(&table->hdr.hdr_uuid, 1);
372	table->hdr.hdr_entries = basetable->gpt_entries;
373	table->hdr.hdr_entsz = sizeof(struct gpt_ent);
374
375	basetable->gpt_first = table->hdr.hdr_lba_start;
376	basetable->gpt_last = table->hdr.hdr_lba_end;
377	return (0);
378}
379
380static int
381g_part_gpt_destroy(struct g_part_table *basetable, struct g_part_parms *gpp)
382{
383
384	/*
385	 * Wipe the first 2 sectors as well as the last to clear the
386	 * partitioning.
387	 */
388	basetable->gpt_smhead |= 3;
389	basetable->gpt_smtail |= 1;
390	return (0);
391}
392
393static int
394g_part_gpt_dumpto(struct g_part_table *table, struct g_part_entry *baseentry)
395{
396	struct g_part_gpt_entry *entry;
397
398	entry = (struct g_part_gpt_entry *)baseentry;
399	return ((EQUUID(&entry->ent.ent_type, &gpt_uuid_freebsd_swap) ||
400	    EQUUID(&entry->ent.ent_type, &gpt_uuid_linux_swap)) ? 1 : 0);
401}
402
403static int
404g_part_gpt_modify(struct g_part_table *basetable,
405    struct g_part_entry *baseentry, struct g_part_parms *gpp)
406{
407	struct g_part_gpt_entry *entry;
408	int error;
409
410	entry = (struct g_part_gpt_entry *)baseentry;
411	if (gpp->gpp_parms & G_PART_PARM_TYPE) {
412		error = gpt_parse_type(gpp->gpp_type, &entry->ent.ent_type);
413		if (error)
414			return (error);
415	}
416	/* XXX label */
417	return (0);
418}
419
420static char *
421g_part_gpt_name(struct g_part_table *table, struct g_part_entry *baseentry,
422    char *buf, size_t bufsz)
423{
424	struct g_part_gpt_entry *entry;
425	char c;
426
427	entry = (struct g_part_gpt_entry *)baseentry;
428	c = (EQUUID(&entry->ent.ent_type, &gpt_uuid_freebsd)) ? 's' : 'p';
429	snprintf(buf, bufsz, "%c%d", c, baseentry->gpe_index);
430	return (buf);
431}
432
433static int
434g_part_gpt_probe(struct g_part_table *table, struct g_consumer *cp)
435{
436	struct g_provider *pp;
437	char *buf;
438	int error, res;
439
440	/* We don't nest, which means that our depth should be 0. */
441	if (table->gpt_depth != 0)
442		return (ENXIO);
443
444	pp = cp->provider;
445
446	/*
447	 * Sanity-check the provider. Since the first sector on the provider
448	 * must be a PMBR and a PMBR is 512 bytes large, the sector size
449	 * must be at least 512 bytes.  Also, since the theoretical minimum
450	 * number of sectors needed by GPT is 6, any medium that has less
451	 * than 6 sectors is never going to be able to hold a GPT. The
452	 * number 6 comes from:
453	 *	1 sector for the PMBR
454	 *	2 sectors for the GPT headers (each 1 sector)
455	 *	2 sectors for the GPT tables (each 1 sector)
456	 *	1 sector for an actual partition
457	 * It's better to catch this pathological case early than behaving
458	 * pathologically later on...
459	 */
460	if (pp->sectorsize < 512 || pp->mediasize < 6 * pp->sectorsize)
461		return (ENOSPC);
462
463	/* Check that there's a MBR. */
464	buf = g_read_data(cp, 0L, pp->sectorsize, &error);
465	if (buf == NULL)
466		return (error);
467	res = le16dec(buf + DOSMAGICOFFSET);
468	g_free(buf);
469	if (res != DOSMAGIC)
470		return (ENXIO);
471
472	/* Check that there's a primary header. */
473	buf = g_read_data(cp, pp->sectorsize, pp->sectorsize, &error);
474	if (buf == NULL)
475		return (error);
476	res = memcmp(buf, GPT_HDR_SIG, 8);
477	g_free(buf);
478	if (res == 0)
479		return (G_PART_PROBE_PRI_HIGH);
480
481	/* No primary? Check that there's a secondary. */
482	buf = g_read_data(cp, pp->mediasize - pp->sectorsize, pp->sectorsize,
483	    &error);
484	if (buf == NULL)
485		return (error);
486	res = memcmp(buf, GPT_HDR_SIG, 8);
487	g_free(buf);
488	return ((res == 0) ? G_PART_PROBE_PRI_HIGH : ENXIO);
489}
490
491static int
492g_part_gpt_read(struct g_part_table *basetable, struct g_consumer *cp)
493{
494	struct gpt_hdr prihdr, sechdr;
495	struct gpt_ent *tbl, *pritbl, *sectbl;
496	struct g_provider *pp;
497	struct g_part_gpt_table *table;
498	struct g_part_gpt_entry *entry;
499	int index;
500
501	table = (struct g_part_gpt_table *)basetable;
502	pp = cp->provider;
503
504	/* Read the primary header and table. */
505	gpt_read_hdr(table, cp, GPT_ELT_PRIHDR, &prihdr);
506	if (table->state[GPT_ELT_PRIHDR] == GPT_STATE_OK) {
507		pritbl = gpt_read_tbl(table, cp, GPT_ELT_PRITBL, &prihdr);
508	} else {
509		table->state[GPT_ELT_PRITBL] = GPT_STATE_MISSING;
510		pritbl = NULL;
511	}
512
513	/* Read the secondary header and table. */
514	gpt_read_hdr(table, cp, GPT_ELT_SECHDR, &sechdr);
515	if (table->state[GPT_ELT_SECHDR] == GPT_STATE_OK) {
516		sectbl = gpt_read_tbl(table, cp, GPT_ELT_SECTBL, &sechdr);
517	} else {
518		table->state[GPT_ELT_SECTBL] = GPT_STATE_MISSING;
519		sectbl = NULL;
520	}
521
522	/* Fail if we haven't got any good tables at all. */
523	if (table->state[GPT_ELT_PRITBL] != GPT_STATE_OK &&
524	    table->state[GPT_ELT_SECTBL] != GPT_STATE_OK) {
525		printf("GEOM: %s: corrupt or invalid GPT detected.\n",
526		    pp->name);
527		printf("GEOM: %s: GPT rejected -- may not be recoverable.\n",
528		    pp->name);
529		return (EINVAL);
530	}
531
532	/*
533	 * If both headers are good but they disagree with each other,
534	 * then invalidate one. We prefer to keep the primary header,
535	 * unless the primary table is corrupt.
536	 */
537	if (table->state[GPT_ELT_PRIHDR] == GPT_STATE_OK &&
538	    table->state[GPT_ELT_SECHDR] == GPT_STATE_OK &&
539	    !gpt_matched_hdrs(&prihdr, &sechdr)) {
540		if (table->state[GPT_ELT_PRITBL] == GPT_STATE_OK)
541			table->state[GPT_ELT_SECHDR] = GPT_STATE_INVALID;
542		else
543			table->state[GPT_ELT_PRIHDR] = GPT_STATE_INVALID;
544	}
545
546	if (table->state[GPT_ELT_PRIHDR] != GPT_STATE_OK) {
547		printf("GEOM: %s: the primary GPT table is corrupt or "
548		    "invalid.\n", pp->name);
549		printf("GEOM: %s: using the secondary instead -- recovery "
550		    "strongly advised.\n", pp->name);
551		table->hdr = sechdr;
552		tbl = sectbl;
553		if (pritbl != NULL)
554			g_free(pritbl);
555	} else {
556		if (table->state[GPT_ELT_SECHDR] != GPT_STATE_OK) {
557			printf("GEOM: %s: the secondary GPT table is corrupt "
558			    "or invalid.\n", pp->name);
559			printf("GEOM: %s: using the primary only -- recovery "
560			    "suggested.\n", pp->name);
561		}
562		table->hdr = prihdr;
563		tbl = pritbl;
564		if (sectbl != NULL)
565			g_free(sectbl);
566	}
567
568	basetable->gpt_first = table->hdr.hdr_lba_start;
569	basetable->gpt_last = table->hdr.hdr_lba_end;
570	basetable->gpt_entries = table->hdr.hdr_entries;
571
572	for (index = basetable->gpt_entries - 1; index >= 0; index--) {
573		if (EQUUID(&tbl[index].ent_type, &gpt_uuid_unused))
574			continue;
575		entry = (struct g_part_gpt_entry *)g_part_new_entry(basetable,
576		    index+1, tbl[index].ent_lba_start, tbl[index].ent_lba_end);
577		entry->ent = tbl[index];
578	}
579
580	g_free(tbl);
581	return (0);
582}
583
584static const char *
585g_part_gpt_type(struct g_part_table *basetable, struct g_part_entry *baseentry,
586    char *buf, size_t bufsz)
587{
588	struct g_part_gpt_entry *entry;
589	struct uuid *type;
590
591	entry = (struct g_part_gpt_entry *)baseentry;
592	type = &entry->ent.ent_type;
593	if (EQUUID(type, &gpt_uuid_efi))
594		return (g_part_alias_name(G_PART_ALIAS_EFI));
595	if (EQUUID(type, &gpt_uuid_freebsd))
596		return (g_part_alias_name(G_PART_ALIAS_FREEBSD));
597	if (EQUUID(type, &gpt_uuid_freebsd_swap))
598		return (g_part_alias_name(G_PART_ALIAS_FREEBSD_SWAP));
599	if (EQUUID(type, &gpt_uuid_freebsd_ufs))
600		return (g_part_alias_name(G_PART_ALIAS_FREEBSD_UFS));
601	if (EQUUID(type, &gpt_uuid_freebsd_vinum))
602		return (g_part_alias_name(G_PART_ALIAS_FREEBSD_VINUM));
603	if (EQUUID(type, &gpt_uuid_mbr))
604		return (g_part_alias_name(G_PART_ALIAS_MBR));
605	snprintf_uuid(buf, bufsz, type);
606	return (buf);
607}
608
609static int
610g_part_gpt_write(struct g_part_table *basetable, struct g_consumer *cp)
611{
612	unsigned char *buf, *bp;
613	struct g_provider *pp;
614	struct g_part_entry *baseentry;
615	struct g_part_gpt_entry *entry;
616	struct g_part_gpt_table *table;
617	size_t tlbsz;
618	uint32_t crc;
619	int error, index;
620
621	pp = cp->provider;
622	table = (struct g_part_gpt_table *)basetable;
623	tlbsz = (table->hdr.hdr_entries * table->hdr.hdr_entsz +
624	    pp->sectorsize - 1) / pp->sectorsize;
625
626	if (basetable->gpt_created) {
627		buf = g_malloc(pp->sectorsize, M_WAITOK | M_ZERO);
628		le16enc(buf + DOSMAGICOFFSET, DOSMAGIC);
629		buf[DOSPARTOFF + 1] = 0xff;		/* shd */
630		buf[DOSPARTOFF + 2] = 0xff;		/* ssect */
631		buf[DOSPARTOFF + 3] = 0xff;		/* scyl */
632		buf[DOSPARTOFF + 4] = 0xee;		/* typ */
633		buf[DOSPARTOFF + 5] = 0xff;		/* ehd */
634		buf[DOSPARTOFF + 6] = 0xff;		/* esect */
635		buf[DOSPARTOFF + 7] = 0xff;		/* ecyl */
636		le32enc(buf + DOSPARTOFF + 8, 1);	/* start */
637		le32enc(buf + DOSPARTOFF + 12,
638		    MIN(pp->mediasize / pp->sectorsize - 1, 0xffffffffLL));
639		error = g_write_data(cp, 0, buf, pp->sectorsize);
640		g_free(buf);
641		if (error)
642			return (error);
643	}
644
645	/* Allocate space for the header and entries. */
646	buf = g_malloc((tlbsz + 1) * pp->sectorsize, M_WAITOK | M_ZERO);
647
648	memcpy(buf, table->hdr.hdr_sig, sizeof(table->hdr.hdr_sig));
649	le32enc(buf + 8, table->hdr.hdr_revision);
650	le32enc(buf + 12, table->hdr.hdr_size);
651	le64enc(buf + 40, table->hdr.hdr_lba_start);
652	le64enc(buf + 48, table->hdr.hdr_lba_end);
653	le_uuid_enc(buf + 56, &table->hdr.hdr_uuid);
654	le32enc(buf + 80, table->hdr.hdr_entries);
655	le32enc(buf + 84, table->hdr.hdr_entsz);
656
657	LIST_FOREACH(baseentry, &basetable->gpt_entry, gpe_entry) {
658		if (baseentry->gpe_deleted)
659			continue;
660		entry = (struct g_part_gpt_entry *)baseentry;
661		index = baseentry->gpe_index - 1;
662		bp = buf + pp->sectorsize + table->hdr.hdr_entsz * index;
663		le_uuid_enc(bp, &entry->ent.ent_type);
664		le_uuid_enc(bp + 16, &entry->ent.ent_uuid);
665		le64enc(bp + 32, entry->ent.ent_lba_start);
666		le64enc(bp + 40, entry->ent.ent_lba_end);
667		le64enc(bp + 48, entry->ent.ent_attr);
668		memcpy(bp + 56, entry->ent.ent_name,
669		    sizeof(entry->ent.ent_name));
670	}
671
672	crc = crc32(buf + pp->sectorsize,
673	    table->hdr.hdr_entries * table->hdr.hdr_entsz);
674	le32enc(buf + 88, crc);
675
676	/* Write primary meta-data. */
677	le32enc(buf + 16, 0);	/* hdr_crc_self. */
678	le64enc(buf + 24, table->lba[GPT_ELT_PRIHDR]);	/* hdr_lba_self. */
679	le64enc(buf + 32, table->lba[GPT_ELT_SECHDR]);	/* hdr_lba_alt. */
680	le64enc(buf + 72, table->lba[GPT_ELT_PRITBL]);	/* hdr_lba_table. */
681	crc = crc32(buf, table->hdr.hdr_size);
682	le32enc(buf + 16, crc);
683
684	error = g_write_data(cp, table->lba[GPT_ELT_PRITBL] * pp->sectorsize,
685	    buf + pp->sectorsize, tlbsz * pp->sectorsize);
686	if (error)
687		goto out;
688	error = g_write_data(cp, table->lba[GPT_ELT_PRIHDR] * pp->sectorsize,
689	    buf, pp->sectorsize);
690	if (error)
691		goto out;
692
693	/* Write secondary meta-data. */
694	le32enc(buf + 16, 0);	/* hdr_crc_self. */
695	le64enc(buf + 24, table->lba[GPT_ELT_SECHDR]);	/* hdr_lba_self. */
696	le64enc(buf + 32, table->lba[GPT_ELT_PRIHDR]);	/* hdr_lba_alt. */
697	le64enc(buf + 72, table->lba[GPT_ELT_SECTBL]);	/* hdr_lba_table. */
698	crc = crc32(buf, table->hdr.hdr_size);
699	le32enc(buf + 16, crc);
700
701	error = g_write_data(cp, table->lba[GPT_ELT_SECTBL] * pp->sectorsize,
702	    buf + pp->sectorsize, tlbsz * pp->sectorsize);
703	if (error)
704		goto out;
705	error = g_write_data(cp, table->lba[GPT_ELT_SECHDR] * pp->sectorsize,
706	    buf, pp->sectorsize);
707
708 out:
709	g_free(buf);
710	return (error);
711}
712
713#if 0
714static void
715g_gpt_to_utf8(struct sbuf *sb, uint16_t *str, size_t len)
716{
717	u_int bo;
718	uint32_t ch;
719	uint16_t c;
720
721	bo = BYTE_ORDER;
722	while (len > 0 && *str != 0) {
723		ch = (bo == BIG_ENDIAN) ? be16toh(*str) : le16toh(*str);
724		str++, len--;
725		if ((ch & 0xf800) == 0xd800) {
726			if (len > 0) {
727				c = (bo == BIG_ENDIAN) ? be16toh(*str)
728				    : le16toh(*str);
729				str++, len--;
730			} else
731				c = 0xfffd;
732			if ((ch & 0x400) == 0 && (c & 0xfc00) == 0xdc00) {
733				ch = ((ch & 0x3ff) << 10) + (c & 0x3ff);
734				ch += 0x10000;
735			} else
736				ch = 0xfffd;
737		} else if (ch == 0xfffe) { /* BOM (U+FEFF) swapped. */
738			bo = (bo == BIG_ENDIAN) ? LITTLE_ENDIAN : BIG_ENDIAN;
739			continue;
740		} else if (ch == 0xfeff) /* BOM (U+FEFF) unswapped. */
741			continue;
742
743		if (ch < 0x80)
744			sbuf_printf(sb, "%c", ch);
745		else if (ch < 0x800)
746			sbuf_printf(sb, "%c%c", 0xc0 | (ch >> 6),
747			    0x80 | (ch & 0x3f));
748		else if (ch < 0x10000)
749			sbuf_printf(sb, "%c%c%c", 0xe0 | (ch >> 12),
750			    0x80 | ((ch >> 6) & 0x3f), 0x80 | (ch & 0x3f));
751		else if (ch < 0x200000)
752			sbuf_printf(sb, "%c%c%c%c", 0xf0 | (ch >> 18),
753			    0x80 | ((ch >> 12) & 0x3f),
754			    0x80 | ((ch >> 6) & 0x3f), 0x80 | (ch & 0x3f));
755	}
756}
757#endif
758