g_part.c revision 177510
198186Sgordon/*-
298186Sgordon * Copyright (c) 2002, 2005, 2006, 2007 Marcel Moolenaar
378344Sobrien * All rights reserved.
498186Sgordon *
578344Sobrien * Redistribution and use in source and binary forms, with or without
678344Sobrien * modification, are permitted provided that the following conditions
778344Sobrien * are met:
878344Sobrien *
978344Sobrien * 1. Redistributions of source code must retain the above copyright
1078344Sobrien *    notice, this list of conditions and the following disclaimer.
1178344Sobrien * 2. Redistributions in binary form must reproduce the above copyright
1278344Sobrien *    notice, this list of conditions and the following disclaimer in the
1378344Sobrien *    documentation and/or other materials provided with the distribution.
1478344Sobrien *
1578344Sobrien * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
1678344Sobrien * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
1778344Sobrien * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
1878344Sobrien * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
1978344Sobrien * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
2078344Sobrien * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
2178344Sobrien * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
2278344Sobrien * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
2378344Sobrien * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
2478344Sobrien * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2578344Sobrien */
2678344Sobrien
2778344Sobrien#include <sys/cdefs.h>
2878344Sobrien__FBSDID("$FreeBSD: head/sys/geom/part/g_part.c 177510 2008-03-23 01:31:59Z marcel $");
2978344Sobrien
3078344Sobrien#include <sys/param.h>
3178344Sobrien#include <sys/bio.h>
3278344Sobrien#include <sys/diskmbr.h>
3378344Sobrien#include <sys/endian.h>
3478344Sobrien#include <sys/kernel.h>
3578344Sobrien#include <sys/kobj.h>
3678344Sobrien#include <sys/limits.h>
3778344Sobrien#include <sys/lock.h>
3878344Sobrien#include <sys/malloc.h>
3978344Sobrien#include <sys/mutex.h>
4078344Sobrien#include <sys/queue.h>
4178344Sobrien#include <sys/sbuf.h>
4278344Sobrien#include <sys/systm.h>
4398186Sgordon#include <sys/uuid.h>
4498186Sgordon#include <geom/geom.h>
4598186Sgordon#include <geom/geom_ctl.h>
4698186Sgordon#include <geom/geom_int.h>
4798186Sgordon#include <geom/part/g_part.h>
4898186Sgordon
49103018Sgordon#include "g_part_if.h"
5098186Sgordon
51103018Sgordonstatic kobj_method_t g_part_null_methods[] = {
5298186Sgordon	{ 0, 0 }
5398186Sgordon};
5498186Sgordon
5598186Sgordonstatic struct g_part_scheme g_part_null_scheme = {
5698186Sgordon	"(none)",
5798186Sgordon	g_part_null_methods,
5898186Sgordon	sizeof(struct g_part_table),
5998186Sgordon};
6098186Sgordon
6178344SobrienTAILQ_HEAD(, g_part_scheme) g_part_schemes =
6278344Sobrien    TAILQ_HEAD_INITIALIZER(g_part_schemes);
6378344Sobrien
6478344Sobrienstruct g_part_alias_list {
6598186Sgordon	const char *lexeme;
6698186Sgordon	enum g_part_alias alias;
6798186Sgordon} g_part_alias_list[G_PART_ALIAS_COUNT] = {
6898186Sgordon	{ "efi", G_PART_ALIAS_EFI },
6998186Sgordon	{ "freebsd", G_PART_ALIAS_FREEBSD },
7098186Sgordon	{ "freebsd-boot", G_PART_ALIAS_FREEBSD_BOOT },
7198186Sgordon	{ "freebsd-swap", G_PART_ALIAS_FREEBSD_SWAP },
7298186Sgordon	{ "freebsd-ufs", G_PART_ALIAS_FREEBSD_UFS },
7398186Sgordon	{ "freebsd-vinum", G_PART_ALIAS_FREEBSD_VINUM },
7498186Sgordon	{ "freebsd-zfs", G_PART_ALIAS_FREEBSD_ZFS },
7598186Sgordon	{ "mbr", G_PART_ALIAS_MBR }
7698186Sgordon};
7798186Sgordon
7898186Sgordon/*
7998186Sgordon * The GEOM partitioning class.
8098186Sgordon */
8198186Sgordonstatic g_ctl_req_t g_part_ctlreq;
82103018Sgordonstatic g_ctl_destroy_geom_t g_part_destroy_geom;
8398186Sgordonstatic g_fini_t g_part_fini;
8498186Sgordonstatic g_init_t g_part_init;
8598186Sgordonstatic g_taste_t g_part_taste;
8698186Sgordon
8798186Sgordonstatic g_access_t g_part_access;
8898186Sgordonstatic g_dumpconf_t g_part_dumpconf;
8998186Sgordonstatic g_orphan_t g_part_orphan;
9098186Sgordonstatic g_spoiled_t g_part_spoiled;
9198186Sgordonstatic g_start_t g_part_start;
9298186Sgordon
9398186Sgordonstatic struct g_class g_part_class = {
9498186Sgordon	.name = "PART",
9598186Sgordon	.version = G_VERSION,
9698186Sgordon	/* Class methods. */
9798186Sgordon	.ctlreq = g_part_ctlreq,
9898186Sgordon	.destroy_geom = g_part_destroy_geom,
9998186Sgordon	.fini = g_part_fini,
10098186Sgordon	.init = g_part_init,
10198186Sgordon	.taste = g_part_taste,
10298186Sgordon	/* Geom methods. */
10398186Sgordon	.access = g_part_access,
10498186Sgordon	.dumpconf = g_part_dumpconf,
10598186Sgordon	.orphan = g_part_orphan,
10698186Sgordon	.spoiled = g_part_spoiled,
10798186Sgordon	.start = g_part_start,
10898186Sgordon};
10998186Sgordon
11098186SgordonDECLARE_GEOM_CLASS(g_part_class, g_part);
11198186Sgordon
11298186Sgordonenum g_part_ctl {
11398186Sgordon	G_PART_CTL_NONE,
11498186Sgordon	G_PART_CTL_ADD,
11578344Sobrien	G_PART_CTL_COMMIT,
11678344Sobrien	G_PART_CTL_CREATE,
11778344Sobrien	G_PART_CTL_DELETE,
11878344Sobrien	G_PART_CTL_DESTROY,
11978344Sobrien	G_PART_CTL_MODIFY,
12078344Sobrien	G_PART_CTL_MOVE,
12178344Sobrien	G_PART_CTL_RECOVER,
12298186Sgordon	G_PART_CTL_RESIZE,
12378344Sobrien	G_PART_CTL_UNDO
12478344Sobrien};
12578344Sobrien
12678344Sobrien/*
12778344Sobrien * Support functions.
12878344Sobrien */
12978344Sobrien
13078344Sobrienstatic void g_part_wither(struct g_geom *, int);
13178344Sobrien
13278344Sobrienconst char *
13378344Sobrieng_part_alias_name(enum g_part_alias alias)
13478344Sobrien{
135106643Sgordon	int i;
13678344Sobrien
13778344Sobrien	for (i = 0; i < G_PART_ALIAS_COUNT; i++) {
13878344Sobrien		if (g_part_alias_list[i].alias != alias)
13978344Sobrien			continue;
14078344Sobrien		return (g_part_alias_list[i].lexeme);
14198186Sgordon	}
14298186Sgordon
14378344Sobrien	return (NULL);
14498186Sgordon}
14598186Sgordon
14698186Sgordonvoid
14798186Sgordong_part_geometry_heads(off_t blocks, u_int sectors, off_t *bestchs,
14898186Sgordon    u_int *bestheads)
14998186Sgordon{
15098186Sgordon	static u_int candidate_heads[] = { 1, 2, 16, 32, 64, 128, 255, 0 };
15198186Sgordon	off_t chs, cylinders;
15298186Sgordon	u_int heads;
15378344Sobrien	int idx;
15498186Sgordon
15598186Sgordon	*bestchs = 0;
15698186Sgordon	*bestheads = 0;
15798186Sgordon	for (idx = 0; candidate_heads[idx] != 0; idx++) {
15898186Sgordon		heads = candidate_heads[idx];
15978344Sobrien		cylinders = blocks / heads / sectors;
16078344Sobrien		if (cylinders < heads || cylinders < sectors)
16198186Sgordon			break;
16278344Sobrien		if (cylinders > 1023)
16378344Sobrien			continue;
16478344Sobrien		chs = cylinders * heads * sectors;
16578344Sobrien		if (chs > *bestchs || (chs == *bestchs && *bestheads == 1)) {
16678344Sobrien			*bestchs = chs;
16778344Sobrien			*bestheads = heads;
16878344Sobrien		}
16978344Sobrien	}
17098186Sgordon}
17178344Sobrien
17278344Sobrienstatic void
17398186Sgordong_part_geometry(struct g_part_table *table, struct g_consumer *cp,
17478344Sobrien    off_t blocks)
17578344Sobrien{
17678344Sobrien	static u_int candidate_sectors[] = { 1, 9, 17, 33, 63, 0 };
17778344Sobrien	off_t chs, bestchs;
17898186Sgordon	u_int heads, sectors;
17998186Sgordon	int idx;
18078344Sobrien
18198186Sgordon	if (g_getattr("GEOM::fwsectors", cp, &sectors) != 0 ||
18298186Sgordon	    sectors < 1 || sectors > 63 ||
18378344Sobrien	    g_getattr("GEOM::fwheads", cp, &heads) != 0 ||
18478344Sobrien	    heads < 1 || heads > 255) {
18578344Sobrien		table->gpt_fixgeom = 0;
18678344Sobrien		table->gpt_heads = 0;
18778344Sobrien		table->gpt_sectors = 0;
18898186Sgordon		bestchs = 0;
18978344Sobrien		for (idx = 0; candidate_sectors[idx] != 0; idx++) {
19098186Sgordon			sectors = candidate_sectors[idx];
19178344Sobrien			g_part_geometry_heads(blocks, sectors, &chs, &heads);
19278344Sobrien			if (chs == 0)
19398186Sgordon				continue;
19478344Sobrien			/*
19578344Sobrien			 * Prefer a geometry with sectors > 1, but only if
19678344Sobrien			 * it doesn't bump down the numbver of heads to 1.
19778344Sobrien			 */
19898186Sgordon			if (chs > bestchs || (chs == bestchs && heads > 1 &&
19978344Sobrien			    table->gpt_sectors == 1)) {
20078344Sobrien				bestchs = chs;
20198186Sgordon				table->gpt_heads = heads;
20278344Sobrien				table->gpt_sectors = sectors;
20378344Sobrien			}
20478344Sobrien		}
20598186Sgordon		/*
20678344Sobrien		 * If we didn't find a geometry at all, then the disk is
20798186Sgordon		 * too big. This means we can use the maximum number of
20898186Sgordon		 * heads and sectors.
20978344Sobrien		 */
21078344Sobrien		if (bestchs == 0) {
21178344Sobrien			table->gpt_heads = 255;
21278344Sobrien			table->gpt_sectors = 63;
21398186Sgordon		}
21478344Sobrien	} else {
21598186Sgordon		table->gpt_fixgeom = 1;
21678344Sobrien		table->gpt_heads = heads;
21798186Sgordon		table->gpt_sectors = sectors;
21898186Sgordon	}
21998186Sgordon}
22098186Sgordon
22198186Sgordonstruct g_part_entry *
22298186Sgordong_part_new_entry(struct g_part_table *table, int index, quad_t start,
22398186Sgordon    quad_t end)
22498186Sgordon{
22598186Sgordon	struct g_part_entry *entry, *last;
22698186Sgordon
22798186Sgordon	last = NULL;
22898186Sgordon	LIST_FOREACH(entry, &table->gpt_entry, gpe_entry) {
22998186Sgordon		if (entry->gpe_index == index)
23098186Sgordon			break;
23198186Sgordon		if (entry->gpe_index > index) {
23298186Sgordon			entry = NULL;
23398186Sgordon			break;
23498186Sgordon		}
23598186Sgordon		last = entry;
23698186Sgordon	}
23798186Sgordon	if (entry == NULL) {
23898186Sgordon		entry = g_malloc(table->gpt_scheme->gps_entrysz,
23998186Sgordon		    M_WAITOK | M_ZERO);
24098186Sgordon		entry->gpe_index = index;
24198186Sgordon		if (last == NULL)
24298186Sgordon			LIST_INSERT_HEAD(&table->gpt_entry, entry, gpe_entry);
24398186Sgordon		else
24498186Sgordon			LIST_INSERT_AFTER(last, entry, gpe_entry);
24598186Sgordon	}
24678344Sobrien	entry->gpe_start = start;
24798186Sgordon	entry->gpe_end = end;
24898186Sgordon	return (entry);
24998186Sgordon}
25098186Sgordon
25198186Sgordonstatic void
25298186Sgordong_part_new_provider(struct g_geom *gp, struct g_part_table *table,
25378344Sobrien    struct g_part_entry *entry)
25498186Sgordon{
25598186Sgordon	char buf[32];
25698186Sgordon	struct g_consumer *cp;
25798186Sgordon	struct g_provider *pp;
25898186Sgordon
25998186Sgordon	cp = LIST_FIRST(&gp->consumer);
26098186Sgordon	pp = cp->provider;
26198186Sgordon
26298186Sgordon	entry->gpe_offset = entry->gpe_start * pp->sectorsize;
26398186Sgordon
26498186Sgordon	if (entry->gpe_pp == NULL) {
26598186Sgordon		entry->gpe_pp = g_new_providerf(gp, "%s%s", gp->name,
26698186Sgordon		    G_PART_NAME(table, entry, buf, sizeof(buf)));
26798186Sgordon		entry->gpe_pp->private = entry;		/* Close the circle. */
26898186Sgordon	}
26998186Sgordon	entry->gpe_pp->index = entry->gpe_index - 1;	/* index is 1-based. */
27098186Sgordon	entry->gpe_pp->mediasize = (entry->gpe_end - entry->gpe_start + 1) *
27198186Sgordon	    pp->sectorsize;
27298186Sgordon	entry->gpe_pp->sectorsize = pp->sectorsize;
27398186Sgordon	entry->gpe_pp->flags = pp->flags & G_PF_CANDELETE;
27498186Sgordon	if (pp->stripesize > 0) {
27598186Sgordon		entry->gpe_pp->stripesize = pp->stripesize;
27698186Sgordon		entry->gpe_pp->stripeoffset = (pp->stripeoffset +
27798186Sgordon		    entry->gpe_offset) % pp->stripesize;
27898186Sgordon	}
27998186Sgordon	g_error_provider(entry->gpe_pp, 0);
28098186Sgordon}
28198186Sgordon
28298186Sgordonstatic int
28398186Sgordong_part_parm_geom(const char *p, struct g_geom **v)
28498186Sgordon{
28598186Sgordon	struct g_geom *gp;
28698186Sgordon
28798186Sgordon	LIST_FOREACH(gp, &g_part_class.geom, geom) {
28898186Sgordon		if (!strcmp(p, gp->name))
28998186Sgordon			break;
29098186Sgordon	}
29198186Sgordon	if (gp == NULL)
29298186Sgordon		return (EINVAL);
29398186Sgordon	*v = gp;
29498186Sgordon	return (0);
29598186Sgordon}
29698186Sgordon
29798186Sgordonstatic int
29898186Sgordong_part_parm_provider(const char *p, struct g_provider **v)
29998186Sgordon{
30098186Sgordon	struct g_provider *pp;
30198186Sgordon
30278344Sobrien	pp = g_provider_by_name(p);
30398186Sgordon	if (pp == NULL)
30498186Sgordon		return (EINVAL);
30598186Sgordon	*v = pp;
30698186Sgordon	return (0);
30778344Sobrien}
30898186Sgordon
30998186Sgordonstatic int
31098186Sgordong_part_parm_quad(const char *p, quad_t *v)
31178344Sobrien{
31278344Sobrien	char *x;
31378344Sobrien	quad_t q;
31498186Sgordon
31598186Sgordon	q = strtoq(p, &x, 0);
31698186Sgordon	if (*x != '\0' || q < 0)
31798186Sgordon		return (EINVAL);
31898186Sgordon	*v = q;
31978344Sobrien	return (0);
32098186Sgordon}
32198186Sgordon
32278344Sobrienstatic int
32398186Sgordong_part_parm_scheme(const char *p, struct g_part_scheme **v)
32498186Sgordon{
32578344Sobrien	struct g_part_scheme *s;
32678344Sobrien
32778344Sobrien	TAILQ_FOREACH(s, &g_part_schemes, scheme_list) {
32898186Sgordon		if (s == &g_part_null_scheme)
32998186Sgordon			continue;
33078344Sobrien		if (!strcasecmp(s->name, p))
33178344Sobrien			break;
33278344Sobrien	}
33398186Sgordon	if (s == NULL)
33478344Sobrien		return (EINVAL);
33578344Sobrien	*v = s;
33678344Sobrien	return (0);
33778344Sobrien}
33898186Sgordon
33998186Sgordonstatic int
34098186Sgordong_part_parm_str(const char *p, const char **v)
34178344Sobrien{
34278344Sobrien
34398186Sgordon	if (p[0] == '\0')
34498186Sgordon		return (EINVAL);
34598186Sgordon	*v = p;
34678344Sobrien	return (0);
34798186Sgordon}
34898186Sgordon
34978344Sobrienstatic int
35078344Sobrieng_part_parm_uint(const char *p, u_int *v)
35178344Sobrien{
35278344Sobrien	char *x;
35398186Sgordon	long l;
35478344Sobrien
35578344Sobrien	l = strtol(p, &x, 0);
35678344Sobrien	if (*x != '\0' || l < 0 || l > INT_MAX)
35778344Sobrien		return (EINVAL);
35878344Sobrien	*v = (unsigned int)l;
35978344Sobrien	return (0);
36078344Sobrien}
36178344Sobrien
36278344Sobrienstatic int
36378344Sobrieng_part_probe(struct g_geom *gp, struct g_consumer *cp, int depth)
36478344Sobrien{
36578344Sobrien	struct g_part_scheme *iter, *scheme;
36698186Sgordon	struct g_part_table *table;
36778344Sobrien	int pri, probe;
36878344Sobrien
36998186Sgordon	table = gp->softc;
37078344Sobrien	scheme = (table != NULL) ? table->gpt_scheme : NULL;
37198186Sgordon	pri = (scheme != NULL) ? G_PART_PROBE(table, cp) : INT_MIN;
37298186Sgordon	if (pri == 0)
37398186Sgordon		goto done;
37478344Sobrien	if (pri > 0) {	/* error */
37598186Sgordon		scheme = NULL;
37678344Sobrien		pri = INT_MIN;
37778344Sobrien	}
37898186Sgordon
37998186Sgordon	TAILQ_FOREACH(iter, &g_part_schemes, scheme_list) {
38098186Sgordon		if (iter == &g_part_null_scheme)
38198186Sgordon			continue;
38278344Sobrien		table = (void *)kobj_create((kobj_class_t)iter, M_GEOM,
38398186Sgordon		    M_WAITOK);
38478344Sobrien		table->gpt_gp = gp;
38598186Sgordon		table->gpt_scheme = iter;
38698186Sgordon		table->gpt_depth = depth;
38798186Sgordon		probe = G_PART_PROBE(table, cp);
38898186Sgordon		if (probe <= 0 && probe > pri) {
38978344Sobrien			pri = probe;
39078344Sobrien			scheme = iter;
39178344Sobrien			if (gp->softc != NULL)
39278344Sobrien				kobj_delete((kobj_t)gp->softc, M_GEOM);
39378344Sobrien			gp->softc = table;
39478344Sobrien			if (pri == 0)
39578344Sobrien				goto done;
39678344Sobrien		} else
39778344Sobrien			kobj_delete((kobj_t)table, M_GEOM);
39878344Sobrien	}
39978344Sobrien
40078344Sobriendone:
40198186Sgordon	return ((scheme == NULL) ? ENXIO : 0);
40298186Sgordon}
40378344Sobrien
40498186Sgordon/*
40598186Sgordon * Control request functions.
40678344Sobrien */
40778344Sobrien
40878344Sobrienstatic int
40978344Sobrieng_part_ctl_add(struct gctl_req *req, struct g_part_parms *gpp)
41098186Sgordon{
41178344Sobrien	char buf[32];
41298186Sgordon	struct g_geom *gp;
41398186Sgordon	struct g_provider *pp;
41498186Sgordon	struct g_part_entry *delent, *last, *entry;
41598186Sgordon	struct g_part_table *table;
41678344Sobrien	struct sbuf *sb;
41798186Sgordon	quad_t end;
41898186Sgordon	unsigned int index;
41978344Sobrien	int error;
42078344Sobrien
42178344Sobrien	gp = gpp->gpp_geom;
42278344Sobrien	G_PART_TRACE((G_T_TOPOLOGY, "%s(%s)", __func__, gp->name));
42398186Sgordon	g_topology_assert();
42478344Sobrien
42598186Sgordon	pp = LIST_FIRST(&gp->consumer)->provider;
42698186Sgordon	table = gp->softc;
42798186Sgordon	end = gpp->gpp_start + gpp->gpp_size - 1;
42898186Sgordon
42998186Sgordon	if (gpp->gpp_start < table->gpt_first ||
43098186Sgordon	    gpp->gpp_start > table->gpt_last) {
43198186Sgordon		gctl_error(req, "%d start '%jd'", EINVAL,
43298186Sgordon		    (intmax_t)gpp->gpp_start);
43398186Sgordon		return (EINVAL);
43498186Sgordon	}
43598186Sgordon	if (end < gpp->gpp_start || end > table->gpt_last) {
43698186Sgordon		gctl_error(req, "%d size '%jd'", EINVAL,
43798186Sgordon		    (intmax_t)gpp->gpp_size);
43898186Sgordon		return (EINVAL);
43998186Sgordon	}
44098186Sgordon	if (gpp->gpp_index > table->gpt_entries) {
44198186Sgordon		gctl_error(req, "%d index '%d'", EINVAL, gpp->gpp_index);
44298186Sgordon		return (EINVAL);
44398186Sgordon	}
44498186Sgordon
44598186Sgordon	delent = last = NULL;
44698186Sgordon	index = (gpp->gpp_index > 0) ? gpp->gpp_index : 1;
44798186Sgordon	LIST_FOREACH(entry, &table->gpt_entry, gpe_entry) {
44898186Sgordon		if (entry->gpe_deleted) {
44978344Sobrien			if (entry->gpe_index == index)
45078344Sobrien				delent = entry;
45198186Sgordon			continue;
45278344Sobrien		}
45398186Sgordon		if (entry->gpe_index == index)
45478344Sobrien			index = entry->gpe_index + 1;
45578344Sobrien		if (entry->gpe_index < index)
45698186Sgordon			last = entry;
45778344Sobrien		if (entry->gpe_internal)
45898186Sgordon			continue;
45998186Sgordon		if (gpp->gpp_start >= entry->gpe_start &&
46078344Sobrien		    gpp->gpp_start <= entry->gpe_end) {
46178344Sobrien			gctl_error(req, "%d start '%jd'", ENOSPC,
46298186Sgordon			    (intmax_t)gpp->gpp_start);
46398186Sgordon			return (ENOSPC);
46478344Sobrien		}
46578344Sobrien		if (end >= entry->gpe_start && end <= entry->gpe_end) {
46678344Sobrien			gctl_error(req, "%d end '%jd'", ENOSPC, (intmax_t)end);
46778344Sobrien			return (ENOSPC);
46878344Sobrien		}
46978344Sobrien		if (gpp->gpp_start < entry->gpe_start && end > entry->gpe_end) {
47098186Sgordon			gctl_error(req, "%d size '%jd'", ENOSPC,
47198186Sgordon			    (intmax_t)gpp->gpp_size);
47298186Sgordon			return (ENOSPC);
47398186Sgordon		}
47498186Sgordon	}
47578344Sobrien	if (gpp->gpp_index > 0 && index != gpp->gpp_index) {
47698186Sgordon		gctl_error(req, "%d index '%d'", EEXIST, gpp->gpp_index);
47778344Sobrien		return (EEXIST);
47898186Sgordon	}
47998186Sgordon
48078344Sobrien	entry = (delent == NULL) ? g_malloc(table->gpt_scheme->gps_entrysz,
48198186Sgordon	    M_WAITOK | M_ZERO) : delent;
48278344Sobrien	entry->gpe_index = index;
48398186Sgordon	entry->gpe_start = gpp->gpp_start;
48498186Sgordon	entry->gpe_end = end;
48598186Sgordon	error = G_PART_ADD(table, entry, gpp);
48678344Sobrien	if (error) {
48778344Sobrien		gctl_error(req, "%d", error);
48898186Sgordon		if (delent == NULL)
48978344Sobrien			g_free(entry);
49078344Sobrien		return (error);
49178344Sobrien	}
49298186Sgordon	if (delent == NULL) {
49378344Sobrien		if (last == NULL)
49478344Sobrien			LIST_INSERT_HEAD(&table->gpt_entry, entry, gpe_entry);
49578344Sobrien		else
49678344Sobrien			LIST_INSERT_AFTER(last, entry, gpe_entry);
49798186Sgordon		entry->gpe_created = 1;
49878344Sobrien	} else {
49998186Sgordon		entry->gpe_deleted = 0;
50078344Sobrien		entry->gpe_modified = 1;
50198186Sgordon	}
50298186Sgordon	g_part_new_provider(gp, table, entry);
50398186Sgordon
50478344Sobrien	/* Provide feedback if so requested. */
50598186Sgordon	if (gpp->gpp_parms & G_PART_PARM_OUTPUT) {
50698186Sgordon		sb = sbuf_new(NULL, NULL, 0, SBUF_AUTOEXTEND);
50798186Sgordon		sbuf_printf(sb, "%s%s added\n", gp->name,
50898186Sgordon		    G_PART_NAME(table, entry, buf, sizeof(buf)));
50998186Sgordon		sbuf_finish(sb);
51098186Sgordon		gctl_set_param(req, "output", sbuf_data(sb), sbuf_len(sb) + 1);
51178344Sobrien		sbuf_delete(sb);
51298186Sgordon	}
51378344Sobrien	return (0);
51478344Sobrien}
51578344Sobrien
51698186Sgordonstatic int
51778344Sobrieng_part_ctl_commit(struct gctl_req *req, struct g_part_parms *gpp)
51878344Sobrien{
51978344Sobrien	struct g_consumer *cp;
52078344Sobrien	struct g_geom *gp;
52178344Sobrien	struct g_provider *pp;
52278344Sobrien	struct g_part_entry *entry, *tmp;
52378344Sobrien	struct g_part_table *table;
52478344Sobrien	char *buf;
52598186Sgordon	int error, i;
52678344Sobrien
52778344Sobrien	gp = gpp->gpp_geom;
52878344Sobrien	G_PART_TRACE((G_T_TOPOLOGY, "%s(%s)", __func__, gp->name));
52978344Sobrien	g_topology_assert();
53078344Sobrien
53178344Sobrien	table = gp->softc;
53298186Sgordon	if (!table->gpt_opened) {
53398186Sgordon		gctl_error(req, "%d", EPERM);
53478344Sobrien		return (EPERM);
53598186Sgordon	}
53678344Sobrien
53778344Sobrien	cp = LIST_FIRST(&gp->consumer);
53878344Sobrien	if ((table->gpt_smhead | table->gpt_smtail) != 0) {
53998186Sgordon		pp = cp->provider;
54078344Sobrien		buf = g_malloc(pp->sectorsize, M_WAITOK | M_ZERO);
54178344Sobrien		while (table->gpt_smhead != 0) {
54278344Sobrien			i = ffs(table->gpt_smhead) - 1;
54398186Sgordon			error = g_write_data(cp, i * pp->sectorsize, buf,
54498186Sgordon			    pp->sectorsize);
54598186Sgordon			if (error) {
54698186Sgordon				g_free(buf);
54778344Sobrien				goto fail;
54878344Sobrien			}
54978344Sobrien			table->gpt_smhead &= ~(1 << i);
55098186Sgordon		}
55178344Sobrien		while (table->gpt_smtail != 0) {
55278344Sobrien			i = ffs(table->gpt_smtail) - 1;
55398186Sgordon			error = g_write_data(cp, pp->mediasize - (i + 1) *
55498186Sgordon			    pp->sectorsize, buf, pp->sectorsize);
55578344Sobrien			if (error) {
55678344Sobrien				g_free(buf);
55778344Sobrien				goto fail;
55878344Sobrien			}
55978344Sobrien			table->gpt_smtail &= ~(1 << i);
56078344Sobrien		}
56178344Sobrien		g_free(buf);
56298186Sgordon	}
56398186Sgordon
56478344Sobrien	if (table->gpt_scheme == &g_part_null_scheme) {
56578344Sobrien		g_access(cp, -1, -1, -1);
56678344Sobrien		g_part_wither(gp, ENXIO);
56778344Sobrien		return (0);
56898186Sgordon	}
56978344Sobrien
57078344Sobrien	error = G_PART_WRITE(table, cp);
57178344Sobrien	if (error)
57278344Sobrien		goto fail;
57378344Sobrien
57478344Sobrien	LIST_FOREACH_SAFE(entry, &table->gpt_entry, gpe_entry, tmp) {
57578344Sobrien		if (!entry->gpe_deleted) {
57678344Sobrien			entry->gpe_created = 0;
57778344Sobrien			entry->gpe_modified = 0;
57898186Sgordon			continue;
57978344Sobrien		}
58078344Sobrien		LIST_REMOVE(entry, gpe_entry);
58178344Sobrien		g_free(entry);
58278344Sobrien	}
58378344Sobrien	table->gpt_created = 0;
58478344Sobrien	table->gpt_opened = 0;
58578344Sobrien	g_access(cp, -1, -1, -1);
58698186Sgordon	return (0);
58778344Sobrien
58878344Sobrienfail:
58978344Sobrien	gctl_error(req, "%d", error);
59078344Sobrien	return (error);
59178344Sobrien}
59278344Sobrien
59378344Sobrienstatic int
59498186Sgordong_part_ctl_create(struct gctl_req *req, struct g_part_parms *gpp)
59578344Sobrien{
59678344Sobrien	struct g_consumer *cp;
59778344Sobrien	struct g_geom *gp;
59878344Sobrien	struct g_provider *pp;
59978344Sobrien	struct g_part_scheme *scheme;
60078344Sobrien	struct g_part_table *null, *table;
60178344Sobrien	struct sbuf *sb;
60278344Sobrien	int attr, error;
60398186Sgordon
60478344Sobrien	pp = gpp->gpp_provider;
60578344Sobrien	scheme = gpp->gpp_scheme;
60678344Sobrien	G_PART_TRACE((G_T_TOPOLOGY, "%s(%s)", __func__, pp->name));
60778344Sobrien	g_topology_assert();
60878344Sobrien
60978344Sobrien	/* Check that there isn't already a g_part geom on the provider. */
61078344Sobrien	error = g_part_parm_geom(pp->name, &gp);
61178344Sobrien	if (!error) {
61278344Sobrien		null = gp->softc;
61378344Sobrien		if (null->gpt_scheme != &g_part_null_scheme) {
61498186Sgordon			gctl_error(req, "%d geom '%s'", EEXIST, pp->name);
61578344Sobrien			return (EEXIST);
61678344Sobrien		}
61778344Sobrien	} else
61878344Sobrien		null = NULL;
61998186Sgordon
62098186Sgordon	if ((gpp->gpp_parms & G_PART_PARM_ENTRIES) &&
62198186Sgordon	    (gpp->gpp_entries < scheme->gps_minent ||
62298186Sgordon	     gpp->gpp_entries > scheme->gps_maxent)) {
62378344Sobrien		gctl_error(req, "%d entries '%d'", EINVAL, gpp->gpp_entries);
62498186Sgordon		return (EINVAL);
62598186Sgordon	}
62698186Sgordon
62798186Sgordon	if (null == NULL)
62898186Sgordon		gp = g_new_geomf(&g_part_class, "%s", pp->name);
62998186Sgordon	gp->softc = kobj_create((kobj_class_t)gpp->gpp_scheme, M_GEOM,
63098186Sgordon	    M_WAITOK);
63198186Sgordon	table = gp->softc;
63298186Sgordon	table->gpt_gp = gp;
63398186Sgordon	table->gpt_scheme = gpp->gpp_scheme;
63498186Sgordon	table->gpt_entries = (gpp->gpp_parms & G_PART_PARM_ENTRIES) ?
63598186Sgordon	    gpp->gpp_entries : scheme->gps_minent;
63678344Sobrien	LIST_INIT(&table->gpt_entry);
63778344Sobrien	if (null == NULL) {
63878344Sobrien		cp = g_new_consumer(gp);
63998186Sgordon		error = g_attach(cp, pp);
64078344Sobrien		if (error == 0)
64178344Sobrien			error = g_access(cp, 1, 1, 1);
64278344Sobrien		if (error != 0) {
64378344Sobrien			g_part_wither(gp, error);
64478344Sobrien			gctl_error(req, "%d geom '%s'", error, pp->name);
64578344Sobrien			return (error);
64678344Sobrien		}
64778344Sobrien		table->gpt_opened = 1;
64878344Sobrien	} else {
64998186Sgordon		cp = LIST_FIRST(&gp->consumer);
65098186Sgordon		table->gpt_opened = null->gpt_opened;
65198186Sgordon		table->gpt_smhead = null->gpt_smhead;
65298186Sgordon		table->gpt_smtail = null->gpt_smtail;
65378344Sobrien	}
65478344Sobrien
65598186Sgordon	g_topology_unlock();
65698186Sgordon
65798186Sgordon	/* Make sure the provider has media. */
65878344Sobrien	if (pp->mediasize == 0 || pp->sectorsize == 0) {
65998186Sgordon		error = ENODEV;
66098186Sgordon		goto fail;
66198186Sgordon	}
66298186Sgordon
66398186Sgordon	/* Make sure we can nest and if so, determine our depth. */
66498186Sgordon	error = g_getattr("PART::isleaf", cp, &attr);
66598186Sgordon	if (!error && attr) {
66698186Sgordon		error = ENODEV;
66798186Sgordon		goto fail;
66898186Sgordon	}
66998186Sgordon	error = g_getattr("PART::depth", cp, &attr);
67098186Sgordon	table->gpt_depth = (!error) ? attr + 1 : 0;
67198186Sgordon
67298186Sgordon	/* If we're nested, get the absolute sector offset on disk. */
67398186Sgordon	if (table->gpt_depth) {
67498186Sgordon		error = g_getattr("PART::offset", cp, &attr);
67578344Sobrien		if (error)
67678344Sobrien			goto fail;
67778344Sobrien		table->gpt_offset = attr;
67898186Sgordon	}
67978344Sobrien
68078344Sobrien	/*
68178344Sobrien	 * Synthesize a disk geometry. Some partitioning schemes
68278344Sobrien	 * depend on it and since some file systems need it even
68378344Sobrien	 * when the partitition scheme doesn't, we do it here in
68478344Sobrien	 * scheme-independent code.
68578344Sobrien	 */
68678344Sobrien	g_part_geometry(table, cp, pp->mediasize / pp->sectorsize);
68778344Sobrien
68898186Sgordon	error = G_PART_CREATE(table, gpp);
68978344Sobrien	if (error)
69078344Sobrien		goto fail;
69198186Sgordon
69298186Sgordon	g_topology_lock();
69398186Sgordon
69498186Sgordon	table->gpt_created = 1;
69598186Sgordon	if (null != NULL)
69698186Sgordon		kobj_delete((kobj_t)null, M_GEOM);
69798186Sgordon
69898186Sgordon	/*
69978344Sobrien	 * Support automatic commit by filling in the gpp_geom
70078344Sobrien	 * parameter.
70178344Sobrien	 */
70298186Sgordon	gpp->gpp_parms |= G_PART_PARM_GEOM;
70378344Sobrien	gpp->gpp_geom = gp;
70478344Sobrien
70578344Sobrien	/* Provide feedback if so requested. */
70678344Sobrien	if (gpp->gpp_parms & G_PART_PARM_OUTPUT) {
70778344Sobrien		sb = sbuf_new(NULL, NULL, 0, SBUF_AUTOEXTEND);
70878344Sobrien		sbuf_printf(sb, "%s created\n", gp->name);
70978344Sobrien		sbuf_finish(sb);
71078344Sobrien		gctl_set_param(req, "output", sbuf_data(sb), sbuf_len(sb) + 1);
71178344Sobrien		sbuf_delete(sb);
71278344Sobrien	}
71398186Sgordon	return (0);
71498186Sgordon
71598186Sgordonfail:
71698186Sgordon	g_topology_lock();
71778344Sobrien	if (null == NULL) {
71878344Sobrien		g_access(cp, -1, -1, -1);
71998186Sgordon		g_part_wither(gp, error);
72098186Sgordon	} else {
72198186Sgordon		kobj_delete((kobj_t)gp->softc, M_GEOM);
72298186Sgordon		gp->softc = null;
72398186Sgordon	}
72498186Sgordon	gctl_error(req, "%d provider", error);
72578344Sobrien	return (error);
72678344Sobrien}
72778344Sobrien
72878344Sobrienstatic int
72978344Sobrieng_part_ctl_delete(struct gctl_req *req, struct g_part_parms *gpp)
73078344Sobrien{
73178344Sobrien	char buf[32];
73278344Sobrien	struct g_geom *gp;
73378344Sobrien	struct g_provider *pp;
73478344Sobrien	struct g_part_entry *entry;
73578344Sobrien	struct g_part_table *table;
73678344Sobrien	struct sbuf *sb;
73778344Sobrien
73878344Sobrien	gp = gpp->gpp_geom;
73978344Sobrien	G_PART_TRACE((G_T_TOPOLOGY, "%s(%s)", __func__, gp->name));
74078344Sobrien	g_topology_assert();
74178344Sobrien
74278344Sobrien	table = gp->softc;
74378344Sobrien
74498186Sgordon	LIST_FOREACH(entry, &table->gpt_entry, gpe_entry) {
74578344Sobrien		if (entry->gpe_deleted || entry->gpe_internal)
74678344Sobrien			continue;
74778344Sobrien		if (entry->gpe_index == gpp->gpp_index)
74878344Sobrien			break;
74978344Sobrien	}
75078344Sobrien	if (entry == NULL) {
75178344Sobrien		gctl_error(req, "%d index '%d'", ENOENT, gpp->gpp_index);
75278344Sobrien		return (ENOENT);
75398186Sgordon	}
75498186Sgordon
75598186Sgordon	pp = entry->gpe_pp;
75678344Sobrien	if (pp != NULL) {
75778344Sobrien		if (pp->acr > 0 || pp->acw > 0 || pp->ace > 0) {
75878344Sobrien			gctl_error(req, "%d", EBUSY);
75978344Sobrien			return (EBUSY);
76078344Sobrien		}
76178344Sobrien
76278344Sobrien		pp->private = NULL;
76378344Sobrien		entry->gpe_pp = NULL;
76478344Sobrien	}
76598186Sgordon
76698186Sgordon	if (entry->gpe_created) {
76798186Sgordon		LIST_REMOVE(entry, gpe_entry);
76898186Sgordon		g_free(entry);
76998186Sgordon	} else {
77098186Sgordon		entry->gpe_modified = 0;
77198186Sgordon		entry->gpe_deleted = 1;
77278344Sobrien	}
77378344Sobrien
77478344Sobrien	if (pp != NULL)
77578344Sobrien		g_wither_provider(pp, ENXIO);
77698186Sgordon
77798186Sgordon	/* Provide feedback if so requested. */
77898186Sgordon	if (gpp->gpp_parms & G_PART_PARM_OUTPUT) {
77978344Sobrien		sb = sbuf_new(NULL, NULL, 0, SBUF_AUTOEXTEND);
78098186Sgordon		sbuf_printf(sb, "%s%s deleted\n", gp->name,
78198186Sgordon		    G_PART_NAME(table, entry, buf, sizeof(buf)));
78298186Sgordon		sbuf_finish(sb);
78398186Sgordon		gctl_set_param(req, "output", sbuf_data(sb), sbuf_len(sb) + 1);
78498186Sgordon		sbuf_delete(sb);
78598186Sgordon	}
78698186Sgordon	return (0);
78798186Sgordon}
78878344Sobrien
78978344Sobrienstatic int
79078344Sobrieng_part_ctl_destroy(struct gctl_req *req, struct g_part_parms *gpp)
79178344Sobrien{
79278344Sobrien	struct g_geom *gp;
79378344Sobrien	struct g_part_entry *entry;
79478344Sobrien	struct g_part_table *null, *table;
79578344Sobrien	struct sbuf *sb;
79678344Sobrien	int error;
79778344Sobrien
79878344Sobrien	gp = gpp->gpp_geom;
79978344Sobrien	G_PART_TRACE((G_T_TOPOLOGY, "%s(%s)", __func__, gp->name));
80078344Sobrien	g_topology_assert();
80178344Sobrien
80278344Sobrien	table = gp->softc;
80398186Sgordon	LIST_FOREACH(entry, &table->gpt_entry, gpe_entry) {
80498186Sgordon		if (entry->gpe_deleted || entry->gpe_internal)
80598186Sgordon			continue;
80698186Sgordon		gctl_error(req, "%d", EBUSY);
80798186Sgordon		return (EBUSY);
80898186Sgordon	}
80998186Sgordon
81098186Sgordon	error = G_PART_DESTROY(table, gpp);
81198186Sgordon	if (error) {
81298186Sgordon		gctl_error(req, "%d", error);
81398186Sgordon		return (error);
81478344Sobrien	}
81598186Sgordon
81678344Sobrien	gp->softc = kobj_create((kobj_class_t)&g_part_null_scheme, M_GEOM,
81778344Sobrien	    M_WAITOK);
818101850Sgordon	null = gp->softc;
819101850Sgordon	null->gpt_gp = gp;
820101850Sgordon	null->gpt_scheme = &g_part_null_scheme;
821103018Sgordon	LIST_INIT(&null->gpt_entry);
822101850Sgordon	null->gpt_depth = table->gpt_depth;
823101850Sgordon	null->gpt_opened = table->gpt_opened;
824101850Sgordon	null->gpt_smhead = table->gpt_smhead;
825101850Sgordon	null->gpt_smtail = table->gpt_smtail;
826101850Sgordon
827101850Sgordon	while ((entry = LIST_FIRST(&table->gpt_entry)) != NULL) {
828101850Sgordon		LIST_REMOVE(entry, gpe_entry);
829101850Sgordon		g_free(entry);
830101850Sgordon	}
831101850Sgordon	kobj_delete((kobj_t)table, M_GEOM);
832101850Sgordon
83378344Sobrien	/* Provide feedback if so requested. */
83478344Sobrien	if (gpp->gpp_parms & G_PART_PARM_OUTPUT) {
83578344Sobrien		sb = sbuf_new(NULL, NULL, 0, SBUF_AUTOEXTEND);
83678344Sobrien		sbuf_printf(sb, "%s destroyed\n", gp->name);
83778344Sobrien		sbuf_finish(sb);
83878344Sobrien		gctl_set_param(req, "output", sbuf_data(sb), sbuf_len(sb) + 1);
83978344Sobrien		sbuf_delete(sb);
84078344Sobrien	}
84178344Sobrien	return (0);
842106643Sgordon}
84378344Sobrien
84478344Sobrienstatic int
84578344Sobrieng_part_ctl_modify(struct gctl_req *req, struct g_part_parms *gpp)
84678344Sobrien{
84778344Sobrien	char buf[32];
84878344Sobrien	struct g_geom *gp;
84978344Sobrien	struct g_part_entry *entry;
85078344Sobrien	struct g_part_table *table;
85178344Sobrien	struct sbuf *sb;
85278344Sobrien	int error;
85378344Sobrien
85478344Sobrien	gp = gpp->gpp_geom;
85578344Sobrien	G_PART_TRACE((G_T_TOPOLOGY, "%s(%s)", __func__, gp->name));
85678344Sobrien	g_topology_assert();
85778344Sobrien
85878344Sobrien	table = gp->softc;
85978344Sobrien
86078344Sobrien	LIST_FOREACH(entry, &table->gpt_entry, gpe_entry) {
86178344Sobrien		if (entry->gpe_deleted || entry->gpe_internal)
862106643Sgordon			continue;
863106643Sgordon		if (entry->gpe_index == gpp->gpp_index)
864106643Sgordon			break;
865106643Sgordon	}
86678344Sobrien	if (entry == NULL) {
86778344Sobrien		gctl_error(req, "%d index '%d'", ENOENT, gpp->gpp_index);
86878344Sobrien		return (ENOENT);
86978344Sobrien	}
87078344Sobrien
87178344Sobrien	error = G_PART_MODIFY(table, entry, gpp);
87278344Sobrien	if (error) {
87378344Sobrien		gctl_error(req, "%d", error);
87478344Sobrien		return (error);
875106643Sgordon	}
876106643Sgordon
877106643Sgordon	if (!entry->gpe_created)
878106643Sgordon		entry->gpe_modified = 1;
87978344Sobrien
88098186Sgordon	/* Provide feedback if so requested. */
88198186Sgordon	if (gpp->gpp_parms & G_PART_PARM_OUTPUT) {
88298186Sgordon		sb = sbuf_new(NULL, NULL, 0, SBUF_AUTOEXTEND);
88398186Sgordon		sbuf_printf(sb, "%s%s modified\n", gp->name,
88498186Sgordon		    G_PART_NAME(table, entry, buf, sizeof(buf)));
88598186Sgordon		sbuf_finish(sb);
88698186Sgordon		gctl_set_param(req, "output", sbuf_data(sb), sbuf_len(sb) + 1);
887106643Sgordon		sbuf_delete(sb);
888106643Sgordon	}
889106643Sgordon	return (0);
890106643Sgordon}
89198186Sgordon
89298186Sgordonstatic int
89398186Sgordong_part_ctl_move(struct gctl_req *req, struct g_part_parms *gpp)
89498186Sgordon{
895106643Sgordon	gctl_error(req, "%d verb 'move'", ENOSYS);
89698186Sgordon	return (ENOSYS);
89798186Sgordon}
89898186Sgordon
89998186Sgordonstatic int
90098186Sgordong_part_ctl_recover(struct gctl_req *req, struct g_part_parms *gpp)
90198186Sgordon{
90298186Sgordon	gctl_error(req, "%d verb 'recover'", ENOSYS);
903106700Sgordon	return (ENOSYS);
904106700Sgordon}
905106700Sgordon
906106643Sgordonstatic int
90798186Sgordong_part_ctl_resize(struct gctl_req *req, struct g_part_parms *gpp)
90898186Sgordon{
90998186Sgordon	gctl_error(req, "%d verb 'resize'", ENOSYS);
91098186Sgordon	return (ENOSYS);
91198186Sgordon}
91298186Sgordon
91398186Sgordonstatic int
91498186Sgordong_part_ctl_undo(struct gctl_req *req, struct g_part_parms *gpp)
91598186Sgordon{
91698186Sgordon	struct g_consumer *cp;
91798186Sgordon	struct g_provider *pp;
91898186Sgordon	struct g_geom *gp;
91998186Sgordon	struct g_part_entry *entry, *tmp;
92098186Sgordon	struct g_part_table *table;
92198186Sgordon	int error, reprobe;
92298186Sgordon
92398186Sgordon	gp = gpp->gpp_geom;
92498186Sgordon	G_PART_TRACE((G_T_TOPOLOGY, "%s(%s)", __func__, gp->name));
92598186Sgordon	g_topology_assert();
92698186Sgordon
92798186Sgordon	table = gp->softc;
92898186Sgordon	if (!table->gpt_opened) {
92998186Sgordon		gctl_error(req, "%d", EPERM);
93098186Sgordon		return (EPERM);
93198186Sgordon	}
93298186Sgordon
93398186Sgordon	cp = LIST_FIRST(&gp->consumer);
93498186Sgordon	LIST_FOREACH_SAFE(entry, &table->gpt_entry, gpe_entry, tmp) {
93598186Sgordon		entry->gpe_modified = 0;
93698186Sgordon		if (entry->gpe_created) {
93798186Sgordon			pp = entry->gpe_pp;
93898186Sgordon			if (pp != NULL) {
93998186Sgordon				pp->private = NULL;
94098186Sgordon				entry->gpe_pp = NULL;
94198186Sgordon				g_wither_provider(pp, ENXIO);
94298186Sgordon			}
94398186Sgordon			entry->gpe_deleted = 1;
94498186Sgordon		}
94598186Sgordon		if (entry->gpe_deleted) {
94698186Sgordon			LIST_REMOVE(entry, gpe_entry);
94798186Sgordon			g_free(entry);
94898186Sgordon		}
94998186Sgordon	}
95098186Sgordon
95198186Sgordon	g_topology_unlock();
95298186Sgordon
95398186Sgordon	reprobe = (table->gpt_scheme == &g_part_null_scheme ||
95498186Sgordon	    table->gpt_created) ? 1 : 0;
95598186Sgordon
95698186Sgordon	if (reprobe) {
95798186Sgordon		if (!LIST_EMPTY(&table->gpt_entry)) {
95898186Sgordon			error = EBUSY;
95998186Sgordon			goto fail;
96098186Sgordon		}
96198186Sgordon		error = g_part_probe(gp, cp, table->gpt_depth);
96298186Sgordon		if (error) {
96398186Sgordon			g_topology_lock();
96498186Sgordon			g_access(cp, -1, -1, -1);
96598186Sgordon			g_part_wither(gp, error);
96698186Sgordon			return (0);
96798186Sgordon		}
96898186Sgordon		table = gp->softc;
96998186Sgordon	}
97098186Sgordon
97198186Sgordon	error = G_PART_READ(table, cp);
97298186Sgordon	if (error)
97398186Sgordon		goto fail;
97498186Sgordon
97598186Sgordon	g_topology_lock();
97698186Sgordon
97798186Sgordon	LIST_FOREACH(entry, &table->gpt_entry, gpe_entry) {
97898186Sgordon		if (!entry->gpe_internal)
97998186Sgordon			g_part_new_provider(gp, table, entry);
98098186Sgordon	}
98198186Sgordon
98298186Sgordon	table->gpt_opened = 0;
98398186Sgordon	g_access(cp, -1, -1, -1);
98498186Sgordon	return (0);
98598186Sgordon
98698186Sgordonfail:
98798186Sgordon	g_topology_lock();
98898186Sgordon	gctl_error(req, "%d", error);
98998186Sgordon	return (error);
99098186Sgordon}
99198186Sgordon
99298186Sgordonstatic void
99398186Sgordong_part_wither(struct g_geom *gp, int error)
99498186Sgordon{
99598186Sgordon	struct g_part_entry *entry;
996	struct g_part_table *table;
997
998	table = gp->softc;
999	if (table != NULL) {
1000		while ((entry = LIST_FIRST(&table->gpt_entry)) != NULL) {
1001			LIST_REMOVE(entry, gpe_entry);
1002			g_free(entry);
1003		}
1004		if (gp->softc != NULL) {
1005			kobj_delete((kobj_t)gp->softc, M_GEOM);
1006			gp->softc = NULL;
1007		}
1008	}
1009	g_wither_geom(gp, error);
1010}
1011
1012/*
1013 * Class methods.
1014 */
1015
1016static void
1017g_part_ctlreq(struct gctl_req *req, struct g_class *mp, const char *verb)
1018{
1019	struct g_part_parms gpp;
1020	struct g_part_table *table;
1021	struct gctl_req_arg *ap;
1022	const char *p;
1023	enum g_part_ctl ctlreq;
1024	unsigned int i, mparms, oparms, parm;
1025	int auto_commit, close_on_error;
1026	int error, modifies;
1027
1028	G_PART_TRACE((G_T_TOPOLOGY, "%s(%s,%s)", __func__, mp->name, verb));
1029	g_topology_assert();
1030
1031	ctlreq = G_PART_CTL_NONE;
1032	modifies = 1;
1033	mparms = 0;
1034	oparms = G_PART_PARM_FLAGS | G_PART_PARM_OUTPUT | G_PART_PARM_VERSION;
1035	switch (*verb) {
1036	case 'a':
1037		if (!strcmp(verb, "add")) {
1038			ctlreq = G_PART_CTL_ADD;
1039			mparms |= G_PART_PARM_GEOM | G_PART_PARM_SIZE |
1040			    G_PART_PARM_START | G_PART_PARM_TYPE;
1041			oparms |= G_PART_PARM_INDEX | G_PART_PARM_LABEL;
1042		}
1043		break;
1044	case 'c':
1045		if (!strcmp(verb, "commit")) {
1046			ctlreq = G_PART_CTL_COMMIT;
1047			mparms |= G_PART_PARM_GEOM;
1048			modifies = 0;
1049		} else if (!strcmp(verb, "create")) {
1050			ctlreq = G_PART_CTL_CREATE;
1051			mparms |= G_PART_PARM_PROVIDER | G_PART_PARM_SCHEME;
1052			oparms |= G_PART_PARM_ENTRIES;
1053		}
1054		break;
1055	case 'd':
1056		if (!strcmp(verb, "delete")) {
1057			ctlreq = G_PART_CTL_DELETE;
1058			mparms |= G_PART_PARM_GEOM | G_PART_PARM_INDEX;
1059		} else if (!strcmp(verb, "destroy")) {
1060			ctlreq = G_PART_CTL_DESTROY;
1061			mparms |= G_PART_PARM_GEOM;
1062		}
1063		break;
1064	case 'm':
1065		if (!strcmp(verb, "modify")) {
1066			ctlreq = G_PART_CTL_MODIFY;
1067			mparms |= G_PART_PARM_GEOM | G_PART_PARM_INDEX;
1068			oparms |= G_PART_PARM_LABEL | G_PART_PARM_TYPE;
1069		} else if (!strcmp(verb, "move")) {
1070			ctlreq = G_PART_CTL_MOVE;
1071			mparms |= G_PART_PARM_GEOM | G_PART_PARM_INDEX;
1072		}
1073		break;
1074	case 'r':
1075		if (!strcmp(verb, "recover")) {
1076			ctlreq = G_PART_CTL_RECOVER;
1077			mparms |= G_PART_PARM_GEOM;
1078		} else if (!strcmp(verb, "resize")) {
1079			ctlreq = G_PART_CTL_RESIZE;
1080			mparms |= G_PART_PARM_GEOM | G_PART_PARM_INDEX;
1081		}
1082		break;
1083	case 'u':
1084		if (!strcmp(verb, "undo")) {
1085			ctlreq = G_PART_CTL_UNDO;
1086			mparms |= G_PART_PARM_GEOM;
1087			modifies = 0;
1088		}
1089		break;
1090	}
1091	if (ctlreq == G_PART_CTL_NONE) {
1092		gctl_error(req, "%d verb '%s'", EINVAL, verb);
1093		return;
1094	}
1095
1096	bzero(&gpp, sizeof(gpp));
1097	for (i = 0; i < req->narg; i++) {
1098		ap = &req->arg[i];
1099		parm = 0;
1100		switch (ap->name[0]) {
1101		case 'c':
1102			if (!strcmp(ap->name, "class"))
1103				continue;
1104			break;
1105		case 'e':
1106			if (!strcmp(ap->name, "entries"))
1107				parm = G_PART_PARM_ENTRIES;
1108			break;
1109		case 'f':
1110			if (!strcmp(ap->name, "flags"))
1111				parm = G_PART_PARM_FLAGS;
1112			break;
1113		case 'g':
1114			if (!strcmp(ap->name, "geom"))
1115				parm = G_PART_PARM_GEOM;
1116			break;
1117		case 'i':
1118			if (!strcmp(ap->name, "index"))
1119				parm = G_PART_PARM_INDEX;
1120			break;
1121		case 'l':
1122			if (!strcmp(ap->name, "label"))
1123				parm = G_PART_PARM_LABEL;
1124			break;
1125		case 'o':
1126			if (!strcmp(ap->name, "output"))
1127				parm = G_PART_PARM_OUTPUT;
1128			break;
1129		case 'p':
1130			if (!strcmp(ap->name, "provider"))
1131				parm = G_PART_PARM_PROVIDER;
1132			break;
1133		case 's':
1134			if (!strcmp(ap->name, "scheme"))
1135				parm = G_PART_PARM_SCHEME;
1136			else if (!strcmp(ap->name, "size"))
1137				parm = G_PART_PARM_SIZE;
1138			else if (!strcmp(ap->name, "start"))
1139				parm = G_PART_PARM_START;
1140			break;
1141		case 't':
1142			if (!strcmp(ap->name, "type"))
1143				parm = G_PART_PARM_TYPE;
1144			break;
1145		case 'v':
1146			if (!strcmp(ap->name, "verb"))
1147				continue;
1148			else if (!strcmp(ap->name, "version"))
1149				parm = G_PART_PARM_VERSION;
1150			break;
1151		}
1152		if ((parm & (mparms | oparms)) == 0) {
1153			gctl_error(req, "%d param '%s'", EINVAL, ap->name);
1154			return;
1155		}
1156		p = gctl_get_asciiparam(req, ap->name);
1157		if (p == NULL) {
1158			gctl_error(req, "%d param '%s'", ENOATTR, ap->name);
1159			return;
1160		}
1161		switch (parm) {
1162		case G_PART_PARM_ENTRIES:
1163			error = g_part_parm_uint(p, &gpp.gpp_entries);
1164			break;
1165		case G_PART_PARM_FLAGS:
1166			if (p[0] == '\0')
1167				continue;
1168			error = g_part_parm_str(p, &gpp.gpp_flags);
1169			break;
1170		case G_PART_PARM_GEOM:
1171			error = g_part_parm_geom(p, &gpp.gpp_geom);
1172			break;
1173		case G_PART_PARM_INDEX:
1174			error = g_part_parm_uint(p, &gpp.gpp_index);
1175			break;
1176		case G_PART_PARM_LABEL:
1177			/* An empty label is always valid. */
1178			gpp.gpp_label = p;
1179			error = 0;
1180			break;
1181		case G_PART_PARM_OUTPUT:
1182			error = 0;	/* Write-only parameter */
1183			break;
1184		case G_PART_PARM_PROVIDER:
1185			error = g_part_parm_provider(p, &gpp.gpp_provider);
1186			break;
1187		case G_PART_PARM_SCHEME:
1188			error = g_part_parm_scheme(p, &gpp.gpp_scheme);
1189			break;
1190		case G_PART_PARM_SIZE:
1191			error = g_part_parm_quad(p, &gpp.gpp_size);
1192			break;
1193		case G_PART_PARM_START:
1194			error = g_part_parm_quad(p, &gpp.gpp_start);
1195			break;
1196		case G_PART_PARM_TYPE:
1197			error = g_part_parm_str(p, &gpp.gpp_type);
1198			break;
1199		case G_PART_PARM_VERSION:
1200			error = g_part_parm_uint(p, &gpp.gpp_version);
1201			break;
1202		default:
1203			error = EDOOFUS;
1204			break;
1205		}
1206		if (error) {
1207			gctl_error(req, "%d %s '%s'", error, ap->name, p);
1208			return;
1209		}
1210		gpp.gpp_parms |= parm;
1211	}
1212	if ((gpp.gpp_parms & mparms) != mparms) {
1213		parm = mparms - (gpp.gpp_parms & mparms);
1214		gctl_error(req, "%d param '%x'", ENOATTR, parm);
1215		return;
1216	}
1217
1218	/* Obtain permissions if possible/necessary. */
1219	close_on_error = 0;
1220	table = NULL;	/* Suppress uninit. warning. */
1221	if (modifies && (gpp.gpp_parms & G_PART_PARM_GEOM)) {
1222		table = gpp.gpp_geom->softc;
1223		if (table != NULL && !table->gpt_opened) {
1224			error = g_access(LIST_FIRST(&gpp.gpp_geom->consumer),
1225			    1, 1, 1);
1226			if (error) {
1227				gctl_error(req, "%d geom '%s'", error,
1228				    gpp.gpp_geom->name);
1229				return;
1230			}
1231			table->gpt_opened = 1;
1232			close_on_error = 1;
1233		}
1234	}
1235
1236	error = EDOOFUS;	/* Prevent bogus  uninit. warning. */
1237	switch (ctlreq) {
1238	case G_PART_CTL_NONE:
1239		panic("%s", __func__);
1240	case G_PART_CTL_ADD:
1241		error = g_part_ctl_add(req, &gpp);
1242		break;
1243	case G_PART_CTL_COMMIT:
1244		error = g_part_ctl_commit(req, &gpp);
1245		break;
1246	case G_PART_CTL_CREATE:
1247		error = g_part_ctl_create(req, &gpp);
1248		break;
1249	case G_PART_CTL_DELETE:
1250		error = g_part_ctl_delete(req, &gpp);
1251		break;
1252	case G_PART_CTL_DESTROY:
1253		error = g_part_ctl_destroy(req, &gpp);
1254		break;
1255	case G_PART_CTL_MODIFY:
1256		error = g_part_ctl_modify(req, &gpp);
1257		break;
1258	case G_PART_CTL_MOVE:
1259		error = g_part_ctl_move(req, &gpp);
1260		break;
1261	case G_PART_CTL_RECOVER:
1262		error = g_part_ctl_recover(req, &gpp);
1263		break;
1264	case G_PART_CTL_RESIZE:
1265		error = g_part_ctl_resize(req, &gpp);
1266		break;
1267	case G_PART_CTL_UNDO:
1268		error = g_part_ctl_undo(req, &gpp);
1269		break;
1270	}
1271
1272	/* Implement automatic commit. */
1273	if (!error) {
1274		auto_commit = (modifies &&
1275		    (gpp.gpp_parms & G_PART_PARM_FLAGS) &&
1276		    strchr(gpp.gpp_flags, 'C') != NULL) ? 1 : 0;
1277		if (auto_commit) {
1278			KASSERT(gpp.gpp_parms & G_PART_PARM_GEOM, (__func__));
1279			error = g_part_ctl_commit(req, &gpp);
1280		}
1281	}
1282
1283	if (error && close_on_error) {
1284		g_access(LIST_FIRST(&gpp.gpp_geom->consumer), -1, -1, -1);
1285		table->gpt_opened = 0;
1286	}
1287}
1288
1289static int
1290g_part_destroy_geom(struct gctl_req *req, struct g_class *mp,
1291    struct g_geom *gp)
1292{
1293
1294	G_PART_TRACE((G_T_TOPOLOGY, "%s(%s,%s)", __func__, mp->name, gp->name));
1295	g_topology_assert();
1296
1297	g_part_wither(gp, EINVAL);
1298	return (0);
1299}
1300
1301static struct g_geom *
1302g_part_taste(struct g_class *mp, struct g_provider *pp, int flags __unused)
1303{
1304	struct g_consumer *cp;
1305	struct g_geom *gp;
1306	struct g_part_entry *entry;
1307	struct g_part_table *table;
1308	int attr, depth;
1309	int error;
1310
1311	G_PART_TRACE((G_T_TOPOLOGY, "%s(%s,%s)", __func__, mp->name, pp->name));
1312	g_topology_assert();
1313
1314	/*
1315	 * Create a GEOM with consumer and hook it up to the provider.
1316	 * With that we become part of the topology. Optain read access
1317	 * to the provider.
1318	 */
1319	gp = g_new_geomf(mp, "%s", pp->name);
1320	cp = g_new_consumer(gp);
1321	error = g_attach(cp, pp);
1322	if (error == 0)
1323		error = g_access(cp, 1, 0, 0);
1324	if (error != 0) {
1325		g_part_wither(gp, error);
1326		return (NULL);
1327	}
1328
1329	g_topology_unlock();
1330
1331	/*
1332	 * Short-circuit the whole probing galore when there's no
1333	 * media present.
1334	 */
1335	if (pp->mediasize == 0 || pp->sectorsize == 0) {
1336		error = ENODEV;
1337		goto fail;
1338	}
1339
1340	/* Make sure we can nest and if so, determine our depth. */
1341	error = g_getattr("PART::isleaf", cp, &attr);
1342	if (!error && attr) {
1343		error = ENODEV;
1344		goto fail;
1345	}
1346	error = g_getattr("PART::depth", cp, &attr);
1347	depth = (!error) ? attr + 1 : 0;
1348
1349	error = g_part_probe(gp, cp, depth);
1350	if (error)
1351		goto fail;
1352
1353	table = gp->softc;
1354
1355	/* If we're nested, get the absolute sector offset on disk. */
1356	if (table->gpt_depth) {
1357		error = g_getattr("PART::offset", cp, &attr);
1358		if (error)
1359			goto fail;
1360		table->gpt_offset = attr;
1361	}
1362
1363	/*
1364	 * Synthesize a disk geometry. Some partitioning schemes
1365	 * depend on it and since some file systems need it even
1366	 * when the partitition scheme doesn't, we do it here in
1367	 * scheme-independent code.
1368	 */
1369	g_part_geometry(table, cp, pp->mediasize / pp->sectorsize);
1370
1371	error = G_PART_READ(table, cp);
1372	if (error)
1373		goto fail;
1374
1375	g_topology_lock();
1376	LIST_FOREACH(entry, &table->gpt_entry, gpe_entry) {
1377		if (!entry->gpe_internal)
1378			g_part_new_provider(gp, table, entry);
1379	}
1380
1381	g_access(cp, -1, 0, 0);
1382	return (gp);
1383
1384 fail:
1385	g_topology_lock();
1386	g_access(cp, -1, 0, 0);
1387	g_part_wither(gp, error);
1388	return (NULL);
1389}
1390
1391/*
1392 * Geom methods.
1393 */
1394
1395static int
1396g_part_access(struct g_provider *pp, int dr, int dw, int de)
1397{
1398	struct g_consumer *cp;
1399
1400	G_PART_TRACE((G_T_ACCESS, "%s(%s,%d,%d,%d)", __func__, pp->name, dr,
1401	    dw, de));
1402
1403	cp = LIST_FIRST(&pp->geom->consumer);
1404
1405	/* We always gain write-exclusive access. */
1406	return (g_access(cp, dr, dw, dw + de));
1407}
1408
1409static void
1410g_part_dumpconf(struct sbuf *sb, const char *indent, struct g_geom *gp,
1411    struct g_consumer *cp, struct g_provider *pp)
1412{
1413	char buf[64];
1414	struct g_part_entry *entry;
1415	struct g_part_table *table;
1416
1417	KASSERT(sb != NULL && gp != NULL, (__func__));
1418	table = gp->softc;
1419
1420	if (indent == NULL) {
1421		KASSERT(cp == NULL && pp != NULL, (__func__));
1422		entry = pp->private;
1423		if (entry == NULL)
1424			return;
1425		sbuf_printf(sb, " i %u o %ju ty %s", entry->gpe_index,
1426		    (uintmax_t)entry->gpe_offset,
1427		    G_PART_TYPE(table, entry, buf, sizeof(buf)));
1428	} else if (cp != NULL) {	/* Consumer configuration. */
1429		KASSERT(pp == NULL, (__func__));
1430		/* none */
1431	} else if (pp != NULL) {	/* Provider configuration. */
1432		entry = pp->private;
1433		if (entry == NULL)
1434			return;
1435		sbuf_printf(sb, "%s<index>%u</index>\n", indent,
1436		    entry->gpe_index);
1437		sbuf_printf(sb, "%s<type>%s</type>\n", indent,
1438		    G_PART_TYPE(table, entry, buf, sizeof(buf)));
1439		sbuf_printf(sb, "%s<offset>%ju</offset>\n", indent,
1440		    (uintmax_t)entry->gpe_offset);
1441		sbuf_printf(sb, "%s<length>%ju</length>\n", indent,
1442		    (uintmax_t)pp->mediasize);
1443		G_PART_DUMPCONF(table, entry, sb, indent);
1444	} else {			/* Geom configuration. */
1445		sbuf_printf(sb, "%s<scheme>%s</scheme>\n", indent,
1446		    table->gpt_scheme->name);
1447		sbuf_printf(sb, "%s<entries>%u</entries>\n", indent,
1448		    table->gpt_entries);
1449		sbuf_printf(sb, "%s<first>%ju</first>\n", indent,
1450		    (uintmax_t)table->gpt_first);
1451		sbuf_printf(sb, "%s<last>%ju</last>\n", indent,
1452		    (uintmax_t)table->gpt_last);
1453		sbuf_printf(sb, "%s<fwsectors>%u</fwsectors>\n", indent,
1454		    table->gpt_sectors);
1455		sbuf_printf(sb, "%s<fwheads>%u</fwheads>\n", indent,
1456		    table->gpt_heads);
1457		G_PART_DUMPCONF(table, NULL, sb, indent);
1458	}
1459}
1460
1461static void
1462g_part_orphan(struct g_consumer *cp)
1463{
1464	struct g_provider *pp;
1465
1466	pp = cp->provider;
1467	KASSERT(pp != NULL, (__func__));
1468	G_PART_TRACE((G_T_TOPOLOGY, "%s(%s)", __func__, pp->name));
1469	g_topology_assert();
1470
1471	KASSERT(pp->error != 0, (__func__));
1472	g_part_wither(cp->geom, pp->error);
1473}
1474
1475static void
1476g_part_spoiled(struct g_consumer *cp)
1477{
1478
1479	G_PART_TRACE((G_T_TOPOLOGY, "%s(%s)", __func__, cp->provider->name));
1480	g_topology_assert();
1481
1482	g_part_wither(cp->geom, ENXIO);
1483}
1484
1485static void
1486g_part_start(struct bio *bp)
1487{
1488	struct bio *bp2;
1489	struct g_consumer *cp;
1490	struct g_geom *gp;
1491	struct g_part_entry *entry;
1492	struct g_part_table *table;
1493	struct g_kerneldump *gkd;
1494	struct g_provider *pp;
1495
1496	pp = bp->bio_to;
1497	gp = pp->geom;
1498	table = gp->softc;
1499	cp = LIST_FIRST(&gp->consumer);
1500
1501	G_PART_TRACE((G_T_BIO, "%s: cmd=%d, provider=%s", __func__, bp->bio_cmd,
1502	    pp->name));
1503
1504	entry = pp->private;
1505	if (entry == NULL) {
1506		g_io_deliver(bp, ENXIO);
1507		return;
1508	}
1509
1510	switch(bp->bio_cmd) {
1511	case BIO_DELETE:
1512	case BIO_READ:
1513	case BIO_WRITE:
1514		if (bp->bio_offset >= pp->mediasize) {
1515			g_io_deliver(bp, EIO);
1516			return;
1517		}
1518		bp2 = g_clone_bio(bp);
1519		if (bp2 == NULL) {
1520			g_io_deliver(bp, ENOMEM);
1521			return;
1522		}
1523		if (bp2->bio_offset + bp2->bio_length > pp->mediasize)
1524			bp2->bio_length = pp->mediasize - bp2->bio_offset;
1525		bp2->bio_done = g_std_done;
1526		bp2->bio_offset += entry->gpe_offset;
1527		g_io_request(bp2, cp);
1528		return;
1529	case BIO_FLUSH:
1530		break;
1531	case BIO_GETATTR:
1532		if (g_handleattr_int(bp, "GEOM::fwheads", table->gpt_heads))
1533			return;
1534		if (g_handleattr_int(bp, "GEOM::fwsectors", table->gpt_sectors))
1535			return;
1536		if (g_handleattr_int(bp, "PART::isleaf", table->gpt_isleaf))
1537			return;
1538		if (g_handleattr_int(bp, "PART::depth", table->gpt_depth))
1539			return;
1540		if (g_handleattr_int(bp, "PART::offset",
1541		    table->gpt_offset + entry->gpe_start))
1542			return;
1543		if (!strcmp("GEOM::kerneldump", bp->bio_attribute)) {
1544			/*
1545			 * Check that the partition is suitable for kernel
1546			 * dumps. Typically only swap partitions should be
1547			 * used.
1548			 */
1549			if (!G_PART_DUMPTO(table, entry)) {
1550				g_io_deliver(bp, ENXIO);
1551				return;
1552			}
1553			gkd = (struct g_kerneldump *)bp->bio_data;
1554			if (gkd->offset >= pp->mediasize) {
1555				g_io_deliver(bp, EIO);
1556				return;
1557			}
1558			if (gkd->offset + gkd->length > pp->mediasize)
1559				gkd->length = pp->mediasize - gkd->offset;
1560			gkd->offset += entry->gpe_offset;
1561		}
1562		break;
1563	default:
1564		g_io_deliver(bp, EOPNOTSUPP);
1565		return;
1566	}
1567
1568	bp2 = g_clone_bio(bp);
1569	if (bp2 == NULL) {
1570		g_io_deliver(bp, ENOMEM);
1571		return;
1572	}
1573	bp2->bio_done = g_std_done;
1574	g_io_request(bp2, cp);
1575}
1576
1577static void
1578g_part_init(struct g_class *mp)
1579{
1580
1581	TAILQ_INSERT_TAIL(&g_part_schemes, &g_part_null_scheme, scheme_list);
1582}
1583
1584static void
1585g_part_fini(struct g_class *mp)
1586{
1587
1588	TAILQ_REMOVE(&g_part_schemes, &g_part_null_scheme, scheme_list);
1589}
1590
1591static void
1592g_part_unload_event(void *arg, int flag)
1593{
1594	struct g_consumer *cp;
1595	struct g_geom *gp;
1596	struct g_provider *pp;
1597	struct g_part_scheme *scheme;
1598	struct g_part_table *table;
1599	uintptr_t *xchg;
1600	int acc, error;
1601
1602	if (flag == EV_CANCEL)
1603		return;
1604
1605	xchg = arg;
1606	error = 0;
1607	scheme = (void *)(*xchg);
1608
1609	g_topology_assert();
1610
1611	LIST_FOREACH(gp, &g_part_class.geom, geom) {
1612		table = gp->softc;
1613		if (table->gpt_scheme != scheme)
1614			continue;
1615
1616		acc = 0;
1617		LIST_FOREACH(pp, &gp->provider, provider)
1618			acc += pp->acr + pp->acw + pp->ace;
1619		LIST_FOREACH(cp, &gp->consumer, consumer)
1620			acc += cp->acr + cp->acw + cp->ace;
1621
1622		if (!acc)
1623			g_part_wither(gp, ENOSYS);
1624		else
1625			error = EBUSY;
1626	}
1627
1628	if (!error)
1629		TAILQ_REMOVE(&g_part_schemes, scheme, scheme_list);
1630
1631	*xchg = error;
1632}
1633
1634int
1635g_part_modevent(module_t mod, int type, struct g_part_scheme *scheme)
1636{
1637	uintptr_t arg;
1638	int error;
1639
1640	switch (type) {
1641	case MOD_LOAD:
1642		TAILQ_INSERT_TAIL(&g_part_schemes, scheme, scheme_list);
1643
1644		error = g_retaste(&g_part_class);
1645		if (error)
1646			TAILQ_REMOVE(&g_part_schemes, scheme, scheme_list);
1647		break;
1648	case MOD_UNLOAD:
1649		arg = (uintptr_t)scheme;
1650		error = g_waitfor_event(g_part_unload_event, &arg, M_WAITOK,
1651		    NULL);
1652		if (!error)
1653			error = (arg == (uintptr_t)scheme) ? EDOOFUS : arg;
1654		break;
1655	default:
1656		error = EOPNOTSUPP;
1657		break;
1658	}
1659
1660	return (error);
1661}
1662