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