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