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