g_part_ebr.c revision 251588
1257752Semaste/*-
2257752Semaste * Copyright (c) 2007-2009 Marcel Moolenaar
3257752Semaste * All rights reserved.
4257752Semaste *
5257752Semaste * Redistribution and use in source and binary forms, with or without
6257752Semaste * modification, are permitted provided that the following conditions
7257752Semaste * are met:
8257752Semaste *
9257752Semaste * 1. Redistributions of source code must retain the above copyright
10257752Semaste *    notice, this list of conditions and the following disclaimer.
11257752Semaste * 2. Redistributions in binary form must reproduce the above copyright
12257752Semaste *    notice, this list of conditions and the following disclaimer in the
13257752Semaste *    documentation and/or other materials provided with the distribution.
14257752Semaste *
15257752Semaste * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16257752Semaste * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17257752Semaste * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18257752Semaste * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19257752Semaste * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20257752Semaste * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21257752Semaste * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22257752Semaste * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23257752Semaste * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24257752Semaste * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25257752Semaste */
26257752Semaste
27257752Semaste#include "opt_geom.h"
28257752Semaste
29257752Semaste#include <sys/cdefs.h>
30257752Semaste__FBSDID("$FreeBSD: head/sys/geom/part/g_part_ebr.c 251588 2013-06-09 23:34:26Z marcel $");
31257752Semaste
32257752Semaste#include <sys/param.h>
33257752Semaste#include <sys/bio.h>
34257752Semaste#include <sys/diskmbr.h>
35257752Semaste#include <sys/endian.h>
36257752Semaste#include <sys/kernel.h>
37257752Semaste#include <sys/kobj.h>
38257752Semaste#include <sys/limits.h>
39257752Semaste#include <sys/lock.h>
40257752Semaste#include <sys/malloc.h>
41257752Semaste#include <sys/mutex.h>
42257752Semaste#include <sys/queue.h>
43257752Semaste#include <sys/sbuf.h>
44257752Semaste#include <sys/systm.h>
45257752Semaste#include <sys/sysctl.h>
46257752Semaste#include <geom/geom.h>
47257752Semaste#include <geom/part/g_part.h>
48257752Semaste
49257752Semaste#include "g_part_if.h"
50257752Semaste
51257752SemasteFEATURE(geom_part_ebr,
52257752Semaste    "GEOM partitioning class for extended boot records support");
53257752Semaste#if defined(GEOM_PART_EBR_COMPAT)
54257752SemasteFEATURE(geom_part_ebr_compat,
55257752Semaste    "GEOM EBR partitioning class: backward-compatible partition names");
56257752Semaste#endif
57257752Semaste
58257752Semaste#define	EBRSIZE		512
59257752Semaste
60257752Semastestruct g_part_ebr_table {
61257752Semaste	struct g_part_table	base;
62257752Semaste#ifndef GEOM_PART_EBR_COMPAT
63257752Semaste	u_char		ebr[EBRSIZE];
64257752Semaste#endif
65257752Semaste};
66257752Semaste
67257752Semastestruct g_part_ebr_entry {
68257752Semaste	struct g_part_entry	base;
69257752Semaste	struct dos_partition	ent;
70257752Semaste};
71257752Semaste
72257752Semastestatic int g_part_ebr_add(struct g_part_table *, struct g_part_entry *,
73257752Semaste    struct g_part_parms *);
74257752Semastestatic int g_part_ebr_create(struct g_part_table *, struct g_part_parms *);
75257752Semastestatic int g_part_ebr_destroy(struct g_part_table *, struct g_part_parms *);
76257752Semastestatic void g_part_ebr_dumpconf(struct g_part_table *, struct g_part_entry *,
77257752Semaste    struct sbuf *, const char *);
78257752Semastestatic int g_part_ebr_dumpto(struct g_part_table *, struct g_part_entry *);
79257752Semaste#if defined(GEOM_PART_EBR_COMPAT)
80257752Semastestatic void g_part_ebr_fullname(struct g_part_table *, struct g_part_entry *,
81257752Semaste    struct sbuf *, const char *);
82257752Semaste#endif
83257752Semastestatic int g_part_ebr_modify(struct g_part_table *, struct g_part_entry *,
84257752Semaste    struct g_part_parms *);
85257752Semastestatic const char *g_part_ebr_name(struct g_part_table *, struct g_part_entry *,
86257752Semaste    char *, size_t);
87257752Semastestatic int g_part_ebr_precheck(struct g_part_table *, enum g_part_ctl,
88257752Semaste    struct g_part_parms *);
89257752Semastestatic int g_part_ebr_probe(struct g_part_table *, struct g_consumer *);
90257752Semastestatic int g_part_ebr_read(struct g_part_table *, struct g_consumer *);
91257752Semastestatic int g_part_ebr_setunset(struct g_part_table *, struct g_part_entry *,
92257752Semaste    const char *, unsigned int);
93257752Semastestatic const char *g_part_ebr_type(struct g_part_table *, struct g_part_entry *,
94257752Semaste    char *, size_t);
95257752Semastestatic int g_part_ebr_write(struct g_part_table *, struct g_consumer *);
96257752Semaste
97257752Semastestatic kobj_method_t g_part_ebr_methods[] = {
98257752Semaste	KOBJMETHOD(g_part_add,		g_part_ebr_add),
99257752Semaste	KOBJMETHOD(g_part_create,	g_part_ebr_create),
100257752Semaste	KOBJMETHOD(g_part_destroy,	g_part_ebr_destroy),
101257752Semaste	KOBJMETHOD(g_part_dumpconf,	g_part_ebr_dumpconf),
102257752Semaste	KOBJMETHOD(g_part_dumpto,	g_part_ebr_dumpto),
103257752Semaste#if defined(GEOM_PART_EBR_COMPAT)
104257752Semaste	KOBJMETHOD(g_part_fullname,	g_part_ebr_fullname),
105257752Semaste#endif
106257752Semaste	KOBJMETHOD(g_part_modify,	g_part_ebr_modify),
107257752Semaste	KOBJMETHOD(g_part_name,		g_part_ebr_name),
108257752Semaste	KOBJMETHOD(g_part_precheck,	g_part_ebr_precheck),
109257752Semaste	KOBJMETHOD(g_part_probe,	g_part_ebr_probe),
110257752Semaste	KOBJMETHOD(g_part_read,		g_part_ebr_read),
111257752Semaste	KOBJMETHOD(g_part_setunset,	g_part_ebr_setunset),
112257752Semaste	KOBJMETHOD(g_part_type,		g_part_ebr_type),
113257752Semaste	KOBJMETHOD(g_part_write,	g_part_ebr_write),
114257752Semaste	{ 0, 0 }
115257752Semaste};
116257752Semaste
117257752Semastestatic struct g_part_scheme g_part_ebr_scheme = {
118257752Semaste	"EBR",
119257752Semaste	g_part_ebr_methods,
120257752Semaste	sizeof(struct g_part_ebr_table),
121257752Semaste	.gps_entrysz = sizeof(struct g_part_ebr_entry),
122257752Semaste	.gps_minent = 1,
123257752Semaste	.gps_maxent = INT_MAX,
124257752Semaste};
125257752SemasteG_PART_SCHEME_DECLARE(g_part_ebr);
126257752Semaste
127257752Semastestatic struct g_part_ebr_alias {
128257752Semaste	u_char		typ;
129257752Semaste	int		alias;
130257752Semaste} ebr_alias_match[] = {
131257752Semaste	{ DOSPTYP_386BSD,	G_PART_ALIAS_FREEBSD },
132257752Semaste	{ DOSPTYP_NTFS,		G_PART_ALIAS_MS_NTFS },
133257752Semaste	{ DOSPTYP_FAT32,	G_PART_ALIAS_MS_FAT32 },
134257752Semaste	{ DOSPTYP_LINSWP,	G_PART_ALIAS_LINUX_SWAP },
135257752Semaste	{ DOSPTYP_LINUX,	G_PART_ALIAS_LINUX_DATA },
136257752Semaste	{ DOSPTYP_LINLVM,	G_PART_ALIAS_LINUX_LVM },
137257752Semaste	{ DOSPTYP_LINRAID,	G_PART_ALIAS_LINUX_RAID },
138257752Semaste};
139257752Semaste
140257752Semastestatic void ebr_set_chs(struct g_part_table *, uint32_t, u_char *, u_char *,
141257752Semaste    u_char *);
142257752Semaste
143257752Semastestatic void
144257752Semasteebr_entry_decode(const char *p, struct dos_partition *ent)
145257752Semaste{
146257752Semaste	ent->dp_flag = p[0];
147257752Semaste	ent->dp_shd = p[1];
148257752Semaste	ent->dp_ssect = p[2];
149257752Semaste	ent->dp_scyl = p[3];
150257752Semaste	ent->dp_typ = p[4];
151257752Semaste	ent->dp_ehd = p[5];
152257752Semaste	ent->dp_esect = p[6];
153257752Semaste	ent->dp_ecyl = p[7];
154257752Semaste	ent->dp_start = le32dec(p + 8);
155257752Semaste	ent->dp_size = le32dec(p + 12);
156257752Semaste}
157257752Semaste
158257752Semastestatic void
159257752Semasteebr_entry_link(struct g_part_table *table, uint32_t start, uint32_t end,
160257752Semaste   u_char *buf)
161257752Semaste{
162257752Semaste
163257752Semaste	buf[0] = 0 /* dp_flag */;
164257752Semaste	ebr_set_chs(table, start, &buf[3] /* dp_scyl */, &buf[1] /* dp_shd */,
165257752Semaste	    &buf[2] /* dp_ssect */);
166257752Semaste	buf[4] = 5 /* dp_typ */;
167257752Semaste	ebr_set_chs(table, end, &buf[7] /* dp_ecyl */, &buf[5] /* dp_ehd */,
168257752Semaste	    &buf[6] /* dp_esect */);
169257752Semaste	le32enc(buf + 8, start);
170257752Semaste	le32enc(buf + 12, end - start + 1);
171257752Semaste}
172257752Semaste
173257752Semastestatic int
174257752Semasteebr_parse_type(const char *type, u_char *dp_typ)
175257752Semaste{
176257752Semaste	const char *alias;
177257752Semaste	char *endp;
178257752Semaste	long lt;
179257752Semaste	int i;
180257752Semaste
181257752Semaste	if (type[0] == '!') {
182257752Semaste		lt = strtol(type + 1, &endp, 0);
183257752Semaste		if (type[1] == '\0' || *endp != '\0' || lt <= 0 || lt >= 256)
184257752Semaste			return (EINVAL);
185257752Semaste		*dp_typ = (u_char)lt;
186257752Semaste		return (0);
187257752Semaste	}
188257752Semaste	for (i = 0;
189257752Semaste	    i < sizeof(ebr_alias_match) / sizeof(ebr_alias_match[0]); i++) {
190257752Semaste		alias = g_part_alias_name(ebr_alias_match[i].alias);
191257752Semaste		if (strcasecmp(type, alias) == 0) {
192257752Semaste			*dp_typ = ebr_alias_match[i].typ;
193257752Semaste			return (0);
194257752Semaste		}
195257752Semaste	}
196257752Semaste	return (EINVAL);
197257752Semaste}
198257752Semaste
199257752Semaste
200257752Semastestatic void
201257752Semasteebr_set_chs(struct g_part_table *table, uint32_t lba, u_char *cylp, u_char *hdp,
202257752Semaste    u_char *secp)
203257752Semaste{
204257752Semaste	uint32_t cyl, hd, sec;
205257752Semaste
206257752Semaste	sec = lba % table->gpt_sectors + 1;
207257752Semaste	lba /= table->gpt_sectors;
208257752Semaste	hd = lba % table->gpt_heads;
209257752Semaste	lba /= table->gpt_heads;
210257752Semaste	cyl = lba;
211257752Semaste	if (cyl > 1023)
212257752Semaste		sec = hd = cyl = ~0;
213257752Semaste
214257752Semaste	*cylp = cyl & 0xff;
215257752Semaste	*hdp = hd & 0xff;
216257752Semaste	*secp = (sec & 0x3f) | ((cyl >> 2) & 0xc0);
217257752Semaste}
218257752Semaste
219257752Semastestatic int
220257752Semasteg_part_ebr_add(struct g_part_table *basetable, struct g_part_entry *baseentry,
221257752Semaste    struct g_part_parms *gpp)
222257752Semaste{
223257752Semaste	struct g_geom *gp;
224257752Semaste	struct g_provider *pp;
225257752Semaste	struct g_part_ebr_entry *entry;
226257752Semaste	uint32_t start, size, sectors;
227257752Semaste
228257752Semaste	if (gpp->gpp_parms & G_PART_PARM_LABEL)
229257752Semaste		return (EINVAL);
230257752Semaste
231257752Semaste	gp = basetable->gpt_gp;
232257752Semaste	pp = LIST_FIRST(&gp->consumer)->provider;
233257752Semaste	sectors = basetable->gpt_sectors;
234257752Semaste
235257752Semaste	entry = (struct g_part_ebr_entry *)baseentry;
236257752Semaste
237257752Semaste	start = gpp->gpp_start;
238257752Semaste	size = gpp->gpp_size;
239257752Semaste	if (size < 2 * sectors)
240257752Semaste		return (EINVAL);
241257752Semaste	if (start % sectors) {
242257752Semaste		size = size - sectors + (start % sectors);
243257752Semaste		start = start - (start % sectors) + sectors;
244257752Semaste	}
245257752Semaste	if (size % sectors)
246257752Semaste		size = size - (size % sectors);
247257752Semaste	if (size < 2 * sectors)
248257752Semaste		return (EINVAL);
249257752Semaste
250257752Semaste	if (baseentry->gpe_deleted)
251257752Semaste		bzero(&entry->ent, sizeof(entry->ent));
252257752Semaste
253257752Semaste	KASSERT(baseentry->gpe_start <= start, ("%s", __func__));
254257752Semaste	KASSERT(baseentry->gpe_end >= start + size - 1, ("%s", __func__));
255257752Semaste	baseentry->gpe_index = (start / sectors) + 1;
256257752Semaste	baseentry->gpe_offset = (off_t)(start + sectors) * pp->sectorsize;
257257752Semaste	baseentry->gpe_start = start;
258257752Semaste	baseentry->gpe_end = start + size - 1;
259257752Semaste	entry->ent.dp_start = sectors;
260257752Semaste	entry->ent.dp_size = size - sectors;
261257752Semaste	ebr_set_chs(basetable, entry->ent.dp_start, &entry->ent.dp_scyl,
262257752Semaste	    &entry->ent.dp_shd, &entry->ent.dp_ssect);
263257752Semaste	ebr_set_chs(basetable, baseentry->gpe_end, &entry->ent.dp_ecyl,
264257752Semaste	    &entry->ent.dp_ehd, &entry->ent.dp_esect);
265257752Semaste	return (ebr_parse_type(gpp->gpp_type, &entry->ent.dp_typ));
266257752Semaste}
267257752Semaste
268257752Semastestatic int
269257752Semasteg_part_ebr_create(struct g_part_table *basetable, struct g_part_parms *gpp)
270257752Semaste{
271257752Semaste	char type[64];
272257752Semaste	struct g_consumer *cp;
273257752Semaste	struct g_provider *pp;
274257752Semaste	uint32_t msize;
275257752Semaste	int error;
276257752Semaste
277257752Semaste	pp = gpp->gpp_provider;
278257752Semaste
279257752Semaste	if (pp->sectorsize < EBRSIZE)
280257752Semaste		return (ENOSPC);
281257752Semaste	if (pp->sectorsize > 4096)
282257752Semaste		return (ENXIO);
283257752Semaste
284257752Semaste	/* Check that we have a parent and that it's a MBR. */
285257752Semaste	if (basetable->gpt_depth == 0)
286257752Semaste		return (ENXIO);
287257752Semaste	cp = LIST_FIRST(&pp->consumers);
288257752Semaste	error = g_getattr("PART::scheme", cp, &type);
289257752Semaste	if (error != 0)
290257752Semaste		return (error);
291257752Semaste	if (strcmp(type, "MBR") != 0)
292257752Semaste		return (ENXIO);
293257752Semaste	error = g_getattr("PART::type", cp, &type);
294257752Semaste	if (error != 0)
295257752Semaste		return (error);
296257752Semaste	if (strcmp(type, "ebr") != 0)
297257752Semaste		return (ENXIO);
298257752Semaste
299257752Semaste	msize = MIN(pp->mediasize / pp->sectorsize, UINT32_MAX);
300257752Semaste	basetable->gpt_first = 0;
301257752Semaste	basetable->gpt_last = msize - 1;
302257752Semaste	basetable->gpt_entries = msize / basetable->gpt_sectors;
303257752Semaste	return (0);
304257752Semaste}
305257752Semaste
306257752Semastestatic int
307257752Semasteg_part_ebr_destroy(struct g_part_table *basetable, struct g_part_parms *gpp)
308257752Semaste{
309257752Semaste
310257752Semaste	/* Wipe the first sector to clear the partitioning. */
311257752Semaste	basetable->gpt_smhead |= 1;
312257752Semaste	return (0);
313257752Semaste}
314257752Semaste
315257752Semastestatic void
316257752Semasteg_part_ebr_dumpconf(struct g_part_table *table, struct g_part_entry *baseentry,
317257752Semaste    struct sbuf *sb, const char *indent)
318257752Semaste{
319257752Semaste	struct g_part_ebr_entry *entry;
320257752Semaste
321257752Semaste	entry = (struct g_part_ebr_entry *)baseentry;
322257752Semaste	if (indent == NULL) {
323257752Semaste		/* conftxt: libdisk compatibility */
324257752Semaste		sbuf_printf(sb, " xs MBREXT xt %u", entry->ent.dp_typ);
325257752Semaste	} else if (entry != NULL) {
326257752Semaste		/* confxml: partition entry information */
327257752Semaste		sbuf_printf(sb, "%s<rawtype>%u</rawtype>\n", indent,
328257752Semaste		    entry->ent.dp_typ);
329257752Semaste		if (entry->ent.dp_flag & 0x80)
330257752Semaste			sbuf_printf(sb, "%s<attrib>active</attrib>\n", indent);
331257752Semaste	} else {
332257752Semaste		/* confxml: scheme information */
333257752Semaste	}
334257752Semaste}
335257752Semaste
336257752Semastestatic int
337257752Semasteg_part_ebr_dumpto(struct g_part_table *table, struct g_part_entry *baseentry)
338257752Semaste{
339257752Semaste	struct g_part_ebr_entry *entry;
340257752Semaste
341257752Semaste	/* Allow dumping to a FreeBSD partition or Linux swap partition only. */
342257752Semaste	entry = (struct g_part_ebr_entry *)baseentry;
343257752Semaste	return ((entry->ent.dp_typ == DOSPTYP_386BSD ||
344257752Semaste	    entry->ent.dp_typ == DOSPTYP_LINSWP) ? 1 : 0);
345257752Semaste}
346257752Semaste
347257752Semaste#if defined(GEOM_PART_EBR_COMPAT)
348257752Semastestatic void
349257752Semasteg_part_ebr_fullname(struct g_part_table *table, struct g_part_entry *entry,
350257752Semaste    struct sbuf *sb, const char *pfx)
351257752Semaste{
352257752Semaste	struct g_part_entry *iter;
353257752Semaste	u_int idx;
354257752Semaste
355257752Semaste	idx = 5;
356257752Semaste	LIST_FOREACH(iter, &table->gpt_entry, gpe_entry) {
357257752Semaste		if (iter == entry)
358257752Semaste			break;
359257752Semaste		idx++;
360257752Semaste	}
361257752Semaste	sbuf_printf(sb, "%.*s%u", (int)strlen(pfx) - 1, pfx, idx);
362257752Semaste}
363257752Semaste#endif
364257752Semaste
365257752Semastestatic int
366269024Semasteg_part_ebr_modify(struct g_part_table *basetable,
367269024Semaste    struct g_part_entry *baseentry, struct g_part_parms *gpp)
368269024Semaste{
369257752Semaste	struct g_part_ebr_entry *entry;
370257752Semaste
371257752Semaste	if (gpp->gpp_parms & G_PART_PARM_LABEL)
372257752Semaste		return (EINVAL);
373257752Semaste
374257752Semaste	entry = (struct g_part_ebr_entry *)baseentry;
375257752Semaste	if (gpp->gpp_parms & G_PART_PARM_TYPE)
376257752Semaste		return (ebr_parse_type(gpp->gpp_type, &entry->ent.dp_typ));
377257752Semaste	return (0);
378257752Semaste}
379257752Semaste
380257752Semastestatic const char *
381257752Semasteg_part_ebr_name(struct g_part_table *table, struct g_part_entry *entry,
382257752Semaste    char *buf, size_t bufsz)
383257752Semaste{
384257752Semaste
385257752Semaste	snprintf(buf, bufsz, "+%08u", entry->gpe_index);
386257752Semaste	return (buf);
387257752Semaste}
388257752Semaste
389269024Semastestatic int
390269024Semasteg_part_ebr_precheck(struct g_part_table *table, enum g_part_ctl req,
391257752Semaste    struct g_part_parms *gpp)
392257752Semaste{
393257752Semaste#if defined(GEOM_PART_EBR_COMPAT)
394257752Semaste	if (req == G_PART_CTL_DESTROY)
395257752Semaste		return (0);
396257752Semaste	return (ECANCELED);
397#else
398	/*
399	 * The index is a function of the start of the partition.
400	 * This is not something the user can override, nor is it
401	 * something the common code will do right. We can set the
402	 * index now so that we get what we need.
403	 */
404	if (req == G_PART_CTL_ADD)
405		gpp->gpp_index = (gpp->gpp_start / table->gpt_sectors) + 1;
406	return (0);
407#endif
408}
409
410static int
411g_part_ebr_probe(struct g_part_table *table, struct g_consumer *cp)
412{
413	char type[64];
414	struct g_provider *pp;
415	u_char *buf, *p;
416	int error, index, res;
417	uint16_t magic;
418
419	pp = cp->provider;
420
421	/* Sanity-check the provider. */
422	if (pp->sectorsize < EBRSIZE || pp->mediasize < pp->sectorsize)
423		return (ENOSPC);
424	if (pp->sectorsize > 4096)
425		return (ENXIO);
426
427	/* Check that we have a parent and that it's a MBR. */
428	if (table->gpt_depth == 0)
429		return (ENXIO);
430	error = g_getattr("PART::scheme", cp, &type);
431	if (error != 0)
432		return (error);
433	if (strcmp(type, "MBR") != 0)
434		return (ENXIO);
435	/* Check that partition has type DOSPTYP_EBR. */
436	error = g_getattr("PART::type", cp, &type);
437	if (error != 0)
438		return (error);
439	if (strcmp(type, "ebr") != 0)
440		return (ENXIO);
441
442	/* Check that there's a EBR. */
443	buf = g_read_data(cp, 0L, pp->sectorsize, &error);
444	if (buf == NULL)
445		return (error);
446
447	/* We goto out on mismatch. */
448	res = ENXIO;
449
450	magic = le16dec(buf + DOSMAGICOFFSET);
451	if (magic != DOSMAGIC)
452		goto out;
453
454	for (index = 0; index < 2; index++) {
455		p = buf + DOSPARTOFF + index * DOSPARTSIZE;
456		if (p[0] != 0 && p[0] != 0x80)
457			goto out;
458	}
459	res = G_PART_PROBE_PRI_NORM;
460
461 out:
462	g_free(buf);
463	return (res);
464}
465
466static int
467g_part_ebr_read(struct g_part_table *basetable, struct g_consumer *cp)
468{
469	struct dos_partition ent[2];
470	struct g_provider *pp;
471	struct g_part_entry *baseentry;
472	struct g_part_ebr_table *table;
473	struct g_part_ebr_entry *entry;
474	u_char *buf;
475	off_t ofs, msize;
476	u_int lba;
477	int error, index;
478
479	pp = cp->provider;
480	table = (struct g_part_ebr_table *)basetable;
481	msize = MIN(pp->mediasize / pp->sectorsize, UINT32_MAX);
482
483	lba = 0;
484	while (1) {
485		ofs = (off_t)lba * pp->sectorsize;
486		buf = g_read_data(cp, ofs, pp->sectorsize, &error);
487		if (buf == NULL)
488			return (error);
489
490		ebr_entry_decode(buf + DOSPARTOFF + 0 * DOSPARTSIZE, ent + 0);
491		ebr_entry_decode(buf + DOSPARTOFF + 1 * DOSPARTSIZE, ent + 1);
492
493		/* The 3rd & 4th entries should be zeroes. */
494		if (le64dec(buf + DOSPARTOFF + 2 * DOSPARTSIZE) +
495		    le64dec(buf + DOSPARTOFF + 3 * DOSPARTSIZE) != 0) {
496			basetable->gpt_corrupt = 1;
497			printf("GEOM: %s: invalid entries in the EBR ignored.\n",
498			    pp->name);
499		}
500#ifndef GEOM_PART_EBR_COMPAT
501		/* Save the first EBR, it can contain a boot code */
502		if (lba == 0)
503			bcopy(buf, table->ebr, sizeof(table->ebr));
504#endif
505		g_free(buf);
506
507		if (ent[0].dp_typ == 0)
508			break;
509
510		if (ent[0].dp_typ == 5 && ent[1].dp_typ == 0) {
511			lba = ent[0].dp_start;
512			continue;
513		}
514
515		index = (lba / basetable->gpt_sectors) + 1;
516		baseentry = (struct g_part_entry *)g_part_new_entry(basetable,
517		    index, lba, lba + ent[0].dp_start + ent[0].dp_size - 1);
518		baseentry->gpe_offset = (off_t)(lba + ent[0].dp_start) *
519		    pp->sectorsize;
520		entry = (struct g_part_ebr_entry *)baseentry;
521		entry->ent = ent[0];
522
523		if (ent[1].dp_typ == 0)
524			break;
525
526		lba = ent[1].dp_start;
527	}
528
529	basetable->gpt_entries = msize / basetable->gpt_sectors;
530	basetable->gpt_first = 0;
531	basetable->gpt_last = msize - 1;
532	return (0);
533}
534
535static int
536g_part_ebr_setunset(struct g_part_table *table, struct g_part_entry *baseentry,
537    const char *attrib, unsigned int set)
538{
539	struct g_part_entry *iter;
540	struct g_part_ebr_entry *entry;
541	int changed;
542
543	if (baseentry == NULL)
544		return (ENODEV);
545	if (strcasecmp(attrib, "active") != 0)
546		return (EINVAL);
547
548	/* Only one entry can have the active attribute. */
549	LIST_FOREACH(iter, &table->gpt_entry, gpe_entry) {
550		if (iter->gpe_deleted)
551			continue;
552		changed = 0;
553		entry = (struct g_part_ebr_entry *)iter;
554		if (iter == baseentry) {
555			if (set && (entry->ent.dp_flag & 0x80) == 0) {
556				entry->ent.dp_flag |= 0x80;
557				changed = 1;
558			} else if (!set && (entry->ent.dp_flag & 0x80)) {
559				entry->ent.dp_flag &= ~0x80;
560				changed = 1;
561			}
562		} else {
563			if (set && (entry->ent.dp_flag & 0x80)) {
564				entry->ent.dp_flag &= ~0x80;
565				changed = 1;
566			}
567		}
568		if (changed && !iter->gpe_created)
569			iter->gpe_modified = 1;
570	}
571	return (0);
572}
573
574static const char *
575g_part_ebr_type(struct g_part_table *basetable, struct g_part_entry *baseentry,
576    char *buf, size_t bufsz)
577{
578	struct g_part_ebr_entry *entry;
579	int i;
580
581	entry = (struct g_part_ebr_entry *)baseentry;
582	for (i = 0;
583	    i < sizeof(ebr_alias_match) / sizeof(ebr_alias_match[0]); i++) {
584		if (ebr_alias_match[i].typ == entry->ent.dp_typ)
585			return (g_part_alias_name(ebr_alias_match[i].alias));
586	}
587	snprintf(buf, bufsz, "!%d", entry->ent.dp_typ);
588	return (buf);
589}
590
591static int
592g_part_ebr_write(struct g_part_table *basetable, struct g_consumer *cp)
593{
594#ifndef GEOM_PART_EBR_COMPAT
595	struct g_part_ebr_table *table;
596#endif
597	struct g_provider *pp;
598	struct g_part_entry *baseentry, *next;
599	struct g_part_ebr_entry *entry;
600	u_char *buf;
601	u_char *p;
602	int error;
603
604	pp = cp->provider;
605	buf = g_malloc(pp->sectorsize, M_WAITOK | M_ZERO);
606#ifndef GEOM_PART_EBR_COMPAT
607	table = (struct g_part_ebr_table *)basetable;
608	bcopy(table->ebr, buf, DOSPARTOFF);
609#endif
610	le16enc(buf + DOSMAGICOFFSET, DOSMAGIC);
611
612	baseentry = LIST_FIRST(&basetable->gpt_entry);
613	while (baseentry != NULL && baseentry->gpe_deleted)
614		baseentry = LIST_NEXT(baseentry, gpe_entry);
615
616	/* Wipe-out the first EBR when there are no slices. */
617	if (baseentry == NULL) {
618		error = g_write_data(cp, 0, buf, pp->sectorsize);
619		goto out;
620	}
621
622	/*
623	 * If the first partition is not in LBA 0, we need to
624	 * put a "link" EBR in LBA 0.
625	 */
626	if (baseentry->gpe_start != 0) {
627		ebr_entry_link(basetable, (uint32_t)baseentry->gpe_start,
628		    (uint32_t)baseentry->gpe_end, buf + DOSPARTOFF);
629		error = g_write_data(cp, 0, buf, pp->sectorsize);
630		if (error)
631			goto out;
632	}
633
634	do {
635		entry = (struct g_part_ebr_entry *)baseentry;
636
637		p = buf + DOSPARTOFF;
638		p[0] = entry->ent.dp_flag;
639		p[1] = entry->ent.dp_shd;
640		p[2] = entry->ent.dp_ssect;
641		p[3] = entry->ent.dp_scyl;
642		p[4] = entry->ent.dp_typ;
643		p[5] = entry->ent.dp_ehd;
644		p[6] = entry->ent.dp_esect;
645		p[7] = entry->ent.dp_ecyl;
646		le32enc(p + 8, entry->ent.dp_start);
647		le32enc(p + 12, entry->ent.dp_size);
648
649		next = LIST_NEXT(baseentry, gpe_entry);
650		while (next != NULL && next->gpe_deleted)
651			next = LIST_NEXT(next, gpe_entry);
652
653		p += DOSPARTSIZE;
654		if (next != NULL)
655			ebr_entry_link(basetable, (uint32_t)next->gpe_start,
656			    (uint32_t)next->gpe_end, p);
657		else
658			bzero(p, DOSPARTSIZE);
659
660		error = g_write_data(cp, baseentry->gpe_start * pp->sectorsize,
661		    buf, pp->sectorsize);
662#ifndef GEOM_PART_EBR_COMPAT
663		if (baseentry->gpe_start == 0)
664			bzero(buf, DOSPARTOFF);
665#endif
666		baseentry = next;
667	} while (!error && baseentry != NULL);
668
669 out:
670	g_free(buf);
671	return (error);
672}
673