g_part.c revision 267357
1118611Snjl/*-
2118611Snjl * Copyright (c) 2002, 2005-2009 Marcel Moolenaar
3118611Snjl * All rights reserved.
4118611Snjl *
5118611Snjl * Redistribution and use in source and binary forms, with or without
6118611Snjl * modification, are permitted provided that the following conditions
7118611Snjl * are met:
8217365Sjkim *
9229989Sjkim * 1. Redistributions of source code must retain the above copyright
10118611Snjl *    notice, this list of conditions and the following disclaimer.
11118611Snjl * 2. Redistributions in binary form must reproduce the above copyright
12217365Sjkim *    notice, this list of conditions and the following disclaimer in the
13217365Sjkim *    documentation and/or other materials provided with the distribution.
14217365Sjkim *
15217365Sjkim * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16217365Sjkim * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17217365Sjkim * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18217365Sjkim * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19217365Sjkim * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20217365Sjkim * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21217365Sjkim * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22217365Sjkim * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23217365Sjkim * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24217365Sjkim * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25217365Sjkim */
26118611Snjl
27217365Sjkim#include <sys/cdefs.h>
28217365Sjkim__FBSDID("$FreeBSD: head/sys/geom/part/g_part.c 267357 2014-06-11 10:19:11Z ae $");
29217365Sjkim
30118611Snjl#include <sys/param.h>
31217365Sjkim#include <sys/bio.h>
32217365Sjkim#include <sys/endian.h>
33217365Sjkim#include <sys/kernel.h>
34217365Sjkim#include <sys/kobj.h>
35217365Sjkim#include <sys/limits.h>
36217365Sjkim#include <sys/lock.h>
37217365Sjkim#include <sys/malloc.h>
38217365Sjkim#include <sys/mutex.h>
39217365Sjkim#include <sys/queue.h>
40217365Sjkim#include <sys/sbuf.h>
41217365Sjkim#include <sys/sysctl.h>
42217365Sjkim#include <sys/systm.h>
43217365Sjkim#include <sys/uuid.h>
44118611Snjl#include <geom/geom.h>
45118611Snjl#include <geom/geom_ctl.h>
46151937Sjkim#include <geom/geom_int.h>
47118611Snjl#include <geom/part/g_part.h>
48228110Sjkim
49218590Sjkim#include "g_part_if.h"
50118611Snjl
51118611Snjl#ifndef _PATH_DEV
52118611Snjl#define _PATH_DEV "/dev/"
53118611Snjl#endif
54151937Sjkim
55118611Snjlstatic kobj_method_t g_part_null_methods[] = {
56151937Sjkim	{ 0, 0 }
57151937Sjkim};
58151937Sjkim
59151937Sjkimstatic struct g_part_scheme g_part_null_scheme = {
60151937Sjkim	"(none)",
61151937Sjkim	g_part_null_methods,
62151937Sjkim	sizeof(struct g_part_table),
63151937Sjkim};
64151937Sjkim
65118611SnjlTAILQ_HEAD(, g_part_scheme) g_part_schemes =
66118611Snjl    TAILQ_HEAD_INITIALIZER(g_part_schemes);
67118611Snjl
68118611Snjlstruct g_part_alias_list {
69118611Snjl	const char *lexeme;
70118611Snjl	enum g_part_alias alias;
71239340Sjkim} g_part_alias_list[G_PART_ALIAS_COUNT] = {
72118611Snjl	{ "apple-boot", G_PART_ALIAS_APPLE_BOOT },
73239340Sjkim	{ "apple-hfs", G_PART_ALIAS_APPLE_HFS },
74118611Snjl	{ "apple-label", G_PART_ALIAS_APPLE_LABEL },
75118611Snjl	{ "apple-raid", G_PART_ALIAS_APPLE_RAID },
76118611Snjl	{ "apple-raid-offline", G_PART_ALIAS_APPLE_RAID_OFFLINE },
77118611Snjl	{ "apple-tv-recovery", G_PART_ALIAS_APPLE_TV_RECOVERY },
78118611Snjl	{ "apple-ufs", G_PART_ALIAS_APPLE_UFS },
79151937Sjkim	{ "bios-boot", G_PART_ALIAS_BIOS_BOOT },
80151937Sjkim	{ "ebr", G_PART_ALIAS_EBR },
81151937Sjkim	{ "efi", G_PART_ALIAS_EFI },
82118611Snjl	{ "fat16", G_PART_ALIAS_MS_FAT16 },
83118611Snjl	{ "fat32", G_PART_ALIAS_MS_FAT32 },
84118611Snjl	{ "freebsd", G_PART_ALIAS_FREEBSD },
85118611Snjl	{ "freebsd-boot", G_PART_ALIAS_FREEBSD_BOOT },
86151937Sjkim	{ "freebsd-nandfs", G_PART_ALIAS_FREEBSD_NANDFS },
87151937Sjkim	{ "freebsd-swap", G_PART_ALIAS_FREEBSD_SWAP },
88118611Snjl	{ "freebsd-ufs", G_PART_ALIAS_FREEBSD_UFS },
89118611Snjl	{ "freebsd-vinum", G_PART_ALIAS_FREEBSD_VINUM },
90118611Snjl	{ "freebsd-zfs", G_PART_ALIAS_FREEBSD_ZFS },
91118611Snjl	{ "linux-data", G_PART_ALIAS_LINUX_DATA },
92118611Snjl	{ "linux-lvm", G_PART_ALIAS_LINUX_LVM },
93118611Snjl	{ "linux-raid", G_PART_ALIAS_LINUX_RAID },
94118611Snjl	{ "linux-swap", G_PART_ALIAS_LINUX_SWAP },
95118611Snjl	{ "mbr", G_PART_ALIAS_MBR },
96118611Snjl	{ "ms-basic-data", G_PART_ALIAS_MS_BASIC_DATA },
97118611Snjl	{ "ms-ldm-data", G_PART_ALIAS_MS_LDM_DATA },
98118611Snjl	{ "ms-ldm-metadata", G_PART_ALIAS_MS_LDM_METADATA },
99118611Snjl	{ "ms-reserved", G_PART_ALIAS_MS_RESERVED },
100118611Snjl	{ "ntfs", G_PART_ALIAS_MS_NTFS },
101239340Sjkim	{ "netbsd-ccd", G_PART_ALIAS_NETBSD_CCD },
102118611Snjl	{ "netbsd-cgd", G_PART_ALIAS_NETBSD_CGD },
103118611Snjl	{ "netbsd-ffs", G_PART_ALIAS_NETBSD_FFS },
104118611Snjl	{ "netbsd-lfs", G_PART_ALIAS_NETBSD_LFS },
105118611Snjl	{ "netbsd-raid", G_PART_ALIAS_NETBSD_RAID },
106118611Snjl	{ "netbsd-swap", G_PART_ALIAS_NETBSD_SWAP },
107118611Snjl	{ "vmware-vmfs", G_PART_ALIAS_VMFS },
108118611Snjl	{ "vmware-vmkdiag", G_PART_ALIAS_VMKDIAG },
109118611Snjl	{ "vmware-reserved", G_PART_ALIAS_VMRESERVED },
110118611Snjl	{ "vmware-vsanhdr", G_PART_ALIAS_VMVSANHDR },
111118611Snjl	{ "dragonfly-label32", G_PART_ALIAS_DFBSD },
112118611Snjl	{ "dragonfly-label64", G_PART_ALIAS_DFBSD64 },
113118611Snjl	{ "dragonfly-swap", G_PART_ALIAS_DFBSD_SWAP },
114118611Snjl	{ "dragonfly-ufs", G_PART_ALIAS_DFBSD_UFS },
115118611Snjl	{ "dragonfly-vinum", G_PART_ALIAS_DFBSD_VINUM },
116118611Snjl	{ "dragonfly-ccd", G_PART_ALIAS_DFBSD_CCD },
117118611Snjl	{ "dragonfly-legacy", G_PART_ALIAS_DFBSD_LEGACY },
118118611Snjl	{ "dragonfly-hammer", G_PART_ALIAS_DFBSD_HAMMER },
119118611Snjl	{ "dragonfly-hammer2", G_PART_ALIAS_DFBSD_HAMMER2 },
120118611Snjl};
121118611Snjl
122118611SnjlSYSCTL_DECL(_kern_geom);
123118611SnjlSYSCTL_NODE(_kern_geom, OID_AUTO, part, CTLFLAG_RW, 0,
124118611Snjl    "GEOM_PART stuff");
125118611Snjlstatic u_int check_integrity = 1;
126118611SnjlTUNABLE_INT("kern.geom.part.check_integrity", &check_integrity);
127118611SnjlSYSCTL_UINT(_kern_geom_part, OID_AUTO, check_integrity,
128118611Snjl    CTLFLAG_RW | CTLFLAG_TUN, &check_integrity, 1,
129118611Snjl    "Enable integrity checking");
130118611Snjl
131118611Snjl/*
132118611Snjl * The GEOM partitioning class.
133118611Snjl */
134118611Snjlstatic g_ctl_req_t g_part_ctlreq;
135118611Snjlstatic g_ctl_destroy_geom_t g_part_destroy_geom;
136239340Sjkimstatic g_fini_t g_part_fini;
137118611Snjlstatic g_init_t g_part_init;
138118611Snjlstatic g_taste_t g_part_taste;
139118611Snjl
140118611Snjlstatic g_access_t g_part_access;
141118611Snjlstatic g_dumpconf_t g_part_dumpconf;
142118611Snjlstatic g_orphan_t g_part_orphan;
143118611Snjlstatic g_spoiled_t g_part_spoiled;
144118611Snjlstatic g_start_t g_part_start;
145118611Snjlstatic g_resize_t g_part_resize;
146118611Snjl
147118611Snjlstatic struct g_class g_part_class = {
148118611Snjl	.name = "PART",
149118611Snjl	.version = G_VERSION,
150118611Snjl	/* Class methods. */
151118611Snjl	.ctlreq = g_part_ctlreq,
152118611Snjl	.destroy_geom = g_part_destroy_geom,
153118611Snjl	.fini = g_part_fini,
154118611Snjl	.init = g_part_init,
155118611Snjl	.taste = g_part_taste,
156118611Snjl	/* Geom methods. */
157118611Snjl	.access = g_part_access,
158118611Snjl	.dumpconf = g_part_dumpconf,
159239340Sjkim	.orphan = g_part_orphan,
160118611Snjl	.spoiled = g_part_spoiled,
161239340Sjkim	.start = g_part_start,
162118611Snjl	.resize = g_part_resize
163118611Snjl};
164118611Snjl
165118611SnjlDECLARE_GEOM_CLASS(g_part_class, g_part);
166118611SnjlMODULE_VERSION(g_part, 0);
167118611Snjl
168118611Snjl/*
169118611Snjl * Support functions.
170118611Snjl */
171118611Snjl
172118611Snjlstatic void g_part_wither(struct g_geom *, int);
173118611Snjl
174118611Snjlconst char *
175118611Snjlg_part_alias_name(enum g_part_alias alias)
176118611Snjl{
177118611Snjl	int i;
178118611Snjl
179118611Snjl	for (i = 0; i < G_PART_ALIAS_COUNT; i++) {
180118611Snjl		if (g_part_alias_list[i].alias != alias)
181118611Snjl			continue;
182118611Snjl		return (g_part_alias_list[i].lexeme);
183118611Snjl	}
184118611Snjl
185118611Snjl	return (NULL);
186118611Snjl}
187118611Snjl
188118611Snjlvoid
189239340Sjkimg_part_geometry_heads(off_t blocks, u_int sectors, off_t *bestchs,
190118611Snjl    u_int *bestheads)
191118611Snjl{
192118611Snjl	static u_int candidate_heads[] = { 1, 2, 16, 32, 64, 128, 255, 0 };
193239340Sjkim	off_t chs, cylinders;
194118611Snjl	u_int heads;
195118611Snjl	int idx;
196118611Snjl
197239340Sjkim	*bestchs = 0;
198118611Snjl	*bestheads = 0;
199118611Snjl	for (idx = 0; candidate_heads[idx] != 0; idx++) {
200239340Sjkim		heads = candidate_heads[idx];
201239340Sjkim		cylinders = blocks / heads / sectors;
202118611Snjl		if (cylinders < heads || cylinders < sectors)
203239340Sjkim			break;
204118611Snjl		if (cylinders > 1023)
205118611Snjl			continue;
206118611Snjl		chs = cylinders * heads * sectors;
207118611Snjl		if (chs > *bestchs || (chs == *bestchs && *bestheads == 1)) {
208118611Snjl			*bestchs = chs;
209118611Snjl			*bestheads = heads;
210118611Snjl		}
211118611Snjl	}
212118611Snjl}
213118611Snjl
214118611Snjlstatic void
215118611Snjlg_part_geometry(struct g_part_table *table, struct g_consumer *cp,
216118611Snjl    off_t blocks)
217118611Snjl{
218239340Sjkim	static u_int candidate_sectors[] = { 1, 9, 17, 33, 63, 0 };
219239340Sjkim	off_t chs, bestchs;
220118611Snjl	u_int heads, sectors;
221118611Snjl	int idx;
222118611Snjl
223239340Sjkim	if (g_getattr("GEOM::fwsectors", cp, &sectors) != 0 || sectors == 0 ||
224239340Sjkim	    g_getattr("GEOM::fwheads", cp, &heads) != 0 || heads == 0) {
225118611Snjl		table->gpt_fixgeom = 0;
226118611Snjl		table->gpt_heads = 0;
227118611Snjl		table->gpt_sectors = 0;
228239340Sjkim		bestchs = 0;
229239340Sjkim		for (idx = 0; candidate_sectors[idx] != 0; idx++) {
230118611Snjl			sectors = candidate_sectors[idx];
231118611Snjl			g_part_geometry_heads(blocks, sectors, &chs, &heads);
232118611Snjl			if (chs == 0)
233118611Snjl				continue;
234118611Snjl			/*
235118611Snjl			 * Prefer a geometry with sectors > 1, but only if
236118611Snjl			 * it doesn't bump down the number of heads to 1.
237118611Snjl			 */
238118611Snjl			if (chs > bestchs || (chs == bestchs && heads > 1 &&
239118611Snjl			    table->gpt_sectors == 1)) {
240118611Snjl				bestchs = chs;
241118611Snjl				table->gpt_heads = heads;
242118611Snjl				table->gpt_sectors = sectors;
243118611Snjl			}
244118611Snjl		}
245118611Snjl		/*
246118611Snjl		 * If we didn't find a geometry at all, then the disk is
247151937Sjkim		 * too big. This means we can use the maximum number of
248118611Snjl		 * heads and sectors.
249118611Snjl		 */
250118611Snjl		if (bestchs == 0) {
251118611Snjl			table->gpt_heads = 255;
252118611Snjl			table->gpt_sectors = 63;
253151937Sjkim		}
254118611Snjl	} else {
255118611Snjl		table->gpt_fixgeom = 1;
256118611Snjl		table->gpt_heads = heads;
257118611Snjl		table->gpt_sectors = sectors;
258118611Snjl	}
259118611Snjl}
260118611Snjl
261118611Snjl#define	DPRINTF(...)	if (bootverbose) {	\
262118611Snjl	printf("GEOM_PART: " __VA_ARGS__);	\
263118611Snjl}
264118611Snjl
265118611Snjlstatic int
266118611Snjlg_part_check_integrity(struct g_part_table *table, struct g_consumer *cp)
267118611Snjl{
268118611Snjl	struct g_part_entry *e1, *e2;
269118611Snjl	struct g_provider *pp;
270118611Snjl	off_t offset;
271118611Snjl	int failed;
272118611Snjl
273118611Snjl	failed = 0;
274118611Snjl	pp = cp->provider;
275118611Snjl	if (table->gpt_last < table->gpt_first) {
276118611Snjl		DPRINTF("last LBA is below first LBA: %jd < %jd\n",
277118611Snjl		    (intmax_t)table->gpt_last, (intmax_t)table->gpt_first);
278118611Snjl		failed++;
279118611Snjl	}
280118611Snjl	if (table->gpt_last > pp->mediasize / pp->sectorsize - 1) {
281118611Snjl		DPRINTF("last LBA extends beyond mediasize: "
282118611Snjl		    "%jd > %jd\n", (intmax_t)table->gpt_last,
283118611Snjl		    (intmax_t)pp->mediasize / pp->sectorsize - 1);
284118611Snjl		failed++;
285118611Snjl	}
286118611Snjl	LIST_FOREACH(e1, &table->gpt_entry, gpe_entry) {
287118611Snjl		if (e1->gpe_deleted || e1->gpe_internal)
288118611Snjl			continue;
289118611Snjl		if (e1->gpe_start < table->gpt_first) {
290118611Snjl			DPRINTF("partition %d has start offset below first "
291118611Snjl			    "LBA: %jd < %jd\n", e1->gpe_index,
292118611Snjl			    (intmax_t)e1->gpe_start,
293118611Snjl			    (intmax_t)table->gpt_first);
294118611Snjl			failed++;
295118611Snjl		}
296118611Snjl		if (e1->gpe_start > table->gpt_last) {
297118611Snjl			DPRINTF("partition %d has start offset beyond last "
298118611Snjl			    "LBA: %jd > %jd\n", e1->gpe_index,
299118611Snjl			    (intmax_t)e1->gpe_start,
300118611Snjl			    (intmax_t)table->gpt_last);
301118611Snjl			failed++;
302118611Snjl		}
303118611Snjl		if (e1->gpe_end < e1->gpe_start) {
304118611Snjl			DPRINTF("partition %d has end offset below start "
305118611Snjl			    "offset: %jd < %jd\n", e1->gpe_index,
306118611Snjl			    (intmax_t)e1->gpe_end,
307118611Snjl			    (intmax_t)e1->gpe_start);
308118611Snjl			failed++;
309118611Snjl		}
310118611Snjl		if (e1->gpe_end > table->gpt_last) {
311118611Snjl			DPRINTF("partition %d has end offset beyond last "
312118611Snjl			    "LBA: %jd > %jd\n", e1->gpe_index,
313118611Snjl			    (intmax_t)e1->gpe_end,
314118611Snjl			    (intmax_t)table->gpt_last);
315118611Snjl			failed++;
316118611Snjl		}
317118611Snjl		if (pp->stripesize > 0) {
318151937Sjkim			offset = e1->gpe_start * pp->sectorsize;
319118611Snjl			if (e1->gpe_offset > offset)
320118611Snjl				offset = e1->gpe_offset;
321151937Sjkim			if ((offset + pp->stripeoffset) % pp->stripesize) {
322118611Snjl				DPRINTF("partition %d is not aligned on %u "
323239340Sjkim				    "bytes\n", e1->gpe_index, pp->stripesize);
324118611Snjl				/* Don't treat this as a critical failure */
325118611Snjl			}
326118611Snjl		}
327118611Snjl		e2 = e1;
328118611Snjl		while ((e2 = LIST_NEXT(e2, gpe_entry)) != NULL) {
329118611Snjl			if (e2->gpe_deleted || e2->gpe_internal)
330118611Snjl				continue;
331118611Snjl			if (e1->gpe_start >= e2->gpe_start &&
332118611Snjl			    e1->gpe_start <= e2->gpe_end) {
333118611Snjl				DPRINTF("partition %d has start offset inside "
334151937Sjkim				    "partition %d: start[%d] %jd >= start[%d] "
335151937Sjkim				    "%jd <= end[%d] %jd\n",
336118611Snjl				    e1->gpe_index, e2->gpe_index,
337118611Snjl				    e2->gpe_index, (intmax_t)e2->gpe_start,
338118611Snjl				    e1->gpe_index, (intmax_t)e1->gpe_start,
339118611Snjl				    e2->gpe_index, (intmax_t)e2->gpe_end);
340118611Snjl				failed++;
341118611Snjl			}
342118611Snjl			if (e1->gpe_end >= e2->gpe_start &&
343239340Sjkim			    e1->gpe_end <= e2->gpe_end) {
344239340Sjkim				DPRINTF("partition %d has end offset inside "
345118611Snjl				    "partition %d: start[%d] %jd >= end[%d] "
346239340Sjkim				    "%jd <= end[%d] %jd\n",
347239340Sjkim				    e1->gpe_index, e2->gpe_index,
348239340Sjkim				    e2->gpe_index, (intmax_t)e2->gpe_start,
349239340Sjkim				    e1->gpe_index, (intmax_t)e1->gpe_end,
350239340Sjkim				    e2->gpe_index, (intmax_t)e2->gpe_end);
351239340Sjkim				failed++;
352239340Sjkim			}
353239340Sjkim			if (e1->gpe_start < e2->gpe_start &&
354239340Sjkim			    e1->gpe_end > e2->gpe_end) {
355239340Sjkim				DPRINTF("partition %d contains partition %d: "
356239340Sjkim				    "start[%d] %jd > start[%d] %jd, end[%d] "
357239340Sjkim				    "%jd < end[%d] %jd\n",
358239340Sjkim				    e1->gpe_index, e2->gpe_index,
359239340Sjkim				    e1->gpe_index, (intmax_t)e1->gpe_start,
360239340Sjkim				    e2->gpe_index, (intmax_t)e2->gpe_start,
361239340Sjkim				    e2->gpe_index, (intmax_t)e2->gpe_end,
362239340Sjkim				    e1->gpe_index, (intmax_t)e1->gpe_end);
363239340Sjkim				failed++;
364239340Sjkim			}
365239340Sjkim		}
366239340Sjkim	}
367239340Sjkim	if (failed != 0) {
368239340Sjkim		printf("GEOM_PART: integrity check failed (%s, %s)\n",
369239340Sjkim		    pp->name, table->gpt_scheme->name);
370239340Sjkim		if (check_integrity != 0)
371239340Sjkim			return (EINVAL);
372239340Sjkim		table->gpt_corrupt = 1;
373239340Sjkim	}
374239340Sjkim	return (0);
375239340Sjkim}
376239340Sjkim#undef	DPRINTF
377239340Sjkim
378118611Snjlstruct g_part_entry *
379118611Snjlg_part_new_entry(struct g_part_table *table, int index, quad_t start,
380118611Snjl    quad_t end)
381118611Snjl{
382118611Snjl	struct g_part_entry *entry, *last;
383118611Snjl
384118611Snjl	last = NULL;
385118611Snjl	LIST_FOREACH(entry, &table->gpt_entry, gpe_entry) {
386118611Snjl		if (entry->gpe_index == index)
387118611Snjl			break;
388118611Snjl		if (entry->gpe_index > index) {
389118611Snjl			entry = NULL;
390118611Snjl			break;
391118611Snjl		}
392118611Snjl		last = entry;
393118611Snjl	}
394118611Snjl	if (entry == NULL) {
395118611Snjl		entry = g_malloc(table->gpt_scheme->gps_entrysz,
396118611Snjl		    M_WAITOK | M_ZERO);
397118611Snjl		entry->gpe_index = index;
398118611Snjl		if (last == NULL)
399118611Snjl			LIST_INSERT_HEAD(&table->gpt_entry, entry, gpe_entry);
400118611Snjl		else
401118611Snjl			LIST_INSERT_AFTER(last, entry, gpe_entry);
402118611Snjl	} else
403118611Snjl		entry->gpe_offset = 0;
404118611Snjl	entry->gpe_start = start;
405118611Snjl	entry->gpe_end = end;
406118611Snjl	return (entry);
407118611Snjl}
408118611Snjl
409118611Snjlstatic void
410118611Snjlg_part_new_provider(struct g_geom *gp, struct g_part_table *table,
411118611Snjl    struct g_part_entry *entry)
412118611Snjl{
413118611Snjl	struct g_consumer *cp;
414118611Snjl	struct g_provider *pp;
415118611Snjl	struct sbuf *sb;
416118611Snjl	off_t offset;
417239340Sjkim
418118611Snjl	cp = LIST_FIRST(&gp->consumer);
419118611Snjl	pp = cp->provider;
420118611Snjl
421118611Snjl	offset = entry->gpe_start * pp->sectorsize;
422118611Snjl	if (entry->gpe_offset < offset)
423118611Snjl		entry->gpe_offset = offset;
424118611Snjl
425118611Snjl	if (entry->gpe_pp == NULL) {
426118611Snjl		sb = sbuf_new_auto();
427118611Snjl		G_PART_FULLNAME(table, entry, sb, gp->name);
428118611Snjl		sbuf_finish(sb);
429118611Snjl		entry->gpe_pp = g_new_providerf(gp, "%s", sbuf_data(sb));
430118611Snjl		sbuf_delete(sb);
431118611Snjl		entry->gpe_pp->flags |= G_PF_DIRECT_SEND | G_PF_DIRECT_RECEIVE;
432118611Snjl		entry->gpe_pp->private = entry;		/* Close the circle. */
433118611Snjl	}
434209746Sjkim	entry->gpe_pp->index = entry->gpe_index - 1;	/* index is 1-based. */
435167802Sjkim	entry->gpe_pp->mediasize = (entry->gpe_end - entry->gpe_start + 1) *
436118611Snjl	    pp->sectorsize;
437118611Snjl	entry->gpe_pp->mediasize -= entry->gpe_offset - offset;
438118611Snjl	entry->gpe_pp->sectorsize = pp->sectorsize;
439118611Snjl	entry->gpe_pp->stripesize = pp->stripesize;
440118611Snjl	entry->gpe_pp->stripeoffset = pp->stripeoffset + entry->gpe_offset;
441118611Snjl	if (pp->stripesize > 0)
442118611Snjl		entry->gpe_pp->stripeoffset %= pp->stripesize;
443218590Sjkim	entry->gpe_pp->flags |= pp->flags & G_PF_ACCEPT_UNMAPPED;
444218590Sjkim	g_error_provider(entry->gpe_pp, 0);
445218590Sjkim}
446218590Sjkim
447239340Sjkimstatic struct g_geom*
448218590Sjkimg_part_find_geom(const char *name)
449218590Sjkim{
450218590Sjkim	struct g_geom *gp;
451218590Sjkim	LIST_FOREACH(gp, &g_part_class.geom, geom) {
452218590Sjkim		if (!strcmp(name, gp->name))
453218590Sjkim			break;
454218590Sjkim	}
455218590Sjkim	return (gp);
456218590Sjkim}
457218590Sjkim
458218590Sjkimstatic int
459218590Sjkimg_part_parm_geom(struct gctl_req *req, const char *name, struct g_geom **v)
460218590Sjkim{
461218590Sjkim	struct g_geom *gp;
462218590Sjkim	const char *gname;
463218590Sjkim
464218590Sjkim	gname = gctl_get_asciiparam(req, name);
465218590Sjkim	if (gname == NULL)
466218590Sjkim		return (ENOATTR);
467228110Sjkim	if (strncmp(gname, _PATH_DEV, sizeof(_PATH_DEV) - 1) == 0)
468228110Sjkim		gname += sizeof(_PATH_DEV) - 1;
469218590Sjkim	gp = g_part_find_geom(gname);
470218590Sjkim	if (gp == NULL) {
471218590Sjkim		gctl_error(req, "%d %s '%s'", EINVAL, name, gname);
472218590Sjkim		return (EINVAL);
473218590Sjkim	}
474218590Sjkim	if ((gp->flags & G_GEOM_WITHER) != 0) {
475218590Sjkim		gctl_error(req, "%d %s", ENXIO, gname);
476218590Sjkim		return (ENXIO);
477218590Sjkim	}
478228110Sjkim	*v = gp;
479218590Sjkim	return (0);
480218590Sjkim}
481218590Sjkim
482218590Sjkimstatic int
483218590Sjkimg_part_parm_provider(struct gctl_req *req, const char *name,
484218590Sjkim    struct g_provider **v)
485218590Sjkim{
486228110Sjkim	struct g_provider *pp;
487218590Sjkim	const char *pname;
488218590Sjkim
489228110Sjkim	pname = gctl_get_asciiparam(req, name);
490228110Sjkim	if (pname == NULL)
491228110Sjkim		return (ENOATTR);
492228110Sjkim	if (strncmp(pname, _PATH_DEV, sizeof(_PATH_DEV) - 1) == 0)
493228110Sjkim		pname += sizeof(_PATH_DEV) - 1;
494228110Sjkim	pp = g_provider_by_name(pname);
495228110Sjkim	if (pp == NULL) {
496228110Sjkim		gctl_error(req, "%d %s '%s'", EINVAL, name, pname);
497228110Sjkim		return (EINVAL);
498228110Sjkim	}
499218590Sjkim	*v = pp;
500218590Sjkim	return (0);
501218590Sjkim}
502218590Sjkim
503218590Sjkimstatic int
504218590Sjkimg_part_parm_quad(struct gctl_req *req, const char *name, quad_t *v)
505218590Sjkim{
506218590Sjkim	const char *p;
507218590Sjkim	char *x;
508218590Sjkim	quad_t q;
509218590Sjkim
510218590Sjkim	p = gctl_get_asciiparam(req, name);
511218590Sjkim	if (p == NULL)
512218590Sjkim		return (ENOATTR);
513218590Sjkim	q = strtoq(p, &x, 0);
514218590Sjkim	if (*x != '\0' || q < 0) {
515218590Sjkim		gctl_error(req, "%d %s '%s'", EINVAL, name, p);
516218590Sjkim		return (EINVAL);
517218590Sjkim	}
518218590Sjkim	*v = q;
519218590Sjkim	return (0);
520218590Sjkim}
521218590Sjkim
522218590Sjkimstatic int
523218590Sjkimg_part_parm_scheme(struct gctl_req *req, const char *name,
524118611Snjl    struct g_part_scheme **v)
525118611Snjl{
526118611Snjl	struct g_part_scheme *s;
527118611Snjl	const char *p;
528118611Snjl
529239340Sjkim	p = gctl_get_asciiparam(req, name);
530118611Snjl	if (p == NULL)
531118611Snjl		return (ENOATTR);
532118611Snjl	TAILQ_FOREACH(s, &g_part_schemes, scheme_list) {
533118611Snjl		if (s == &g_part_null_scheme)
534118611Snjl			continue;
535118611Snjl		if (!strcasecmp(s->name, p))
536118611Snjl			break;
537118611Snjl	}
538118611Snjl	if (s == NULL) {
539202771Sjkim		gctl_error(req, "%d %s '%s'", EINVAL, name, p);
540118611Snjl		return (EINVAL);
541118611Snjl	}
542118611Snjl	*v = s;
543118611Snjl	return (0);
544118611Snjl}
545118611Snjl
546118611Snjlstatic int
547209746Sjkimg_part_parm_str(struct gctl_req *req, const char *name, const char **v)
548167802Sjkim{
549123315Snjl	const char *p;
550118611Snjl
551118611Snjl	p = gctl_get_asciiparam(req, name);
552118611Snjl	if (p == NULL)
553118611Snjl		return (ENOATTR);
554118611Snjl	/* An empty label is always valid. */
555118611Snjl	if (strcmp(name, "label") != 0 && p[0] == '\0') {
556118611Snjl		gctl_error(req, "%d %s '%s'", EINVAL, name, p);
557118611Snjl		return (EINVAL);
558118611Snjl	}
559118611Snjl	*v = p;
560118611Snjl	return (0);
561118611Snjl}
562118611Snjl
563118611Snjlstatic int
564118611Snjlg_part_parm_intmax(struct gctl_req *req, const char *name, u_int *v)
565118611Snjl{
566118611Snjl	const intmax_t *p;
567118611Snjl	int size;
568118611Snjl
569118611Snjl	p = gctl_get_param(req, name, &size);
570118611Snjl	if (p == NULL)
571118611Snjl		return (ENOATTR);
572118611Snjl	if (size != sizeof(*p) || *p < 0 || *p > INT_MAX) {
573118611Snjl		gctl_error(req, "%d %s '%jd'", EINVAL, name, *p);
574118611Snjl		return (EINVAL);
575118611Snjl	}
576118611Snjl	*v = (u_int)*p;
577118611Snjl	return (0);
578118611Snjl}
579118611Snjl
580118611Snjlstatic int
581118611Snjlg_part_parm_uint32(struct gctl_req *req, const char *name, u_int *v)
582118611Snjl{
583118611Snjl	const uint32_t *p;
584118611Snjl	int size;
585118611Snjl
586118611Snjl	p = gctl_get_param(req, name, &size);
587118611Snjl	if (p == NULL)
588118611Snjl		return (ENOATTR);
589118611Snjl	if (size != sizeof(*p) || *p > INT_MAX) {
590118611Snjl		gctl_error(req, "%d %s '%u'", EINVAL, name, (unsigned int)*p);
591118611Snjl		return (EINVAL);
592118611Snjl	}
593118611Snjl	*v = (u_int)*p;
594239340Sjkim	return (0);
595118611Snjl}
596239340Sjkim
597118611Snjlstatic int
598118611Snjlg_part_parm_bootcode(struct gctl_req *req, const char *name, const void **v,
599118611Snjl    unsigned int *s)
600118611Snjl{
601118611Snjl	const void *p;
602118611Snjl	int size;
603118611Snjl
604118611Snjl	p = gctl_get_param(req, name, &size);
605118611Snjl	if (p == NULL)
606118611Snjl		return (ENOATTR);
607118611Snjl	*v = p;
608118611Snjl	*s = size;
609118611Snjl	return (0);
610118611Snjl}
611118611Snjl
612118611Snjlstatic int
613118611Snjlg_part_probe(struct g_geom *gp, struct g_consumer *cp, int depth)
614118611Snjl{
615118611Snjl	struct g_part_scheme *iter, *scheme;
616118611Snjl	struct g_part_table *table;
617118611Snjl	int pri, probe;
618118611Snjl
619118611Snjl	table = gp->softc;
620118611Snjl	scheme = (table != NULL) ? table->gpt_scheme : NULL;
621118611Snjl	pri = (scheme != NULL) ? G_PART_PROBE(table, cp) : INT_MIN;
622118611Snjl	if (pri == 0)
623118611Snjl		goto done;
624209746Sjkim	if (pri > 0) {	/* error */
625167802Sjkim		scheme = NULL;
626118611Snjl		pri = INT_MIN;
627118611Snjl	}
628118611Snjl
629118611Snjl	TAILQ_FOREACH(iter, &g_part_schemes, scheme_list) {
630118611Snjl		if (iter == &g_part_null_scheme)
631118611Snjl			continue;
632118611Snjl		table = (void *)kobj_create((kobj_class_t)iter, M_GEOM,
633118611Snjl		    M_WAITOK);
634118611Snjl		table->gpt_gp = gp;
635118611Snjl		table->gpt_scheme = iter;
636118611Snjl		table->gpt_depth = depth;
637118611Snjl		probe = G_PART_PROBE(table, cp);
638118611Snjl		if (probe <= 0 && probe > pri) {
639118611Snjl			pri = probe;
640118611Snjl			scheme = iter;
641118611Snjl			if (gp->softc != NULL)
642118611Snjl				kobj_delete((kobj_t)gp->softc, M_GEOM);
643118611Snjl			gp->softc = table;
644118611Snjl			if (pri == 0)
645118611Snjl				goto done;
646118611Snjl		} else
647118611Snjl			kobj_delete((kobj_t)table, M_GEOM);
648118611Snjl	}
649118611Snjl
650118611Snjldone:
651118611Snjl	return ((scheme == NULL) ? ENXIO : 0);
652118611Snjl}
653118611Snjl
654118611Snjl/*
655118611Snjl * Control request functions.
656118611Snjl */
657118611Snjl
658118611Snjlstatic int
659118611Snjlg_part_ctl_add(struct gctl_req *req, struct g_part_parms *gpp)
660118611Snjl{
661118611Snjl	struct g_geom *gp;
662239340Sjkim	struct g_provider *pp;
663118611Snjl	struct g_part_entry *delent, *last, *entry;
664118611Snjl	struct g_part_table *table;
665118611Snjl	struct sbuf *sb;
666118611Snjl	quad_t end;
667118611Snjl	unsigned int index;
668118611Snjl	int error;
669118611Snjl
670118611Snjl	gp = gpp->gpp_geom;
671118611Snjl	G_PART_TRACE((G_T_TOPOLOGY, "%s(%s)", __func__, gp->name));
672118611Snjl	g_topology_assert();
673118611Snjl
674118611Snjl	pp = LIST_FIRST(&gp->consumer)->provider;
675118611Snjl	table = gp->softc;
676118611Snjl	end = gpp->gpp_start + gpp->gpp_size - 1;
677118611Snjl
678118611Snjl	if (gpp->gpp_start < table->gpt_first ||
679118611Snjl	    gpp->gpp_start > table->gpt_last) {
680118611Snjl		gctl_error(req, "%d start '%jd'", EINVAL,
681118611Snjl		    (intmax_t)gpp->gpp_start);
682118611Snjl		return (EINVAL);
683118611Snjl	}
684118611Snjl	if (end < gpp->gpp_start || end > table->gpt_last) {
685118611Snjl		gctl_error(req, "%d size '%jd'", EINVAL,
686118611Snjl		    (intmax_t)gpp->gpp_size);
687118611Snjl		return (EINVAL);
688118611Snjl	}
689118611Snjl	if (gpp->gpp_index > table->gpt_entries) {
690118611Snjl		gctl_error(req, "%d index '%d'", EINVAL, gpp->gpp_index);
691118611Snjl		return (EINVAL);
692118611Snjl	}
693118611Snjl
694118611Snjl	delent = last = NULL;
695118611Snjl	index = (gpp->gpp_index > 0) ? gpp->gpp_index : 1;
696118611Snjl	LIST_FOREACH(entry, &table->gpt_entry, gpe_entry) {
697118611Snjl		if (entry->gpe_deleted) {
698118611Snjl			if (entry->gpe_index == index)
699118611Snjl				delent = entry;
700118611Snjl			continue;
701118611Snjl		}
702118611Snjl		if (entry->gpe_index == index)
703118611Snjl			index = entry->gpe_index + 1;
704118611Snjl		if (entry->gpe_index < index)
705118611Snjl			last = entry;
706118611Snjl		if (entry->gpe_internal)
707118611Snjl			continue;
708118611Snjl		if (gpp->gpp_start >= entry->gpe_start &&
709118611Snjl		    gpp->gpp_start <= entry->gpe_end) {
710118611Snjl			gctl_error(req, "%d start '%jd'", ENOSPC,
711118611Snjl			    (intmax_t)gpp->gpp_start);
712118611Snjl			return (ENOSPC);
713118611Snjl		}
714118611Snjl		if (end >= entry->gpe_start && end <= entry->gpe_end) {
715118611Snjl			gctl_error(req, "%d end '%jd'", ENOSPC, (intmax_t)end);
716239340Sjkim			return (ENOSPC);
717118611Snjl		}
718118611Snjl		if (gpp->gpp_start < entry->gpe_start && end > entry->gpe_end) {
719118611Snjl			gctl_error(req, "%d size '%jd'", ENOSPC,
720118611Snjl			    (intmax_t)gpp->gpp_size);
721118611Snjl			return (ENOSPC);
722118611Snjl		}
723118611Snjl	}
724118611Snjl	if (gpp->gpp_index > 0 && index != gpp->gpp_index) {
725118611Snjl		gctl_error(req, "%d index '%d'", EEXIST, gpp->gpp_index);
726118611Snjl		return (EEXIST);
727118611Snjl	}
728118611Snjl	if (index > table->gpt_entries) {
729118611Snjl		gctl_error(req, "%d index '%d'", ENOSPC, index);
730118611Snjl		return (ENOSPC);
731118611Snjl	}
732118611Snjl
733118611Snjl	entry = (delent == NULL) ? g_malloc(table->gpt_scheme->gps_entrysz,
734118611Snjl	    M_WAITOK | M_ZERO) : delent;
735118611Snjl	entry->gpe_index = index;
736118611Snjl	entry->gpe_start = gpp->gpp_start;
737118611Snjl	entry->gpe_end = end;
738118611Snjl	error = G_PART_ADD(table, entry, gpp);
739118611Snjl	if (error) {
740118611Snjl		gctl_error(req, "%d", error);
741118611Snjl		if (delent == NULL)
742118611Snjl			g_free(entry);
743209746Sjkim		return (error);
744118611Snjl	}
745118611Snjl	if (delent == NULL) {
746118611Snjl		if (last == NULL)
747118611Snjl			LIST_INSERT_HEAD(&table->gpt_entry, entry, gpe_entry);
748118611Snjl		else
749118611Snjl			LIST_INSERT_AFTER(last, entry, gpe_entry);
750118611Snjl		entry->gpe_created = 1;
751118611Snjl	} else {
752118611Snjl		entry->gpe_deleted = 0;
753118611Snjl		entry->gpe_modified = 1;
754118611Snjl	}
755118611Snjl	g_part_new_provider(gp, table, entry);
756118611Snjl
757118611Snjl	/* Provide feedback if so requested. */
758118611Snjl	if (gpp->gpp_parms & G_PART_PARM_OUTPUT) {
759118611Snjl		sb = sbuf_new_auto();
760118611Snjl		G_PART_FULLNAME(table, entry, sb, gp->name);
761118611Snjl		if (pp->stripesize > 0 && entry->gpe_pp->stripeoffset != 0)
762118611Snjl			sbuf_printf(sb, " added, but partition is not "
763118611Snjl			    "aligned on %u bytes\n", pp->stripesize);
764118611Snjl		else
765118611Snjl			sbuf_cat(sb, " added\n");
766118611Snjl		sbuf_finish(sb);
767118611Snjl		gctl_set_param(req, "output", sbuf_data(sb), sbuf_len(sb) + 1);
768118611Snjl		sbuf_delete(sb);
769118611Snjl	}
770118611Snjl	return (0);
771118611Snjl}
772118611Snjl
773118611Snjlstatic int
774118611Snjlg_part_ctl_bootcode(struct gctl_req *req, struct g_part_parms *gpp)
775118611Snjl{
776118611Snjl	struct g_geom *gp;
777151937Sjkim	struct g_part_table *table;
778151937Sjkim	struct sbuf *sb;
779118611Snjl	int error, sz;
780118611Snjl
781118611Snjl	gp = gpp->gpp_geom;
782118611Snjl	G_PART_TRACE((G_T_TOPOLOGY, "%s(%s)", __func__, gp->name));
783118611Snjl	g_topology_assert();
784118611Snjl
785118611Snjl	table = gp->softc;
786239340Sjkim	sz = table->gpt_scheme->gps_bootcodesz;
787118611Snjl	if (sz == 0) {
788118611Snjl		error = ENODEV;
789118611Snjl		goto fail;
790118611Snjl	}
791118611Snjl	if (gpp->gpp_codesize > sz) {
792118611Snjl		error = EFBIG;
793118611Snjl		goto fail;
794118611Snjl	}
795118611Snjl
796118611Snjl	error = G_PART_BOOTCODE(table, gpp);
797118611Snjl	if (error)
798118611Snjl		goto fail;
799118611Snjl
800118611Snjl	/* Provide feedback if so requested. */
801118611Snjl	if (gpp->gpp_parms & G_PART_PARM_OUTPUT) {
802118611Snjl		sb = sbuf_new_auto();
803118611Snjl		sbuf_printf(sb, "bootcode written to %s\n", gp->name);
804118611Snjl		sbuf_finish(sb);
805118611Snjl		gctl_set_param(req, "output", sbuf_data(sb), sbuf_len(sb) + 1);
806118611Snjl		sbuf_delete(sb);
807118611Snjl	}
808118611Snjl	return (0);
809118611Snjl
810118611Snjl fail:
811118611Snjl	gctl_error(req, "%d", error);
812118611Snjl	return (error);
813118611Snjl}
814118611Snjl
815118611Snjlstatic int
816118611Snjlg_part_ctl_commit(struct gctl_req *req, struct g_part_parms *gpp)
817118611Snjl{
818118611Snjl	struct g_consumer *cp;
819118611Snjl	struct g_geom *gp;
820118611Snjl	struct g_provider *pp;
821118611Snjl	struct g_part_entry *entry, *tmp;
822118611Snjl	struct g_part_table *table;
823118611Snjl	char *buf;
824118611Snjl	int error, i;
825118611Snjl
826118611Snjl	gp = gpp->gpp_geom;
827118611Snjl	G_PART_TRACE((G_T_TOPOLOGY, "%s(%s)", __func__, gp->name));
828118611Snjl	g_topology_assert();
829118611Snjl
830118611Snjl	table = gp->softc;
831118611Snjl	if (!table->gpt_opened) {
832118611Snjl		gctl_error(req, "%d", EPERM);
833118611Snjl		return (EPERM);
834118611Snjl	}
835118611Snjl
836118611Snjl	g_topology_unlock();
837118611Snjl
838118611Snjl	cp = LIST_FIRST(&gp->consumer);
839118611Snjl	if ((table->gpt_smhead | table->gpt_smtail) != 0) {
840118611Snjl		pp = cp->provider;
841239340Sjkim		buf = g_malloc(pp->sectorsize, M_WAITOK | M_ZERO);
842118611Snjl		while (table->gpt_smhead != 0) {
843118611Snjl			i = ffs(table->gpt_smhead) - 1;
844118611Snjl			error = g_write_data(cp, i * pp->sectorsize, buf,
845118611Snjl			    pp->sectorsize);
846118611Snjl			if (error) {
847118611Snjl				g_free(buf);
848118611Snjl				goto fail;
849118611Snjl			}
850118611Snjl			table->gpt_smhead &= ~(1 << i);
851118611Snjl		}
852118611Snjl		while (table->gpt_smtail != 0) {
853118611Snjl			i = ffs(table->gpt_smtail) - 1;
854118611Snjl			error = g_write_data(cp, pp->mediasize - (i + 1) *
855118611Snjl			    pp->sectorsize, buf, pp->sectorsize);
856118611Snjl			if (error) {
857118611Snjl				g_free(buf);
858118611Snjl				goto fail;
859118611Snjl			}
860118611Snjl			table->gpt_smtail &= ~(1 << i);
861118611Snjl		}
862118611Snjl		g_free(buf);
863118611Snjl	}
864118611Snjl
865118611Snjl	if (table->gpt_scheme == &g_part_null_scheme) {
866118611Snjl		g_topology_lock();
867118611Snjl		g_access(cp, -1, -1, -1);
868118611Snjl		g_part_wither(gp, ENXIO);
869118611Snjl		return (0);
870118611Snjl	}
871118611Snjl
872118611Snjl	error = G_PART_WRITE(table, cp);
873118611Snjl	if (error)
874118611Snjl		goto fail;
875118611Snjl
876118611Snjl	LIST_FOREACH_SAFE(entry, &table->gpt_entry, gpe_entry, tmp) {
877118611Snjl		if (!entry->gpe_deleted) {
878118611Snjl			entry->gpe_created = 0;
879118611Snjl			entry->gpe_modified = 0;
880151937Sjkim			continue;
881151937Sjkim		}
882151937Sjkim		LIST_REMOVE(entry, gpe_entry);
883151937Sjkim		g_free(entry);
884118611Snjl	}
885118611Snjl	table->gpt_created = 0;
886118611Snjl	table->gpt_opened = 0;
887118611Snjl
888118611Snjl	g_topology_lock();
889118611Snjl	g_access(cp, -1, -1, -1);
890118611Snjl	return (0);
891118611Snjl
892118611Snjlfail:
893118611Snjl	g_topology_lock();
894118611Snjl	gctl_error(req, "%d", error);
895118611Snjl	return (error);
896118611Snjl}
897118611Snjl
898118611Snjlstatic int
899118611Snjlg_part_ctl_create(struct gctl_req *req, struct g_part_parms *gpp)
900118611Snjl{
901118611Snjl	struct g_consumer *cp;
902118611Snjl	struct g_geom *gp;
903118611Snjl	struct g_provider *pp;
904118611Snjl	struct g_part_scheme *scheme;
905118611Snjl	struct g_part_table *null, *table;
906118611Snjl	struct sbuf *sb;
907118611Snjl	int attr, error;
908118611Snjl
909118611Snjl	pp = gpp->gpp_provider;
910118611Snjl	scheme = gpp->gpp_scheme;
911118611Snjl	G_PART_TRACE((G_T_TOPOLOGY, "%s(%s)", __func__, pp->name));
912118611Snjl	g_topology_assert();
913118611Snjl
914118611Snjl	/* Check that there isn't already a g_part geom on the provider. */
915118611Snjl	gp = g_part_find_geom(pp->name);
916118611Snjl	if (gp != NULL) {
917118611Snjl		null = gp->softc;
918118611Snjl		if (null->gpt_scheme != &g_part_null_scheme) {
919118611Snjl			gctl_error(req, "%d geom '%s'", EEXIST, pp->name);
920118611Snjl			return (EEXIST);
921118611Snjl		}
922118611Snjl	} else
923118611Snjl		null = NULL;
924118611Snjl
925118611Snjl	if ((gpp->gpp_parms & G_PART_PARM_ENTRIES) &&
926118611Snjl	    (gpp->gpp_entries < scheme->gps_minent ||
927118611Snjl	     gpp->gpp_entries > scheme->gps_maxent)) {
928118611Snjl		gctl_error(req, "%d entries '%d'", EINVAL, gpp->gpp_entries);
929118611Snjl		return (EINVAL);
930118611Snjl	}
931209746Sjkim
932118611Snjl	if (null == NULL)
933118611Snjl		gp = g_new_geomf(&g_part_class, "%s", pp->name);
934118611Snjl	gp->softc = kobj_create((kobj_class_t)gpp->gpp_scheme, M_GEOM,
935118611Snjl	    M_WAITOK);
936118611Snjl	table = gp->softc;
937118611Snjl	table->gpt_gp = gp;
938118611Snjl	table->gpt_scheme = gpp->gpp_scheme;
939118611Snjl	table->gpt_entries = (gpp->gpp_parms & G_PART_PARM_ENTRIES) ?
940118611Snjl	    gpp->gpp_entries : scheme->gps_minent;
941118611Snjl	LIST_INIT(&table->gpt_entry);
942209746Sjkim	if (null == NULL) {
943118611Snjl		cp = g_new_consumer(gp);
944118611Snjl		cp->flags |= G_CF_DIRECT_SEND | G_CF_DIRECT_RECEIVE;
945118611Snjl		error = g_attach(cp, pp);
946118611Snjl		if (error == 0)
947118611Snjl			error = g_access(cp, 1, 1, 1);
948118611Snjl		if (error != 0) {
949118611Snjl			g_part_wither(gp, error);
950118611Snjl			gctl_error(req, "%d geom '%s'", error, pp->name);
951118611Snjl			return (error);
952118611Snjl		}
953118611Snjl		table->gpt_opened = 1;
954118611Snjl	} else {
955118611Snjl		cp = LIST_FIRST(&gp->consumer);
956118611Snjl		table->gpt_opened = null->gpt_opened;
957118611Snjl		table->gpt_smhead = null->gpt_smhead;
958118611Snjl		table->gpt_smtail = null->gpt_smtail;
959118611Snjl	}
960118611Snjl
961118611Snjl	g_topology_unlock();
962193529Sjkim
963118611Snjl	/* Make sure the provider has media. */
964118611Snjl	if (pp->mediasize == 0 || pp->sectorsize == 0) {
965118611Snjl		error = ENODEV;
966118611Snjl		goto fail;
967118611Snjl	}
968118611Snjl
969118611Snjl	/* Make sure we can nest and if so, determine our depth. */
970118611Snjl	error = g_getattr("PART::isleaf", cp, &attr);
971118611Snjl	if (!error && attr) {
972118611Snjl		error = ENODEV;
973118611Snjl		goto fail;
974118611Snjl	}
975118611Snjl	error = g_getattr("PART::depth", cp, &attr);
976118611Snjl	table->gpt_depth = (!error) ? attr + 1 : 0;
977118611Snjl
978118611Snjl	/*
979118611Snjl	 * Synthesize a disk geometry. Some partitioning schemes
980118611Snjl	 * depend on it and since some file systems need it even
981118611Snjl	 * when the partitition scheme doesn't, we do it here in
982118611Snjl	 * scheme-independent code.
983118611Snjl	 */
984118611Snjl	g_part_geometry(table, cp, pp->mediasize / pp->sectorsize);
985118611Snjl
986118611Snjl	error = G_PART_CREATE(table, gpp);
987118611Snjl	if (error)
988118611Snjl		goto fail;
989118611Snjl
990118611Snjl	g_topology_lock();
991118611Snjl
992118611Snjl	table->gpt_created = 1;
993118611Snjl	if (null != NULL)
994118611Snjl		kobj_delete((kobj_t)null, M_GEOM);
995118611Snjl
996118611Snjl	/*
997118611Snjl	 * Support automatic commit by filling in the gpp_geom
998118611Snjl	 * parameter.
999118611Snjl	 */
1000118611Snjl	gpp->gpp_parms |= G_PART_PARM_GEOM;
1001118611Snjl	gpp->gpp_geom = gp;
1002118611Snjl
1003118611Snjl	/* Provide feedback if so requested. */
1004118611Snjl	if (gpp->gpp_parms & G_PART_PARM_OUTPUT) {
1005118611Snjl		sb = sbuf_new_auto();
1006118611Snjl		sbuf_printf(sb, "%s created\n", gp->name);
1007118611Snjl		sbuf_finish(sb);
1008118611Snjl		gctl_set_param(req, "output", sbuf_data(sb), sbuf_len(sb) + 1);
1009118611Snjl		sbuf_delete(sb);
1010118611Snjl	}
1011118611Snjl	return (0);
1012118611Snjl
1013118611Snjlfail:
1014118611Snjl	g_topology_lock();
1015118611Snjl	if (null == NULL) {
1016118611Snjl		g_access(cp, -1, -1, -1);
1017118611Snjl		g_part_wither(gp, error);
1018118611Snjl	} else {
1019118611Snjl		kobj_delete((kobj_t)gp->softc, M_GEOM);
1020118611Snjl		gp->softc = null;
1021118611Snjl	}
1022118611Snjl	gctl_error(req, "%d provider", error);
1023118611Snjl	return (error);
1024118611Snjl}
1025118611Snjl
1026118611Snjlstatic int
1027118611Snjlg_part_ctl_delete(struct gctl_req *req, struct g_part_parms *gpp)
1028118611Snjl{
1029118611Snjl	struct g_geom *gp;
1030118611Snjl	struct g_provider *pp;
1031118611Snjl	struct g_part_entry *entry;
1032118611Snjl	struct g_part_table *table;
1033118611Snjl	struct sbuf *sb;
1034118611Snjl
1035118611Snjl	gp = gpp->gpp_geom;
1036118611Snjl	G_PART_TRACE((G_T_TOPOLOGY, "%s(%s)", __func__, gp->name));
1037118611Snjl	g_topology_assert();
1038118611Snjl
1039118611Snjl	table = gp->softc;
1040118611Snjl
1041118611Snjl	LIST_FOREACH(entry, &table->gpt_entry, gpe_entry) {
1042118611Snjl		if (entry->gpe_deleted || entry->gpe_internal)
1043118611Snjl			continue;
1044118611Snjl		if (entry->gpe_index == gpp->gpp_index)
1045118611Snjl			break;
1046118611Snjl	}
1047118611Snjl	if (entry == NULL) {
1048118611Snjl		gctl_error(req, "%d index '%d'", ENOENT, gpp->gpp_index);
1049118611Snjl		return (ENOENT);
1050118611Snjl	}
1051118611Snjl
1052118611Snjl	pp = entry->gpe_pp;
1053118611Snjl	if (pp != NULL) {
1054118611Snjl		if (pp->acr > 0 || pp->acw > 0 || pp->ace > 0) {
1055118611Snjl			gctl_error(req, "%d", EBUSY);
1056118611Snjl			return (EBUSY);
1057118611Snjl		}
1058118611Snjl
1059118611Snjl		pp->private = NULL;
1060151937Sjkim		entry->gpe_pp = NULL;
1061151937Sjkim	}
1062118611Snjl
1063118611Snjl	if (pp != NULL)
1064118611Snjl		g_wither_provider(pp, ENXIO);
1065118611Snjl
1066118611Snjl	/* Provide feedback if so requested. */
1067118611Snjl	if (gpp->gpp_parms & G_PART_PARM_OUTPUT) {
1068118611Snjl		sb = sbuf_new_auto();
1069118611Snjl		G_PART_FULLNAME(table, entry, sb, gp->name);
1070118611Snjl		sbuf_cat(sb, " deleted\n");
1071118611Snjl		sbuf_finish(sb);
1072118611Snjl		gctl_set_param(req, "output", sbuf_data(sb), sbuf_len(sb) + 1);
1073118611Snjl		sbuf_delete(sb);
1074118611Snjl	}
1075118611Snjl
1076118611Snjl	if (entry->gpe_created) {
1077118611Snjl		LIST_REMOVE(entry, gpe_entry);
1078118611Snjl		g_free(entry);
1079118611Snjl	} else {
1080118611Snjl		entry->gpe_modified = 0;
1081118611Snjl		entry->gpe_deleted = 1;
1082118611Snjl	}
1083118611Snjl	return (0);
1084118611Snjl}
1085118611Snjl
1086118611Snjlstatic int
1087118611Snjlg_part_ctl_destroy(struct gctl_req *req, struct g_part_parms *gpp)
1088118611Snjl{
1089118611Snjl	struct g_consumer *cp;
1090118611Snjl	struct g_geom *gp;
1091118611Snjl	struct g_provider *pp;
1092118611Snjl	struct g_part_entry *entry, *tmp;
1093118611Snjl	struct g_part_table *null, *table;
1094118611Snjl	struct sbuf *sb;
1095118611Snjl	int error;
1096118611Snjl
1097118611Snjl	gp = gpp->gpp_geom;
1098118611Snjl	G_PART_TRACE((G_T_TOPOLOGY, "%s(%s)", __func__, gp->name));
1099118611Snjl	g_topology_assert();
1100118611Snjl
1101118611Snjl	table = gp->softc;
1102118611Snjl	/* Check for busy providers. */
1103118611Snjl	LIST_FOREACH(entry, &table->gpt_entry, gpe_entry) {
1104118611Snjl		if (entry->gpe_deleted || entry->gpe_internal)
1105118611Snjl			continue;
1106118611Snjl		if (gpp->gpp_force) {
1107118611Snjl			pp = entry->gpe_pp;
1108118611Snjl			if (pp == NULL)
1109118611Snjl				continue;
1110118611Snjl			if (pp->acr == 0 && pp->acw == 0 && pp->ace == 0)
1111118611Snjl				continue;
1112118611Snjl		}
1113118611Snjl		gctl_error(req, "%d", EBUSY);
1114118611Snjl		return (EBUSY);
1115118611Snjl	}
1116118611Snjl
1117118611Snjl	if (gpp->gpp_force) {
1118118611Snjl		/* Destroy all providers. */
1119118611Snjl		LIST_FOREACH_SAFE(entry, &table->gpt_entry, gpe_entry, tmp) {
1120151937Sjkim			pp = entry->gpe_pp;
1121151937Sjkim			if (pp != NULL) {
1122118611Snjl				pp->private = NULL;
1123118611Snjl				g_wither_provider(pp, ENXIO);
1124118611Snjl			}
1125118611Snjl			LIST_REMOVE(entry, gpe_entry);
1126118611Snjl			g_free(entry);
1127118611Snjl		}
1128118611Snjl	}
1129118611Snjl
1130118611Snjl	error = G_PART_DESTROY(table, gpp);
1131118611Snjl	if (error) {
1132118611Snjl		gctl_error(req, "%d", error);
1133118611Snjl		return (error);
1134118611Snjl	}
1135118611Snjl
1136118611Snjl	gp->softc = kobj_create((kobj_class_t)&g_part_null_scheme, M_GEOM,
1137118611Snjl	    M_WAITOK);
1138118611Snjl	null = gp->softc;
1139118611Snjl	null->gpt_gp = gp;
1140118611Snjl	null->gpt_scheme = &g_part_null_scheme;
1141118611Snjl	LIST_INIT(&null->gpt_entry);
1142118611Snjl
1143118611Snjl	cp = LIST_FIRST(&gp->consumer);
1144118611Snjl	pp = cp->provider;
1145118611Snjl	null->gpt_last = pp->mediasize / pp->sectorsize - 1;
1146118611Snjl
1147118611Snjl	null->gpt_depth = table->gpt_depth;
1148118611Snjl	null->gpt_opened = table->gpt_opened;
1149118611Snjl	null->gpt_smhead = table->gpt_smhead;
1150118611Snjl	null->gpt_smtail = table->gpt_smtail;
1151118611Snjl
1152118611Snjl	while ((entry = LIST_FIRST(&table->gpt_entry)) != NULL) {
1153118611Snjl		LIST_REMOVE(entry, gpe_entry);
1154118611Snjl		g_free(entry);
1155118611Snjl	}
1156118611Snjl	kobj_delete((kobj_t)table, M_GEOM);
1157118611Snjl
1158118611Snjl	/* Provide feedback if so requested. */
1159118611Snjl	if (gpp->gpp_parms & G_PART_PARM_OUTPUT) {
1160118611Snjl		sb = sbuf_new_auto();
1161118611Snjl		sbuf_printf(sb, "%s destroyed\n", gp->name);
1162118611Snjl		sbuf_finish(sb);
1163118611Snjl		gctl_set_param(req, "output", sbuf_data(sb), sbuf_len(sb) + 1);
1164118611Snjl		sbuf_delete(sb);
1165118611Snjl	}
1166118611Snjl	return (0);
1167118611Snjl}
1168118611Snjl
1169118611Snjlstatic int
1170118611Snjlg_part_ctl_modify(struct gctl_req *req, struct g_part_parms *gpp)
1171118611Snjl{
1172118611Snjl	struct g_geom *gp;
1173118611Snjl	struct g_part_entry *entry;
1174118611Snjl	struct g_part_table *table;
1175118611Snjl	struct sbuf *sb;
1176118611Snjl	int error;
1177118611Snjl
1178118611Snjl	gp = gpp->gpp_geom;
1179118611Snjl	G_PART_TRACE((G_T_TOPOLOGY, "%s(%s)", __func__, gp->name));
1180151937Sjkim	g_topology_assert();
1181151937Sjkim
1182118611Snjl	table = gp->softc;
1183118611Snjl
1184118611Snjl	LIST_FOREACH(entry, &table->gpt_entry, gpe_entry) {
1185118611Snjl		if (entry->gpe_deleted || entry->gpe_internal)
1186118611Snjl			continue;
1187118611Snjl		if (entry->gpe_index == gpp->gpp_index)
1188118611Snjl			break;
1189118611Snjl	}
1190118611Snjl	if (entry == NULL) {
1191118611Snjl		gctl_error(req, "%d index '%d'", ENOENT, gpp->gpp_index);
1192118611Snjl		return (ENOENT);
1193118611Snjl	}
1194118611Snjl
1195118611Snjl	error = G_PART_MODIFY(table, entry, gpp);
1196118611Snjl	if (error) {
1197118611Snjl		gctl_error(req, "%d", error);
1198118611Snjl		return (error);
1199118611Snjl	}
1200118611Snjl
1201118611Snjl	if (!entry->gpe_created)
1202118611Snjl		entry->gpe_modified = 1;
1203118611Snjl
1204118611Snjl	/* Provide feedback if so requested. */
1205118611Snjl	if (gpp->gpp_parms & G_PART_PARM_OUTPUT) {
1206118611Snjl		sb = sbuf_new_auto();
1207118611Snjl		G_PART_FULLNAME(table, entry, sb, gp->name);
1208118611Snjl		sbuf_cat(sb, " modified\n");
1209118611Snjl		sbuf_finish(sb);
1210118611Snjl		gctl_set_param(req, "output", sbuf_data(sb), sbuf_len(sb) + 1);
1211118611Snjl		sbuf_delete(sb);
1212118611Snjl	}
1213118611Snjl	return (0);
1214118611Snjl}
1215118611Snjl
1216118611Snjlstatic int
1217118611Snjlg_part_ctl_move(struct gctl_req *req, struct g_part_parms *gpp)
1218118611Snjl{
1219118611Snjl	gctl_error(req, "%d verb 'move'", ENOSYS);
1220118611Snjl	return (ENOSYS);
1221118611Snjl}
1222118611Snjl
1223118611Snjlstatic int
1224118611Snjlg_part_ctl_recover(struct gctl_req *req, struct g_part_parms *gpp)
1225118611Snjl{
1226118611Snjl	struct g_part_table *table;
1227118611Snjl	struct g_geom *gp;
1228118611Snjl	struct sbuf *sb;
1229118611Snjl	int error, recovered;
1230118611Snjl
1231118611Snjl	gp = gpp->gpp_geom;
1232118611Snjl	G_PART_TRACE((G_T_TOPOLOGY, "%s(%s)", __func__, gp->name));
1233118611Snjl	g_topology_assert();
1234118611Snjl	table = gp->softc;
1235118611Snjl	error = recovered = 0;
1236118611Snjl
1237118611Snjl	if (table->gpt_corrupt) {
1238118611Snjl		error = G_PART_RECOVER(table);
1239118611Snjl		if (error == 0)
1240118611Snjl			error = g_part_check_integrity(table,
1241			    LIST_FIRST(&gp->consumer));
1242		if (error) {
1243			gctl_error(req, "%d recovering '%s' failed",
1244			    error, gp->name);
1245			return (error);
1246		}
1247		recovered = 1;
1248	}
1249	/* Provide feedback if so requested. */
1250	if (gpp->gpp_parms & G_PART_PARM_OUTPUT) {
1251		sb = sbuf_new_auto();
1252		if (recovered)
1253			sbuf_printf(sb, "%s recovered\n", gp->name);
1254		else
1255			sbuf_printf(sb, "%s recovering is not needed\n",
1256			    gp->name);
1257		sbuf_finish(sb);
1258		gctl_set_param(req, "output", sbuf_data(sb), sbuf_len(sb) + 1);
1259		sbuf_delete(sb);
1260	}
1261	return (0);
1262}
1263
1264static int
1265g_part_ctl_resize(struct gctl_req *req, struct g_part_parms *gpp)
1266{
1267	struct g_geom *gp;
1268	struct g_provider *pp;
1269	struct g_part_entry *pe, *entry;
1270	struct g_part_table *table;
1271	struct sbuf *sb;
1272	quad_t end;
1273	int error;
1274	off_t mediasize;
1275
1276	gp = gpp->gpp_geom;
1277	G_PART_TRACE((G_T_TOPOLOGY, "%s(%s)", __func__, gp->name));
1278	g_topology_assert();
1279	table = gp->softc;
1280
1281	/* check gpp_index */
1282	LIST_FOREACH(entry, &table->gpt_entry, gpe_entry) {
1283		if (entry->gpe_deleted || entry->gpe_internal)
1284			continue;
1285		if (entry->gpe_index == gpp->gpp_index)
1286			break;
1287	}
1288	if (entry == NULL) {
1289		gctl_error(req, "%d index '%d'", ENOENT, gpp->gpp_index);
1290		return (ENOENT);
1291	}
1292
1293	/* check gpp_size */
1294	end = entry->gpe_start + gpp->gpp_size - 1;
1295	if (gpp->gpp_size < 1 || end > table->gpt_last) {
1296		gctl_error(req, "%d size '%jd'", EINVAL,
1297		    (intmax_t)gpp->gpp_size);
1298		return (EINVAL);
1299	}
1300
1301	LIST_FOREACH(pe, &table->gpt_entry, gpe_entry) {
1302		if (pe->gpe_deleted || pe->gpe_internal || pe == entry)
1303			continue;
1304		if (end >= pe->gpe_start && end <= pe->gpe_end) {
1305			gctl_error(req, "%d end '%jd'", ENOSPC,
1306			    (intmax_t)end);
1307			return (ENOSPC);
1308		}
1309		if (entry->gpe_start < pe->gpe_start && end > pe->gpe_end) {
1310			gctl_error(req, "%d size '%jd'", ENOSPC,
1311			    (intmax_t)gpp->gpp_size);
1312			return (ENOSPC);
1313		}
1314	}
1315
1316	pp = entry->gpe_pp;
1317	if ((g_debugflags & 16) == 0 &&
1318	    (pp->acr > 0 || pp->acw > 0 || pp->ace > 0)) {
1319		if (entry->gpe_end - entry->gpe_start + 1 > gpp->gpp_size) {
1320			/* Deny shrinking of an opened partition. */
1321			gctl_error(req, "%d", EBUSY);
1322			return (EBUSY);
1323		}
1324	}
1325
1326	error = G_PART_RESIZE(table, entry, gpp);
1327	if (error) {
1328		gctl_error(req, "%d%s", error, error != EBUSY ? "":
1329		    " resizing will lead to unexpected shrinking"
1330		    " due to alignment");
1331		return (error);
1332	}
1333
1334	if (!entry->gpe_created)
1335		entry->gpe_modified = 1;
1336
1337	/* update mediasize of changed provider */
1338	mediasize = (entry->gpe_end - entry->gpe_start + 1) *
1339		pp->sectorsize;
1340	g_resize_provider(pp, mediasize);
1341
1342	/* Provide feedback if so requested. */
1343	if (gpp->gpp_parms & G_PART_PARM_OUTPUT) {
1344		sb = sbuf_new_auto();
1345		G_PART_FULLNAME(table, entry, sb, gp->name);
1346		sbuf_cat(sb, " resized\n");
1347		sbuf_finish(sb);
1348		gctl_set_param(req, "output", sbuf_data(sb), sbuf_len(sb) + 1);
1349		sbuf_delete(sb);
1350	}
1351	return (0);
1352}
1353
1354static int
1355g_part_ctl_setunset(struct gctl_req *req, struct g_part_parms *gpp,
1356    unsigned int set)
1357{
1358	struct g_geom *gp;
1359	struct g_part_entry *entry;
1360	struct g_part_table *table;
1361	struct sbuf *sb;
1362	int error;
1363
1364	gp = gpp->gpp_geom;
1365	G_PART_TRACE((G_T_TOPOLOGY, "%s(%s)", __func__, gp->name));
1366	g_topology_assert();
1367
1368	table = gp->softc;
1369
1370	if (gpp->gpp_parms & G_PART_PARM_INDEX) {
1371		LIST_FOREACH(entry, &table->gpt_entry, gpe_entry) {
1372			if (entry->gpe_deleted || entry->gpe_internal)
1373				continue;
1374			if (entry->gpe_index == gpp->gpp_index)
1375				break;
1376		}
1377		if (entry == NULL) {
1378			gctl_error(req, "%d index '%d'", ENOENT,
1379			    gpp->gpp_index);
1380			return (ENOENT);
1381		}
1382	} else
1383		entry = NULL;
1384
1385	error = G_PART_SETUNSET(table, entry, gpp->gpp_attrib, set);
1386	if (error) {
1387		gctl_error(req, "%d attrib '%s'", error, gpp->gpp_attrib);
1388		return (error);
1389	}
1390
1391	/* Provide feedback if so requested. */
1392	if (gpp->gpp_parms & G_PART_PARM_OUTPUT) {
1393		sb = sbuf_new_auto();
1394		sbuf_printf(sb, "%s %sset on ", gpp->gpp_attrib,
1395		    (set) ? "" : "un");
1396		if (entry)
1397			G_PART_FULLNAME(table, entry, sb, gp->name);
1398		else
1399			sbuf_cat(sb, gp->name);
1400		sbuf_cat(sb, "\n");
1401		sbuf_finish(sb);
1402		gctl_set_param(req, "output", sbuf_data(sb), sbuf_len(sb) + 1);
1403		sbuf_delete(sb);
1404	}
1405	return (0);
1406}
1407
1408static int
1409g_part_ctl_undo(struct gctl_req *req, struct g_part_parms *gpp)
1410{
1411	struct g_consumer *cp;
1412	struct g_provider *pp;
1413	struct g_geom *gp;
1414	struct g_part_entry *entry, *tmp;
1415	struct g_part_table *table;
1416	int error, reprobe;
1417
1418	gp = gpp->gpp_geom;
1419	G_PART_TRACE((G_T_TOPOLOGY, "%s(%s)", __func__, gp->name));
1420	g_topology_assert();
1421
1422	table = gp->softc;
1423	if (!table->gpt_opened) {
1424		gctl_error(req, "%d", EPERM);
1425		return (EPERM);
1426	}
1427
1428	cp = LIST_FIRST(&gp->consumer);
1429	LIST_FOREACH_SAFE(entry, &table->gpt_entry, gpe_entry, tmp) {
1430		entry->gpe_modified = 0;
1431		if (entry->gpe_created) {
1432			pp = entry->gpe_pp;
1433			if (pp != NULL) {
1434				pp->private = NULL;
1435				entry->gpe_pp = NULL;
1436				g_wither_provider(pp, ENXIO);
1437			}
1438			entry->gpe_deleted = 1;
1439		}
1440		if (entry->gpe_deleted) {
1441			LIST_REMOVE(entry, gpe_entry);
1442			g_free(entry);
1443		}
1444	}
1445
1446	g_topology_unlock();
1447
1448	reprobe = (table->gpt_scheme == &g_part_null_scheme ||
1449	    table->gpt_created) ? 1 : 0;
1450
1451	if (reprobe) {
1452		LIST_FOREACH(entry, &table->gpt_entry, gpe_entry) {
1453			if (entry->gpe_internal)
1454				continue;
1455			error = EBUSY;
1456			goto fail;
1457		}
1458		while ((entry = LIST_FIRST(&table->gpt_entry)) != NULL) {
1459			LIST_REMOVE(entry, gpe_entry);
1460			g_free(entry);
1461		}
1462		error = g_part_probe(gp, cp, table->gpt_depth);
1463		if (error) {
1464			g_topology_lock();
1465			g_access(cp, -1, -1, -1);
1466			g_part_wither(gp, error);
1467			return (0);
1468		}
1469		table = gp->softc;
1470
1471		/*
1472		 * Synthesize a disk geometry. Some partitioning schemes
1473		 * depend on it and since some file systems need it even
1474		 * when the partitition scheme doesn't, we do it here in
1475		 * scheme-independent code.
1476		 */
1477		pp = cp->provider;
1478		g_part_geometry(table, cp, pp->mediasize / pp->sectorsize);
1479	}
1480
1481	error = G_PART_READ(table, cp);
1482	if (error)
1483		goto fail;
1484	error = g_part_check_integrity(table, cp);
1485	if (error)
1486		goto fail;
1487
1488	g_topology_lock();
1489	LIST_FOREACH(entry, &table->gpt_entry, gpe_entry) {
1490		if (!entry->gpe_internal)
1491			g_part_new_provider(gp, table, entry);
1492	}
1493
1494	table->gpt_opened = 0;
1495	g_access(cp, -1, -1, -1);
1496	return (0);
1497
1498fail:
1499	g_topology_lock();
1500	gctl_error(req, "%d", error);
1501	return (error);
1502}
1503
1504static void
1505g_part_wither(struct g_geom *gp, int error)
1506{
1507	struct g_part_entry *entry;
1508	struct g_part_table *table;
1509
1510	table = gp->softc;
1511	if (table != NULL) {
1512		G_PART_DESTROY(table, NULL);
1513		while ((entry = LIST_FIRST(&table->gpt_entry)) != NULL) {
1514			LIST_REMOVE(entry, gpe_entry);
1515			g_free(entry);
1516		}
1517		if (gp->softc != NULL) {
1518			kobj_delete((kobj_t)gp->softc, M_GEOM);
1519			gp->softc = NULL;
1520		}
1521	}
1522	g_wither_geom(gp, error);
1523}
1524
1525/*
1526 * Class methods.
1527 */
1528
1529static void
1530g_part_ctlreq(struct gctl_req *req, struct g_class *mp, const char *verb)
1531{
1532	struct g_part_parms gpp;
1533	struct g_part_table *table;
1534	struct gctl_req_arg *ap;
1535	enum g_part_ctl ctlreq;
1536	unsigned int i, mparms, oparms, parm;
1537	int auto_commit, close_on_error;
1538	int error, modifies;
1539
1540	G_PART_TRACE((G_T_TOPOLOGY, "%s(%s,%s)", __func__, mp->name, verb));
1541	g_topology_assert();
1542
1543	ctlreq = G_PART_CTL_NONE;
1544	modifies = 1;
1545	mparms = 0;
1546	oparms = G_PART_PARM_FLAGS | G_PART_PARM_OUTPUT | G_PART_PARM_VERSION;
1547	switch (*verb) {
1548	case 'a':
1549		if (!strcmp(verb, "add")) {
1550			ctlreq = G_PART_CTL_ADD;
1551			mparms |= G_PART_PARM_GEOM | G_PART_PARM_SIZE |
1552			    G_PART_PARM_START | G_PART_PARM_TYPE;
1553			oparms |= G_PART_PARM_INDEX | G_PART_PARM_LABEL;
1554		}
1555		break;
1556	case 'b':
1557		if (!strcmp(verb, "bootcode")) {
1558			ctlreq = G_PART_CTL_BOOTCODE;
1559			mparms |= G_PART_PARM_GEOM | G_PART_PARM_BOOTCODE;
1560		}
1561		break;
1562	case 'c':
1563		if (!strcmp(verb, "commit")) {
1564			ctlreq = G_PART_CTL_COMMIT;
1565			mparms |= G_PART_PARM_GEOM;
1566			modifies = 0;
1567		} else if (!strcmp(verb, "create")) {
1568			ctlreq = G_PART_CTL_CREATE;
1569			mparms |= G_PART_PARM_PROVIDER | G_PART_PARM_SCHEME;
1570			oparms |= G_PART_PARM_ENTRIES;
1571		}
1572		break;
1573	case 'd':
1574		if (!strcmp(verb, "delete")) {
1575			ctlreq = G_PART_CTL_DELETE;
1576			mparms |= G_PART_PARM_GEOM | G_PART_PARM_INDEX;
1577		} else if (!strcmp(verb, "destroy")) {
1578			ctlreq = G_PART_CTL_DESTROY;
1579			mparms |= G_PART_PARM_GEOM;
1580			oparms |= G_PART_PARM_FORCE;
1581		}
1582		break;
1583	case 'm':
1584		if (!strcmp(verb, "modify")) {
1585			ctlreq = G_PART_CTL_MODIFY;
1586			mparms |= G_PART_PARM_GEOM | G_PART_PARM_INDEX;
1587			oparms |= G_PART_PARM_LABEL | G_PART_PARM_TYPE;
1588		} else if (!strcmp(verb, "move")) {
1589			ctlreq = G_PART_CTL_MOVE;
1590			mparms |= G_PART_PARM_GEOM | G_PART_PARM_INDEX;
1591		}
1592		break;
1593	case 'r':
1594		if (!strcmp(verb, "recover")) {
1595			ctlreq = G_PART_CTL_RECOVER;
1596			mparms |= G_PART_PARM_GEOM;
1597		} else if (!strcmp(verb, "resize")) {
1598			ctlreq = G_PART_CTL_RESIZE;
1599			mparms |= G_PART_PARM_GEOM | G_PART_PARM_INDEX |
1600			    G_PART_PARM_SIZE;
1601		}
1602		break;
1603	case 's':
1604		if (!strcmp(verb, "set")) {
1605			ctlreq = G_PART_CTL_SET;
1606			mparms |= G_PART_PARM_ATTRIB | G_PART_PARM_GEOM;
1607			oparms |= G_PART_PARM_INDEX;
1608		}
1609		break;
1610	case 'u':
1611		if (!strcmp(verb, "undo")) {
1612			ctlreq = G_PART_CTL_UNDO;
1613			mparms |= G_PART_PARM_GEOM;
1614			modifies = 0;
1615		} else if (!strcmp(verb, "unset")) {
1616			ctlreq = G_PART_CTL_UNSET;
1617			mparms |= G_PART_PARM_ATTRIB | G_PART_PARM_GEOM;
1618			oparms |= G_PART_PARM_INDEX;
1619		}
1620		break;
1621	}
1622	if (ctlreq == G_PART_CTL_NONE) {
1623		gctl_error(req, "%d verb '%s'", EINVAL, verb);
1624		return;
1625	}
1626
1627	bzero(&gpp, sizeof(gpp));
1628	for (i = 0; i < req->narg; i++) {
1629		ap = &req->arg[i];
1630		parm = 0;
1631		switch (ap->name[0]) {
1632		case 'a':
1633			if (!strcmp(ap->name, "arg0")) {
1634				parm = mparms &
1635				    (G_PART_PARM_GEOM | G_PART_PARM_PROVIDER);
1636			}
1637			if (!strcmp(ap->name, "attrib"))
1638				parm = G_PART_PARM_ATTRIB;
1639			break;
1640		case 'b':
1641			if (!strcmp(ap->name, "bootcode"))
1642				parm = G_PART_PARM_BOOTCODE;
1643			break;
1644		case 'c':
1645			if (!strcmp(ap->name, "class"))
1646				continue;
1647			break;
1648		case 'e':
1649			if (!strcmp(ap->name, "entries"))
1650				parm = G_PART_PARM_ENTRIES;
1651			break;
1652		case 'f':
1653			if (!strcmp(ap->name, "flags"))
1654				parm = G_PART_PARM_FLAGS;
1655			else if (!strcmp(ap->name, "force"))
1656				parm = G_PART_PARM_FORCE;
1657			break;
1658		case 'i':
1659			if (!strcmp(ap->name, "index"))
1660				parm = G_PART_PARM_INDEX;
1661			break;
1662		case 'l':
1663			if (!strcmp(ap->name, "label"))
1664				parm = G_PART_PARM_LABEL;
1665			break;
1666		case 'o':
1667			if (!strcmp(ap->name, "output"))
1668				parm = G_PART_PARM_OUTPUT;
1669			break;
1670		case 's':
1671			if (!strcmp(ap->name, "scheme"))
1672				parm = G_PART_PARM_SCHEME;
1673			else if (!strcmp(ap->name, "size"))
1674				parm = G_PART_PARM_SIZE;
1675			else if (!strcmp(ap->name, "start"))
1676				parm = G_PART_PARM_START;
1677			break;
1678		case 't':
1679			if (!strcmp(ap->name, "type"))
1680				parm = G_PART_PARM_TYPE;
1681			break;
1682		case 'v':
1683			if (!strcmp(ap->name, "verb"))
1684				continue;
1685			else if (!strcmp(ap->name, "version"))
1686				parm = G_PART_PARM_VERSION;
1687			break;
1688		}
1689		if ((parm & (mparms | oparms)) == 0) {
1690			gctl_error(req, "%d param '%s'", EINVAL, ap->name);
1691			return;
1692		}
1693		switch (parm) {
1694		case G_PART_PARM_ATTRIB:
1695			error = g_part_parm_str(req, ap->name,
1696			    &gpp.gpp_attrib);
1697			break;
1698		case G_PART_PARM_BOOTCODE:
1699			error = g_part_parm_bootcode(req, ap->name,
1700			    &gpp.gpp_codeptr, &gpp.gpp_codesize);
1701			break;
1702		case G_PART_PARM_ENTRIES:
1703			error = g_part_parm_intmax(req, ap->name,
1704			    &gpp.gpp_entries);
1705			break;
1706		case G_PART_PARM_FLAGS:
1707			error = g_part_parm_str(req, ap->name, &gpp.gpp_flags);
1708			break;
1709		case G_PART_PARM_FORCE:
1710			error = g_part_parm_uint32(req, ap->name,
1711			    &gpp.gpp_force);
1712			break;
1713		case G_PART_PARM_GEOM:
1714			error = g_part_parm_geom(req, ap->name, &gpp.gpp_geom);
1715			break;
1716		case G_PART_PARM_INDEX:
1717			error = g_part_parm_intmax(req, ap->name,
1718			    &gpp.gpp_index);
1719			break;
1720		case G_PART_PARM_LABEL:
1721			error = g_part_parm_str(req, ap->name, &gpp.gpp_label);
1722			break;
1723		case G_PART_PARM_OUTPUT:
1724			error = 0;	/* Write-only parameter */
1725			break;
1726		case G_PART_PARM_PROVIDER:
1727			error = g_part_parm_provider(req, ap->name,
1728			    &gpp.gpp_provider);
1729			break;
1730		case G_PART_PARM_SCHEME:
1731			error = g_part_parm_scheme(req, ap->name,
1732			    &gpp.gpp_scheme);
1733			break;
1734		case G_PART_PARM_SIZE:
1735			error = g_part_parm_quad(req, ap->name, &gpp.gpp_size);
1736			break;
1737		case G_PART_PARM_START:
1738			error = g_part_parm_quad(req, ap->name,
1739			    &gpp.gpp_start);
1740			break;
1741		case G_PART_PARM_TYPE:
1742			error = g_part_parm_str(req, ap->name, &gpp.gpp_type);
1743			break;
1744		case G_PART_PARM_VERSION:
1745			error = g_part_parm_uint32(req, ap->name,
1746			    &gpp.gpp_version);
1747			break;
1748		default:
1749			error = EDOOFUS;
1750			gctl_error(req, "%d %s", error, ap->name);
1751			break;
1752		}
1753		if (error != 0) {
1754			if (error == ENOATTR) {
1755				gctl_error(req, "%d param '%s'", error,
1756				    ap->name);
1757			}
1758			return;
1759		}
1760		gpp.gpp_parms |= parm;
1761	}
1762	if ((gpp.gpp_parms & mparms) != mparms) {
1763		parm = mparms - (gpp.gpp_parms & mparms);
1764		gctl_error(req, "%d param '%x'", ENOATTR, parm);
1765		return;
1766	}
1767
1768	/* Obtain permissions if possible/necessary. */
1769	close_on_error = 0;
1770	table = NULL;
1771	if (modifies && (gpp.gpp_parms & G_PART_PARM_GEOM)) {
1772		table = gpp.gpp_geom->softc;
1773		if (table != NULL && table->gpt_corrupt &&
1774		    ctlreq != G_PART_CTL_DESTROY &&
1775		    ctlreq != G_PART_CTL_RECOVER) {
1776			gctl_error(req, "%d table '%s' is corrupt",
1777			    EPERM, gpp.gpp_geom->name);
1778			return;
1779		}
1780		if (table != NULL && !table->gpt_opened) {
1781			error = g_access(LIST_FIRST(&gpp.gpp_geom->consumer),
1782			    1, 1, 1);
1783			if (error) {
1784				gctl_error(req, "%d geom '%s'", error,
1785				    gpp.gpp_geom->name);
1786				return;
1787			}
1788			table->gpt_opened = 1;
1789			close_on_error = 1;
1790		}
1791	}
1792
1793	/* Allow the scheme to check or modify the parameters. */
1794	if (table != NULL) {
1795		error = G_PART_PRECHECK(table, ctlreq, &gpp);
1796		if (error) {
1797			gctl_error(req, "%d pre-check failed", error);
1798			goto out;
1799		}
1800	} else
1801		error = EDOOFUS;	/* Prevent bogus uninit. warning. */
1802
1803	switch (ctlreq) {
1804	case G_PART_CTL_NONE:
1805		panic("%s", __func__);
1806	case G_PART_CTL_ADD:
1807		error = g_part_ctl_add(req, &gpp);
1808		break;
1809	case G_PART_CTL_BOOTCODE:
1810		error = g_part_ctl_bootcode(req, &gpp);
1811		break;
1812	case G_PART_CTL_COMMIT:
1813		error = g_part_ctl_commit(req, &gpp);
1814		break;
1815	case G_PART_CTL_CREATE:
1816		error = g_part_ctl_create(req, &gpp);
1817		break;
1818	case G_PART_CTL_DELETE:
1819		error = g_part_ctl_delete(req, &gpp);
1820		break;
1821	case G_PART_CTL_DESTROY:
1822		error = g_part_ctl_destroy(req, &gpp);
1823		break;
1824	case G_PART_CTL_MODIFY:
1825		error = g_part_ctl_modify(req, &gpp);
1826		break;
1827	case G_PART_CTL_MOVE:
1828		error = g_part_ctl_move(req, &gpp);
1829		break;
1830	case G_PART_CTL_RECOVER:
1831		error = g_part_ctl_recover(req, &gpp);
1832		break;
1833	case G_PART_CTL_RESIZE:
1834		error = g_part_ctl_resize(req, &gpp);
1835		break;
1836	case G_PART_CTL_SET:
1837		error = g_part_ctl_setunset(req, &gpp, 1);
1838		break;
1839	case G_PART_CTL_UNDO:
1840		error = g_part_ctl_undo(req, &gpp);
1841		break;
1842	case G_PART_CTL_UNSET:
1843		error = g_part_ctl_setunset(req, &gpp, 0);
1844		break;
1845	}
1846
1847	/* Implement automatic commit. */
1848	if (!error) {
1849		auto_commit = (modifies &&
1850		    (gpp.gpp_parms & G_PART_PARM_FLAGS) &&
1851		    strchr(gpp.gpp_flags, 'C') != NULL) ? 1 : 0;
1852		if (auto_commit) {
1853			KASSERT(gpp.gpp_parms & G_PART_PARM_GEOM, ("%s",
1854			    __func__));
1855			error = g_part_ctl_commit(req, &gpp);
1856		}
1857	}
1858
1859 out:
1860	if (error && close_on_error) {
1861		g_access(LIST_FIRST(&gpp.gpp_geom->consumer), -1, -1, -1);
1862		table->gpt_opened = 0;
1863	}
1864}
1865
1866static int
1867g_part_destroy_geom(struct gctl_req *req, struct g_class *mp,
1868    struct g_geom *gp)
1869{
1870
1871	G_PART_TRACE((G_T_TOPOLOGY, "%s(%s,%s)", __func__, mp->name, gp->name));
1872	g_topology_assert();
1873
1874	g_part_wither(gp, EINVAL);
1875	return (0);
1876}
1877
1878static struct g_geom *
1879g_part_taste(struct g_class *mp, struct g_provider *pp, int flags __unused)
1880{
1881	struct g_consumer *cp;
1882	struct g_geom *gp;
1883	struct g_part_entry *entry;
1884	struct g_part_table *table;
1885	struct root_hold_token *rht;
1886	int attr, depth;
1887	int error;
1888
1889	G_PART_TRACE((G_T_TOPOLOGY, "%s(%s,%s)", __func__, mp->name, pp->name));
1890	g_topology_assert();
1891
1892	/* Skip providers that are already open for writing. */
1893	if (pp->acw > 0)
1894		return (NULL);
1895
1896	/*
1897	 * Create a GEOM with consumer and hook it up to the provider.
1898	 * With that we become part of the topology. Optain read access
1899	 * to the provider.
1900	 */
1901	gp = g_new_geomf(mp, "%s", pp->name);
1902	cp = g_new_consumer(gp);
1903	cp->flags |= G_CF_DIRECT_SEND | G_CF_DIRECT_RECEIVE;
1904	error = g_attach(cp, pp);
1905	if (error == 0)
1906		error = g_access(cp, 1, 0, 0);
1907	if (error != 0) {
1908		if (cp->provider)
1909			g_detach(cp);
1910		g_destroy_consumer(cp);
1911		g_destroy_geom(gp);
1912		return (NULL);
1913	}
1914
1915	rht = root_mount_hold(mp->name);
1916	g_topology_unlock();
1917
1918	/*
1919	 * Short-circuit the whole probing galore when there's no
1920	 * media present.
1921	 */
1922	if (pp->mediasize == 0 || pp->sectorsize == 0) {
1923		error = ENODEV;
1924		goto fail;
1925	}
1926
1927	/* Make sure we can nest and if so, determine our depth. */
1928	error = g_getattr("PART::isleaf", cp, &attr);
1929	if (!error && attr) {
1930		error = ENODEV;
1931		goto fail;
1932	}
1933	error = g_getattr("PART::depth", cp, &attr);
1934	depth = (!error) ? attr + 1 : 0;
1935
1936	error = g_part_probe(gp, cp, depth);
1937	if (error)
1938		goto fail;
1939
1940	table = gp->softc;
1941
1942	/*
1943	 * Synthesize a disk geometry. Some partitioning schemes
1944	 * depend on it and since some file systems need it even
1945	 * when the partitition scheme doesn't, we do it here in
1946	 * scheme-independent code.
1947	 */
1948	g_part_geometry(table, cp, pp->mediasize / pp->sectorsize);
1949
1950	error = G_PART_READ(table, cp);
1951	if (error)
1952		goto fail;
1953	error = g_part_check_integrity(table, cp);
1954	if (error)
1955		goto fail;
1956
1957	g_topology_lock();
1958	LIST_FOREACH(entry, &table->gpt_entry, gpe_entry) {
1959		if (!entry->gpe_internal)
1960			g_part_new_provider(gp, table, entry);
1961	}
1962
1963	root_mount_rel(rht);
1964	g_access(cp, -1, 0, 0);
1965	return (gp);
1966
1967 fail:
1968	g_topology_lock();
1969	root_mount_rel(rht);
1970	g_access(cp, -1, 0, 0);
1971	g_detach(cp);
1972	g_destroy_consumer(cp);
1973	g_destroy_geom(gp);
1974	return (NULL);
1975}
1976
1977/*
1978 * Geom methods.
1979 */
1980
1981static int
1982g_part_access(struct g_provider *pp, int dr, int dw, int de)
1983{
1984	struct g_consumer *cp;
1985
1986	G_PART_TRACE((G_T_ACCESS, "%s(%s,%d,%d,%d)", __func__, pp->name, dr,
1987	    dw, de));
1988
1989	cp = LIST_FIRST(&pp->geom->consumer);
1990
1991	/* We always gain write-exclusive access. */
1992	return (g_access(cp, dr, dw, dw + de));
1993}
1994
1995static void
1996g_part_dumpconf(struct sbuf *sb, const char *indent, struct g_geom *gp,
1997    struct g_consumer *cp, struct g_provider *pp)
1998{
1999	char buf[64];
2000	struct g_part_entry *entry;
2001	struct g_part_table *table;
2002
2003	KASSERT(sb != NULL && gp != NULL, ("%s", __func__));
2004	table = gp->softc;
2005
2006	if (indent == NULL) {
2007		KASSERT(cp == NULL && pp != NULL, ("%s", __func__));
2008		entry = pp->private;
2009		if (entry == NULL)
2010			return;
2011		sbuf_printf(sb, " i %u o %ju ty %s", entry->gpe_index,
2012		    (uintmax_t)entry->gpe_offset,
2013		    G_PART_TYPE(table, entry, buf, sizeof(buf)));
2014		/*
2015		 * libdisk compatibility quirk - the scheme dumps the
2016		 * slicer name and partition type in a way that is
2017		 * compatible with libdisk. When libdisk is not used
2018		 * anymore, this should go away.
2019		 */
2020		G_PART_DUMPCONF(table, entry, sb, indent);
2021	} else if (cp != NULL) {	/* Consumer configuration. */
2022		KASSERT(pp == NULL, ("%s", __func__));
2023		/* none */
2024	} else if (pp != NULL) {	/* Provider configuration. */
2025		entry = pp->private;
2026		if (entry == NULL)
2027			return;
2028		sbuf_printf(sb, "%s<start>%ju</start>\n", indent,
2029		    (uintmax_t)entry->gpe_start);
2030		sbuf_printf(sb, "%s<end>%ju</end>\n", indent,
2031		    (uintmax_t)entry->gpe_end);
2032		sbuf_printf(sb, "%s<index>%u</index>\n", indent,
2033		    entry->gpe_index);
2034		sbuf_printf(sb, "%s<type>%s</type>\n", indent,
2035		    G_PART_TYPE(table, entry, buf, sizeof(buf)));
2036		sbuf_printf(sb, "%s<offset>%ju</offset>\n", indent,
2037		    (uintmax_t)entry->gpe_offset);
2038		sbuf_printf(sb, "%s<length>%ju</length>\n", indent,
2039		    (uintmax_t)pp->mediasize);
2040		G_PART_DUMPCONF(table, entry, sb, indent);
2041	} else {			/* Geom configuration. */
2042		sbuf_printf(sb, "%s<scheme>%s</scheme>\n", indent,
2043		    table->gpt_scheme->name);
2044		sbuf_printf(sb, "%s<entries>%u</entries>\n", indent,
2045		    table->gpt_entries);
2046		sbuf_printf(sb, "%s<first>%ju</first>\n", indent,
2047		    (uintmax_t)table->gpt_first);
2048		sbuf_printf(sb, "%s<last>%ju</last>\n", indent,
2049		    (uintmax_t)table->gpt_last);
2050		sbuf_printf(sb, "%s<fwsectors>%u</fwsectors>\n", indent,
2051		    table->gpt_sectors);
2052		sbuf_printf(sb, "%s<fwheads>%u</fwheads>\n", indent,
2053		    table->gpt_heads);
2054		sbuf_printf(sb, "%s<state>%s</state>\n", indent,
2055		    table->gpt_corrupt ? "CORRUPT": "OK");
2056		sbuf_printf(sb, "%s<modified>%s</modified>\n", indent,
2057		    table->gpt_opened ? "true": "false");
2058		G_PART_DUMPCONF(table, NULL, sb, indent);
2059	}
2060}
2061
2062static void
2063g_part_resize(struct g_consumer *cp)
2064{
2065	struct g_part_table *table;
2066
2067	G_PART_TRACE((G_T_TOPOLOGY, "%s(%s)", __func__, cp->provider->name));
2068	g_topology_assert();
2069
2070	table = cp->geom->softc;
2071	if (table->gpt_opened == 0) {
2072		if (g_access(cp, 1, 1, 1) != 0)
2073			return;
2074		table->gpt_opened = 1;
2075	}
2076	if (G_PART_RESIZE(table, NULL, NULL) == 0)
2077		printf("GEOM_PART: %s was automatically resized.\n"
2078		    "  Use `gpart commit %s` to save changes or "
2079		    "`gpart undo %s` to revert them.\n", cp->geom->name,
2080		    cp->geom->name, cp->geom->name);
2081	if (g_part_check_integrity(table, cp) != 0) {
2082		g_access(cp, -1, -1, -1);
2083		table->gpt_opened = 0;
2084		g_part_wither(table->gpt_gp, ENXIO);
2085	}
2086}
2087
2088static void
2089g_part_orphan(struct g_consumer *cp)
2090{
2091	struct g_provider *pp;
2092	struct g_part_table *table;
2093
2094	pp = cp->provider;
2095	KASSERT(pp != NULL, ("%s", __func__));
2096	G_PART_TRACE((G_T_TOPOLOGY, "%s(%s)", __func__, pp->name));
2097	g_topology_assert();
2098
2099	KASSERT(pp->error != 0, ("%s", __func__));
2100	table = cp->geom->softc;
2101	if (table != NULL && table->gpt_opened)
2102		g_access(cp, -1, -1, -1);
2103	g_part_wither(cp->geom, pp->error);
2104}
2105
2106static void
2107g_part_spoiled(struct g_consumer *cp)
2108{
2109
2110	G_PART_TRACE((G_T_TOPOLOGY, "%s(%s)", __func__, cp->provider->name));
2111	g_topology_assert();
2112
2113	cp->flags |= G_CF_ORPHAN;
2114	g_part_wither(cp->geom, ENXIO);
2115}
2116
2117static void
2118g_part_start(struct bio *bp)
2119{
2120	struct bio *bp2;
2121	struct g_consumer *cp;
2122	struct g_geom *gp;
2123	struct g_part_entry *entry;
2124	struct g_part_table *table;
2125	struct g_kerneldump *gkd;
2126	struct g_provider *pp;
2127	char buf[64];
2128
2129	pp = bp->bio_to;
2130	gp = pp->geom;
2131	table = gp->softc;
2132	cp = LIST_FIRST(&gp->consumer);
2133
2134	G_PART_TRACE((G_T_BIO, "%s: cmd=%d, provider=%s", __func__, bp->bio_cmd,
2135	    pp->name));
2136
2137	entry = pp->private;
2138	if (entry == NULL) {
2139		g_io_deliver(bp, ENXIO);
2140		return;
2141	}
2142
2143	switch(bp->bio_cmd) {
2144	case BIO_DELETE:
2145	case BIO_READ:
2146	case BIO_WRITE:
2147		if (bp->bio_offset >= pp->mediasize) {
2148			g_io_deliver(bp, EIO);
2149			return;
2150		}
2151		bp2 = g_clone_bio(bp);
2152		if (bp2 == NULL) {
2153			g_io_deliver(bp, ENOMEM);
2154			return;
2155		}
2156		if (bp2->bio_offset + bp2->bio_length > pp->mediasize)
2157			bp2->bio_length = pp->mediasize - bp2->bio_offset;
2158		bp2->bio_done = g_std_done;
2159		bp2->bio_offset += entry->gpe_offset;
2160		g_io_request(bp2, cp);
2161		return;
2162	case BIO_FLUSH:
2163		break;
2164	case BIO_GETATTR:
2165		if (g_handleattr_int(bp, "GEOM::fwheads", table->gpt_heads))
2166			return;
2167		if (g_handleattr_int(bp, "GEOM::fwsectors", table->gpt_sectors))
2168			return;
2169		if (g_handleattr_int(bp, "PART::isleaf", table->gpt_isleaf))
2170			return;
2171		if (g_handleattr_int(bp, "PART::depth", table->gpt_depth))
2172			return;
2173		if (g_handleattr_str(bp, "PART::scheme",
2174		    table->gpt_scheme->name))
2175			return;
2176		if (g_handleattr_str(bp, "PART::type",
2177		    G_PART_TYPE(table, entry, buf, sizeof(buf))))
2178			return;
2179		if (!strcmp("GEOM::kerneldump", bp->bio_attribute)) {
2180			/*
2181			 * Check that the partition is suitable for kernel
2182			 * dumps. Typically only swap partitions should be
2183			 * used. If the request comes from the nested scheme
2184			 * we allow dumping there as well.
2185			 */
2186			if ((bp->bio_from == NULL ||
2187			    bp->bio_from->geom->class != &g_part_class) &&
2188			    G_PART_DUMPTO(table, entry) == 0) {
2189				g_io_deliver(bp, ENODEV);
2190				printf("GEOM_PART: Partition '%s' not suitable"
2191				    " for kernel dumps (wrong type?)\n",
2192				    pp->name);
2193				return;
2194			}
2195			gkd = (struct g_kerneldump *)bp->bio_data;
2196			if (gkd->offset >= pp->mediasize) {
2197				g_io_deliver(bp, EIO);
2198				return;
2199			}
2200			if (gkd->offset + gkd->length > pp->mediasize)
2201				gkd->length = pp->mediasize - gkd->offset;
2202			gkd->offset += entry->gpe_offset;
2203		}
2204		break;
2205	default:
2206		g_io_deliver(bp, EOPNOTSUPP);
2207		return;
2208	}
2209
2210	bp2 = g_clone_bio(bp);
2211	if (bp2 == NULL) {
2212		g_io_deliver(bp, ENOMEM);
2213		return;
2214	}
2215	bp2->bio_done = g_std_done;
2216	g_io_request(bp2, cp);
2217}
2218
2219static void
2220g_part_init(struct g_class *mp)
2221{
2222
2223	TAILQ_INSERT_HEAD(&g_part_schemes, &g_part_null_scheme, scheme_list);
2224}
2225
2226static void
2227g_part_fini(struct g_class *mp)
2228{
2229
2230	TAILQ_REMOVE(&g_part_schemes, &g_part_null_scheme, scheme_list);
2231}
2232
2233static void
2234g_part_unload_event(void *arg, int flag)
2235{
2236	struct g_consumer *cp;
2237	struct g_geom *gp;
2238	struct g_provider *pp;
2239	struct g_part_scheme *scheme;
2240	struct g_part_table *table;
2241	uintptr_t *xchg;
2242	int acc, error;
2243
2244	if (flag == EV_CANCEL)
2245		return;
2246
2247	xchg = arg;
2248	error = 0;
2249	scheme = (void *)(*xchg);
2250
2251	g_topology_assert();
2252
2253	LIST_FOREACH(gp, &g_part_class.geom, geom) {
2254		table = gp->softc;
2255		if (table->gpt_scheme != scheme)
2256			continue;
2257
2258		acc = 0;
2259		LIST_FOREACH(pp, &gp->provider, provider)
2260			acc += pp->acr + pp->acw + pp->ace;
2261		LIST_FOREACH(cp, &gp->consumer, consumer)
2262			acc += cp->acr + cp->acw + cp->ace;
2263
2264		if (!acc)
2265			g_part_wither(gp, ENOSYS);
2266		else
2267			error = EBUSY;
2268	}
2269
2270	if (!error)
2271		TAILQ_REMOVE(&g_part_schemes, scheme, scheme_list);
2272
2273	*xchg = error;
2274}
2275
2276int
2277g_part_modevent(module_t mod, int type, struct g_part_scheme *scheme)
2278{
2279	struct g_part_scheme *iter;
2280	uintptr_t arg;
2281	int error;
2282
2283	error = 0;
2284	switch (type) {
2285	case MOD_LOAD:
2286		TAILQ_FOREACH(iter, &g_part_schemes, scheme_list) {
2287			if (scheme == iter) {
2288				printf("GEOM_PART: scheme %s is already "
2289				    "registered!\n", scheme->name);
2290				break;
2291			}
2292		}
2293		if (iter == NULL) {
2294			TAILQ_INSERT_TAIL(&g_part_schemes, scheme,
2295			    scheme_list);
2296			g_retaste(&g_part_class);
2297		}
2298		break;
2299	case MOD_UNLOAD:
2300		arg = (uintptr_t)scheme;
2301		error = g_waitfor_event(g_part_unload_event, &arg, M_WAITOK,
2302		    NULL);
2303		if (error == 0)
2304			error = arg;
2305		break;
2306	default:
2307		error = EOPNOTSUPP;
2308		break;
2309	}
2310
2311	return (error);
2312}
2313