g_part.c revision 217040
1/*-
2 * Copyright (c) 2002, 2005-2009 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: head/sys/geom/part/g_part.c 217040 2011-01-06 03:36:04Z nwhitehorn $");
29
30#include <sys/param.h>
31#include <sys/bio.h>
32#include <sys/diskmbr.h>
33#include <sys/endian.h>
34#include <sys/kernel.h>
35#include <sys/kobj.h>
36#include <sys/limits.h>
37#include <sys/lock.h>
38#include <sys/malloc.h>
39#include <sys/mutex.h>
40#include <sys/queue.h>
41#include <sys/sbuf.h>
42#include <sys/systm.h>
43#include <sys/uuid.h>
44#include <geom/geom.h>
45#include <geom/geom_ctl.h>
46#include <geom/geom_int.h>
47#include <geom/part/g_part.h>
48
49#include "g_part_if.h"
50
51#ifndef _PATH_DEV
52#define _PATH_DEV "/dev/"
53#endif
54
55static kobj_method_t g_part_null_methods[] = {
56	{ 0, 0 }
57};
58
59static struct g_part_scheme g_part_null_scheme = {
60	"(none)",
61	g_part_null_methods,
62	sizeof(struct g_part_table),
63};
64
65TAILQ_HEAD(, g_part_scheme) g_part_schemes =
66    TAILQ_HEAD_INITIALIZER(g_part_schemes);
67
68struct g_part_alias_list {
69	const char *lexeme;
70	enum g_part_alias alias;
71} g_part_alias_list[G_PART_ALIAS_COUNT] = {
72	{ "apple-boot", G_PART_ALIAS_APPLE_BOOT },
73	{ "apple-hfs", G_PART_ALIAS_APPLE_HFS },
74	{ "apple-label", G_PART_ALIAS_APPLE_LABEL },
75	{ "apple-raid", G_PART_ALIAS_APPLE_RAID },
76	{ "apple-raid-offline", G_PART_ALIAS_APPLE_RAID_OFFLINE },
77	{ "apple-tv-recovery", G_PART_ALIAS_APPLE_TV_RECOVERY },
78	{ "apple-ufs", G_PART_ALIAS_APPLE_UFS },
79	{ "efi", G_PART_ALIAS_EFI },
80	{ "freebsd", G_PART_ALIAS_FREEBSD },
81	{ "freebsd-boot", G_PART_ALIAS_FREEBSD_BOOT },
82	{ "freebsd-swap", G_PART_ALIAS_FREEBSD_SWAP },
83	{ "freebsd-ufs", G_PART_ALIAS_FREEBSD_UFS },
84	{ "freebsd-vinum", G_PART_ALIAS_FREEBSD_VINUM },
85	{ "freebsd-zfs", G_PART_ALIAS_FREEBSD_ZFS },
86	{ "linux-data", G_PART_ALIAS_LINUX_DATA },
87	{ "linux-lvm", G_PART_ALIAS_LINUX_LVM },
88	{ "linux-raid", G_PART_ALIAS_LINUX_RAID },
89	{ "linux-swap", G_PART_ALIAS_LINUX_SWAP },
90	{ "ms-basic-data", G_PART_ALIAS_MS_BASIC_DATA },
91	{ "ms-ldm-data", G_PART_ALIAS_MS_LDM_DATA },
92	{ "ms-ldm-metadata", G_PART_ALIAS_MS_LDM_METADATA },
93	{ "ms-reserved", G_PART_ALIAS_MS_RESERVED },
94	{ "ntfs", G_PART_ALIAS_MS_NTFS },
95	{ "netbsd-ccd", G_PART_ALIAS_NETBSD_CCD },
96	{ "netbsd-cgd", G_PART_ALIAS_NETBSD_CGD },
97	{ "netbsd-ffs", G_PART_ALIAS_NETBSD_FFS },
98	{ "netbsd-lfs", G_PART_ALIAS_NETBSD_LFS },
99	{ "netbsd-raid", G_PART_ALIAS_NETBSD_RAID },
100	{ "netbsd-swap", G_PART_ALIAS_NETBSD_SWAP },
101	{ "mbr", G_PART_ALIAS_MBR }
102};
103
104/*
105 * The GEOM partitioning class.
106 */
107static g_ctl_req_t g_part_ctlreq;
108static g_ctl_destroy_geom_t g_part_destroy_geom;
109static g_fini_t g_part_fini;
110static g_init_t g_part_init;
111static g_taste_t g_part_taste;
112
113static g_access_t g_part_access;
114static g_dumpconf_t g_part_dumpconf;
115static g_orphan_t g_part_orphan;
116static g_spoiled_t g_part_spoiled;
117static g_start_t g_part_start;
118
119static struct g_class g_part_class = {
120	.name = "PART",
121	.version = G_VERSION,
122	/* Class methods. */
123	.ctlreq = g_part_ctlreq,
124	.destroy_geom = g_part_destroy_geom,
125	.fini = g_part_fini,
126	.init = g_part_init,
127	.taste = g_part_taste,
128	/* Geom methods. */
129	.access = g_part_access,
130	.dumpconf = g_part_dumpconf,
131	.orphan = g_part_orphan,
132	.spoiled = g_part_spoiled,
133	.start = g_part_start,
134};
135
136DECLARE_GEOM_CLASS(g_part_class, g_part);
137
138/*
139 * Support functions.
140 */
141
142static void g_part_wither(struct g_geom *, int);
143
144const char *
145g_part_alias_name(enum g_part_alias alias)
146{
147	int i;
148
149	for (i = 0; i < G_PART_ALIAS_COUNT; i++) {
150		if (g_part_alias_list[i].alias != alias)
151			continue;
152		return (g_part_alias_list[i].lexeme);
153	}
154
155	return (NULL);
156}
157
158void
159g_part_geometry_heads(off_t blocks, u_int sectors, off_t *bestchs,
160    u_int *bestheads)
161{
162	static u_int candidate_heads[] = { 1, 2, 16, 32, 64, 128, 255, 0 };
163	off_t chs, cylinders;
164	u_int heads;
165	int idx;
166
167	*bestchs = 0;
168	*bestheads = 0;
169	for (idx = 0; candidate_heads[idx] != 0; idx++) {
170		heads = candidate_heads[idx];
171		cylinders = blocks / heads / sectors;
172		if (cylinders < heads || cylinders < sectors)
173			break;
174		if (cylinders > 1023)
175			continue;
176		chs = cylinders * heads * sectors;
177		if (chs > *bestchs || (chs == *bestchs && *bestheads == 1)) {
178			*bestchs = chs;
179			*bestheads = heads;
180		}
181	}
182}
183
184static void
185g_part_geometry(struct g_part_table *table, struct g_consumer *cp,
186    off_t blocks)
187{
188	static u_int candidate_sectors[] = { 1, 9, 17, 33, 63, 0 };
189	off_t chs, bestchs;
190	u_int heads, sectors;
191	int idx;
192
193	if (g_getattr("GEOM::fwsectors", cp, &sectors) != 0 || sectors == 0 ||
194	    g_getattr("GEOM::fwheads", cp, &heads) != 0 || heads == 0) {
195		table->gpt_fixgeom = 0;
196		table->gpt_heads = 0;
197		table->gpt_sectors = 0;
198		bestchs = 0;
199		for (idx = 0; candidate_sectors[idx] != 0; idx++) {
200			sectors = candidate_sectors[idx];
201			g_part_geometry_heads(blocks, sectors, &chs, &heads);
202			if (chs == 0)
203				continue;
204			/*
205			 * Prefer a geometry with sectors > 1, but only if
206			 * it doesn't bump down the numbver of heads to 1.
207			 */
208			if (chs > bestchs || (chs == bestchs && heads > 1 &&
209			    table->gpt_sectors == 1)) {
210				bestchs = chs;
211				table->gpt_heads = heads;
212				table->gpt_sectors = sectors;
213			}
214		}
215		/*
216		 * If we didn't find a geometry at all, then the disk is
217		 * too big. This means we can use the maximum number of
218		 * heads and sectors.
219		 */
220		if (bestchs == 0) {
221			table->gpt_heads = 255;
222			table->gpt_sectors = 63;
223		}
224	} else {
225		table->gpt_fixgeom = 1;
226		table->gpt_heads = heads;
227		table->gpt_sectors = sectors;
228	}
229}
230
231struct g_part_entry *
232g_part_new_entry(struct g_part_table *table, int index, quad_t start,
233    quad_t end)
234{
235	struct g_part_entry *entry, *last;
236
237	last = NULL;
238	LIST_FOREACH(entry, &table->gpt_entry, gpe_entry) {
239		if (entry->gpe_index == index)
240			break;
241		if (entry->gpe_index > index) {
242			entry = NULL;
243			break;
244		}
245		last = entry;
246	}
247	if (entry == NULL) {
248		entry = g_malloc(table->gpt_scheme->gps_entrysz,
249		    M_WAITOK | M_ZERO);
250		entry->gpe_index = index;
251		if (last == NULL)
252			LIST_INSERT_HEAD(&table->gpt_entry, entry, gpe_entry);
253		else
254			LIST_INSERT_AFTER(last, entry, gpe_entry);
255	} else
256		entry->gpe_offset = 0;
257	entry->gpe_start = start;
258	entry->gpe_end = end;
259	return (entry);
260}
261
262static void
263g_part_new_provider(struct g_geom *gp, struct g_part_table *table,
264    struct g_part_entry *entry)
265{
266	struct g_consumer *cp;
267	struct g_provider *pp;
268	struct sbuf *sb;
269	off_t offset;
270
271	cp = LIST_FIRST(&gp->consumer);
272	pp = cp->provider;
273
274	offset = entry->gpe_start * pp->sectorsize;
275	if (entry->gpe_offset < offset)
276		entry->gpe_offset = offset;
277
278	if (entry->gpe_pp == NULL) {
279		sb = sbuf_new_auto();
280		G_PART_FULLNAME(table, entry, sb, gp->name);
281		sbuf_finish(sb);
282		entry->gpe_pp = g_new_providerf(gp, "%s", sbuf_data(sb));
283		sbuf_delete(sb);
284		entry->gpe_pp->private = entry;		/* Close the circle. */
285	}
286	entry->gpe_pp->index = entry->gpe_index - 1;	/* index is 1-based. */
287	entry->gpe_pp->mediasize = (entry->gpe_end - entry->gpe_start + 1) *
288	    pp->sectorsize;
289	entry->gpe_pp->mediasize -= entry->gpe_offset - offset;
290	entry->gpe_pp->sectorsize = pp->sectorsize;
291	entry->gpe_pp->flags = pp->flags & G_PF_CANDELETE;
292	entry->gpe_pp->stripesize = pp->stripesize;
293	entry->gpe_pp->stripeoffset = pp->stripeoffset + entry->gpe_offset;
294	if (pp->stripesize > 0)
295		entry->gpe_pp->stripeoffset %= pp->stripesize;
296	g_error_provider(entry->gpe_pp, 0);
297}
298
299static struct g_geom*
300g_part_find_geom(const char *name)
301{
302	struct g_geom *gp;
303	LIST_FOREACH(gp, &g_part_class.geom, geom) {
304		if (!strcmp(name, gp->name))
305			break;
306	}
307	return (gp);
308}
309
310static int
311g_part_parm_geom(struct gctl_req *req, const char *name, struct g_geom **v)
312{
313	struct g_geom *gp;
314	const char *gname;
315
316	gname = gctl_get_asciiparam(req, name);
317	if (gname == NULL)
318		return (ENOATTR);
319	if (strncmp(gname, _PATH_DEV, sizeof(_PATH_DEV) - 1) == 0)
320		gname += sizeof(_PATH_DEV) - 1;
321	gp = g_part_find_geom(gname);
322	if (gp == NULL) {
323		gctl_error(req, "%d %s '%s'", EINVAL, name, gname);
324		return (EINVAL);
325	}
326	*v = gp;
327	return (0);
328}
329
330static int
331g_part_parm_provider(struct gctl_req *req, const char *name,
332    struct g_provider **v)
333{
334	struct g_provider *pp;
335	const char *pname;
336
337	pname = gctl_get_asciiparam(req, name);
338	if (pname == NULL)
339		return (ENOATTR);
340	if (strncmp(pname, _PATH_DEV, sizeof(_PATH_DEV) - 1) == 0)
341		pname += sizeof(_PATH_DEV) - 1;
342	pp = g_provider_by_name(pname);
343	if (pp == NULL) {
344		gctl_error(req, "%d %s '%s'", EINVAL, name, pname);
345		return (EINVAL);
346	}
347	*v = pp;
348	return (0);
349}
350
351static int
352g_part_parm_quad(struct gctl_req *req, const char *name, quad_t *v)
353{
354	const char *p;
355	char *x;
356	quad_t q;
357
358	p = gctl_get_asciiparam(req, name);
359	if (p == NULL)
360		return (ENOATTR);
361	q = strtoq(p, &x, 0);
362	if (*x != '\0' || q < 0) {
363		gctl_error(req, "%d %s '%s'", EINVAL, name, p);
364		return (EINVAL);
365	}
366	*v = q;
367	return (0);
368}
369
370static int
371g_part_parm_scheme(struct gctl_req *req, const char *name,
372    struct g_part_scheme **v)
373{
374	struct g_part_scheme *s;
375	const char *p;
376
377	p = gctl_get_asciiparam(req, name);
378	if (p == NULL)
379		return (ENOATTR);
380	TAILQ_FOREACH(s, &g_part_schemes, scheme_list) {
381		if (s == &g_part_null_scheme)
382			continue;
383		if (!strcasecmp(s->name, p))
384			break;
385	}
386	if (s == NULL) {
387		gctl_error(req, "%d %s '%s'", EINVAL, name, p);
388		return (EINVAL);
389	}
390	*v = s;
391	return (0);
392}
393
394static int
395g_part_parm_str(struct gctl_req *req, const char *name, const char **v)
396{
397	const char *p;
398
399	p = gctl_get_asciiparam(req, name);
400	if (p == NULL)
401		return (ENOATTR);
402	/* An empty label is always valid. */
403	if (strcmp(name, "label") != 0 && p[0] == '\0') {
404		gctl_error(req, "%d %s '%s'", EINVAL, name, p);
405		return (EINVAL);
406	}
407	*v = p;
408	return (0);
409}
410
411static int
412g_part_parm_intmax(struct gctl_req *req, const char *name, u_int *v)
413{
414	const intmax_t *p;
415	int size;
416
417	p = gctl_get_param(req, name, &size);
418	if (p == NULL)
419		return (ENOATTR);
420	if (size != sizeof(*p) || *p < 0 || *p > INT_MAX) {
421		gctl_error(req, "%d %s '%jd'", EINVAL, name, *p);
422		return (EINVAL);
423	}
424	*v = (u_int)*p;
425	return (0);
426}
427
428static int
429g_part_parm_uint32(struct gctl_req *req, const char *name, u_int *v)
430{
431	const uint32_t *p;
432	int size;
433
434	p = gctl_get_param(req, name, &size);
435	if (p == NULL)
436		return (ENOATTR);
437	if (size != sizeof(*p) || *p > INT_MAX) {
438		gctl_error(req, "%d %s '%u'", EINVAL, name, (unsigned int)*p);
439		return (EINVAL);
440	}
441	*v = (u_int)*p;
442	return (0);
443}
444
445static int
446g_part_parm_bootcode(struct gctl_req *req, const char *name, const void **v,
447    unsigned int *s)
448{
449	const void *p;
450	int size;
451
452	p = gctl_get_param(req, name, &size);
453	if (p == NULL)
454		return (ENOATTR);
455	*v = p;
456	*s = size;
457	return (0);
458}
459
460static int
461g_part_probe(struct g_geom *gp, struct g_consumer *cp, int depth)
462{
463	struct g_part_scheme *iter, *scheme;
464	struct g_part_table *table;
465	int pri, probe;
466
467	table = gp->softc;
468	scheme = (table != NULL) ? table->gpt_scheme : NULL;
469	pri = (scheme != NULL) ? G_PART_PROBE(table, cp) : INT_MIN;
470	if (pri == 0)
471		goto done;
472	if (pri > 0) {	/* error */
473		scheme = NULL;
474		pri = INT_MIN;
475	}
476
477	TAILQ_FOREACH(iter, &g_part_schemes, scheme_list) {
478		if (iter == &g_part_null_scheme)
479			continue;
480		table = (void *)kobj_create((kobj_class_t)iter, M_GEOM,
481		    M_WAITOK);
482		table->gpt_gp = gp;
483		table->gpt_scheme = iter;
484		table->gpt_depth = depth;
485		probe = G_PART_PROBE(table, cp);
486		if (probe <= 0 && probe > pri) {
487			pri = probe;
488			scheme = iter;
489			if (gp->softc != NULL)
490				kobj_delete((kobj_t)gp->softc, M_GEOM);
491			gp->softc = table;
492			if (pri == 0)
493				goto done;
494		} else
495			kobj_delete((kobj_t)table, M_GEOM);
496	}
497
498done:
499	return ((scheme == NULL) ? ENXIO : 0);
500}
501
502/*
503 * Control request functions.
504 */
505
506static int
507g_part_ctl_add(struct gctl_req *req, struct g_part_parms *gpp)
508{
509	struct g_geom *gp;
510	struct g_provider *pp;
511	struct g_part_entry *delent, *last, *entry;
512	struct g_part_table *table;
513	struct sbuf *sb;
514	quad_t end;
515	unsigned int index;
516	int error;
517
518	gp = gpp->gpp_geom;
519	G_PART_TRACE((G_T_TOPOLOGY, "%s(%s)", __func__, gp->name));
520	g_topology_assert();
521
522	pp = LIST_FIRST(&gp->consumer)->provider;
523	table = gp->softc;
524	end = gpp->gpp_start + gpp->gpp_size - 1;
525
526	if (gpp->gpp_start < table->gpt_first ||
527	    gpp->gpp_start > table->gpt_last) {
528		gctl_error(req, "%d start '%jd'", EINVAL,
529		    (intmax_t)gpp->gpp_start);
530		return (EINVAL);
531	}
532	if (end < gpp->gpp_start || end > table->gpt_last) {
533		gctl_error(req, "%d size '%jd'", EINVAL,
534		    (intmax_t)gpp->gpp_size);
535		return (EINVAL);
536	}
537	if (gpp->gpp_index > table->gpt_entries) {
538		gctl_error(req, "%d index '%d'", EINVAL, gpp->gpp_index);
539		return (EINVAL);
540	}
541
542	delent = last = NULL;
543	index = (gpp->gpp_index > 0) ? gpp->gpp_index : 1;
544	LIST_FOREACH(entry, &table->gpt_entry, gpe_entry) {
545		if (entry->gpe_deleted) {
546			if (entry->gpe_index == index)
547				delent = entry;
548			continue;
549		}
550		if (entry->gpe_index == index)
551			index = entry->gpe_index + 1;
552		if (entry->gpe_index < index)
553			last = entry;
554		if (entry->gpe_internal)
555			continue;
556		if (gpp->gpp_start >= entry->gpe_start &&
557		    gpp->gpp_start <= entry->gpe_end) {
558			gctl_error(req, "%d start '%jd'", ENOSPC,
559			    (intmax_t)gpp->gpp_start);
560			return (ENOSPC);
561		}
562		if (end >= entry->gpe_start && end <= entry->gpe_end) {
563			gctl_error(req, "%d end '%jd'", ENOSPC, (intmax_t)end);
564			return (ENOSPC);
565		}
566		if (gpp->gpp_start < entry->gpe_start && end > entry->gpe_end) {
567			gctl_error(req, "%d size '%jd'", ENOSPC,
568			    (intmax_t)gpp->gpp_size);
569			return (ENOSPC);
570		}
571	}
572	if (gpp->gpp_index > 0 && index != gpp->gpp_index) {
573		gctl_error(req, "%d index '%d'", EEXIST, gpp->gpp_index);
574		return (EEXIST);
575	}
576	if (index > table->gpt_entries) {
577		gctl_error(req, "%d index '%d'", ENOSPC, index);
578		return (ENOSPC);
579	}
580
581	entry = (delent == NULL) ? g_malloc(table->gpt_scheme->gps_entrysz,
582	    M_WAITOK | M_ZERO) : delent;
583	entry->gpe_index = index;
584	entry->gpe_start = gpp->gpp_start;
585	entry->gpe_end = end;
586	error = G_PART_ADD(table, entry, gpp);
587	if (error) {
588		gctl_error(req, "%d", error);
589		if (delent == NULL)
590			g_free(entry);
591		return (error);
592	}
593	if (delent == NULL) {
594		if (last == NULL)
595			LIST_INSERT_HEAD(&table->gpt_entry, entry, gpe_entry);
596		else
597			LIST_INSERT_AFTER(last, entry, gpe_entry);
598		entry->gpe_created = 1;
599	} else {
600		entry->gpe_deleted = 0;
601		entry->gpe_modified = 1;
602	}
603	g_part_new_provider(gp, table, entry);
604
605	/* Provide feedback if so requested. */
606	if (gpp->gpp_parms & G_PART_PARM_OUTPUT) {
607		sb = sbuf_new_auto();
608		G_PART_FULLNAME(table, entry, sb, gp->name);
609		sbuf_cat(sb, " added\n");
610		sbuf_finish(sb);
611		gctl_set_param(req, "output", sbuf_data(sb), sbuf_len(sb) + 1);
612		sbuf_delete(sb);
613	}
614	return (0);
615}
616
617static int
618g_part_ctl_bootcode(struct gctl_req *req, struct g_part_parms *gpp)
619{
620	struct g_geom *gp;
621	struct g_part_table *table;
622	struct sbuf *sb;
623	int error, sz;
624
625	gp = gpp->gpp_geom;
626	G_PART_TRACE((G_T_TOPOLOGY, "%s(%s)", __func__, gp->name));
627	g_topology_assert();
628
629	table = gp->softc;
630	sz = table->gpt_scheme->gps_bootcodesz;
631	if (sz == 0) {
632		error = ENODEV;
633		goto fail;
634	}
635	if (gpp->gpp_codesize > sz) {
636		error = EFBIG;
637		goto fail;
638	}
639
640	error = G_PART_BOOTCODE(table, gpp);
641	if (error)
642		goto fail;
643
644	/* Provide feedback if so requested. */
645	if (gpp->gpp_parms & G_PART_PARM_OUTPUT) {
646		sb = sbuf_new_auto();
647		sbuf_printf(sb, "bootcode written to %s\n", gp->name);
648		sbuf_finish(sb);
649		gctl_set_param(req, "output", sbuf_data(sb), sbuf_len(sb) + 1);
650		sbuf_delete(sb);
651	}
652	return (0);
653
654 fail:
655	gctl_error(req, "%d", error);
656	return (error);
657}
658
659static int
660g_part_ctl_commit(struct gctl_req *req, struct g_part_parms *gpp)
661{
662	struct g_consumer *cp;
663	struct g_geom *gp;
664	struct g_provider *pp;
665	struct g_part_entry *entry, *tmp;
666	struct g_part_table *table;
667	char *buf;
668	int error, i;
669
670	gp = gpp->gpp_geom;
671	G_PART_TRACE((G_T_TOPOLOGY, "%s(%s)", __func__, gp->name));
672	g_topology_assert();
673
674	table = gp->softc;
675	if (!table->gpt_opened) {
676		gctl_error(req, "%d", EPERM);
677		return (EPERM);
678	}
679
680	g_topology_unlock();
681
682	cp = LIST_FIRST(&gp->consumer);
683	if ((table->gpt_smhead | table->gpt_smtail) != 0) {
684		pp = cp->provider;
685		buf = g_malloc(pp->sectorsize, M_WAITOK | M_ZERO);
686		while (table->gpt_smhead != 0) {
687			i = ffs(table->gpt_smhead) - 1;
688			error = g_write_data(cp, i * pp->sectorsize, buf,
689			    pp->sectorsize);
690			if (error) {
691				g_free(buf);
692				goto fail;
693			}
694			table->gpt_smhead &= ~(1 << i);
695		}
696		while (table->gpt_smtail != 0) {
697			i = ffs(table->gpt_smtail) - 1;
698			error = g_write_data(cp, pp->mediasize - (i + 1) *
699			    pp->sectorsize, buf, pp->sectorsize);
700			if (error) {
701				g_free(buf);
702				goto fail;
703			}
704			table->gpt_smtail &= ~(1 << i);
705		}
706		g_free(buf);
707	}
708
709	if (table->gpt_scheme == &g_part_null_scheme) {
710		g_topology_lock();
711		g_access(cp, -1, -1, -1);
712		g_part_wither(gp, ENXIO);
713		return (0);
714	}
715
716	error = G_PART_WRITE(table, cp);
717	if (error)
718		goto fail;
719
720	LIST_FOREACH_SAFE(entry, &table->gpt_entry, gpe_entry, tmp) {
721		if (!entry->gpe_deleted) {
722			entry->gpe_created = 0;
723			entry->gpe_modified = 0;
724			continue;
725		}
726		LIST_REMOVE(entry, gpe_entry);
727		g_free(entry);
728	}
729	table->gpt_created = 0;
730	table->gpt_opened = 0;
731
732	g_topology_lock();
733	g_access(cp, -1, -1, -1);
734	return (0);
735
736fail:
737	g_topology_lock();
738	gctl_error(req, "%d", error);
739	return (error);
740}
741
742static int
743g_part_ctl_create(struct gctl_req *req, struct g_part_parms *gpp)
744{
745	struct g_consumer *cp;
746	struct g_geom *gp;
747	struct g_provider *pp;
748	struct g_part_scheme *scheme;
749	struct g_part_table *null, *table;
750	struct sbuf *sb;
751	int attr, error;
752
753	pp = gpp->gpp_provider;
754	scheme = gpp->gpp_scheme;
755	G_PART_TRACE((G_T_TOPOLOGY, "%s(%s)", __func__, pp->name));
756	g_topology_assert();
757
758	/* Check that there isn't already a g_part geom on the provider. */
759	gp = g_part_find_geom(pp->name);
760	if (gp != NULL) {
761		null = gp->softc;
762		if (null->gpt_scheme != &g_part_null_scheme) {
763			gctl_error(req, "%d geom '%s'", EEXIST, pp->name);
764			return (EEXIST);
765		}
766	} else
767		null = NULL;
768
769	if ((gpp->gpp_parms & G_PART_PARM_ENTRIES) &&
770	    (gpp->gpp_entries < scheme->gps_minent ||
771	     gpp->gpp_entries > scheme->gps_maxent)) {
772		gctl_error(req, "%d entries '%d'", EINVAL, gpp->gpp_entries);
773		return (EINVAL);
774	}
775
776	if (null == NULL)
777		gp = g_new_geomf(&g_part_class, "%s", pp->name);
778	gp->softc = kobj_create((kobj_class_t)gpp->gpp_scheme, M_GEOM,
779	    M_WAITOK);
780	table = gp->softc;
781	table->gpt_gp = gp;
782	table->gpt_scheme = gpp->gpp_scheme;
783	table->gpt_entries = (gpp->gpp_parms & G_PART_PARM_ENTRIES) ?
784	    gpp->gpp_entries : scheme->gps_minent;
785	LIST_INIT(&table->gpt_entry);
786	if (null == NULL) {
787		cp = g_new_consumer(gp);
788		error = g_attach(cp, pp);
789		if (error == 0)
790			error = g_access(cp, 1, 1, 1);
791		if (error != 0) {
792			g_part_wither(gp, error);
793			gctl_error(req, "%d geom '%s'", error, pp->name);
794			return (error);
795		}
796		table->gpt_opened = 1;
797	} else {
798		cp = LIST_FIRST(&gp->consumer);
799		table->gpt_opened = null->gpt_opened;
800		table->gpt_smhead = null->gpt_smhead;
801		table->gpt_smtail = null->gpt_smtail;
802	}
803
804	g_topology_unlock();
805
806	/* Make sure the provider has media. */
807	if (pp->mediasize == 0 || pp->sectorsize == 0) {
808		error = ENODEV;
809		goto fail;
810	}
811
812	/* Make sure we can nest and if so, determine our depth. */
813	error = g_getattr("PART::isleaf", cp, &attr);
814	if (!error && attr) {
815		error = ENODEV;
816		goto fail;
817	}
818	error = g_getattr("PART::depth", cp, &attr);
819	table->gpt_depth = (!error) ? attr + 1 : 0;
820
821	/*
822	 * Synthesize a disk geometry. Some partitioning schemes
823	 * depend on it and since some file systems need it even
824	 * when the partitition scheme doesn't, we do it here in
825	 * scheme-independent code.
826	 */
827	g_part_geometry(table, cp, pp->mediasize / pp->sectorsize);
828
829	error = G_PART_CREATE(table, gpp);
830	if (error)
831		goto fail;
832
833	g_topology_lock();
834
835	table->gpt_created = 1;
836	if (null != NULL)
837		kobj_delete((kobj_t)null, M_GEOM);
838
839	/*
840	 * Support automatic commit by filling in the gpp_geom
841	 * parameter.
842	 */
843	gpp->gpp_parms |= G_PART_PARM_GEOM;
844	gpp->gpp_geom = gp;
845
846	/* Provide feedback if so requested. */
847	if (gpp->gpp_parms & G_PART_PARM_OUTPUT) {
848		sb = sbuf_new_auto();
849		sbuf_printf(sb, "%s created\n", gp->name);
850		sbuf_finish(sb);
851		gctl_set_param(req, "output", sbuf_data(sb), sbuf_len(sb) + 1);
852		sbuf_delete(sb);
853	}
854	return (0);
855
856fail:
857	g_topology_lock();
858	if (null == NULL) {
859		g_access(cp, -1, -1, -1);
860		g_part_wither(gp, error);
861	} else {
862		kobj_delete((kobj_t)gp->softc, M_GEOM);
863		gp->softc = null;
864	}
865	gctl_error(req, "%d provider", error);
866	return (error);
867}
868
869static int
870g_part_ctl_delete(struct gctl_req *req, struct g_part_parms *gpp)
871{
872	struct g_geom *gp;
873	struct g_provider *pp;
874	struct g_part_entry *entry;
875	struct g_part_table *table;
876	struct sbuf *sb;
877
878	gp = gpp->gpp_geom;
879	G_PART_TRACE((G_T_TOPOLOGY, "%s(%s)", __func__, gp->name));
880	g_topology_assert();
881
882	table = gp->softc;
883
884	LIST_FOREACH(entry, &table->gpt_entry, gpe_entry) {
885		if (entry->gpe_deleted || entry->gpe_internal)
886			continue;
887		if (entry->gpe_index == gpp->gpp_index)
888			break;
889	}
890	if (entry == NULL) {
891		gctl_error(req, "%d index '%d'", ENOENT, gpp->gpp_index);
892		return (ENOENT);
893	}
894
895	pp = entry->gpe_pp;
896	if (pp != NULL) {
897		if (pp->acr > 0 || pp->acw > 0 || pp->ace > 0) {
898			gctl_error(req, "%d", EBUSY);
899			return (EBUSY);
900		}
901
902		pp->private = NULL;
903		entry->gpe_pp = NULL;
904	}
905
906	if (pp != NULL)
907		g_wither_provider(pp, ENXIO);
908
909	/* Provide feedback if so requested. */
910	if (gpp->gpp_parms & G_PART_PARM_OUTPUT) {
911		sb = sbuf_new_auto();
912		G_PART_FULLNAME(table, entry, sb, gp->name);
913		sbuf_cat(sb, " deleted\n");
914		sbuf_finish(sb);
915		gctl_set_param(req, "output", sbuf_data(sb), sbuf_len(sb) + 1);
916		sbuf_delete(sb);
917	}
918
919	if (entry->gpe_created) {
920		LIST_REMOVE(entry, gpe_entry);
921		g_free(entry);
922	} else {
923		entry->gpe_modified = 0;
924		entry->gpe_deleted = 1;
925	}
926	return (0);
927}
928
929static int
930g_part_ctl_destroy(struct gctl_req *req, struct g_part_parms *gpp)
931{
932	struct g_consumer *cp;
933	struct g_geom *gp;
934	struct g_provider *pp;
935	struct g_part_entry *entry, *tmp;
936	struct g_part_table *null, *table;
937	struct sbuf *sb;
938	int error;
939
940	gp = gpp->gpp_geom;
941	G_PART_TRACE((G_T_TOPOLOGY, "%s(%s)", __func__, gp->name));
942	g_topology_assert();
943
944	table = gp->softc;
945	/* Check for busy providers. */
946	LIST_FOREACH(entry, &table->gpt_entry, gpe_entry) {
947		if (entry->gpe_deleted || entry->gpe_internal)
948			continue;
949		if (gpp->gpp_force) {
950			pp = entry->gpe_pp;
951			if (pp == NULL)
952				continue;
953			if (pp->acr == 0 && pp->acw == 0 && pp->ace == 0)
954				continue;
955		}
956		gctl_error(req, "%d", EBUSY);
957		return (EBUSY);
958	}
959
960	if (gpp->gpp_force) {
961		/* Destroy all providers. */
962		LIST_FOREACH_SAFE(entry, &table->gpt_entry, gpe_entry, tmp) {
963			pp = entry->gpe_pp;
964			if (pp != NULL) {
965				pp->private = NULL;
966				g_wither_provider(pp, ENXIO);
967			}
968			LIST_REMOVE(entry, gpe_entry);
969			g_free(entry);
970		}
971	}
972
973	error = G_PART_DESTROY(table, gpp);
974	if (error) {
975		gctl_error(req, "%d", error);
976		return (error);
977	}
978
979	gp->softc = kobj_create((kobj_class_t)&g_part_null_scheme, M_GEOM,
980	    M_WAITOK);
981	null = gp->softc;
982	null->gpt_gp = gp;
983	null->gpt_scheme = &g_part_null_scheme;
984	LIST_INIT(&null->gpt_entry);
985
986	cp = LIST_FIRST(&gp->consumer);
987	pp = cp->provider;
988	null->gpt_last = pp->mediasize / pp->sectorsize - 1;
989
990	null->gpt_depth = table->gpt_depth;
991	null->gpt_opened = table->gpt_opened;
992	null->gpt_smhead = table->gpt_smhead;
993	null->gpt_smtail = table->gpt_smtail;
994
995	while ((entry = LIST_FIRST(&table->gpt_entry)) != NULL) {
996		LIST_REMOVE(entry, gpe_entry);
997		g_free(entry);
998	}
999	kobj_delete((kobj_t)table, M_GEOM);
1000
1001	/* Provide feedback if so requested. */
1002	if (gpp->gpp_parms & G_PART_PARM_OUTPUT) {
1003		sb = sbuf_new_auto();
1004		sbuf_printf(sb, "%s destroyed\n", gp->name);
1005		sbuf_finish(sb);
1006		gctl_set_param(req, "output", sbuf_data(sb), sbuf_len(sb) + 1);
1007		sbuf_delete(sb);
1008	}
1009	return (0);
1010}
1011
1012static int
1013g_part_ctl_modify(struct gctl_req *req, struct g_part_parms *gpp)
1014{
1015	struct g_geom *gp;
1016	struct g_part_entry *entry;
1017	struct g_part_table *table;
1018	struct sbuf *sb;
1019	int error;
1020
1021	gp = gpp->gpp_geom;
1022	G_PART_TRACE((G_T_TOPOLOGY, "%s(%s)", __func__, gp->name));
1023	g_topology_assert();
1024
1025	table = gp->softc;
1026
1027	LIST_FOREACH(entry, &table->gpt_entry, gpe_entry) {
1028		if (entry->gpe_deleted || entry->gpe_internal)
1029			continue;
1030		if (entry->gpe_index == gpp->gpp_index)
1031			break;
1032	}
1033	if (entry == NULL) {
1034		gctl_error(req, "%d index '%d'", ENOENT, gpp->gpp_index);
1035		return (ENOENT);
1036	}
1037
1038	error = G_PART_MODIFY(table, entry, gpp);
1039	if (error) {
1040		gctl_error(req, "%d", error);
1041		return (error);
1042	}
1043
1044	if (!entry->gpe_created)
1045		entry->gpe_modified = 1;
1046
1047	/* Provide feedback if so requested. */
1048	if (gpp->gpp_parms & G_PART_PARM_OUTPUT) {
1049		sb = sbuf_new_auto();
1050		G_PART_FULLNAME(table, entry, sb, gp->name);
1051		sbuf_cat(sb, " modified\n");
1052		sbuf_finish(sb);
1053		gctl_set_param(req, "output", sbuf_data(sb), sbuf_len(sb) + 1);
1054		sbuf_delete(sb);
1055	}
1056	return (0);
1057}
1058
1059static int
1060g_part_ctl_move(struct gctl_req *req, struct g_part_parms *gpp)
1061{
1062	gctl_error(req, "%d verb 'move'", ENOSYS);
1063	return (ENOSYS);
1064}
1065
1066static int
1067g_part_ctl_recover(struct gctl_req *req, struct g_part_parms *gpp)
1068{
1069	struct g_part_table *table;
1070	struct g_geom *gp;
1071	struct sbuf *sb;
1072	int error, recovered;
1073
1074	gp = gpp->gpp_geom;
1075	G_PART_TRACE((G_T_TOPOLOGY, "%s(%s)", __func__, gp->name));
1076	g_topology_assert();
1077	table = gp->softc;
1078	error = recovered = 0;
1079
1080	if (table->gpt_corrupt) {
1081		error = G_PART_RECOVER(table);
1082		if (error) {
1083			gctl_error(req, "%d recovering '%s' failed",
1084			    error, gp->name);
1085			return (error);
1086		}
1087		recovered = 1;
1088	}
1089	/* Provide feedback if so requested. */
1090	if (gpp->gpp_parms & G_PART_PARM_OUTPUT) {
1091		sb = sbuf_new_auto();
1092		if (recovered)
1093			sbuf_printf(sb, "%s recovered\n", gp->name);
1094		else
1095			sbuf_printf(sb, "%s recovering is not needed\n",
1096			    gp->name);
1097		sbuf_finish(sb);
1098		gctl_set_param(req, "output", sbuf_data(sb), sbuf_len(sb) + 1);
1099		sbuf_delete(sb);
1100	}
1101	return (0);
1102}
1103
1104static int
1105g_part_ctl_resize(struct gctl_req *req, struct g_part_parms *gpp)
1106{
1107	struct g_geom *gp;
1108	struct g_provider *pp;
1109	struct g_part_entry *pe, *entry;
1110	struct g_part_table *table;
1111	struct sbuf *sb;
1112	quad_t end;
1113	int error;
1114
1115	gp = gpp->gpp_geom;
1116	G_PART_TRACE((G_T_TOPOLOGY, "%s(%s)", __func__, gp->name));
1117	g_topology_assert();
1118	table = gp->softc;
1119
1120	/* check gpp_index */
1121	LIST_FOREACH(entry, &table->gpt_entry, gpe_entry) {
1122		if (entry->gpe_deleted || entry->gpe_internal)
1123			continue;
1124		if (entry->gpe_index == gpp->gpp_index)
1125			break;
1126	}
1127	if (entry == NULL) {
1128		gctl_error(req, "%d index '%d'", ENOENT, gpp->gpp_index);
1129		return (ENOENT);
1130	}
1131
1132	/* check gpp_size */
1133	end = entry->gpe_start + gpp->gpp_size - 1;
1134	if (gpp->gpp_size < 1 || end > table->gpt_last) {
1135		gctl_error(req, "%d size '%jd'", EINVAL,
1136		    (intmax_t)gpp->gpp_size);
1137		return (EINVAL);
1138	}
1139
1140	LIST_FOREACH(pe, &table->gpt_entry, gpe_entry) {
1141		if (pe->gpe_deleted || pe->gpe_internal || pe == entry)
1142			continue;
1143		if (end >= pe->gpe_start && end <= pe->gpe_end) {
1144			gctl_error(req, "%d end '%jd'", ENOSPC,
1145			    (intmax_t)end);
1146			return (ENOSPC);
1147		}
1148		if (entry->gpe_start < pe->gpe_start && end > pe->gpe_end) {
1149			gctl_error(req, "%d size '%jd'", ENOSPC,
1150			    (intmax_t)gpp->gpp_size);
1151			return (ENOSPC);
1152		}
1153	}
1154
1155	pp = entry->gpe_pp;
1156	if ((g_debugflags & 16) == 0 &&
1157	    (pp->acr > 0 || pp->acw > 0 || pp->ace > 0)) {
1158		gctl_error(req, "%d", EBUSY);
1159		return (EBUSY);
1160	}
1161
1162	error = G_PART_RESIZE(table, entry, gpp);
1163	if (error) {
1164		gctl_error(req, "%d", error);
1165		return (error);
1166	}
1167
1168	if (!entry->gpe_created)
1169		entry->gpe_modified = 1;
1170
1171	/* update mediasize of changed provider */
1172	pp->mediasize = (entry->gpe_end - entry->gpe_start + 1) *
1173		pp->sectorsize;
1174
1175	/* Provide feedback if so requested. */
1176	if (gpp->gpp_parms & G_PART_PARM_OUTPUT) {
1177		sb = sbuf_new_auto();
1178		G_PART_FULLNAME(table, entry, sb, gp->name);
1179		sbuf_cat(sb, " resized\n");
1180		sbuf_finish(sb);
1181		gctl_set_param(req, "output", sbuf_data(sb), sbuf_len(sb) + 1);
1182		sbuf_delete(sb);
1183	}
1184	return (0);
1185}
1186
1187static int
1188g_part_ctl_setunset(struct gctl_req *req, struct g_part_parms *gpp,
1189    unsigned int set)
1190{
1191	struct g_geom *gp;
1192	struct g_part_entry *entry;
1193	struct g_part_table *table;
1194	struct sbuf *sb;
1195	int error;
1196
1197	gp = gpp->gpp_geom;
1198	G_PART_TRACE((G_T_TOPOLOGY, "%s(%s)", __func__, gp->name));
1199	g_topology_assert();
1200
1201	table = gp->softc;
1202
1203	LIST_FOREACH(entry, &table->gpt_entry, gpe_entry) {
1204		if (entry->gpe_deleted || entry->gpe_internal)
1205			continue;
1206		if (entry->gpe_index == gpp->gpp_index)
1207			break;
1208	}
1209	if (entry == NULL) {
1210		gctl_error(req, "%d index '%d'", ENOENT, gpp->gpp_index);
1211		return (ENOENT);
1212	}
1213
1214	error = G_PART_SETUNSET(table, entry, gpp->gpp_attrib, set);
1215	if (error) {
1216		gctl_error(req, "%d attrib '%s'", error, gpp->gpp_attrib);
1217		return (error);
1218	}
1219
1220	/* Provide feedback if so requested. */
1221	if (gpp->gpp_parms & G_PART_PARM_OUTPUT) {
1222		sb = sbuf_new_auto();
1223		sbuf_printf(sb, "%s %sset on ", gpp->gpp_attrib,
1224		    (set) ? "" : "un");
1225		G_PART_FULLNAME(table, entry, sb, gp->name);
1226		sbuf_printf(sb, "\n");
1227		sbuf_finish(sb);
1228		gctl_set_param(req, "output", sbuf_data(sb), sbuf_len(sb) + 1);
1229		sbuf_delete(sb);
1230	}
1231	return (0);
1232}
1233
1234static int
1235g_part_ctl_undo(struct gctl_req *req, struct g_part_parms *gpp)
1236{
1237	struct g_consumer *cp;
1238	struct g_provider *pp;
1239	struct g_geom *gp;
1240	struct g_part_entry *entry, *tmp;
1241	struct g_part_table *table;
1242	int error, reprobe;
1243
1244	gp = gpp->gpp_geom;
1245	G_PART_TRACE((G_T_TOPOLOGY, "%s(%s)", __func__, gp->name));
1246	g_topology_assert();
1247
1248	table = gp->softc;
1249	if (!table->gpt_opened) {
1250		gctl_error(req, "%d", EPERM);
1251		return (EPERM);
1252	}
1253
1254	cp = LIST_FIRST(&gp->consumer);
1255	LIST_FOREACH_SAFE(entry, &table->gpt_entry, gpe_entry, tmp) {
1256		entry->gpe_modified = 0;
1257		if (entry->gpe_created) {
1258			pp = entry->gpe_pp;
1259			if (pp != NULL) {
1260				pp->private = NULL;
1261				entry->gpe_pp = NULL;
1262				g_wither_provider(pp, ENXIO);
1263			}
1264			entry->gpe_deleted = 1;
1265		}
1266		if (entry->gpe_deleted) {
1267			LIST_REMOVE(entry, gpe_entry);
1268			g_free(entry);
1269		}
1270	}
1271
1272	g_topology_unlock();
1273
1274	reprobe = (table->gpt_scheme == &g_part_null_scheme ||
1275	    table->gpt_created) ? 1 : 0;
1276
1277	if (reprobe) {
1278		LIST_FOREACH(entry, &table->gpt_entry, gpe_entry) {
1279			if (entry->gpe_internal)
1280				continue;
1281			error = EBUSY;
1282			goto fail;
1283		}
1284		while ((entry = LIST_FIRST(&table->gpt_entry)) != NULL) {
1285			LIST_REMOVE(entry, gpe_entry);
1286			g_free(entry);
1287		}
1288		error = g_part_probe(gp, cp, table->gpt_depth);
1289		if (error) {
1290			g_topology_lock();
1291			g_access(cp, -1, -1, -1);
1292			g_part_wither(gp, error);
1293			return (0);
1294		}
1295		table = gp->softc;
1296
1297		/*
1298		 * Synthesize a disk geometry. Some partitioning schemes
1299		 * depend on it and since some file systems need it even
1300		 * when the partitition scheme doesn't, we do it here in
1301		 * scheme-independent code.
1302		 */
1303		pp = cp->provider;
1304		g_part_geometry(table, cp, pp->mediasize / pp->sectorsize);
1305	}
1306
1307	error = G_PART_READ(table, cp);
1308	if (error)
1309		goto fail;
1310
1311	g_topology_lock();
1312
1313	LIST_FOREACH(entry, &table->gpt_entry, gpe_entry) {
1314		if (!entry->gpe_internal)
1315			g_part_new_provider(gp, table, entry);
1316	}
1317
1318	table->gpt_opened = 0;
1319	g_access(cp, -1, -1, -1);
1320	return (0);
1321
1322fail:
1323	g_topology_lock();
1324	gctl_error(req, "%d", error);
1325	return (error);
1326}
1327
1328static void
1329g_part_wither(struct g_geom *gp, int error)
1330{
1331	struct g_part_entry *entry;
1332	struct g_part_table *table;
1333
1334	table = gp->softc;
1335	if (table != NULL) {
1336		G_PART_DESTROY(table, NULL);
1337		while ((entry = LIST_FIRST(&table->gpt_entry)) != NULL) {
1338			LIST_REMOVE(entry, gpe_entry);
1339			g_free(entry);
1340		}
1341		if (gp->softc != NULL) {
1342			kobj_delete((kobj_t)gp->softc, M_GEOM);
1343			gp->softc = NULL;
1344		}
1345	}
1346	g_wither_geom(gp, error);
1347}
1348
1349/*
1350 * Class methods.
1351 */
1352
1353static void
1354g_part_ctlreq(struct gctl_req *req, struct g_class *mp, const char *verb)
1355{
1356	struct g_part_parms gpp;
1357	struct g_part_table *table;
1358	struct gctl_req_arg *ap;
1359	enum g_part_ctl ctlreq;
1360	unsigned int i, mparms, oparms, parm;
1361	int auto_commit, close_on_error;
1362	int error, modifies;
1363
1364	G_PART_TRACE((G_T_TOPOLOGY, "%s(%s,%s)", __func__, mp->name, verb));
1365	g_topology_assert();
1366
1367	ctlreq = G_PART_CTL_NONE;
1368	modifies = 1;
1369	mparms = 0;
1370	oparms = G_PART_PARM_FLAGS | G_PART_PARM_OUTPUT | G_PART_PARM_VERSION;
1371	switch (*verb) {
1372	case 'a':
1373		if (!strcmp(verb, "add")) {
1374			ctlreq = G_PART_CTL_ADD;
1375			mparms |= G_PART_PARM_GEOM | G_PART_PARM_SIZE |
1376			    G_PART_PARM_START | G_PART_PARM_TYPE;
1377			oparms |= G_PART_PARM_INDEX | G_PART_PARM_LABEL;
1378		}
1379		break;
1380	case 'b':
1381		if (!strcmp(verb, "bootcode")) {
1382			ctlreq = G_PART_CTL_BOOTCODE;
1383			mparms |= G_PART_PARM_GEOM | G_PART_PARM_BOOTCODE;
1384		}
1385		break;
1386	case 'c':
1387		if (!strcmp(verb, "commit")) {
1388			ctlreq = G_PART_CTL_COMMIT;
1389			mparms |= G_PART_PARM_GEOM;
1390			modifies = 0;
1391		} else if (!strcmp(verb, "create")) {
1392			ctlreq = G_PART_CTL_CREATE;
1393			mparms |= G_PART_PARM_PROVIDER | G_PART_PARM_SCHEME;
1394			oparms |= G_PART_PARM_ENTRIES;
1395		}
1396		break;
1397	case 'd':
1398		if (!strcmp(verb, "delete")) {
1399			ctlreq = G_PART_CTL_DELETE;
1400			mparms |= G_PART_PARM_GEOM | G_PART_PARM_INDEX;
1401		} else if (!strcmp(verb, "destroy")) {
1402			ctlreq = G_PART_CTL_DESTROY;
1403			mparms |= G_PART_PARM_GEOM;
1404			oparms |= G_PART_PARM_FORCE;
1405		}
1406		break;
1407	case 'm':
1408		if (!strcmp(verb, "modify")) {
1409			ctlreq = G_PART_CTL_MODIFY;
1410			mparms |= G_PART_PARM_GEOM | G_PART_PARM_INDEX;
1411			oparms |= G_PART_PARM_LABEL | G_PART_PARM_TYPE;
1412		} else if (!strcmp(verb, "move")) {
1413			ctlreq = G_PART_CTL_MOVE;
1414			mparms |= G_PART_PARM_GEOM | G_PART_PARM_INDEX;
1415		}
1416		break;
1417	case 'r':
1418		if (!strcmp(verb, "recover")) {
1419			ctlreq = G_PART_CTL_RECOVER;
1420			mparms |= G_PART_PARM_GEOM;
1421		} else if (!strcmp(verb, "resize")) {
1422			ctlreq = G_PART_CTL_RESIZE;
1423			mparms |= G_PART_PARM_GEOM | G_PART_PARM_INDEX |
1424			    G_PART_PARM_SIZE;
1425		}
1426		break;
1427	case 's':
1428		if (!strcmp(verb, "set")) {
1429			ctlreq = G_PART_CTL_SET;
1430			mparms |= G_PART_PARM_ATTRIB | G_PART_PARM_GEOM |
1431			    G_PART_PARM_INDEX;
1432		}
1433		break;
1434	case 'u':
1435		if (!strcmp(verb, "undo")) {
1436			ctlreq = G_PART_CTL_UNDO;
1437			mparms |= G_PART_PARM_GEOM;
1438			modifies = 0;
1439		} else if (!strcmp(verb, "unset")) {
1440			ctlreq = G_PART_CTL_UNSET;
1441			mparms |= G_PART_PARM_ATTRIB | G_PART_PARM_GEOM |
1442			    G_PART_PARM_INDEX;
1443		}
1444		break;
1445	}
1446	if (ctlreq == G_PART_CTL_NONE) {
1447		gctl_error(req, "%d verb '%s'", EINVAL, verb);
1448		return;
1449	}
1450
1451	bzero(&gpp, sizeof(gpp));
1452	for (i = 0; i < req->narg; i++) {
1453		ap = &req->arg[i];
1454		parm = 0;
1455		switch (ap->name[0]) {
1456		case 'a':
1457			if (!strcmp(ap->name, "arg0")) {
1458				parm = mparms &
1459				    (G_PART_PARM_GEOM | G_PART_PARM_PROVIDER);
1460			}
1461			if (!strcmp(ap->name, "attrib"))
1462				parm = G_PART_PARM_ATTRIB;
1463			break;
1464		case 'b':
1465			if (!strcmp(ap->name, "bootcode"))
1466				parm = G_PART_PARM_BOOTCODE;
1467			break;
1468		case 'c':
1469			if (!strcmp(ap->name, "class"))
1470				continue;
1471			break;
1472		case 'e':
1473			if (!strcmp(ap->name, "entries"))
1474				parm = G_PART_PARM_ENTRIES;
1475			break;
1476		case 'f':
1477			if (!strcmp(ap->name, "flags"))
1478				parm = G_PART_PARM_FLAGS;
1479			else if (!strcmp(ap->name, "force"))
1480				parm = G_PART_PARM_FORCE;
1481			break;
1482		case 'i':
1483			if (!strcmp(ap->name, "index"))
1484				parm = G_PART_PARM_INDEX;
1485			break;
1486		case 'l':
1487			if (!strcmp(ap->name, "label"))
1488				parm = G_PART_PARM_LABEL;
1489			break;
1490		case 'o':
1491			if (!strcmp(ap->name, "output"))
1492				parm = G_PART_PARM_OUTPUT;
1493			break;
1494		case 's':
1495			if (!strcmp(ap->name, "scheme"))
1496				parm = G_PART_PARM_SCHEME;
1497			else if (!strcmp(ap->name, "size"))
1498				parm = G_PART_PARM_SIZE;
1499			else if (!strcmp(ap->name, "start"))
1500				parm = G_PART_PARM_START;
1501			break;
1502		case 't':
1503			if (!strcmp(ap->name, "type"))
1504				parm = G_PART_PARM_TYPE;
1505			break;
1506		case 'v':
1507			if (!strcmp(ap->name, "verb"))
1508				continue;
1509			else if (!strcmp(ap->name, "version"))
1510				parm = G_PART_PARM_VERSION;
1511			break;
1512		}
1513		if ((parm & (mparms | oparms)) == 0) {
1514			gctl_error(req, "%d param '%s'", EINVAL, ap->name);
1515			return;
1516		}
1517		switch (parm) {
1518		case G_PART_PARM_ATTRIB:
1519			error = g_part_parm_str(req, ap->name,
1520			    &gpp.gpp_attrib);
1521			break;
1522		case G_PART_PARM_BOOTCODE:
1523			error = g_part_parm_bootcode(req, ap->name,
1524			    &gpp.gpp_codeptr, &gpp.gpp_codesize);
1525			break;
1526		case G_PART_PARM_ENTRIES:
1527			error = g_part_parm_intmax(req, ap->name,
1528			    &gpp.gpp_entries);
1529			break;
1530		case G_PART_PARM_FLAGS:
1531			error = g_part_parm_str(req, ap->name, &gpp.gpp_flags);
1532			break;
1533		case G_PART_PARM_FORCE:
1534			error = g_part_parm_uint32(req, ap->name,
1535			    &gpp.gpp_force);
1536			break;
1537		case G_PART_PARM_GEOM:
1538			error = g_part_parm_geom(req, ap->name, &gpp.gpp_geom);
1539			break;
1540		case G_PART_PARM_INDEX:
1541			error = g_part_parm_intmax(req, ap->name,
1542			    &gpp.gpp_index);
1543			break;
1544		case G_PART_PARM_LABEL:
1545			error = g_part_parm_str(req, ap->name, &gpp.gpp_label);
1546			break;
1547		case G_PART_PARM_OUTPUT:
1548			error = 0;	/* Write-only parameter */
1549			break;
1550		case G_PART_PARM_PROVIDER:
1551			error = g_part_parm_provider(req, ap->name,
1552			    &gpp.gpp_provider);
1553			break;
1554		case G_PART_PARM_SCHEME:
1555			error = g_part_parm_scheme(req, ap->name,
1556			    &gpp.gpp_scheme);
1557			break;
1558		case G_PART_PARM_SIZE:
1559			error = g_part_parm_quad(req, ap->name, &gpp.gpp_size);
1560			break;
1561		case G_PART_PARM_START:
1562			error = g_part_parm_quad(req, ap->name,
1563			    &gpp.gpp_start);
1564			break;
1565		case G_PART_PARM_TYPE:
1566			error = g_part_parm_str(req, ap->name, &gpp.gpp_type);
1567			break;
1568		case G_PART_PARM_VERSION:
1569			error = g_part_parm_uint32(req, ap->name,
1570			    &gpp.gpp_version);
1571			break;
1572		default:
1573			error = EDOOFUS;
1574			gctl_error(req, "%d %s", error, ap->name);
1575			break;
1576		}
1577		if (error != 0) {
1578			if (error == ENOATTR) {
1579				gctl_error(req, "%d param '%s'", error,
1580				    ap->name);
1581			}
1582			return;
1583		}
1584		gpp.gpp_parms |= parm;
1585	}
1586	if ((gpp.gpp_parms & mparms) != mparms) {
1587		parm = mparms - (gpp.gpp_parms & mparms);
1588		gctl_error(req, "%d param '%x'", ENOATTR, parm);
1589		return;
1590	}
1591
1592	/* Obtain permissions if possible/necessary. */
1593	close_on_error = 0;
1594	table = NULL;
1595	if (modifies && (gpp.gpp_parms & G_PART_PARM_GEOM)) {
1596		table = gpp.gpp_geom->softc;
1597		if (table != NULL && table->gpt_corrupt &&
1598		    ctlreq != G_PART_CTL_DESTROY &&
1599		    ctlreq != G_PART_CTL_RECOVER) {
1600			gctl_error(req, "%d table '%s' is corrupt",
1601			    EPERM, gpp.gpp_geom->name);
1602			return;
1603		}
1604		if (table != NULL && !table->gpt_opened) {
1605			error = g_access(LIST_FIRST(&gpp.gpp_geom->consumer),
1606			    1, 1, 1);
1607			if (error) {
1608				gctl_error(req, "%d geom '%s'", error,
1609				    gpp.gpp_geom->name);
1610				return;
1611			}
1612			table->gpt_opened = 1;
1613			close_on_error = 1;
1614		}
1615	}
1616
1617	/* Allow the scheme to check or modify the parameters. */
1618	if (table != NULL) {
1619		error = G_PART_PRECHECK(table, ctlreq, &gpp);
1620		if (error) {
1621			gctl_error(req, "%d pre-check failed", error);
1622			goto out;
1623		}
1624	} else
1625		error = EDOOFUS;	/* Prevent bogus uninit. warning. */
1626
1627	switch (ctlreq) {
1628	case G_PART_CTL_NONE:
1629		panic("%s", __func__);
1630	case G_PART_CTL_ADD:
1631		error = g_part_ctl_add(req, &gpp);
1632		break;
1633	case G_PART_CTL_BOOTCODE:
1634		error = g_part_ctl_bootcode(req, &gpp);
1635		break;
1636	case G_PART_CTL_COMMIT:
1637		error = g_part_ctl_commit(req, &gpp);
1638		break;
1639	case G_PART_CTL_CREATE:
1640		error = g_part_ctl_create(req, &gpp);
1641		break;
1642	case G_PART_CTL_DELETE:
1643		error = g_part_ctl_delete(req, &gpp);
1644		break;
1645	case G_PART_CTL_DESTROY:
1646		error = g_part_ctl_destroy(req, &gpp);
1647		break;
1648	case G_PART_CTL_MODIFY:
1649		error = g_part_ctl_modify(req, &gpp);
1650		break;
1651	case G_PART_CTL_MOVE:
1652		error = g_part_ctl_move(req, &gpp);
1653		break;
1654	case G_PART_CTL_RECOVER:
1655		error = g_part_ctl_recover(req, &gpp);
1656		break;
1657	case G_PART_CTL_RESIZE:
1658		error = g_part_ctl_resize(req, &gpp);
1659		break;
1660	case G_PART_CTL_SET:
1661		error = g_part_ctl_setunset(req, &gpp, 1);
1662		break;
1663	case G_PART_CTL_UNDO:
1664		error = g_part_ctl_undo(req, &gpp);
1665		break;
1666	case G_PART_CTL_UNSET:
1667		error = g_part_ctl_setunset(req, &gpp, 0);
1668		break;
1669	}
1670
1671	/* Implement automatic commit. */
1672	if (!error) {
1673		auto_commit = (modifies &&
1674		    (gpp.gpp_parms & G_PART_PARM_FLAGS) &&
1675		    strchr(gpp.gpp_flags, 'C') != NULL) ? 1 : 0;
1676		if (auto_commit) {
1677			KASSERT(gpp.gpp_parms & G_PART_PARM_GEOM, ("%s",
1678			    __func__));
1679			error = g_part_ctl_commit(req, &gpp);
1680		}
1681	}
1682
1683 out:
1684	if (error && close_on_error) {
1685		g_access(LIST_FIRST(&gpp.gpp_geom->consumer), -1, -1, -1);
1686		table->gpt_opened = 0;
1687	}
1688}
1689
1690static int
1691g_part_destroy_geom(struct gctl_req *req, struct g_class *mp,
1692    struct g_geom *gp)
1693{
1694
1695	G_PART_TRACE((G_T_TOPOLOGY, "%s(%s,%s)", __func__, mp->name, gp->name));
1696	g_topology_assert();
1697
1698	g_part_wither(gp, EINVAL);
1699	return (0);
1700}
1701
1702static struct g_geom *
1703g_part_taste(struct g_class *mp, struct g_provider *pp, int flags __unused)
1704{
1705	struct g_consumer *cp;
1706	struct g_geom *gp;
1707	struct g_part_entry *entry;
1708	struct g_part_table *table;
1709	struct root_hold_token *rht;
1710	int attr, depth;
1711	int error;
1712
1713	G_PART_TRACE((G_T_TOPOLOGY, "%s(%s,%s)", __func__, mp->name, pp->name));
1714	g_topology_assert();
1715
1716	/* Skip providers that are already open for writing. */
1717	if (pp->acw > 0)
1718		return (NULL);
1719
1720	/*
1721	 * Create a GEOM with consumer and hook it up to the provider.
1722	 * With that we become part of the topology. Optain read access
1723	 * to the provider.
1724	 */
1725	gp = g_new_geomf(mp, "%s", pp->name);
1726	cp = g_new_consumer(gp);
1727	error = g_attach(cp, pp);
1728	if (error == 0)
1729		error = g_access(cp, 1, 0, 0);
1730	if (error != 0) {
1731		g_part_wither(gp, error);
1732		return (NULL);
1733	}
1734
1735	rht = root_mount_hold(mp->name);
1736	g_topology_unlock();
1737
1738	/*
1739	 * Short-circuit the whole probing galore when there's no
1740	 * media present.
1741	 */
1742	if (pp->mediasize == 0 || pp->sectorsize == 0) {
1743		error = ENODEV;
1744		goto fail;
1745	}
1746
1747	/* Make sure we can nest and if so, determine our depth. */
1748	error = g_getattr("PART::isleaf", cp, &attr);
1749	if (!error && attr) {
1750		error = ENODEV;
1751		goto fail;
1752	}
1753	error = g_getattr("PART::depth", cp, &attr);
1754	depth = (!error) ? attr + 1 : 0;
1755
1756	error = g_part_probe(gp, cp, depth);
1757	if (error)
1758		goto fail;
1759
1760	table = gp->softc;
1761
1762	/*
1763	 * Synthesize a disk geometry. Some partitioning schemes
1764	 * depend on it and since some file systems need it even
1765	 * when the partitition scheme doesn't, we do it here in
1766	 * scheme-independent code.
1767	 */
1768	g_part_geometry(table, cp, pp->mediasize / pp->sectorsize);
1769
1770	error = G_PART_READ(table, cp);
1771	if (error)
1772		goto fail;
1773
1774	g_topology_lock();
1775	LIST_FOREACH(entry, &table->gpt_entry, gpe_entry) {
1776		if (!entry->gpe_internal)
1777			g_part_new_provider(gp, table, entry);
1778	}
1779
1780	root_mount_rel(rht);
1781	g_access(cp, -1, 0, 0);
1782	return (gp);
1783
1784 fail:
1785	g_topology_lock();
1786	root_mount_rel(rht);
1787	g_access(cp, -1, 0, 0);
1788	g_part_wither(gp, error);
1789	return (NULL);
1790}
1791
1792/*
1793 * Geom methods.
1794 */
1795
1796static int
1797g_part_access(struct g_provider *pp, int dr, int dw, int de)
1798{
1799	struct g_consumer *cp;
1800
1801	G_PART_TRACE((G_T_ACCESS, "%s(%s,%d,%d,%d)", __func__, pp->name, dr,
1802	    dw, de));
1803
1804	cp = LIST_FIRST(&pp->geom->consumer);
1805
1806	/* We always gain write-exclusive access. */
1807	return (g_access(cp, dr, dw, dw + de));
1808}
1809
1810static void
1811g_part_dumpconf(struct sbuf *sb, const char *indent, struct g_geom *gp,
1812    struct g_consumer *cp, struct g_provider *pp)
1813{
1814	char buf[64];
1815	struct g_part_entry *entry;
1816	struct g_part_table *table;
1817
1818	KASSERT(sb != NULL && gp != NULL, ("%s", __func__));
1819	table = gp->softc;
1820
1821	if (indent == NULL) {
1822		KASSERT(cp == NULL && pp != NULL, ("%s", __func__));
1823		entry = pp->private;
1824		if (entry == NULL)
1825			return;
1826		sbuf_printf(sb, " i %u o %ju ty %s", entry->gpe_index,
1827		    (uintmax_t)entry->gpe_offset,
1828		    G_PART_TYPE(table, entry, buf, sizeof(buf)));
1829		/*
1830		 * libdisk compatibility quirk - the scheme dumps the
1831		 * slicer name and partition type in a way that is
1832		 * compatible with libdisk. When libdisk is not used
1833		 * anymore, this should go away.
1834		 */
1835		G_PART_DUMPCONF(table, entry, sb, indent);
1836	} else if (cp != NULL) {	/* Consumer configuration. */
1837		KASSERT(pp == NULL, ("%s", __func__));
1838		/* none */
1839	} else if (pp != NULL) {	/* Provider configuration. */
1840		entry = pp->private;
1841		if (entry == NULL)
1842			return;
1843		sbuf_printf(sb, "%s<start>%ju</start>\n", indent,
1844		    (uintmax_t)entry->gpe_start);
1845		sbuf_printf(sb, "%s<end>%ju</end>\n", indent,
1846		    (uintmax_t)entry->gpe_end);
1847		sbuf_printf(sb, "%s<index>%u</index>\n", indent,
1848		    entry->gpe_index);
1849		sbuf_printf(sb, "%s<type>%s</type>\n", indent,
1850		    G_PART_TYPE(table, entry, buf, sizeof(buf)));
1851		sbuf_printf(sb, "%s<offset>%ju</offset>\n", indent,
1852		    (uintmax_t)entry->gpe_offset);
1853		sbuf_printf(sb, "%s<length>%ju</length>\n", indent,
1854		    (uintmax_t)pp->mediasize);
1855		G_PART_DUMPCONF(table, entry, sb, indent);
1856	} else {			/* Geom configuration. */
1857		sbuf_printf(sb, "%s<scheme>%s</scheme>\n", indent,
1858		    table->gpt_scheme->name);
1859		sbuf_printf(sb, "%s<entries>%u</entries>\n", indent,
1860		    table->gpt_entries);
1861		sbuf_printf(sb, "%s<first>%ju</first>\n", indent,
1862		    (uintmax_t)table->gpt_first);
1863		sbuf_printf(sb, "%s<last>%ju</last>\n", indent,
1864		    (uintmax_t)table->gpt_last);
1865		sbuf_printf(sb, "%s<fwsectors>%u</fwsectors>\n", indent,
1866		    table->gpt_sectors);
1867		sbuf_printf(sb, "%s<fwheads>%u</fwheads>\n", indent,
1868		    table->gpt_heads);
1869		sbuf_printf(sb, "%s<state>%s</state>\n", indent,
1870		    table->gpt_corrupt ? "CORRUPT": "OK");
1871		sbuf_printf(sb, "%s<modified>%s</modified>\n", indent,
1872		    table->gpt_opened ? "true": "false");
1873		G_PART_DUMPCONF(table, NULL, sb, indent);
1874	}
1875}
1876
1877static void
1878g_part_orphan(struct g_consumer *cp)
1879{
1880	struct g_provider *pp;
1881	struct g_part_table *table;
1882
1883	pp = cp->provider;
1884	KASSERT(pp != NULL, ("%s", __func__));
1885	G_PART_TRACE((G_T_TOPOLOGY, "%s(%s)", __func__, pp->name));
1886	g_topology_assert();
1887
1888	KASSERT(pp->error != 0, ("%s", __func__));
1889	table = cp->geom->softc;
1890	if (table != NULL && table->gpt_opened)
1891		g_access(cp, -1, -1, -1);
1892	g_part_wither(cp->geom, pp->error);
1893}
1894
1895static void
1896g_part_spoiled(struct g_consumer *cp)
1897{
1898
1899	G_PART_TRACE((G_T_TOPOLOGY, "%s(%s)", __func__, cp->provider->name));
1900	g_topology_assert();
1901
1902	g_part_wither(cp->geom, ENXIO);
1903}
1904
1905static void
1906g_part_start(struct bio *bp)
1907{
1908	struct bio *bp2;
1909	struct g_consumer *cp;
1910	struct g_geom *gp;
1911	struct g_part_entry *entry;
1912	struct g_part_table *table;
1913	struct g_kerneldump *gkd;
1914	struct g_provider *pp;
1915
1916	pp = bp->bio_to;
1917	gp = pp->geom;
1918	table = gp->softc;
1919	cp = LIST_FIRST(&gp->consumer);
1920
1921	G_PART_TRACE((G_T_BIO, "%s: cmd=%d, provider=%s", __func__, bp->bio_cmd,
1922	    pp->name));
1923
1924	entry = pp->private;
1925	if (entry == NULL) {
1926		g_io_deliver(bp, ENXIO);
1927		return;
1928	}
1929
1930	switch(bp->bio_cmd) {
1931	case BIO_DELETE:
1932	case BIO_READ:
1933	case BIO_WRITE:
1934		if (bp->bio_offset >= pp->mediasize) {
1935			g_io_deliver(bp, EIO);
1936			return;
1937		}
1938		bp2 = g_clone_bio(bp);
1939		if (bp2 == NULL) {
1940			g_io_deliver(bp, ENOMEM);
1941			return;
1942		}
1943		if (bp2->bio_offset + bp2->bio_length > pp->mediasize)
1944			bp2->bio_length = pp->mediasize - bp2->bio_offset;
1945		bp2->bio_done = g_std_done;
1946		bp2->bio_offset += entry->gpe_offset;
1947		g_io_request(bp2, cp);
1948		return;
1949	case BIO_FLUSH:
1950		break;
1951	case BIO_GETATTR:
1952		if (g_handleattr_int(bp, "GEOM::fwheads", table->gpt_heads))
1953			return;
1954		if (g_handleattr_int(bp, "GEOM::fwsectors", table->gpt_sectors))
1955			return;
1956		if (g_handleattr_int(bp, "PART::isleaf", table->gpt_isleaf))
1957			return;
1958		if (g_handleattr_int(bp, "PART::depth", table->gpt_depth))
1959			return;
1960		if (g_handleattr_str(bp, "PART::scheme",
1961		    table->gpt_scheme->name))
1962			return;
1963		if (!strcmp("GEOM::kerneldump", bp->bio_attribute)) {
1964			/*
1965			 * Check that the partition is suitable for kernel
1966			 * dumps. Typically only swap partitions should be
1967			 * used.
1968			 */
1969			if (!G_PART_DUMPTO(table, entry)) {
1970				g_io_deliver(bp, ENODEV);
1971				printf("GEOM_PART: Partition '%s' not suitable"
1972				    " for kernel dumps (wrong type?)\n",
1973				    pp->name);
1974				return;
1975			}
1976			gkd = (struct g_kerneldump *)bp->bio_data;
1977			if (gkd->offset >= pp->mediasize) {
1978				g_io_deliver(bp, EIO);
1979				return;
1980			}
1981			if (gkd->offset + gkd->length > pp->mediasize)
1982				gkd->length = pp->mediasize - gkd->offset;
1983			gkd->offset += entry->gpe_offset;
1984		}
1985		break;
1986	default:
1987		g_io_deliver(bp, EOPNOTSUPP);
1988		return;
1989	}
1990
1991	bp2 = g_clone_bio(bp);
1992	if (bp2 == NULL) {
1993		g_io_deliver(bp, ENOMEM);
1994		return;
1995	}
1996	bp2->bio_done = g_std_done;
1997	g_io_request(bp2, cp);
1998}
1999
2000static void
2001g_part_init(struct g_class *mp)
2002{
2003
2004	TAILQ_INSERT_HEAD(&g_part_schemes, &g_part_null_scheme, scheme_list);
2005}
2006
2007static void
2008g_part_fini(struct g_class *mp)
2009{
2010
2011	TAILQ_REMOVE(&g_part_schemes, &g_part_null_scheme, scheme_list);
2012}
2013
2014static void
2015g_part_unload_event(void *arg, int flag)
2016{
2017	struct g_consumer *cp;
2018	struct g_geom *gp;
2019	struct g_provider *pp;
2020	struct g_part_scheme *scheme;
2021	struct g_part_table *table;
2022	uintptr_t *xchg;
2023	int acc, error;
2024
2025	if (flag == EV_CANCEL)
2026		return;
2027
2028	xchg = arg;
2029	error = 0;
2030	scheme = (void *)(*xchg);
2031
2032	g_topology_assert();
2033
2034	LIST_FOREACH(gp, &g_part_class.geom, geom) {
2035		table = gp->softc;
2036		if (table->gpt_scheme != scheme)
2037			continue;
2038
2039		acc = 0;
2040		LIST_FOREACH(pp, &gp->provider, provider)
2041			acc += pp->acr + pp->acw + pp->ace;
2042		LIST_FOREACH(cp, &gp->consumer, consumer)
2043			acc += cp->acr + cp->acw + cp->ace;
2044
2045		if (!acc)
2046			g_part_wither(gp, ENOSYS);
2047		else
2048			error = EBUSY;
2049	}
2050
2051	if (!error)
2052		TAILQ_REMOVE(&g_part_schemes, scheme, scheme_list);
2053
2054	*xchg = error;
2055}
2056
2057int
2058g_part_modevent(module_t mod, int type, struct g_part_scheme *scheme)
2059{
2060	uintptr_t arg;
2061	int error;
2062
2063	switch (type) {
2064	case MOD_LOAD:
2065		TAILQ_INSERT_TAIL(&g_part_schemes, scheme, scheme_list);
2066
2067		error = g_retaste(&g_part_class);
2068		if (error)
2069			TAILQ_REMOVE(&g_part_schemes, scheme, scheme_list);
2070		break;
2071	case MOD_UNLOAD:
2072		arg = (uintptr_t)scheme;
2073		error = g_waitfor_event(g_part_unload_event, &arg, M_WAITOK,
2074		    NULL);
2075		if (!error)
2076			error = (arg == (uintptr_t)scheme) ? EDOOFUS : arg;
2077		break;
2078	default:
2079		error = EOPNOTSUPP;
2080		break;
2081	}
2082
2083	return (error);
2084}
2085