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