1/*-
2 * Copyright (c) 2014 Andrey V. Elsukov <ae@FreeBSD.org>
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/param.h>
28#include <sys/bio.h>
29#include <sys/gsb_crc32.h>
30#include <sys/disklabel.h>
31#include <sys/endian.h>
32#include <sys/gpt.h>
33#include <sys/kernel.h>
34#include <sys/kobj.h>
35#include <sys/limits.h>
36#include <sys/lock.h>
37#include <sys/malloc.h>
38#include <sys/mutex.h>
39#include <sys/queue.h>
40#include <sys/sbuf.h>
41#include <sys/systm.h>
42#include <sys/sysctl.h>
43#include <geom/geom.h>
44#include <geom/geom_int.h>
45#include <geom/part/g_part.h>
46
47#include "g_part_if.h"
48
49FEATURE(geom_part_bsd64, "GEOM partitioning class for 64-bit BSD disklabels");
50
51/* XXX: move this to sys/disklabel64.h */
52#define	DISKMAGIC64     ((uint32_t)0xc4464c59)
53#define	MAXPARTITIONS64	16
54#define	RESPARTITIONS64	32
55
56struct disklabel64 {
57	char	  d_reserved0[512];	/* reserved or unused */
58	uint32_t d_magic;		/* the magic number */
59	uint32_t d_crc;		/* crc32() d_magic through last part */
60	uint32_t d_align;		/* partition alignment requirement */
61	uint32_t d_npartitions;	/* number of partitions */
62	struct uuid d_stor_uuid;	/* unique uuid for label */
63
64	uint64_t d_total_size;		/* total size incl everything (bytes) */
65	uint64_t d_bbase;		/* boot area base offset (bytes) */
66					/* boot area is pbase - bbase */
67	uint64_t d_pbase;		/* first allocatable offset (bytes) */
68	uint64_t d_pstop;		/* last allocatable offset+1 (bytes) */
69	uint64_t d_abase;		/* location of backup copy if not 0 */
70
71	u_char	  d_packname[64];
72	u_char    d_reserved[64];
73
74	/*
75	 * Note: offsets are relative to the base of the slice, NOT to
76	 * d_pbase.  Unlike 32 bit disklabels the on-disk format for
77	 * a 64 bit disklabel remains slice-relative.
78	 *
79	 * An uninitialized partition has a p_boffset and p_bsize of 0.
80	 *
81	 * If p_fstype is not supported for a live partition it is set
82	 * to FS_OTHER.  This is typically the case when the filesystem
83	 * is identified by its uuid.
84	 */
85	struct partition64 {		/* the partition table */
86		uint64_t p_boffset;	/* slice relative offset, in bytes */
87		uint64_t p_bsize;	/* size of partition, in bytes */
88		uint8_t  p_fstype;
89		uint8_t  p_unused01;	/* reserved, must be 0 */
90		uint8_t  p_unused02;	/* reserved, must be 0 */
91		uint8_t  p_unused03;	/* reserved, must be 0 */
92		uint32_t p_unused04;	/* reserved, must be 0 */
93		uint32_t p_unused05;	/* reserved, must be 0 */
94		uint32_t p_unused06;	/* reserved, must be 0 */
95		struct uuid p_type_uuid;/* mount type as UUID */
96		struct uuid p_stor_uuid;/* unique uuid for storage */
97	} d_partitions[MAXPARTITIONS64];/* actually may be more */
98};
99
100struct g_part_bsd64_table {
101	struct g_part_table	base;
102
103	uint32_t		d_align;
104	uint64_t		d_bbase;
105	uint64_t		d_abase;
106	struct uuid		d_stor_uuid;
107	char			d_reserved0[512];
108	u_char			d_packname[64];
109	u_char			d_reserved[64];
110};
111
112struct g_part_bsd64_entry {
113	struct g_part_entry	base;
114
115	uint8_t			fstype;
116	struct uuid		type_uuid;
117	struct uuid		stor_uuid;
118};
119
120static int g_part_bsd64_add(struct g_part_table *, struct g_part_entry *,
121    struct g_part_parms *);
122static int g_part_bsd64_bootcode(struct g_part_table *, struct g_part_parms *);
123static int g_part_bsd64_create(struct g_part_table *, struct g_part_parms *);
124static int g_part_bsd64_destroy(struct g_part_table *, struct g_part_parms *);
125static void g_part_bsd64_dumpconf(struct g_part_table *, struct g_part_entry *,
126    struct sbuf *, const char *);
127static int g_part_bsd64_dumpto(struct g_part_table *, struct g_part_entry *);
128static int g_part_bsd64_modify(struct g_part_table *, struct g_part_entry *,
129    struct g_part_parms *);
130static const char *g_part_bsd64_name(struct g_part_table *, struct g_part_entry *,
131    char *, size_t);
132static int g_part_bsd64_probe(struct g_part_table *, struct g_consumer *);
133static int g_part_bsd64_read(struct g_part_table *, struct g_consumer *);
134static const char *g_part_bsd64_type(struct g_part_table *, struct g_part_entry *,
135    char *, size_t);
136static int g_part_bsd64_write(struct g_part_table *, struct g_consumer *);
137static int g_part_bsd64_resize(struct g_part_table *, struct g_part_entry *,
138    struct g_part_parms *);
139
140static kobj_method_t g_part_bsd64_methods[] = {
141	KOBJMETHOD(g_part_add,		g_part_bsd64_add),
142	KOBJMETHOD(g_part_bootcode,	g_part_bsd64_bootcode),
143	KOBJMETHOD(g_part_create,	g_part_bsd64_create),
144	KOBJMETHOD(g_part_destroy,	g_part_bsd64_destroy),
145	KOBJMETHOD(g_part_dumpconf,	g_part_bsd64_dumpconf),
146	KOBJMETHOD(g_part_dumpto,	g_part_bsd64_dumpto),
147	KOBJMETHOD(g_part_modify,	g_part_bsd64_modify),
148	KOBJMETHOD(g_part_resize,	g_part_bsd64_resize),
149	KOBJMETHOD(g_part_name,		g_part_bsd64_name),
150	KOBJMETHOD(g_part_probe,	g_part_bsd64_probe),
151	KOBJMETHOD(g_part_read,		g_part_bsd64_read),
152	KOBJMETHOD(g_part_type,		g_part_bsd64_type),
153	KOBJMETHOD(g_part_write,	g_part_bsd64_write),
154	{ 0, 0 }
155};
156
157static struct g_part_scheme g_part_bsd64_scheme = {
158	"BSD64",
159	g_part_bsd64_methods,
160	sizeof(struct g_part_bsd64_table),
161	.gps_entrysz = sizeof(struct g_part_bsd64_entry),
162	.gps_minent = MAXPARTITIONS64,
163	.gps_maxent = MAXPARTITIONS64
164};
165G_PART_SCHEME_DECLARE(g_part_bsd64);
166MODULE_VERSION(geom_part_bsd64, 0);
167
168#define	EQUUID(a, b)	(memcmp(a, b, sizeof(struct uuid)) == 0)
169static struct uuid bsd64_uuid_unused = GPT_ENT_TYPE_UNUSED;
170static struct uuid bsd64_uuid_dfbsd_swap = GPT_ENT_TYPE_DRAGONFLY_SWAP;
171static struct uuid bsd64_uuid_dfbsd_ufs1 = GPT_ENT_TYPE_DRAGONFLY_UFS1;
172static struct uuid bsd64_uuid_dfbsd_vinum = GPT_ENT_TYPE_DRAGONFLY_VINUM;
173static struct uuid bsd64_uuid_dfbsd_ccd = GPT_ENT_TYPE_DRAGONFLY_CCD;
174static struct uuid bsd64_uuid_dfbsd_legacy = GPT_ENT_TYPE_DRAGONFLY_LEGACY;
175static struct uuid bsd64_uuid_dfbsd_hammer = GPT_ENT_TYPE_DRAGONFLY_HAMMER;
176static struct uuid bsd64_uuid_dfbsd_hammer2 = GPT_ENT_TYPE_DRAGONFLY_HAMMER2;
177static struct uuid bsd64_uuid_freebsd_boot = GPT_ENT_TYPE_FREEBSD_BOOT;
178static struct uuid bsd64_uuid_freebsd_nandfs = GPT_ENT_TYPE_FREEBSD_NANDFS;
179static struct uuid bsd64_uuid_freebsd_swap = GPT_ENT_TYPE_FREEBSD_SWAP;
180static struct uuid bsd64_uuid_freebsd_ufs = GPT_ENT_TYPE_FREEBSD_UFS;
181static struct uuid bsd64_uuid_freebsd_vinum = GPT_ENT_TYPE_FREEBSD_VINUM;
182static struct uuid bsd64_uuid_freebsd_zfs = GPT_ENT_TYPE_FREEBSD_ZFS;
183
184struct bsd64_uuid_alias {
185	struct uuid *uuid;
186	uint8_t fstype;
187	int alias;
188};
189static struct bsd64_uuid_alias dfbsd_alias_match[] = {
190	{ &bsd64_uuid_dfbsd_swap, FS_SWAP, G_PART_ALIAS_DFBSD_SWAP },
191	{ &bsd64_uuid_dfbsd_ufs1, FS_BSDFFS, G_PART_ALIAS_DFBSD_UFS },
192	{ &bsd64_uuid_dfbsd_vinum, FS_VINUM, G_PART_ALIAS_DFBSD_VINUM },
193	{ &bsd64_uuid_dfbsd_ccd, FS_CCD, G_PART_ALIAS_DFBSD_CCD },
194	{ &bsd64_uuid_dfbsd_legacy, FS_OTHER, G_PART_ALIAS_DFBSD_LEGACY },
195	{ &bsd64_uuid_dfbsd_hammer, FS_HAMMER, G_PART_ALIAS_DFBSD_HAMMER },
196	{ &bsd64_uuid_dfbsd_hammer2, FS_HAMMER2, G_PART_ALIAS_DFBSD_HAMMER2 },
197	{ NULL, 0, 0}
198};
199static struct bsd64_uuid_alias fbsd_alias_match[] = {
200	{ &bsd64_uuid_freebsd_boot, FS_OTHER, G_PART_ALIAS_FREEBSD_BOOT },
201	{ &bsd64_uuid_freebsd_swap, FS_OTHER, G_PART_ALIAS_FREEBSD_SWAP },
202	{ &bsd64_uuid_freebsd_ufs, FS_OTHER, G_PART_ALIAS_FREEBSD_UFS },
203	{ &bsd64_uuid_freebsd_zfs, FS_OTHER, G_PART_ALIAS_FREEBSD_ZFS },
204	{ &bsd64_uuid_freebsd_vinum, FS_OTHER, G_PART_ALIAS_FREEBSD_VINUM },
205	{ &bsd64_uuid_freebsd_nandfs, FS_OTHER, G_PART_ALIAS_FREEBSD_NANDFS },
206	{ NULL, 0, 0}
207};
208
209static int
210bsd64_parse_type(const char *type, struct g_part_bsd64_entry *entry)
211{
212	struct uuid tmp;
213	const struct bsd64_uuid_alias *uap;
214	const char *alias;
215	char *p;
216	long lt;
217	int error;
218
219	if (type[0] == '!') {
220		if (type[1] == '\0')
221			return (EINVAL);
222		lt = strtol(type + 1, &p, 0);
223		/* The type specified as number */
224		if (*p == '\0') {
225			if (lt <= 0 || lt > 255)
226				return (EINVAL);
227			entry->fstype = lt;
228			entry->type_uuid = bsd64_uuid_unused;
229			return (0);
230		}
231		/* The type specified as uuid */
232		error = parse_uuid(type + 1, &tmp);
233		if (error != 0)
234			return (error);
235		if (EQUUID(&tmp, &bsd64_uuid_unused))
236			return (EINVAL);
237		for (uap = &dfbsd_alias_match[0]; uap->uuid != NULL; uap++) {
238			if (EQUUID(&tmp, uap->uuid)) {
239				/* Prefer fstype for known uuids */
240				entry->type_uuid = bsd64_uuid_unused;
241				entry->fstype = uap->fstype;
242				return (0);
243			}
244		}
245		entry->type_uuid = tmp;
246		entry->fstype = FS_OTHER;
247		return (0);
248	}
249	/* The type specified as symbolic alias name */
250	for (uap = &fbsd_alias_match[0]; uap->uuid != NULL; uap++) {
251		alias = g_part_alias_name(uap->alias);
252		if (!strcasecmp(type, alias)) {
253			entry->type_uuid = *uap->uuid;
254			entry->fstype = uap->fstype;
255			return (0);
256		}
257	}
258	for (uap = &dfbsd_alias_match[0]; uap->uuid != NULL; uap++) {
259		alias = g_part_alias_name(uap->alias);
260		if (!strcasecmp(type, alias)) {
261			entry->type_uuid = bsd64_uuid_unused;
262			entry->fstype = uap->fstype;
263			return (0);
264		}
265	}
266	return (EINVAL);
267}
268
269static int
270g_part_bsd64_add(struct g_part_table *basetable, struct g_part_entry *baseentry,
271    struct g_part_parms *gpp)
272{
273	struct g_part_bsd64_entry *entry;
274
275	if (gpp->gpp_parms & G_PART_PARM_LABEL)
276		return (EINVAL);
277
278	entry = (struct g_part_bsd64_entry *)baseentry;
279	if (bsd64_parse_type(gpp->gpp_type, entry) != 0)
280		return (EINVAL);
281	kern_uuidgen(&entry->stor_uuid, 1);
282	return (0);
283}
284
285static int
286g_part_bsd64_bootcode(struct g_part_table *basetable, struct g_part_parms *gpp)
287{
288
289	return (EOPNOTSUPP);
290}
291
292#define	PALIGN_SIZE	(1024 * 1024)
293#define	PALIGN_MASK	(PALIGN_SIZE - 1)
294#define	BLKSIZE		(4 * 1024)
295#define	BOOTSIZE	(32 * 1024)
296#define	DALIGN_SIZE	(32 * 1024)
297static int
298g_part_bsd64_create(struct g_part_table *basetable, struct g_part_parms *gpp)
299{
300	struct g_part_bsd64_table *table;
301	struct g_part_entry *baseentry;
302	struct g_provider *pp;
303	uint64_t blkmask, pbase;
304	uint32_t blksize, ressize;
305
306	pp = gpp->gpp_provider;
307	if (pp->mediasize < 2* PALIGN_SIZE)
308		return (ENOSPC);
309
310	/*
311	 * Use at least 4KB block size. Blksize is stored in the d_align.
312	 * XXX: Actually it is used just for calculate d_bbase and used
313	 * for better alignment in bsdlabel64(8).
314	 */
315	blksize = pp->sectorsize < BLKSIZE ? BLKSIZE: pp->sectorsize;
316	blkmask = blksize - 1;
317	/* Reserve enough space for RESPARTITIONS64 partitions. */
318	ressize = offsetof(struct disklabel64, d_partitions[RESPARTITIONS64]);
319	ressize = (ressize + blkmask) & ~blkmask;
320	/*
321	 * Reserve enough space for bootcode and align first allocatable
322	 * offset to PALIGN_SIZE.
323	 * XXX: Currently DragonFlyBSD has 32KB bootcode, but the size could
324	 * be bigger, because it is possible change it (it is equal pbase-bbase)
325	 * in the bsdlabel64(8).
326	 */
327	pbase = ressize + ((BOOTSIZE + blkmask) & ~blkmask);
328	pbase = (pbase + PALIGN_MASK) & ~PALIGN_MASK;
329	/*
330	 * Take physical offset into account and make first allocatable
331	 * offset 32KB aligned to the start of the physical disk.
332	 * XXX: Actually there are no such restrictions, this is how
333	 * DragonFlyBSD behaves.
334	 */
335	pbase += DALIGN_SIZE - pp->stripeoffset % DALIGN_SIZE;
336
337	table = (struct g_part_bsd64_table *)basetable;
338	table->d_align = blksize;
339	table->d_bbase = ressize / pp->sectorsize;
340	table->d_abase = ((pp->mediasize - ressize) &
341	    ~blkmask) / pp->sectorsize;
342	kern_uuidgen(&table->d_stor_uuid, 1);
343	basetable->gpt_first = pbase / pp->sectorsize;
344	basetable->gpt_last = table->d_abase - 1; /* XXX */
345	/*
346	 * Create 'c' partition and make it internal, so user will not be
347	 * able use it.
348	 */
349	baseentry = g_part_new_entry(basetable, RAW_PART + 1, 0, 0);
350	baseentry->gpe_internal = 1;
351	return (0);
352}
353
354static int
355g_part_bsd64_destroy(struct g_part_table *basetable, struct g_part_parms *gpp)
356{
357	struct g_provider *pp;
358
359	pp = LIST_FIRST(&basetable->gpt_gp->consumer)->provider;
360	if (pp->sectorsize > offsetof(struct disklabel64, d_magic))
361		basetable->gpt_smhead |= 1;
362	else
363		basetable->gpt_smhead |= 3;
364	return (0);
365}
366
367static void
368g_part_bsd64_dumpconf(struct g_part_table *basetable,
369    struct g_part_entry *baseentry, struct sbuf *sb, const char *indent)
370{
371	struct g_part_bsd64_table *table;
372	struct g_part_bsd64_entry *entry;
373	char buf[sizeof(table->d_packname)];
374
375	entry = (struct g_part_bsd64_entry *)baseentry;
376	if (indent == NULL) {
377		/* conftxt: libdisk compatibility */
378		sbuf_printf(sb, " xs BSD64 xt %u", entry->fstype);
379	} else if (entry != NULL) {
380		/* confxml: partition entry information */
381		sbuf_printf(sb, "%s<rawtype>%u</rawtype>\n", indent,
382		    entry->fstype);
383		if (!EQUUID(&bsd64_uuid_unused, &entry->type_uuid)) {
384			sbuf_printf(sb, "%s<type_uuid>", indent);
385			sbuf_printf_uuid(sb, &entry->type_uuid);
386			sbuf_cat(sb, "</type_uuid>\n");
387		}
388		sbuf_printf(sb, "%s<stor_uuid>", indent);
389		sbuf_printf_uuid(sb, &entry->stor_uuid);
390		sbuf_cat(sb, "</stor_uuid>\n");
391	} else {
392		/* confxml: scheme information */
393		table = (struct g_part_bsd64_table *)basetable;
394		sbuf_printf(sb, "%s<bootbase>%ju</bootbase>\n", indent,
395		    (uintmax_t)table->d_bbase);
396		if (table->d_abase)
397			sbuf_printf(sb, "%s<backupbase>%ju</backupbase>\n",
398			    indent, (uintmax_t)table->d_abase);
399		sbuf_printf(sb, "%s<stor_uuid>", indent);
400		sbuf_printf_uuid(sb, &table->d_stor_uuid);
401		sbuf_cat(sb, "</stor_uuid>\n");
402		sbuf_printf(sb, "%s<label>", indent);
403		strncpy(buf, table->d_packname, sizeof(buf) - 1);
404		buf[sizeof(buf) - 1] = '\0';
405		g_conf_cat_escaped(sb, buf);
406		sbuf_cat(sb, "</label>\n");
407	}
408}
409
410static int
411g_part_bsd64_dumpto(struct g_part_table *table, struct g_part_entry *baseentry)
412{
413	struct g_part_bsd64_entry *entry;
414
415	/* Allow dumping to a swap partition. */
416	entry = (struct g_part_bsd64_entry *)baseentry;
417	if (entry->fstype == FS_SWAP ||
418	    EQUUID(&entry->type_uuid, &bsd64_uuid_dfbsd_swap) ||
419	    EQUUID(&entry->type_uuid, &bsd64_uuid_freebsd_swap))
420		return (1);
421	return (0);
422}
423
424static int
425g_part_bsd64_modify(struct g_part_table *basetable,
426    struct g_part_entry *baseentry, struct g_part_parms *gpp)
427{
428	struct g_part_bsd64_entry *entry;
429
430	if (gpp->gpp_parms & G_PART_PARM_LABEL)
431		return (EINVAL);
432
433	entry = (struct g_part_bsd64_entry *)baseentry;
434	if (gpp->gpp_parms & G_PART_PARM_TYPE)
435		return (bsd64_parse_type(gpp->gpp_type, entry));
436	return (0);
437}
438
439static int
440g_part_bsd64_resize(struct g_part_table *basetable,
441    struct g_part_entry *baseentry, struct g_part_parms *gpp)
442{
443	struct g_part_bsd64_table *table;
444	struct g_provider *pp;
445
446	if (baseentry == NULL) {
447		pp = LIST_FIRST(&basetable->gpt_gp->consumer)->provider;
448		table = (struct g_part_bsd64_table *)basetable;
449		table->d_abase =
450		    rounddown2(pp->mediasize - table->d_bbase * pp->sectorsize,
451		        table->d_align) / pp->sectorsize;
452		basetable->gpt_last = table->d_abase - 1;
453		return (0);
454	}
455	baseentry->gpe_end = baseentry->gpe_start + gpp->gpp_size - 1;
456	return (0);
457}
458
459static const char *
460g_part_bsd64_name(struct g_part_table *table, struct g_part_entry *baseentry,
461    char *buf, size_t bufsz)
462{
463
464	snprintf(buf, bufsz, "%c", 'a' + baseentry->gpe_index - 1);
465	return (buf);
466}
467
468static int
469g_part_bsd64_probe(struct g_part_table *table, struct g_consumer *cp)
470{
471	struct g_provider *pp;
472	uint32_t v;
473	int error;
474	u_char *buf;
475
476	pp = cp->provider;
477	if (pp->mediasize < 2 * PALIGN_SIZE)
478		return (ENOSPC);
479	v = rounddown2(pp->sectorsize + offsetof(struct disklabel64, d_magic),
480		       pp->sectorsize);
481	buf = g_read_data(cp, 0, v, &error);
482	if (buf == NULL)
483		return (error);
484	v = le32dec(buf + offsetof(struct disklabel64, d_magic));
485	g_free(buf);
486	return (v == DISKMAGIC64 ? G_PART_PROBE_PRI_HIGH: ENXIO);
487}
488
489static int
490g_part_bsd64_read(struct g_part_table *basetable, struct g_consumer *cp)
491{
492	struct g_part_bsd64_table *table;
493	struct g_part_bsd64_entry *entry;
494	struct g_part_entry *baseentry;
495	struct g_provider *pp;
496	struct disklabel64 *dlp;
497	uint64_t v64, sz;
498	uint32_t v32;
499	int error, index;
500	u_char *buf;
501
502	pp = cp->provider;
503	table = (struct g_part_bsd64_table *)basetable;
504	v32 = roundup2(sizeof(struct disklabel64), pp->sectorsize);
505	buf = g_read_data(cp, 0, v32, &error);
506	if (buf == NULL)
507		return (error);
508
509	dlp = (struct disklabel64 *)buf;
510	basetable->gpt_entries = le32toh(dlp->d_npartitions);
511	if (basetable->gpt_entries > MAXPARTITIONS64 ||
512	    basetable->gpt_entries < 1)
513		goto invalid_label;
514	v32 = le32toh(dlp->d_crc);
515	dlp->d_crc = 0;
516	if (crc32(&dlp->d_magic, offsetof(struct disklabel64,
517	    d_partitions[basetable->gpt_entries]) -
518	    offsetof(struct disklabel64, d_magic)) != v32)
519		goto invalid_label;
520	table->d_align = le32toh(dlp->d_align);
521	if (table->d_align == 0 || (table->d_align & (pp->sectorsize - 1)))
522		goto invalid_label;
523	if (le64toh(dlp->d_total_size) > pp->mediasize)
524		goto invalid_label;
525	v64 = le64toh(dlp->d_pbase);
526	if (v64 % pp->sectorsize)
527		goto invalid_label;
528	basetable->gpt_first = v64 / pp->sectorsize;
529	v64 = le64toh(dlp->d_pstop);
530	if (v64 % pp->sectorsize)
531		goto invalid_label;
532	basetable->gpt_last = v64 / pp->sectorsize;
533	basetable->gpt_isleaf = 1;
534	v64 = le64toh(dlp->d_bbase);
535	if (v64 % pp->sectorsize)
536		goto invalid_label;
537	table->d_bbase = v64 / pp->sectorsize;
538	v64 = le64toh(dlp->d_abase);
539	if (v64 % pp->sectorsize)
540		goto invalid_label;
541	table->d_abase = v64 / pp->sectorsize;
542	le_uuid_dec(&dlp->d_stor_uuid, &table->d_stor_uuid);
543	for (index = basetable->gpt_entries - 1; index >= 0; index--) {
544		if (index == RAW_PART) {
545			/* Skip 'c' partition. */
546			baseentry = g_part_new_entry(basetable,
547			    index + 1, 0, 0);
548			baseentry->gpe_internal = 1;
549			continue;
550		}
551		v64 = le64toh(dlp->d_partitions[index].p_boffset);
552		sz = le64toh(dlp->d_partitions[index].p_bsize);
553		if (sz == 0 && v64 == 0)
554			continue;
555		if (sz == 0 || (v64 % pp->sectorsize) || (sz % pp->sectorsize))
556			goto invalid_label;
557		baseentry = g_part_new_entry(basetable, index + 1,
558		    v64 / pp->sectorsize, (v64 + sz) / pp->sectorsize - 1);
559		entry = (struct g_part_bsd64_entry *)baseentry;
560		le_uuid_dec(&dlp->d_partitions[index].p_type_uuid,
561		    &entry->type_uuid);
562		le_uuid_dec(&dlp->d_partitions[index].p_stor_uuid,
563		    &entry->stor_uuid);
564		entry->fstype = dlp->d_partitions[index].p_fstype;
565	}
566	bcopy(dlp->d_reserved0, table->d_reserved0,
567	    sizeof(table->d_reserved0));
568	bcopy(dlp->d_packname, table->d_packname, sizeof(table->d_packname));
569	bcopy(dlp->d_reserved, table->d_reserved, sizeof(table->d_reserved));
570	g_free(buf);
571	return (0);
572
573invalid_label:
574	g_free(buf);
575	return (EINVAL);
576}
577
578static const char *
579g_part_bsd64_type(struct g_part_table *basetable, struct g_part_entry *baseentry,
580    char *buf, size_t bufsz)
581{
582	struct g_part_bsd64_entry *entry;
583	struct bsd64_uuid_alias *uap;
584
585	entry = (struct g_part_bsd64_entry *)baseentry;
586	if (entry->fstype != FS_OTHER) {
587		for (uap = &dfbsd_alias_match[0]; uap->uuid != NULL; uap++)
588			if (uap->fstype == entry->fstype)
589				return (g_part_alias_name(uap->alias));
590	} else {
591		for (uap = &fbsd_alias_match[0]; uap->uuid != NULL; uap++)
592			if (EQUUID(uap->uuid, &entry->type_uuid))
593				return (g_part_alias_name(uap->alias));
594		for (uap = &dfbsd_alias_match[0]; uap->uuid != NULL; uap++)
595			if (EQUUID(uap->uuid, &entry->type_uuid))
596				return (g_part_alias_name(uap->alias));
597	}
598	if (EQUUID(&bsd64_uuid_unused, &entry->type_uuid))
599		snprintf(buf, bufsz, "!%d", entry->fstype);
600	else {
601		buf[0] = '!';
602		snprintf_uuid(buf + 1, bufsz - 1, &entry->type_uuid);
603	}
604	return (buf);
605}
606
607static int
608g_part_bsd64_write(struct g_part_table *basetable, struct g_consumer *cp)
609{
610	struct g_provider *pp;
611	struct g_part_entry *baseentry;
612	struct g_part_bsd64_entry *entry;
613	struct g_part_bsd64_table *table;
614	struct disklabel64 *dlp;
615	uint32_t v, sz;
616	int error, index;
617
618	pp = cp->provider;
619	table = (struct g_part_bsd64_table *)basetable;
620	sz = roundup2(sizeof(struct disklabel64), pp->sectorsize);
621	dlp = g_malloc(sz, M_WAITOK | M_ZERO);
622
623	memcpy(dlp->d_reserved0, table->d_reserved0,
624	    sizeof(table->d_reserved0));
625	memcpy(dlp->d_packname, table->d_packname, sizeof(table->d_packname));
626	memcpy(dlp->d_reserved, table->d_reserved, sizeof(table->d_reserved));
627	le32enc(&dlp->d_magic, DISKMAGIC64);
628	le32enc(&dlp->d_align, table->d_align);
629	le32enc(&dlp->d_npartitions, basetable->gpt_entries);
630	le_uuid_enc(&dlp->d_stor_uuid, &table->d_stor_uuid);
631	le64enc(&dlp->d_total_size, pp->mediasize);
632	le64enc(&dlp->d_bbase, table->d_bbase * pp->sectorsize);
633	le64enc(&dlp->d_pbase, basetable->gpt_first * pp->sectorsize);
634	le64enc(&dlp->d_pstop, basetable->gpt_last * pp->sectorsize);
635	le64enc(&dlp->d_abase, table->d_abase * pp->sectorsize);
636
637	LIST_FOREACH(baseentry, &basetable->gpt_entry, gpe_entry) {
638		if (baseentry->gpe_deleted)
639			continue;
640		index = baseentry->gpe_index - 1;
641		entry = (struct g_part_bsd64_entry *)baseentry;
642		if (index == RAW_PART)
643			continue;
644		le64enc(&dlp->d_partitions[index].p_boffset,
645		    baseentry->gpe_start * pp->sectorsize);
646		le64enc(&dlp->d_partitions[index].p_bsize, pp->sectorsize *
647		    (baseentry->gpe_end - baseentry->gpe_start + 1));
648		dlp->d_partitions[index].p_fstype = entry->fstype;
649		le_uuid_enc(&dlp->d_partitions[index].p_type_uuid,
650		    &entry->type_uuid);
651		le_uuid_enc(&dlp->d_partitions[index].p_stor_uuid,
652		    &entry->stor_uuid);
653	}
654	/* Calculate checksum. */
655	v = offsetof(struct disklabel64,
656	    d_partitions[basetable->gpt_entries]) -
657	    offsetof(struct disklabel64, d_magic);
658	le32enc(&dlp->d_crc, crc32(&dlp->d_magic, v));
659	error = g_write_data(cp, 0, dlp, sz);
660	g_free(dlp);
661	return (error);
662}
663