1/*-
2 * Copyright (c) 2008 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$");
29
30#include <sys/param.h>
31#include <sys/bio.h>
32#include <sys/endian.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 <sys/vtoc.h>
44#include <geom/geom.h>
45#include <geom/geom_int.h>
46#include <geom/part/g_part.h>
47
48#include "g_part_if.h"
49
50FEATURE(geom_part_vtoc8, "GEOM partitioning class for SMI VTOC8 disk labels");
51
52struct g_part_vtoc8_table {
53	struct g_part_table	base;
54	struct vtoc8		vtoc;
55	uint32_t		secpercyl;
56};
57
58static int g_part_vtoc8_add(struct g_part_table *, struct g_part_entry *,
59    struct g_part_parms *);
60static int g_part_vtoc8_create(struct g_part_table *, struct g_part_parms *);
61static int g_part_vtoc8_destroy(struct g_part_table *, struct g_part_parms *);
62static void g_part_vtoc8_dumpconf(struct g_part_table *,
63    struct g_part_entry *, struct sbuf *, const char *);
64static int g_part_vtoc8_dumpto(struct g_part_table *, struct g_part_entry *);
65static int g_part_vtoc8_modify(struct g_part_table *, struct g_part_entry *,
66    struct g_part_parms *);
67static const char *g_part_vtoc8_name(struct g_part_table *,
68    struct g_part_entry *, char *, size_t);
69static int g_part_vtoc8_probe(struct g_part_table *, struct g_consumer *);
70static int g_part_vtoc8_read(struct g_part_table *, struct g_consumer *);
71static const char *g_part_vtoc8_type(struct g_part_table *,
72    struct g_part_entry *, char *, size_t);
73static int g_part_vtoc8_write(struct g_part_table *, struct g_consumer *);
74static int g_part_vtoc8_resize(struct g_part_table *, struct g_part_entry *,
75    struct g_part_parms *);
76
77static kobj_method_t g_part_vtoc8_methods[] = {
78	KOBJMETHOD(g_part_add,		g_part_vtoc8_add),
79	KOBJMETHOD(g_part_create,	g_part_vtoc8_create),
80	KOBJMETHOD(g_part_destroy,	g_part_vtoc8_destroy),
81	KOBJMETHOD(g_part_dumpconf,	g_part_vtoc8_dumpconf),
82	KOBJMETHOD(g_part_dumpto,	g_part_vtoc8_dumpto),
83	KOBJMETHOD(g_part_modify,	g_part_vtoc8_modify),
84	KOBJMETHOD(g_part_resize,	g_part_vtoc8_resize),
85	KOBJMETHOD(g_part_name,		g_part_vtoc8_name),
86	KOBJMETHOD(g_part_probe,	g_part_vtoc8_probe),
87	KOBJMETHOD(g_part_read,		g_part_vtoc8_read),
88	KOBJMETHOD(g_part_type,		g_part_vtoc8_type),
89	KOBJMETHOD(g_part_write,	g_part_vtoc8_write),
90	{ 0, 0 }
91};
92
93static struct g_part_scheme g_part_vtoc8_scheme = {
94	"VTOC8",
95	g_part_vtoc8_methods,
96	sizeof(struct g_part_vtoc8_table),
97	.gps_entrysz = sizeof(struct g_part_entry),
98	.gps_minent = VTOC8_NPARTS,
99	.gps_maxent = VTOC8_NPARTS,
100};
101G_PART_SCHEME_DECLARE(g_part_vtoc8);
102
103static int
104vtoc8_parse_type(const char *type, uint16_t *tag)
105{
106	const char *alias;
107	char *endp;
108	long lt;
109
110	if (type[0] == '!') {
111		lt = strtol(type + 1, &endp, 0);
112		if (type[1] == '\0' || *endp != '\0' || lt <= 0 ||
113		    lt >= 65536)
114			return (EINVAL);
115		*tag = (uint16_t)lt;
116		return (0);
117	}
118	alias = g_part_alias_name(G_PART_ALIAS_FREEBSD_NANDFS);
119	if (!strcasecmp(type, alias)) {
120		*tag = VTOC_TAG_FREEBSD_NANDFS;
121		return (0);
122	}
123	alias = g_part_alias_name(G_PART_ALIAS_FREEBSD_SWAP);
124	if (!strcasecmp(type, alias)) {
125		*tag = VTOC_TAG_FREEBSD_SWAP;
126		return (0);
127	}
128	alias = g_part_alias_name(G_PART_ALIAS_FREEBSD_UFS);
129	if (!strcasecmp(type, alias)) {
130		*tag = VTOC_TAG_FREEBSD_UFS;
131		return (0);
132	}
133	alias = g_part_alias_name(G_PART_ALIAS_FREEBSD_VINUM);
134	if (!strcasecmp(type, alias)) {
135		*tag = VTOC_TAG_FREEBSD_VINUM;
136		return (0);
137	}
138	alias = g_part_alias_name(G_PART_ALIAS_FREEBSD_ZFS);
139	if (!strcasecmp(type, alias)) {
140		*tag = VTOC_TAG_FREEBSD_ZFS;
141		return (0);
142	}
143	return (EINVAL);
144}
145
146static int
147vtoc8_align(struct g_part_vtoc8_table *table, uint64_t *start, uint64_t *size)
148{
149
150	if (*size < table->secpercyl)
151		return (EINVAL);
152	if (start != NULL && (*start % table->secpercyl)) {
153		*size += (*start % table->secpercyl) - table->secpercyl;
154		*start -= (*start % table->secpercyl) - table->secpercyl;
155	}
156	if (*size % table->secpercyl)
157		*size -= (*size % table->secpercyl);
158	if (*size < table->secpercyl)
159		return (EINVAL);
160	return (0);
161}
162
163static int
164g_part_vtoc8_add(struct g_part_table *basetable, struct g_part_entry *entry,
165    struct g_part_parms *gpp)
166{
167	struct g_part_vtoc8_table *table;
168	int error, index;
169	uint64_t start, size;
170	uint16_t tag;
171
172	if (gpp->gpp_parms & G_PART_PARM_LABEL)
173		return (EINVAL);
174
175	error = vtoc8_parse_type(gpp->gpp_type, &tag);
176	if (error)
177		return (error);
178
179	table = (struct g_part_vtoc8_table *)basetable;
180	index = entry->gpe_index - 1;
181	start = gpp->gpp_start;
182	size = gpp->gpp_size;
183	if (vtoc8_align(table, &start, &size) != 0)
184		return (EINVAL);
185
186	KASSERT(entry->gpe_start <= start, (__func__));
187	KASSERT(entry->gpe_end >= start + size - 1, (__func__));
188	entry->gpe_start = start;
189	entry->gpe_end = start + size - 1;
190
191	be16enc(&table->vtoc.part[index].tag, tag);
192	be16enc(&table->vtoc.part[index].flag, 0);
193	be32enc(&table->vtoc.timestamp[index], 0);
194	be32enc(&table->vtoc.map[index].cyl, start / table->secpercyl);
195	be32enc(&table->vtoc.map[index].nblks, size);
196	return (0);
197}
198
199static int
200g_part_vtoc8_create(struct g_part_table *basetable, struct g_part_parms *gpp)
201{
202	struct g_provider *pp;
203	struct g_part_entry *entry;
204	struct g_part_vtoc8_table *table;
205	uint64_t msize;
206	uint32_t acyls, ncyls, pcyls;
207
208	pp = gpp->gpp_provider;
209
210	if (pp->sectorsize < sizeof(struct vtoc8))
211		return (ENOSPC);
212	if (pp->sectorsize > sizeof(struct vtoc8))
213		return (ENXIO);
214
215	table = (struct g_part_vtoc8_table *)basetable;
216
217	msize = MIN(pp->mediasize / pp->sectorsize, UINT32_MAX);
218	table->secpercyl = basetable->gpt_sectors * basetable->gpt_heads;
219	pcyls = msize / table->secpercyl;
220	acyls = 2;
221	ncyls = pcyls - acyls;
222	msize = ncyls * table->secpercyl;
223
224	sprintf(table->vtoc.ascii, "FreeBSD%lldM cyl %u alt %u hd %u sec %u",
225	    (long long)(msize / 2048), ncyls, acyls, basetable->gpt_heads,
226	    basetable->gpt_sectors);
227	be32enc(&table->vtoc.version, VTOC_VERSION);
228	be16enc(&table->vtoc.nparts, VTOC8_NPARTS);
229	be32enc(&table->vtoc.sanity, VTOC_SANITY);
230	be16enc(&table->vtoc.rpm, 3600);
231	be16enc(&table->vtoc.physcyls, pcyls);
232	be16enc(&table->vtoc.ncyls, ncyls);
233	be16enc(&table->vtoc.altcyls, acyls);
234	be16enc(&table->vtoc.nheads, basetable->gpt_heads);
235	be16enc(&table->vtoc.nsecs, basetable->gpt_sectors);
236	be16enc(&table->vtoc.magic, VTOC_MAGIC);
237
238	basetable->gpt_first = 0;
239	basetable->gpt_last = msize - 1;
240	basetable->gpt_isleaf = 1;
241
242	entry = g_part_new_entry(basetable, VTOC_RAW_PART + 1,
243	    basetable->gpt_first, basetable->gpt_last);
244	entry->gpe_internal = 1;
245	be16enc(&table->vtoc.part[VTOC_RAW_PART].tag, VTOC_TAG_BACKUP);
246	be32enc(&table->vtoc.map[VTOC_RAW_PART].nblks, msize);
247	return (0);
248}
249
250static int
251g_part_vtoc8_destroy(struct g_part_table *basetable, struct g_part_parms *gpp)
252{
253
254	/* Wipe the first sector to clear the partitioning. */
255	basetable->gpt_smhead |= 1;
256	return (0);
257}
258
259static void
260g_part_vtoc8_dumpconf(struct g_part_table *basetable,
261    struct g_part_entry *entry, struct sbuf *sb, const char *indent)
262{
263	struct g_part_vtoc8_table *table;
264
265	table = (struct g_part_vtoc8_table *)basetable;
266	if (indent == NULL) {
267		/* conftxt: libdisk compatibility */
268		sbuf_printf(sb, " xs SUN sc %u hd %u alt %u",
269		    be16dec(&table->vtoc.nsecs), be16dec(&table->vtoc.nheads),
270		    be16dec(&table->vtoc.altcyls));
271	} else if (entry != NULL) {
272		/* confxml: partition entry information */
273		sbuf_printf(sb, "%s<rawtype>%u</rawtype>\n", indent,
274		    be16dec(&table->vtoc.part[entry->gpe_index - 1].tag));
275	} else {
276		/* confxml: scheme information */
277	}
278}
279
280static int
281g_part_vtoc8_dumpto(struct g_part_table *basetable,
282    struct g_part_entry *entry)
283{
284	struct g_part_vtoc8_table *table;
285	uint16_t tag;
286
287	/*
288	 * Allow dumping to a swap partition or a partition that
289	 * has no type.
290	 */
291	table = (struct g_part_vtoc8_table *)basetable;
292	tag = be16dec(&table->vtoc.part[entry->gpe_index - 1].tag);
293	return ((tag == 0 || tag == VTOC_TAG_FREEBSD_SWAP ||
294	    tag == VTOC_TAG_SWAP) ? 1 : 0);
295}
296
297static int
298g_part_vtoc8_modify(struct g_part_table *basetable,
299    struct g_part_entry *entry, struct g_part_parms *gpp)
300{
301	struct g_part_vtoc8_table *table;
302	int error;
303	uint16_t tag;
304
305	if (gpp->gpp_parms & G_PART_PARM_LABEL)
306		return (EINVAL);
307
308	table = (struct g_part_vtoc8_table *)basetable;
309	if (gpp->gpp_parms & G_PART_PARM_TYPE) {
310		error = vtoc8_parse_type(gpp->gpp_type, &tag);
311		if (error)
312			return(error);
313
314		be16enc(&table->vtoc.part[entry->gpe_index - 1].tag, tag);
315	}
316	return (0);
317}
318
319static int
320vtoc8_set_rawsize(struct g_part_table *basetable, struct g_provider *pp)
321{
322	struct g_part_vtoc8_table *table;
323	struct g_part_entry *baseentry;
324	off_t msize;
325	uint32_t acyls, ncyls, pcyls;
326
327	table = (struct g_part_vtoc8_table *)basetable;
328	msize = MIN(pp->mediasize / pp->sectorsize, UINT32_MAX);
329	pcyls = msize / table->secpercyl;
330	if (pcyls > UINT16_MAX)
331		return (ERANGE);
332	acyls = be16dec(&table->vtoc.altcyls);
333	ncyls = pcyls - acyls;
334	msize = ncyls * table->secpercyl;
335	basetable->gpt_last = msize - 1;
336
337	bzero(table->vtoc.ascii, sizeof(table->vtoc.ascii));
338	sprintf(table->vtoc.ascii, "FreeBSD%lldM cyl %u alt %u hd %u sec %u",
339	    (long long)(msize / 2048), ncyls, acyls, basetable->gpt_heads,
340	    basetable->gpt_sectors);
341	be16enc(&table->vtoc.physcyls, pcyls);
342	be16enc(&table->vtoc.ncyls, ncyls);
343	be32enc(&table->vtoc.map[VTOC_RAW_PART].nblks, msize);
344	if (be32dec(&table->vtoc.sanity) == VTOC_SANITY)
345		be16enc(&table->vtoc.part[VTOC_RAW_PART].tag, VTOC_TAG_BACKUP);
346	LIST_FOREACH(baseentry, &basetable->gpt_entry, gpe_entry) {
347		if (baseentry->gpe_index == VTOC_RAW_PART + 1) {
348			baseentry->gpe_end = basetable->gpt_last;
349			return (0);
350		}
351	}
352	return (ENXIO);
353}
354
355static int
356g_part_vtoc8_resize(struct g_part_table *basetable,
357    struct g_part_entry *entry, struct g_part_parms *gpp)
358{
359	struct g_part_vtoc8_table *table;
360	struct g_provider *pp;
361	uint64_t size;
362
363	if (entry == NULL) {
364		pp = LIST_FIRST(&basetable->gpt_gp->consumer)->provider;
365		return (vtoc8_set_rawsize(basetable, pp));
366	}
367	table = (struct g_part_vtoc8_table *)basetable;
368	size = gpp->gpp_size;
369	if (vtoc8_align(table, NULL, &size) != 0)
370		return (EINVAL);
371	/* XXX: prevent unexpected shrinking. */
372	pp = entry->gpe_pp;
373	if ((g_debugflags & 0x10) == 0 && size < gpp->gpp_size &&
374	    pp->mediasize / pp->sectorsize > size)
375		return (EBUSY);
376	entry->gpe_end = entry->gpe_start + size - 1;
377	be32enc(&table->vtoc.map[entry->gpe_index - 1].nblks, size);
378
379	return (0);
380}
381
382static const char *
383g_part_vtoc8_name(struct g_part_table *table, struct g_part_entry *baseentry,
384    char *buf, size_t bufsz)
385{
386
387	snprintf(buf, bufsz, "%c", 'a' + baseentry->gpe_index - 1);
388	return (buf);
389}
390
391static int
392g_part_vtoc8_probe(struct g_part_table *table, struct g_consumer *cp)
393{
394	struct g_provider *pp;
395	u_char *buf;
396	int error, ofs, res;
397	uint16_t cksum, magic;
398
399	pp = cp->provider;
400
401	/* Sanity-check the provider. */
402	if (pp->sectorsize != sizeof(struct vtoc8))
403		return (ENOSPC);
404
405	/* Check that there's a disklabel. */
406	buf = g_read_data(cp, 0, pp->sectorsize, &error);
407	if (buf == NULL)
408		return (error);
409
410	res = ENXIO;	/* Assume mismatch */
411
412	/* Check the magic */
413	magic = be16dec(buf + offsetof(struct vtoc8, magic));
414	if (magic != VTOC_MAGIC)
415		goto out;
416
417	/* Check the sum */
418	cksum = 0;
419	for (ofs = 0; ofs < sizeof(struct vtoc8); ofs += 2)
420		cksum ^= be16dec(buf + ofs);
421	if (cksum != 0)
422		goto out;
423
424	res = G_PART_PROBE_PRI_NORM;
425
426 out:
427	g_free(buf);
428	return (res);
429}
430
431static int
432g_part_vtoc8_read(struct g_part_table *basetable, struct g_consumer *cp)
433{
434	struct g_provider *pp;
435	struct g_part_vtoc8_table *table;
436	struct g_part_entry *entry;
437	u_char *buf;
438	off_t chs, msize;
439	uint64_t offset, size;
440	u_int cyls, heads, sectors;
441	int error, index, withtags;
442	uint16_t tag;
443
444	pp = cp->provider;
445	buf = g_read_data(cp, 0, pp->sectorsize, &error);
446	if (buf == NULL)
447		return (error);
448
449	table = (struct g_part_vtoc8_table *)basetable;
450	bcopy(buf, &table->vtoc, sizeof(table->vtoc));
451	g_free(buf);
452
453	msize = MIN(pp->mediasize / pp->sectorsize, UINT32_MAX);
454	sectors = be16dec(&table->vtoc.nsecs);
455	if (sectors < 1)
456		goto invalid_label;
457	if (sectors != basetable->gpt_sectors && !basetable->gpt_fixgeom) {
458		g_part_geometry_heads(msize, sectors, &chs, &heads);
459		if (chs != 0) {
460			basetable->gpt_sectors = sectors;
461			basetable->gpt_heads = heads;
462		}
463	}
464
465	heads = be16dec(&table->vtoc.nheads);
466	if (heads < 1)
467		goto invalid_label;
468	if (heads != basetable->gpt_heads && !basetable->gpt_fixgeom)
469		basetable->gpt_heads = heads;
470	/*
471	 * Except for ATA disks > 32GB, Solaris uses the native geometry
472	 * as reported by the target for the labels while da(4) typically
473	 * uses a synthetic one so we don't complain too loudly if these
474	 * geometries don't match.
475	 */
476	if (bootverbose && (sectors != basetable->gpt_sectors ||
477	    heads != basetable->gpt_heads))
478		printf("GEOM: %s: geometry does not match VTOC8 label "
479		    "(label: %uh,%us GEOM: %uh,%us).\n", pp->name, heads,
480		    sectors, basetable->gpt_heads, basetable->gpt_sectors);
481
482	table->secpercyl = heads * sectors;
483	cyls = be16dec(&table->vtoc.ncyls);
484	chs = cyls * table->secpercyl;
485	if (chs < 1 || chs > msize)
486		goto invalid_label;
487
488	basetable->gpt_first = 0;
489	basetable->gpt_last = chs - 1;
490	basetable->gpt_isleaf = 1;
491
492	withtags = (be32dec(&table->vtoc.sanity) == VTOC_SANITY) ? 1 : 0;
493	if (!withtags) {
494		printf("GEOM: %s: adding VTOC8 information.\n", pp->name);
495		be32enc(&table->vtoc.version, VTOC_VERSION);
496		bzero(&table->vtoc.volume, VTOC_VOLUME_LEN);
497		be16enc(&table->vtoc.nparts, VTOC8_NPARTS);
498		bzero(&table->vtoc.part, sizeof(table->vtoc.part));
499		be32enc(&table->vtoc.sanity, VTOC_SANITY);
500	}
501
502	basetable->gpt_entries = be16dec(&table->vtoc.nparts);
503	if (basetable->gpt_entries < g_part_vtoc8_scheme.gps_minent ||
504	    basetable->gpt_entries > g_part_vtoc8_scheme.gps_maxent)
505		goto invalid_label;
506
507	for (index = basetable->gpt_entries - 1; index >= 0; index--) {
508		offset = be32dec(&table->vtoc.map[index].cyl) *
509		    table->secpercyl;
510		size = be32dec(&table->vtoc.map[index].nblks);
511		if (size == 0)
512			continue;
513		if (withtags)
514			tag = be16dec(&table->vtoc.part[index].tag);
515		else
516			tag = (index == VTOC_RAW_PART)
517			    ? VTOC_TAG_BACKUP
518			    : VTOC_TAG_UNASSIGNED;
519
520		if (index == VTOC_RAW_PART && tag != VTOC_TAG_BACKUP)
521			continue;
522		if (index != VTOC_RAW_PART && tag == VTOC_TAG_BACKUP)
523			continue;
524		entry = g_part_new_entry(basetable, index + 1, offset,
525		    offset + size - 1);
526		if (tag == VTOC_TAG_BACKUP)
527			entry->gpe_internal = 1;
528
529		if (!withtags)
530			be16enc(&table->vtoc.part[index].tag, tag);
531	}
532
533	return (0);
534
535 invalid_label:
536	printf("GEOM: %s: invalid VTOC8 label.\n", pp->name);
537	return (EINVAL);
538}
539
540static const char *
541g_part_vtoc8_type(struct g_part_table *basetable, struct g_part_entry *entry,
542    char *buf, size_t bufsz)
543{
544	struct g_part_vtoc8_table *table;
545	uint16_t tag;
546
547	table = (struct g_part_vtoc8_table *)basetable;
548	tag = be16dec(&table->vtoc.part[entry->gpe_index - 1].tag);
549	if (tag == VTOC_TAG_FREEBSD_NANDFS)
550		return (g_part_alias_name(G_PART_ALIAS_FREEBSD_NANDFS));
551	if (tag == VTOC_TAG_FREEBSD_SWAP)
552		return (g_part_alias_name(G_PART_ALIAS_FREEBSD_SWAP));
553	if (tag == VTOC_TAG_FREEBSD_UFS)
554		return (g_part_alias_name(G_PART_ALIAS_FREEBSD_UFS));
555	if (tag == VTOC_TAG_FREEBSD_VINUM)
556		return (g_part_alias_name(G_PART_ALIAS_FREEBSD_VINUM));
557	if (tag == VTOC_TAG_FREEBSD_ZFS)
558		return (g_part_alias_name(G_PART_ALIAS_FREEBSD_ZFS));
559	snprintf(buf, bufsz, "!%d", tag);
560	return (buf);
561}
562
563static int
564g_part_vtoc8_write(struct g_part_table *basetable, struct g_consumer *cp)
565{
566	struct g_provider *pp;
567	struct g_part_entry *entry;
568	struct g_part_vtoc8_table *table;
569	uint16_t sum;
570	u_char *p;
571	int error, index, match, offset;
572
573	pp = cp->provider;
574	table = (struct g_part_vtoc8_table *)basetable;
575	entry = LIST_FIRST(&basetable->gpt_entry);
576	for (index = 0; index < basetable->gpt_entries; index++) {
577		match = (entry != NULL && index == entry->gpe_index - 1)
578		    ? 1 : 0;
579		if (match) {
580			if (entry->gpe_deleted) {
581				be16enc(&table->vtoc.part[index].tag, 0);
582				be16enc(&table->vtoc.part[index].flag, 0);
583				be32enc(&table->vtoc.map[index].cyl, 0);
584				be32enc(&table->vtoc.map[index].nblks, 0);
585			}
586			entry = LIST_NEXT(entry, gpe_entry);
587		}
588	}
589
590	/* Calculate checksum. */
591	sum = 0;
592	p = (void *)&table->vtoc;
593	for (offset = 0; offset < sizeof(table->vtoc) - 2; offset += 2)
594		sum ^= be16dec(p + offset);
595	be16enc(&table->vtoc.cksum, sum);
596
597	error = g_write_data(cp, 0, p, pp->sectorsize);
598	return (error);
599}
600