g_part.c revision 320566
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: stable/11/sys/geom/part/g_part.c 320566 2017-07-02 04:09:05Z araujo $");
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			/* Notify consumers that provider might be changed. */
888			if (entry->gpe_modified && (
889			    entry->gpe_pp->acw + entry->gpe_pp->ace +
890			    entry->gpe_pp->acr) == 0)
891				g_media_changed(entry->gpe_pp, M_NOWAIT);
892			entry->gpe_created = 0;
893			entry->gpe_modified = 0;
894			continue;
895		}
896		LIST_REMOVE(entry, gpe_entry);
897		g_free(entry);
898	}
899	table->gpt_created = 0;
900	table->gpt_opened = 0;
901
902	g_topology_lock();
903	g_access(cp, -1, -1, -1);
904	return (0);
905
906fail:
907	g_topology_lock();
908	gctl_error(req, "%d", error);
909	return (error);
910}
911
912static int
913g_part_ctl_create(struct gctl_req *req, struct g_part_parms *gpp)
914{
915	struct g_consumer *cp;
916	struct g_geom *gp;
917	struct g_provider *pp;
918	struct g_part_scheme *scheme;
919	struct g_part_table *null, *table;
920	struct sbuf *sb;
921	int attr, error;
922
923	pp = gpp->gpp_provider;
924	scheme = gpp->gpp_scheme;
925	G_PART_TRACE((G_T_TOPOLOGY, "%s(%s)", __func__, pp->name));
926	g_topology_assert();
927
928	/* Check that there isn't already a g_part geom on the provider. */
929	gp = g_part_find_geom(pp->name);
930	if (gp != NULL) {
931		null = gp->softc;
932		if (null->gpt_scheme != &g_part_null_scheme) {
933			gctl_error(req, "%d geom '%s'", EEXIST, pp->name);
934			return (EEXIST);
935		}
936	} else
937		null = NULL;
938
939	if ((gpp->gpp_parms & G_PART_PARM_ENTRIES) &&
940	    (gpp->gpp_entries < scheme->gps_minent ||
941	     gpp->gpp_entries > scheme->gps_maxent)) {
942		gctl_error(req, "%d entries '%d'", EINVAL, gpp->gpp_entries);
943		return (EINVAL);
944	}
945
946	if (null == NULL)
947		gp = g_new_geomf(&g_part_class, "%s", pp->name);
948	gp->softc = kobj_create((kobj_class_t)gpp->gpp_scheme, M_GEOM,
949	    M_WAITOK);
950	table = gp->softc;
951	table->gpt_gp = gp;
952	table->gpt_scheme = gpp->gpp_scheme;
953	table->gpt_entries = (gpp->gpp_parms & G_PART_PARM_ENTRIES) ?
954	    gpp->gpp_entries : scheme->gps_minent;
955	LIST_INIT(&table->gpt_entry);
956	if (null == NULL) {
957		cp = g_new_consumer(gp);
958		cp->flags |= G_CF_DIRECT_SEND | G_CF_DIRECT_RECEIVE;
959		error = g_attach(cp, pp);
960		if (error == 0)
961			error = g_access(cp, 1, 1, 1);
962		if (error != 0) {
963			g_part_wither(gp, error);
964			gctl_error(req, "%d geom '%s'", error, pp->name);
965			return (error);
966		}
967		table->gpt_opened = 1;
968	} else {
969		cp = LIST_FIRST(&gp->consumer);
970		table->gpt_opened = null->gpt_opened;
971		table->gpt_smhead = null->gpt_smhead;
972		table->gpt_smtail = null->gpt_smtail;
973	}
974
975	g_topology_unlock();
976
977	/* Make sure the provider has media. */
978	if (pp->mediasize == 0 || pp->sectorsize == 0) {
979		error = ENODEV;
980		goto fail;
981	}
982
983	/* Make sure we can nest and if so, determine our depth. */
984	error = g_getattr("PART::isleaf", cp, &attr);
985	if (!error && attr) {
986		error = ENODEV;
987		goto fail;
988	}
989	error = g_getattr("PART::depth", cp, &attr);
990	table->gpt_depth = (!error) ? attr + 1 : 0;
991
992	/*
993	 * Synthesize a disk geometry. Some partitioning schemes
994	 * depend on it and since some file systems need it even
995	 * when the partitition scheme doesn't, we do it here in
996	 * scheme-independent code.
997	 */
998	g_part_geometry(table, cp, pp->mediasize / pp->sectorsize);
999
1000	error = G_PART_CREATE(table, gpp);
1001	if (error)
1002		goto fail;
1003
1004	g_topology_lock();
1005
1006	table->gpt_created = 1;
1007	if (null != NULL)
1008		kobj_delete((kobj_t)null, M_GEOM);
1009
1010	/*
1011	 * Support automatic commit by filling in the gpp_geom
1012	 * parameter.
1013	 */
1014	gpp->gpp_parms |= G_PART_PARM_GEOM;
1015	gpp->gpp_geom = gp;
1016
1017	/* Provide feedback if so requested. */
1018	if (gpp->gpp_parms & G_PART_PARM_OUTPUT) {
1019		sb = sbuf_new_auto();
1020		sbuf_printf(sb, "%s created\n", gp->name);
1021		sbuf_finish(sb);
1022		gctl_set_param(req, "output", sbuf_data(sb), sbuf_len(sb) + 1);
1023		sbuf_delete(sb);
1024	}
1025	return (0);
1026
1027fail:
1028	g_topology_lock();
1029	if (null == NULL) {
1030		g_access(cp, -1, -1, -1);
1031		g_part_wither(gp, error);
1032	} else {
1033		kobj_delete((kobj_t)gp->softc, M_GEOM);
1034		gp->softc = null;
1035	}
1036	gctl_error(req, "%d provider", error);
1037	return (error);
1038}
1039
1040static int
1041g_part_ctl_delete(struct gctl_req *req, struct g_part_parms *gpp)
1042{
1043	struct g_geom *gp;
1044	struct g_provider *pp;
1045	struct g_part_entry *entry;
1046	struct g_part_table *table;
1047	struct sbuf *sb;
1048
1049	gp = gpp->gpp_geom;
1050	G_PART_TRACE((G_T_TOPOLOGY, "%s(%s)", __func__, gp->name));
1051	g_topology_assert();
1052
1053	table = gp->softc;
1054
1055	LIST_FOREACH(entry, &table->gpt_entry, gpe_entry) {
1056		if (entry->gpe_deleted || entry->gpe_internal)
1057			continue;
1058		if (entry->gpe_index == gpp->gpp_index)
1059			break;
1060	}
1061	if (entry == NULL) {
1062		gctl_error(req, "%d index '%d'", ENOENT, gpp->gpp_index);
1063		return (ENOENT);
1064	}
1065
1066	pp = entry->gpe_pp;
1067	if (pp != NULL) {
1068		if (pp->acr > 0 || pp->acw > 0 || pp->ace > 0) {
1069			gctl_error(req, "%d", EBUSY);
1070			return (EBUSY);
1071		}
1072
1073		pp->private = NULL;
1074		entry->gpe_pp = NULL;
1075	}
1076
1077	if (pp != NULL)
1078		g_wither_provider(pp, ENXIO);
1079
1080	/* Provide feedback if so requested. */
1081	if (gpp->gpp_parms & G_PART_PARM_OUTPUT) {
1082		sb = sbuf_new_auto();
1083		G_PART_FULLNAME(table, entry, sb, gp->name);
1084		sbuf_cat(sb, " deleted\n");
1085		sbuf_finish(sb);
1086		gctl_set_param(req, "output", sbuf_data(sb), sbuf_len(sb) + 1);
1087		sbuf_delete(sb);
1088	}
1089
1090	if (entry->gpe_created) {
1091		LIST_REMOVE(entry, gpe_entry);
1092		g_free(entry);
1093	} else {
1094		entry->gpe_modified = 0;
1095		entry->gpe_deleted = 1;
1096	}
1097	return (0);
1098}
1099
1100static int
1101g_part_ctl_destroy(struct gctl_req *req, struct g_part_parms *gpp)
1102{
1103	struct g_consumer *cp;
1104	struct g_geom *gp;
1105	struct g_provider *pp;
1106	struct g_part_entry *entry, *tmp;
1107	struct g_part_table *null, *table;
1108	struct sbuf *sb;
1109	int error;
1110
1111	gp = gpp->gpp_geom;
1112	G_PART_TRACE((G_T_TOPOLOGY, "%s(%s)", __func__, gp->name));
1113	g_topology_assert();
1114
1115	table = gp->softc;
1116	/* Check for busy providers. */
1117	LIST_FOREACH(entry, &table->gpt_entry, gpe_entry) {
1118		if (entry->gpe_deleted || entry->gpe_internal)
1119			continue;
1120		if (gpp->gpp_force) {
1121			pp = entry->gpe_pp;
1122			if (pp == NULL)
1123				continue;
1124			if (pp->acr == 0 && pp->acw == 0 && pp->ace == 0)
1125				continue;
1126		}
1127		gctl_error(req, "%d", EBUSY);
1128		return (EBUSY);
1129	}
1130
1131	if (gpp->gpp_force) {
1132		/* Destroy all providers. */
1133		LIST_FOREACH_SAFE(entry, &table->gpt_entry, gpe_entry, tmp) {
1134			pp = entry->gpe_pp;
1135			if (pp != NULL) {
1136				pp->private = NULL;
1137				g_wither_provider(pp, ENXIO);
1138			}
1139			LIST_REMOVE(entry, gpe_entry);
1140			g_free(entry);
1141		}
1142	}
1143
1144	error = G_PART_DESTROY(table, gpp);
1145	if (error) {
1146		gctl_error(req, "%d", error);
1147		return (error);
1148	}
1149
1150	gp->softc = kobj_create((kobj_class_t)&g_part_null_scheme, M_GEOM,
1151	    M_WAITOK);
1152	null = gp->softc;
1153	null->gpt_gp = gp;
1154	null->gpt_scheme = &g_part_null_scheme;
1155	LIST_INIT(&null->gpt_entry);
1156
1157	cp = LIST_FIRST(&gp->consumer);
1158	pp = cp->provider;
1159	null->gpt_last = pp->mediasize / pp->sectorsize - 1;
1160
1161	null->gpt_depth = table->gpt_depth;
1162	null->gpt_opened = table->gpt_opened;
1163	null->gpt_smhead = table->gpt_smhead;
1164	null->gpt_smtail = table->gpt_smtail;
1165
1166	while ((entry = LIST_FIRST(&table->gpt_entry)) != NULL) {
1167		LIST_REMOVE(entry, gpe_entry);
1168		g_free(entry);
1169	}
1170	kobj_delete((kobj_t)table, M_GEOM);
1171
1172	/* Provide feedback if so requested. */
1173	if (gpp->gpp_parms & G_PART_PARM_OUTPUT) {
1174		sb = sbuf_new_auto();
1175		sbuf_printf(sb, "%s destroyed\n", gp->name);
1176		sbuf_finish(sb);
1177		gctl_set_param(req, "output", sbuf_data(sb), sbuf_len(sb) + 1);
1178		sbuf_delete(sb);
1179	}
1180	return (0);
1181}
1182
1183static int
1184g_part_ctl_modify(struct gctl_req *req, struct g_part_parms *gpp)
1185{
1186	struct g_geom *gp;
1187	struct g_part_entry *entry;
1188	struct g_part_table *table;
1189	struct sbuf *sb;
1190	int error;
1191
1192	gp = gpp->gpp_geom;
1193	G_PART_TRACE((G_T_TOPOLOGY, "%s(%s)", __func__, gp->name));
1194	g_topology_assert();
1195
1196	table = gp->softc;
1197
1198	LIST_FOREACH(entry, &table->gpt_entry, gpe_entry) {
1199		if (entry->gpe_deleted || entry->gpe_internal)
1200			continue;
1201		if (entry->gpe_index == gpp->gpp_index)
1202			break;
1203	}
1204	if (entry == NULL) {
1205		gctl_error(req, "%d index '%d'", ENOENT, gpp->gpp_index);
1206		return (ENOENT);
1207	}
1208
1209	error = G_PART_MODIFY(table, entry, gpp);
1210	if (error) {
1211		gctl_error(req, "%d", error);
1212		return (error);
1213	}
1214
1215	if (!entry->gpe_created)
1216		entry->gpe_modified = 1;
1217
1218	/* Provide feedback if so requested. */
1219	if (gpp->gpp_parms & G_PART_PARM_OUTPUT) {
1220		sb = sbuf_new_auto();
1221		G_PART_FULLNAME(table, entry, sb, gp->name);
1222		sbuf_cat(sb, " modified\n");
1223		sbuf_finish(sb);
1224		gctl_set_param(req, "output", sbuf_data(sb), sbuf_len(sb) + 1);
1225		sbuf_delete(sb);
1226	}
1227	return (0);
1228}
1229
1230static int
1231g_part_ctl_move(struct gctl_req *req, struct g_part_parms *gpp)
1232{
1233	gctl_error(req, "%d verb 'move'", ENOSYS);
1234	return (ENOSYS);
1235}
1236
1237static int
1238g_part_ctl_recover(struct gctl_req *req, struct g_part_parms *gpp)
1239{
1240	struct g_part_table *table;
1241	struct g_geom *gp;
1242	struct sbuf *sb;
1243	int error, recovered;
1244
1245	gp = gpp->gpp_geom;
1246	G_PART_TRACE((G_T_TOPOLOGY, "%s(%s)", __func__, gp->name));
1247	g_topology_assert();
1248	table = gp->softc;
1249	error = recovered = 0;
1250
1251	if (table->gpt_corrupt) {
1252		error = G_PART_RECOVER(table);
1253		if (error == 0)
1254			error = g_part_check_integrity(table,
1255			    LIST_FIRST(&gp->consumer));
1256		if (error) {
1257			gctl_error(req, "%d recovering '%s' failed",
1258			    error, gp->name);
1259			return (error);
1260		}
1261		recovered = 1;
1262	}
1263	/* Provide feedback if so requested. */
1264	if (gpp->gpp_parms & G_PART_PARM_OUTPUT) {
1265		sb = sbuf_new_auto();
1266		if (recovered)
1267			sbuf_printf(sb, "%s recovered\n", gp->name);
1268		else
1269			sbuf_printf(sb, "%s recovering is not needed\n",
1270			    gp->name);
1271		sbuf_finish(sb);
1272		gctl_set_param(req, "output", sbuf_data(sb), sbuf_len(sb) + 1);
1273		sbuf_delete(sb);
1274	}
1275	return (0);
1276}
1277
1278static int
1279g_part_ctl_resize(struct gctl_req *req, struct g_part_parms *gpp)
1280{
1281	struct g_geom *gp;
1282	struct g_provider *pp;
1283	struct g_part_entry *pe, *entry;
1284	struct g_part_table *table;
1285	struct sbuf *sb;
1286	quad_t end;
1287	int error;
1288	off_t mediasize;
1289
1290	gp = gpp->gpp_geom;
1291	G_PART_TRACE((G_T_TOPOLOGY, "%s(%s)", __func__, gp->name));
1292	g_topology_assert();
1293	table = gp->softc;
1294
1295	/* check gpp_index */
1296	LIST_FOREACH(entry, &table->gpt_entry, gpe_entry) {
1297		if (entry->gpe_deleted || entry->gpe_internal)
1298			continue;
1299		if (entry->gpe_index == gpp->gpp_index)
1300			break;
1301	}
1302	if (entry == NULL) {
1303		gctl_error(req, "%d index '%d'", ENOENT, gpp->gpp_index);
1304		return (ENOENT);
1305	}
1306
1307	/* check gpp_size */
1308	end = entry->gpe_start + gpp->gpp_size - 1;
1309	if (gpp->gpp_size < 1 || end > table->gpt_last) {
1310		gctl_error(req, "%d size '%jd'", EINVAL,
1311		    (intmax_t)gpp->gpp_size);
1312		return (EINVAL);
1313	}
1314
1315	LIST_FOREACH(pe, &table->gpt_entry, gpe_entry) {
1316		if (pe->gpe_deleted || pe->gpe_internal || pe == entry)
1317			continue;
1318		if (end >= pe->gpe_start && end <= pe->gpe_end) {
1319			gctl_error(req, "%d end '%jd'", ENOSPC,
1320			    (intmax_t)end);
1321			return (ENOSPC);
1322		}
1323		if (entry->gpe_start < pe->gpe_start && end > pe->gpe_end) {
1324			gctl_error(req, "%d size '%jd'", ENOSPC,
1325			    (intmax_t)gpp->gpp_size);
1326			return (ENOSPC);
1327		}
1328	}
1329
1330	pp = entry->gpe_pp;
1331	if ((g_debugflags & 16) == 0 &&
1332	    (pp->acr > 0 || pp->acw > 0 || pp->ace > 0)) {
1333		if (entry->gpe_end - entry->gpe_start + 1 > gpp->gpp_size) {
1334			/* Deny shrinking of an opened partition. */
1335			gctl_error(req, "%d", EBUSY);
1336			return (EBUSY);
1337		}
1338	}
1339
1340	error = G_PART_RESIZE(table, entry, gpp);
1341	if (error) {
1342		gctl_error(req, "%d%s", error, error != EBUSY ? "":
1343		    " resizing will lead to unexpected shrinking"
1344		    " due to alignment");
1345		return (error);
1346	}
1347
1348	if (!entry->gpe_created)
1349		entry->gpe_modified = 1;
1350
1351	/* update mediasize of changed provider */
1352	mediasize = (entry->gpe_end - entry->gpe_start + 1) *
1353		pp->sectorsize;
1354	g_resize_provider(pp, mediasize);
1355
1356	/* Provide feedback if so requested. */
1357	if (gpp->gpp_parms & G_PART_PARM_OUTPUT) {
1358		sb = sbuf_new_auto();
1359		G_PART_FULLNAME(table, entry, sb, gp->name);
1360		sbuf_cat(sb, " resized\n");
1361		sbuf_finish(sb);
1362		gctl_set_param(req, "output", sbuf_data(sb), sbuf_len(sb) + 1);
1363		sbuf_delete(sb);
1364	}
1365	return (0);
1366}
1367
1368static int
1369g_part_ctl_setunset(struct gctl_req *req, struct g_part_parms *gpp,
1370    unsigned int set)
1371{
1372	struct g_geom *gp;
1373	struct g_part_entry *entry;
1374	struct g_part_table *table;
1375	struct sbuf *sb;
1376	int error;
1377
1378	gp = gpp->gpp_geom;
1379	G_PART_TRACE((G_T_TOPOLOGY, "%s(%s)", __func__, gp->name));
1380	g_topology_assert();
1381
1382	table = gp->softc;
1383
1384	if (gpp->gpp_parms & G_PART_PARM_INDEX) {
1385		LIST_FOREACH(entry, &table->gpt_entry, gpe_entry) {
1386			if (entry->gpe_deleted || entry->gpe_internal)
1387				continue;
1388			if (entry->gpe_index == gpp->gpp_index)
1389				break;
1390		}
1391		if (entry == NULL) {
1392			gctl_error(req, "%d index '%d'", ENOENT,
1393			    gpp->gpp_index);
1394			return (ENOENT);
1395		}
1396	} else
1397		entry = NULL;
1398
1399	error = G_PART_SETUNSET(table, entry, gpp->gpp_attrib, set);
1400	if (error) {
1401		gctl_error(req, "%d attrib '%s'", error, gpp->gpp_attrib);
1402		return (error);
1403	}
1404
1405	/* Provide feedback if so requested. */
1406	if (gpp->gpp_parms & G_PART_PARM_OUTPUT) {
1407		sb = sbuf_new_auto();
1408		sbuf_printf(sb, "%s %sset on ", gpp->gpp_attrib,
1409		    (set) ? "" : "un");
1410		if (entry)
1411			G_PART_FULLNAME(table, entry, sb, gp->name);
1412		else
1413			sbuf_cat(sb, gp->name);
1414		sbuf_cat(sb, "\n");
1415		sbuf_finish(sb);
1416		gctl_set_param(req, "output", sbuf_data(sb), sbuf_len(sb) + 1);
1417		sbuf_delete(sb);
1418	}
1419	return (0);
1420}
1421
1422static int
1423g_part_ctl_undo(struct gctl_req *req, struct g_part_parms *gpp)
1424{
1425	struct g_consumer *cp;
1426	struct g_provider *pp;
1427	struct g_geom *gp;
1428	struct g_part_entry *entry, *tmp;
1429	struct g_part_table *table;
1430	int error, reprobe;
1431
1432	gp = gpp->gpp_geom;
1433	G_PART_TRACE((G_T_TOPOLOGY, "%s(%s)", __func__, gp->name));
1434	g_topology_assert();
1435
1436	table = gp->softc;
1437	if (!table->gpt_opened) {
1438		gctl_error(req, "%d", EPERM);
1439		return (EPERM);
1440	}
1441
1442	cp = LIST_FIRST(&gp->consumer);
1443	LIST_FOREACH_SAFE(entry, &table->gpt_entry, gpe_entry, tmp) {
1444		entry->gpe_modified = 0;
1445		if (entry->gpe_created) {
1446			pp = entry->gpe_pp;
1447			if (pp != NULL) {
1448				pp->private = NULL;
1449				entry->gpe_pp = NULL;
1450				g_wither_provider(pp, ENXIO);
1451			}
1452			entry->gpe_deleted = 1;
1453		}
1454		if (entry->gpe_deleted) {
1455			LIST_REMOVE(entry, gpe_entry);
1456			g_free(entry);
1457		}
1458	}
1459
1460	g_topology_unlock();
1461
1462	reprobe = (table->gpt_scheme == &g_part_null_scheme ||
1463	    table->gpt_created) ? 1 : 0;
1464
1465	if (reprobe) {
1466		LIST_FOREACH(entry, &table->gpt_entry, gpe_entry) {
1467			if (entry->gpe_internal)
1468				continue;
1469			error = EBUSY;
1470			goto fail;
1471		}
1472		while ((entry = LIST_FIRST(&table->gpt_entry)) != NULL) {
1473			LIST_REMOVE(entry, gpe_entry);
1474			g_free(entry);
1475		}
1476		error = g_part_probe(gp, cp, table->gpt_depth);
1477		if (error) {
1478			g_topology_lock();
1479			g_access(cp, -1, -1, -1);
1480			g_part_wither(gp, error);
1481			return (0);
1482		}
1483		table = gp->softc;
1484
1485		/*
1486		 * Synthesize a disk geometry. Some partitioning schemes
1487		 * depend on it and since some file systems need it even
1488		 * when the partitition scheme doesn't, we do it here in
1489		 * scheme-independent code.
1490		 */
1491		pp = cp->provider;
1492		g_part_geometry(table, cp, pp->mediasize / pp->sectorsize);
1493	}
1494
1495	error = G_PART_READ(table, cp);
1496	if (error)
1497		goto fail;
1498	error = g_part_check_integrity(table, cp);
1499	if (error)
1500		goto fail;
1501
1502	g_topology_lock();
1503	LIST_FOREACH(entry, &table->gpt_entry, gpe_entry) {
1504		if (!entry->gpe_internal)
1505			g_part_new_provider(gp, table, entry);
1506	}
1507
1508	table->gpt_opened = 0;
1509	g_access(cp, -1, -1, -1);
1510	return (0);
1511
1512fail:
1513	g_topology_lock();
1514	gctl_error(req, "%d", error);
1515	return (error);
1516}
1517
1518static void
1519g_part_wither(struct g_geom *gp, int error)
1520{
1521	struct g_part_entry *entry;
1522	struct g_part_table *table;
1523
1524	table = gp->softc;
1525	if (table != NULL) {
1526		G_PART_DESTROY(table, NULL);
1527		while ((entry = LIST_FIRST(&table->gpt_entry)) != NULL) {
1528			LIST_REMOVE(entry, gpe_entry);
1529			g_free(entry);
1530		}
1531		if (gp->softc != NULL) {
1532			kobj_delete((kobj_t)gp->softc, M_GEOM);
1533			gp->softc = NULL;
1534		}
1535	}
1536	g_wither_geom(gp, error);
1537}
1538
1539/*
1540 * Class methods.
1541 */
1542
1543static void
1544g_part_ctlreq(struct gctl_req *req, struct g_class *mp, const char *verb)
1545{
1546	struct g_part_parms gpp;
1547	struct g_part_table *table;
1548	struct gctl_req_arg *ap;
1549	enum g_part_ctl ctlreq;
1550	unsigned int i, mparms, oparms, parm;
1551	int auto_commit, close_on_error;
1552	int error, modifies;
1553
1554	G_PART_TRACE((G_T_TOPOLOGY, "%s(%s,%s)", __func__, mp->name, verb));
1555	g_topology_assert();
1556
1557	ctlreq = G_PART_CTL_NONE;
1558	modifies = 1;
1559	mparms = 0;
1560	oparms = G_PART_PARM_FLAGS | G_PART_PARM_OUTPUT | G_PART_PARM_VERSION;
1561	switch (*verb) {
1562	case 'a':
1563		if (!strcmp(verb, "add")) {
1564			ctlreq = G_PART_CTL_ADD;
1565			mparms |= G_PART_PARM_GEOM | G_PART_PARM_SIZE |
1566			    G_PART_PARM_START | G_PART_PARM_TYPE;
1567			oparms |= G_PART_PARM_INDEX | G_PART_PARM_LABEL;
1568		}
1569		break;
1570	case 'b':
1571		if (!strcmp(verb, "bootcode")) {
1572			ctlreq = G_PART_CTL_BOOTCODE;
1573			mparms |= G_PART_PARM_GEOM | G_PART_PARM_BOOTCODE;
1574		}
1575		break;
1576	case 'c':
1577		if (!strcmp(verb, "commit")) {
1578			ctlreq = G_PART_CTL_COMMIT;
1579			mparms |= G_PART_PARM_GEOM;
1580			modifies = 0;
1581		} else if (!strcmp(verb, "create")) {
1582			ctlreq = G_PART_CTL_CREATE;
1583			mparms |= G_PART_PARM_PROVIDER | G_PART_PARM_SCHEME;
1584			oparms |= G_PART_PARM_ENTRIES;
1585		}
1586		break;
1587	case 'd':
1588		if (!strcmp(verb, "delete")) {
1589			ctlreq = G_PART_CTL_DELETE;
1590			mparms |= G_PART_PARM_GEOM | G_PART_PARM_INDEX;
1591		} else if (!strcmp(verb, "destroy")) {
1592			ctlreq = G_PART_CTL_DESTROY;
1593			mparms |= G_PART_PARM_GEOM;
1594			oparms |= G_PART_PARM_FORCE;
1595		}
1596		break;
1597	case 'm':
1598		if (!strcmp(verb, "modify")) {
1599			ctlreq = G_PART_CTL_MODIFY;
1600			mparms |= G_PART_PARM_GEOM | G_PART_PARM_INDEX;
1601			oparms |= G_PART_PARM_LABEL | G_PART_PARM_TYPE;
1602		} else if (!strcmp(verb, "move")) {
1603			ctlreq = G_PART_CTL_MOVE;
1604			mparms |= G_PART_PARM_GEOM | G_PART_PARM_INDEX;
1605		}
1606		break;
1607	case 'r':
1608		if (!strcmp(verb, "recover")) {
1609			ctlreq = G_PART_CTL_RECOVER;
1610			mparms |= G_PART_PARM_GEOM;
1611		} else if (!strcmp(verb, "resize")) {
1612			ctlreq = G_PART_CTL_RESIZE;
1613			mparms |= G_PART_PARM_GEOM | G_PART_PARM_INDEX |
1614			    G_PART_PARM_SIZE;
1615		}
1616		break;
1617	case 's':
1618		if (!strcmp(verb, "set")) {
1619			ctlreq = G_PART_CTL_SET;
1620			mparms |= G_PART_PARM_ATTRIB | G_PART_PARM_GEOM;
1621			oparms |= G_PART_PARM_INDEX;
1622		}
1623		break;
1624	case 'u':
1625		if (!strcmp(verb, "undo")) {
1626			ctlreq = G_PART_CTL_UNDO;
1627			mparms |= G_PART_PARM_GEOM;
1628			modifies = 0;
1629		} else if (!strcmp(verb, "unset")) {
1630			ctlreq = G_PART_CTL_UNSET;
1631			mparms |= G_PART_PARM_ATTRIB | G_PART_PARM_GEOM;
1632			oparms |= G_PART_PARM_INDEX;
1633		}
1634		break;
1635	}
1636	if (ctlreq == G_PART_CTL_NONE) {
1637		gctl_error(req, "%d verb '%s'", EINVAL, verb);
1638		return;
1639	}
1640
1641	bzero(&gpp, sizeof(gpp));
1642	for (i = 0; i < req->narg; i++) {
1643		ap = &req->arg[i];
1644		parm = 0;
1645		switch (ap->name[0]) {
1646		case 'a':
1647			if (!strcmp(ap->name, "arg0")) {
1648				parm = mparms &
1649				    (G_PART_PARM_GEOM | G_PART_PARM_PROVIDER);
1650			}
1651			if (!strcmp(ap->name, "attrib"))
1652				parm = G_PART_PARM_ATTRIB;
1653			break;
1654		case 'b':
1655			if (!strcmp(ap->name, "bootcode"))
1656				parm = G_PART_PARM_BOOTCODE;
1657			break;
1658		case 'c':
1659			if (!strcmp(ap->name, "class"))
1660				continue;
1661			break;
1662		case 'e':
1663			if (!strcmp(ap->name, "entries"))
1664				parm = G_PART_PARM_ENTRIES;
1665			break;
1666		case 'f':
1667			if (!strcmp(ap->name, "flags"))
1668				parm = G_PART_PARM_FLAGS;
1669			else if (!strcmp(ap->name, "force"))
1670				parm = G_PART_PARM_FORCE;
1671			break;
1672		case 'i':
1673			if (!strcmp(ap->name, "index"))
1674				parm = G_PART_PARM_INDEX;
1675			break;
1676		case 'l':
1677			if (!strcmp(ap->name, "label"))
1678				parm = G_PART_PARM_LABEL;
1679			break;
1680		case 'o':
1681			if (!strcmp(ap->name, "output"))
1682				parm = G_PART_PARM_OUTPUT;
1683			break;
1684		case 's':
1685			if (!strcmp(ap->name, "scheme"))
1686				parm = G_PART_PARM_SCHEME;
1687			else if (!strcmp(ap->name, "size"))
1688				parm = G_PART_PARM_SIZE;
1689			else if (!strcmp(ap->name, "start"))
1690				parm = G_PART_PARM_START;
1691			break;
1692		case 't':
1693			if (!strcmp(ap->name, "type"))
1694				parm = G_PART_PARM_TYPE;
1695			break;
1696		case 'v':
1697			if (!strcmp(ap->name, "verb"))
1698				continue;
1699			else if (!strcmp(ap->name, "version"))
1700				parm = G_PART_PARM_VERSION;
1701			break;
1702		}
1703		if ((parm & (mparms | oparms)) == 0) {
1704			gctl_error(req, "%d param '%s'", EINVAL, ap->name);
1705			return;
1706		}
1707		switch (parm) {
1708		case G_PART_PARM_ATTRIB:
1709			error = g_part_parm_str(req, ap->name,
1710			    &gpp.gpp_attrib);
1711			break;
1712		case G_PART_PARM_BOOTCODE:
1713			error = g_part_parm_bootcode(req, ap->name,
1714			    &gpp.gpp_codeptr, &gpp.gpp_codesize);
1715			break;
1716		case G_PART_PARM_ENTRIES:
1717			error = g_part_parm_intmax(req, ap->name,
1718			    &gpp.gpp_entries);
1719			break;
1720		case G_PART_PARM_FLAGS:
1721			error = g_part_parm_str(req, ap->name, &gpp.gpp_flags);
1722			break;
1723		case G_PART_PARM_FORCE:
1724			error = g_part_parm_uint32(req, ap->name,
1725			    &gpp.gpp_force);
1726			break;
1727		case G_PART_PARM_GEOM:
1728			error = g_part_parm_geom(req, ap->name, &gpp.gpp_geom);
1729			break;
1730		case G_PART_PARM_INDEX:
1731			error = g_part_parm_intmax(req, ap->name,
1732			    &gpp.gpp_index);
1733			break;
1734		case G_PART_PARM_LABEL:
1735			error = g_part_parm_str(req, ap->name, &gpp.gpp_label);
1736			break;
1737		case G_PART_PARM_OUTPUT:
1738			error = 0;	/* Write-only parameter */
1739			break;
1740		case G_PART_PARM_PROVIDER:
1741			error = g_part_parm_provider(req, ap->name,
1742			    &gpp.gpp_provider);
1743			break;
1744		case G_PART_PARM_SCHEME:
1745			error = g_part_parm_scheme(req, ap->name,
1746			    &gpp.gpp_scheme);
1747			break;
1748		case G_PART_PARM_SIZE:
1749			error = g_part_parm_quad(req, ap->name, &gpp.gpp_size);
1750			break;
1751		case G_PART_PARM_START:
1752			error = g_part_parm_quad(req, ap->name,
1753			    &gpp.gpp_start);
1754			break;
1755		case G_PART_PARM_TYPE:
1756			error = g_part_parm_str(req, ap->name, &gpp.gpp_type);
1757			break;
1758		case G_PART_PARM_VERSION:
1759			error = g_part_parm_uint32(req, ap->name,
1760			    &gpp.gpp_version);
1761			break;
1762		default:
1763			error = EDOOFUS;
1764			gctl_error(req, "%d %s", error, ap->name);
1765			break;
1766		}
1767		if (error != 0) {
1768			if (error == ENOATTR) {
1769				gctl_error(req, "%d param '%s'", error,
1770				    ap->name);
1771			}
1772			return;
1773		}
1774		gpp.gpp_parms |= parm;
1775	}
1776	if ((gpp.gpp_parms & mparms) != mparms) {
1777		parm = mparms - (gpp.gpp_parms & mparms);
1778		gctl_error(req, "%d param '%x'", ENOATTR, parm);
1779		return;
1780	}
1781
1782	/* Obtain permissions if possible/necessary. */
1783	close_on_error = 0;
1784	table = NULL;
1785	if (modifies && (gpp.gpp_parms & G_PART_PARM_GEOM)) {
1786		table = gpp.gpp_geom->softc;
1787		if (table != NULL && table->gpt_corrupt &&
1788		    ctlreq != G_PART_CTL_DESTROY &&
1789		    ctlreq != G_PART_CTL_RECOVER) {
1790			gctl_error(req, "%d table '%s' is corrupt",
1791			    EPERM, gpp.gpp_geom->name);
1792			return;
1793		}
1794		if (table != NULL && !table->gpt_opened) {
1795			error = g_access(LIST_FIRST(&gpp.gpp_geom->consumer),
1796			    1, 1, 1);
1797			if (error) {
1798				gctl_error(req, "%d geom '%s'", error,
1799				    gpp.gpp_geom->name);
1800				return;
1801			}
1802			table->gpt_opened = 1;
1803			close_on_error = 1;
1804		}
1805	}
1806
1807	/* Allow the scheme to check or modify the parameters. */
1808	if (table != NULL) {
1809		error = G_PART_PRECHECK(table, ctlreq, &gpp);
1810		if (error) {
1811			gctl_error(req, "%d pre-check failed", error);
1812			goto out;
1813		}
1814	} else
1815		error = EDOOFUS;	/* Prevent bogus uninit. warning. */
1816
1817	switch (ctlreq) {
1818	case G_PART_CTL_NONE:
1819		panic("%s", __func__);
1820	case G_PART_CTL_ADD:
1821		error = g_part_ctl_add(req, &gpp);
1822		break;
1823	case G_PART_CTL_BOOTCODE:
1824		error = g_part_ctl_bootcode(req, &gpp);
1825		break;
1826	case G_PART_CTL_COMMIT:
1827		error = g_part_ctl_commit(req, &gpp);
1828		break;
1829	case G_PART_CTL_CREATE:
1830		error = g_part_ctl_create(req, &gpp);
1831		break;
1832	case G_PART_CTL_DELETE:
1833		error = g_part_ctl_delete(req, &gpp);
1834		break;
1835	case G_PART_CTL_DESTROY:
1836		error = g_part_ctl_destroy(req, &gpp);
1837		break;
1838	case G_PART_CTL_MODIFY:
1839		error = g_part_ctl_modify(req, &gpp);
1840		break;
1841	case G_PART_CTL_MOVE:
1842		error = g_part_ctl_move(req, &gpp);
1843		break;
1844	case G_PART_CTL_RECOVER:
1845		error = g_part_ctl_recover(req, &gpp);
1846		break;
1847	case G_PART_CTL_RESIZE:
1848		error = g_part_ctl_resize(req, &gpp);
1849		break;
1850	case G_PART_CTL_SET:
1851		error = g_part_ctl_setunset(req, &gpp, 1);
1852		break;
1853	case G_PART_CTL_UNDO:
1854		error = g_part_ctl_undo(req, &gpp);
1855		break;
1856	case G_PART_CTL_UNSET:
1857		error = g_part_ctl_setunset(req, &gpp, 0);
1858		break;
1859	}
1860
1861	/* Implement automatic commit. */
1862	if (!error) {
1863		auto_commit = (modifies &&
1864		    (gpp.gpp_parms & G_PART_PARM_FLAGS) &&
1865		    strchr(gpp.gpp_flags, 'C') != NULL) ? 1 : 0;
1866		if (auto_commit) {
1867			KASSERT(gpp.gpp_parms & G_PART_PARM_GEOM, ("%s",
1868			    __func__));
1869			error = g_part_ctl_commit(req, &gpp);
1870		}
1871	}
1872
1873 out:
1874	if (error && close_on_error) {
1875		g_access(LIST_FIRST(&gpp.gpp_geom->consumer), -1, -1, -1);
1876		table->gpt_opened = 0;
1877	}
1878}
1879
1880static int
1881g_part_destroy_geom(struct gctl_req *req, struct g_class *mp,
1882    struct g_geom *gp)
1883{
1884
1885	G_PART_TRACE((G_T_TOPOLOGY, "%s(%s,%s)", __func__, mp->name, gp->name));
1886	g_topology_assert();
1887
1888	g_part_wither(gp, EINVAL);
1889	return (0);
1890}
1891
1892static struct g_geom *
1893g_part_taste(struct g_class *mp, struct g_provider *pp, int flags __unused)
1894{
1895	struct g_consumer *cp;
1896	struct g_geom *gp;
1897	struct g_part_entry *entry;
1898	struct g_part_table *table;
1899	struct root_hold_token *rht;
1900	int attr, depth;
1901	int error;
1902
1903	G_PART_TRACE((G_T_TOPOLOGY, "%s(%s,%s)", __func__, mp->name, pp->name));
1904	g_topology_assert();
1905
1906	/* Skip providers that are already open for writing. */
1907	if (pp->acw > 0)
1908		return (NULL);
1909
1910	/*
1911	 * Create a GEOM with consumer and hook it up to the provider.
1912	 * With that we become part of the topology. Optain read access
1913	 * to the provider.
1914	 */
1915	gp = g_new_geomf(mp, "%s", pp->name);
1916	cp = g_new_consumer(gp);
1917	cp->flags |= G_CF_DIRECT_SEND | G_CF_DIRECT_RECEIVE;
1918	error = g_attach(cp, pp);
1919	if (error == 0)
1920		error = g_access(cp, 1, 0, 0);
1921	if (error != 0) {
1922		if (cp->provider)
1923			g_detach(cp);
1924		g_destroy_consumer(cp);
1925		g_destroy_geom(gp);
1926		return (NULL);
1927	}
1928
1929	rht = root_mount_hold(mp->name);
1930	g_topology_unlock();
1931
1932	/*
1933	 * Short-circuit the whole probing galore when there's no
1934	 * media present.
1935	 */
1936	if (pp->mediasize == 0 || pp->sectorsize == 0) {
1937		error = ENODEV;
1938		goto fail;
1939	}
1940
1941	/* Make sure we can nest and if so, determine our depth. */
1942	error = g_getattr("PART::isleaf", cp, &attr);
1943	if (!error && attr) {
1944		error = ENODEV;
1945		goto fail;
1946	}
1947	error = g_getattr("PART::depth", cp, &attr);
1948	depth = (!error) ? attr + 1 : 0;
1949
1950	error = g_part_probe(gp, cp, depth);
1951	if (error)
1952		goto fail;
1953
1954	table = gp->softc;
1955
1956	/*
1957	 * Synthesize a disk geometry. Some partitioning schemes
1958	 * depend on it and since some file systems need it even
1959	 * when the partitition scheme doesn't, we do it here in
1960	 * scheme-independent code.
1961	 */
1962	g_part_geometry(table, cp, pp->mediasize / pp->sectorsize);
1963
1964	error = G_PART_READ(table, cp);
1965	if (error)
1966		goto fail;
1967	error = g_part_check_integrity(table, cp);
1968	if (error)
1969		goto fail;
1970
1971	g_topology_lock();
1972	LIST_FOREACH(entry, &table->gpt_entry, gpe_entry) {
1973		if (!entry->gpe_internal)
1974			g_part_new_provider(gp, table, entry);
1975	}
1976
1977	root_mount_rel(rht);
1978	g_access(cp, -1, 0, 0);
1979	return (gp);
1980
1981 fail:
1982	g_topology_lock();
1983	root_mount_rel(rht);
1984	g_access(cp, -1, 0, 0);
1985	g_detach(cp);
1986	g_destroy_consumer(cp);
1987	g_destroy_geom(gp);
1988	return (NULL);
1989}
1990
1991/*
1992 * Geom methods.
1993 */
1994
1995static int
1996g_part_access(struct g_provider *pp, int dr, int dw, int de)
1997{
1998	struct g_consumer *cp;
1999
2000	G_PART_TRACE((G_T_ACCESS, "%s(%s,%d,%d,%d)", __func__, pp->name, dr,
2001	    dw, de));
2002
2003	cp = LIST_FIRST(&pp->geom->consumer);
2004
2005	/* We always gain write-exclusive access. */
2006	return (g_access(cp, dr, dw, dw + de));
2007}
2008
2009static void
2010g_part_dumpconf(struct sbuf *sb, const char *indent, struct g_geom *gp,
2011    struct g_consumer *cp, struct g_provider *pp)
2012{
2013	char buf[64];
2014	struct g_part_entry *entry;
2015	struct g_part_table *table;
2016
2017	KASSERT(sb != NULL && gp != NULL, ("%s", __func__));
2018	table = gp->softc;
2019
2020	if (indent == NULL) {
2021		KASSERT(cp == NULL && pp != NULL, ("%s", __func__));
2022		entry = pp->private;
2023		if (entry == NULL)
2024			return;
2025		sbuf_printf(sb, " i %u o %ju ty %s", entry->gpe_index,
2026		    (uintmax_t)entry->gpe_offset,
2027		    G_PART_TYPE(table, entry, buf, sizeof(buf)));
2028		/*
2029		 * libdisk compatibility quirk - the scheme dumps the
2030		 * slicer name and partition type in a way that is
2031		 * compatible with libdisk. When libdisk is not used
2032		 * anymore, this should go away.
2033		 */
2034		G_PART_DUMPCONF(table, entry, sb, indent);
2035	} else if (cp != NULL) {	/* Consumer configuration. */
2036		KASSERT(pp == NULL, ("%s", __func__));
2037		/* none */
2038	} else if (pp != NULL) {	/* Provider configuration. */
2039		entry = pp->private;
2040		if (entry == NULL)
2041			return;
2042		sbuf_printf(sb, "%s<start>%ju</start>\n", indent,
2043		    (uintmax_t)entry->gpe_start);
2044		sbuf_printf(sb, "%s<end>%ju</end>\n", indent,
2045		    (uintmax_t)entry->gpe_end);
2046		sbuf_printf(sb, "%s<index>%u</index>\n", indent,
2047		    entry->gpe_index);
2048		sbuf_printf(sb, "%s<type>%s</type>\n", indent,
2049		    G_PART_TYPE(table, entry, buf, sizeof(buf)));
2050		sbuf_printf(sb, "%s<offset>%ju</offset>\n", indent,
2051		    (uintmax_t)entry->gpe_offset);
2052		sbuf_printf(sb, "%s<length>%ju</length>\n", indent,
2053		    (uintmax_t)pp->mediasize);
2054		G_PART_DUMPCONF(table, entry, sb, indent);
2055	} else {			/* Geom configuration. */
2056		sbuf_printf(sb, "%s<scheme>%s</scheme>\n", indent,
2057		    table->gpt_scheme->name);
2058		sbuf_printf(sb, "%s<entries>%u</entries>\n", indent,
2059		    table->gpt_entries);
2060		sbuf_printf(sb, "%s<first>%ju</first>\n", indent,
2061		    (uintmax_t)table->gpt_first);
2062		sbuf_printf(sb, "%s<last>%ju</last>\n", indent,
2063		    (uintmax_t)table->gpt_last);
2064		sbuf_printf(sb, "%s<fwsectors>%u</fwsectors>\n", indent,
2065		    table->gpt_sectors);
2066		sbuf_printf(sb, "%s<fwheads>%u</fwheads>\n", indent,
2067		    table->gpt_heads);
2068		sbuf_printf(sb, "%s<state>%s</state>\n", indent,
2069		    table->gpt_corrupt ? "CORRUPT": "OK");
2070		sbuf_printf(sb, "%s<modified>%s</modified>\n", indent,
2071		    table->gpt_opened ? "true": "false");
2072		G_PART_DUMPCONF(table, NULL, sb, indent);
2073	}
2074}
2075
2076/*-
2077 * This start routine is only called for non-trivial requests, all the
2078 * trivial ones are handled autonomously by the slice code.
2079 * For requests we handle here, we must call the g_io_deliver() on the
2080 * bio, and return non-zero to indicate to the slice code that we did so.
2081 * This code executes in the "DOWN" I/O path, this means:
2082 *    * No sleeping.
2083 *    * Don't grab the topology lock.
2084 *    * Don't call biowait, g_getattr(), g_setattr() or g_read_data()
2085 */
2086static int
2087g_part_ioctl(struct g_provider *pp, u_long cmd, void *data, int fflag, struct thread *td)
2088{
2089	struct g_part_table *table;
2090
2091	table = pp->geom->softc;
2092	return G_PART_IOCTL(table, pp, cmd, data, fflag, td);
2093}
2094
2095static void
2096g_part_resize(struct g_consumer *cp)
2097{
2098	struct g_part_table *table;
2099
2100	G_PART_TRACE((G_T_TOPOLOGY, "%s(%s)", __func__, cp->provider->name));
2101	g_topology_assert();
2102
2103	table = cp->geom->softc;
2104	if (table->gpt_opened == 0) {
2105		if (g_access(cp, 1, 1, 1) != 0)
2106			return;
2107		table->gpt_opened = 1;
2108	}
2109	if (G_PART_RESIZE(table, NULL, NULL) == 0)
2110		printf("GEOM_PART: %s was automatically resized.\n"
2111		    "  Use `gpart commit %s` to save changes or "
2112		    "`gpart undo %s` to revert them.\n", cp->geom->name,
2113		    cp->geom->name, cp->geom->name);
2114	if (g_part_check_integrity(table, cp) != 0) {
2115		g_access(cp, -1, -1, -1);
2116		table->gpt_opened = 0;
2117		g_part_wither(table->gpt_gp, ENXIO);
2118	}
2119}
2120
2121static void
2122g_part_orphan(struct g_consumer *cp)
2123{
2124	struct g_provider *pp;
2125	struct g_part_table *table;
2126
2127	pp = cp->provider;
2128	KASSERT(pp != NULL, ("%s", __func__));
2129	G_PART_TRACE((G_T_TOPOLOGY, "%s(%s)", __func__, pp->name));
2130	g_topology_assert();
2131
2132	KASSERT(pp->error != 0, ("%s", __func__));
2133	table = cp->geom->softc;
2134	if (table != NULL && table->gpt_opened)
2135		g_access(cp, -1, -1, -1);
2136	g_part_wither(cp->geom, pp->error);
2137}
2138
2139static void
2140g_part_spoiled(struct g_consumer *cp)
2141{
2142
2143	G_PART_TRACE((G_T_TOPOLOGY, "%s(%s)", __func__, cp->provider->name));
2144	g_topology_assert();
2145
2146	cp->flags |= G_CF_ORPHAN;
2147	g_part_wither(cp->geom, ENXIO);
2148}
2149
2150static void
2151g_part_start(struct bio *bp)
2152{
2153	struct bio *bp2;
2154	struct g_consumer *cp;
2155	struct g_geom *gp;
2156	struct g_part_entry *entry;
2157	struct g_part_table *table;
2158	struct g_kerneldump *gkd;
2159	struct g_provider *pp;
2160	char buf[64];
2161
2162	pp = bp->bio_to;
2163	gp = pp->geom;
2164	table = gp->softc;
2165	cp = LIST_FIRST(&gp->consumer);
2166
2167	G_PART_TRACE((G_T_BIO, "%s: cmd=%d, provider=%s", __func__, bp->bio_cmd,
2168	    pp->name));
2169
2170	entry = pp->private;
2171	if (entry == NULL) {
2172		g_io_deliver(bp, ENXIO);
2173		return;
2174	}
2175
2176	switch(bp->bio_cmd) {
2177	case BIO_DELETE:
2178	case BIO_READ:
2179	case BIO_WRITE:
2180		if (bp->bio_offset >= pp->mediasize) {
2181			g_io_deliver(bp, EIO);
2182			return;
2183		}
2184		bp2 = g_clone_bio(bp);
2185		if (bp2 == NULL) {
2186			g_io_deliver(bp, ENOMEM);
2187			return;
2188		}
2189		if (bp2->bio_offset + bp2->bio_length > pp->mediasize)
2190			bp2->bio_length = pp->mediasize - bp2->bio_offset;
2191		bp2->bio_done = g_std_done;
2192		bp2->bio_offset += entry->gpe_offset;
2193		g_io_request(bp2, cp);
2194		return;
2195	case BIO_FLUSH:
2196		break;
2197	case BIO_GETATTR:
2198		if (g_handleattr_int(bp, "GEOM::fwheads", table->gpt_heads))
2199			return;
2200		if (g_handleattr_int(bp, "GEOM::fwsectors", table->gpt_sectors))
2201			return;
2202		if (g_handleattr_int(bp, "PART::isleaf", table->gpt_isleaf))
2203			return;
2204		if (g_handleattr_int(bp, "PART::depth", table->gpt_depth))
2205			return;
2206		if (g_handleattr_str(bp, "PART::scheme",
2207		    table->gpt_scheme->name))
2208			return;
2209		if (g_handleattr_str(bp, "PART::type",
2210		    G_PART_TYPE(table, entry, buf, sizeof(buf))))
2211			return;
2212		if (!strcmp("GEOM::kerneldump", bp->bio_attribute)) {
2213			/*
2214			 * Check that the partition is suitable for kernel
2215			 * dumps. Typically only swap partitions should be
2216			 * used. If the request comes from the nested scheme
2217			 * we allow dumping there as well.
2218			 */
2219			if ((bp->bio_from == NULL ||
2220			    bp->bio_from->geom->class != &g_part_class) &&
2221			    G_PART_DUMPTO(table, entry) == 0) {
2222				g_io_deliver(bp, ENODEV);
2223				printf("GEOM_PART: Partition '%s' not suitable"
2224				    " for kernel dumps (wrong type?)\n",
2225				    pp->name);
2226				return;
2227			}
2228			gkd = (struct g_kerneldump *)bp->bio_data;
2229			if (gkd->offset >= pp->mediasize) {
2230				g_io_deliver(bp, EIO);
2231				return;
2232			}
2233			if (gkd->offset + gkd->length > pp->mediasize)
2234				gkd->length = pp->mediasize - gkd->offset;
2235			gkd->offset += entry->gpe_offset;
2236		}
2237		break;
2238	default:
2239		g_io_deliver(bp, EOPNOTSUPP);
2240		return;
2241	}
2242
2243	bp2 = g_clone_bio(bp);
2244	if (bp2 == NULL) {
2245		g_io_deliver(bp, ENOMEM);
2246		return;
2247	}
2248	bp2->bio_done = g_std_done;
2249	g_io_request(bp2, cp);
2250}
2251
2252static void
2253g_part_init(struct g_class *mp)
2254{
2255
2256	TAILQ_INSERT_HEAD(&g_part_schemes, &g_part_null_scheme, scheme_list);
2257}
2258
2259static void
2260g_part_fini(struct g_class *mp)
2261{
2262
2263	TAILQ_REMOVE(&g_part_schemes, &g_part_null_scheme, scheme_list);
2264}
2265
2266static void
2267g_part_unload_event(void *arg, int flag)
2268{
2269	struct g_consumer *cp;
2270	struct g_geom *gp;
2271	struct g_provider *pp;
2272	struct g_part_scheme *scheme;
2273	struct g_part_table *table;
2274	uintptr_t *xchg;
2275	int acc, error;
2276
2277	if (flag == EV_CANCEL)
2278		return;
2279
2280	xchg = arg;
2281	error = 0;
2282	scheme = (void *)(*xchg);
2283
2284	g_topology_assert();
2285
2286	LIST_FOREACH(gp, &g_part_class.geom, geom) {
2287		table = gp->softc;
2288		if (table->gpt_scheme != scheme)
2289			continue;
2290
2291		acc = 0;
2292		LIST_FOREACH(pp, &gp->provider, provider)
2293			acc += pp->acr + pp->acw + pp->ace;
2294		LIST_FOREACH(cp, &gp->consumer, consumer)
2295			acc += cp->acr + cp->acw + cp->ace;
2296
2297		if (!acc)
2298			g_part_wither(gp, ENOSYS);
2299		else
2300			error = EBUSY;
2301	}
2302
2303	if (!error)
2304		TAILQ_REMOVE(&g_part_schemes, scheme, scheme_list);
2305
2306	*xchg = error;
2307}
2308
2309int
2310g_part_modevent(module_t mod, int type, struct g_part_scheme *scheme)
2311{
2312	struct g_part_scheme *iter;
2313	uintptr_t arg;
2314	int error;
2315
2316	error = 0;
2317	switch (type) {
2318	case MOD_LOAD:
2319		TAILQ_FOREACH(iter, &g_part_schemes, scheme_list) {
2320			if (scheme == iter) {
2321				printf("GEOM_PART: scheme %s is already "
2322				    "registered!\n", scheme->name);
2323				break;
2324			}
2325		}
2326		if (iter == NULL) {
2327			TAILQ_INSERT_TAIL(&g_part_schemes, scheme,
2328			    scheme_list);
2329			g_retaste(&g_part_class);
2330		}
2331		break;
2332	case MOD_UNLOAD:
2333		arg = (uintptr_t)scheme;
2334		error = g_waitfor_event(g_part_unload_event, &arg, M_WAITOK,
2335		    NULL);
2336		if (error == 0)
2337			error = arg;
2338		break;
2339	default:
2340		error = EOPNOTSUPP;
2341		break;
2342	}
2343
2344	return (error);
2345}
2346