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