g_part.c revision 256880
1251881Speter/*-
2251881Speter * Copyright (c) 2002, 2005-2009 Marcel Moolenaar
3251881Speter * All rights reserved.
4251881Speter *
5251881Speter * Redistribution and use in source and binary forms, with or without
6251881Speter * modification, are permitted provided that the following conditions
7251881Speter * are met:
8251881Speter *
9251881Speter * 1. Redistributions of source code must retain the above copyright
10251881Speter *    notice, this list of conditions and the following disclaimer.
11251881Speter * 2. Redistributions in binary form must reproduce the above copyright
12251881Speter *    notice, this list of conditions and the following disclaimer in the
13251881Speter *    documentation and/or other materials provided with the distribution.
14251881Speter *
15251881Speter * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16251881Speter * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17251881Speter * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18251881Speter * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19251881Speter * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20251881Speter * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21251881Speter * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22251881Speter * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23251881Speter * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24251881Speter * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25251881Speter */
26251881Speter
27251881Speter#include <sys/cdefs.h>
28251881Speter__FBSDID("$FreeBSD: head/sys/geom/part/g_part.c 256880 2013-10-22 08:22:19Z mav $");
29251881Speter
30251881Speter#include <sys/param.h>
31251881Speter#include <sys/bio.h>
32251881Speter#include <sys/endian.h>
33251881Speter#include <sys/kernel.h>
34251881Speter#include <sys/kobj.h>
35251881Speter#include <sys/limits.h>
36251881Speter#include <sys/lock.h>
37251881Speter#include <sys/malloc.h>
38251881Speter#include <sys/mutex.h>
39251881Speter#include <sys/queue.h>
40251881Speter#include <sys/sbuf.h>
41251881Speter#include <sys/sysctl.h>
42251881Speter#include <sys/systm.h>
43251881Speter#include <sys/uuid.h>
44251881Speter#include <geom/geom.h>
45251881Speter#include <geom/geom_ctl.h>
46251881Speter#include <geom/geom_int.h>
47251881Speter#include <geom/part/g_part.h>
48251881Speter
49251881Speter#include "g_part_if.h"
50251881Speter
51251881Speter#ifndef _PATH_DEV
52251881Speter#define _PATH_DEV "/dev/"
53251881Speter#endif
54251881Speter
55251881Speterstatic kobj_method_t g_part_null_methods[] = {
56251881Speter	{ 0, 0 }
57251881Speter};
58251881Speter
59251881Speterstatic struct g_part_scheme g_part_null_scheme = {
60251881Speter	"(none)",
61251881Speter	g_part_null_methods,
62251881Speter	sizeof(struct g_part_table),
63251881Speter};
64251881Speter
65251881SpeterTAILQ_HEAD(, g_part_scheme) g_part_schemes =
66251881Speter    TAILQ_HEAD_INITIALIZER(g_part_schemes);
67289180Speter
68289180Speterstruct g_part_alias_list {
69289180Speter	const char *lexeme;
70251881Speter	enum g_part_alias alias;
71251881Speter} g_part_alias_list[G_PART_ALIAS_COUNT] = {
72251881Speter	{ "apple-boot", G_PART_ALIAS_APPLE_BOOT },
73251881Speter	{ "apple-hfs", G_PART_ALIAS_APPLE_HFS },
74251881Speter	{ "apple-label", G_PART_ALIAS_APPLE_LABEL },
75251881Speter	{ "apple-raid", G_PART_ALIAS_APPLE_RAID },
76251881Speter	{ "apple-raid-offline", G_PART_ALIAS_APPLE_RAID_OFFLINE },
77251881Speter	{ "apple-tv-recovery", G_PART_ALIAS_APPLE_TV_RECOVERY },
78251881Speter	{ "apple-ufs", G_PART_ALIAS_APPLE_UFS },
79251881Speter	{ "bios-boot", G_PART_ALIAS_BIOS_BOOT },
80251881Speter	{ "ebr", G_PART_ALIAS_EBR },
81251881Speter	{ "efi", G_PART_ALIAS_EFI },
82251881Speter	{ "fat16", G_PART_ALIAS_MS_FAT16 },
83251881Speter	{ "fat32", G_PART_ALIAS_MS_FAT32 },
84251881Speter	{ "freebsd", G_PART_ALIAS_FREEBSD },
85251881Speter	{ "freebsd-boot", G_PART_ALIAS_FREEBSD_BOOT },
86251881Speter	{ "freebsd-nandfs", G_PART_ALIAS_FREEBSD_NANDFS },
87251881Speter	{ "freebsd-swap", G_PART_ALIAS_FREEBSD_SWAP },
88251881Speter	{ "freebsd-ufs", G_PART_ALIAS_FREEBSD_UFS },
89251881Speter	{ "freebsd-vinum", G_PART_ALIAS_FREEBSD_VINUM },
90251881Speter	{ "freebsd-zfs", G_PART_ALIAS_FREEBSD_ZFS },
91251881Speter	{ "linux-data", G_PART_ALIAS_LINUX_DATA },
92251881Speter	{ "linux-lvm", G_PART_ALIAS_LINUX_LVM },
93251881Speter	{ "linux-raid", G_PART_ALIAS_LINUX_RAID },
94251881Speter	{ "linux-swap", G_PART_ALIAS_LINUX_SWAP },
95251881Speter	{ "mbr", G_PART_ALIAS_MBR },
96251881Speter	{ "ms-basic-data", G_PART_ALIAS_MS_BASIC_DATA },
97251881Speter	{ "ms-ldm-data", G_PART_ALIAS_MS_LDM_DATA },
98251881Speter	{ "ms-ldm-metadata", G_PART_ALIAS_MS_LDM_METADATA },
99251881Speter	{ "ms-reserved", G_PART_ALIAS_MS_RESERVED },
100251881Speter	{ "ntfs", G_PART_ALIAS_MS_NTFS },
101251881Speter	{ "netbsd-ccd", G_PART_ALIAS_NETBSD_CCD },
102251881Speter	{ "netbsd-cgd", G_PART_ALIAS_NETBSD_CGD },
103251881Speter	{ "netbsd-ffs", G_PART_ALIAS_NETBSD_FFS },
104251881Speter	{ "netbsd-lfs", G_PART_ALIAS_NETBSD_LFS },
105251881Speter	{ "netbsd-raid", G_PART_ALIAS_NETBSD_RAID },
106251881Speter	{ "netbsd-swap", G_PART_ALIAS_NETBSD_SWAP },
107251881Speter	{ "vmware-vmfs", G_PART_ALIAS_VMFS },
108251881Speter	{ "vmware-vmkdiag", G_PART_ALIAS_VMKDIAG },
109251881Speter	{ "vmware-reserved", G_PART_ALIAS_VMRESERVED },
110251881Speter};
111251881Speter
112251881SpeterSYSCTL_DECL(_kern_geom);
113251881SpeterSYSCTL_NODE(_kern_geom, OID_AUTO, part, CTLFLAG_RW, 0,
114251881Speter    "GEOM_PART stuff");
115251881Speterstatic u_int check_integrity = 1;
116251881SpeterTUNABLE_INT("kern.geom.part.check_integrity", &check_integrity);
117251881SpeterSYSCTL_UINT(_kern_geom_part, OID_AUTO, check_integrity,
118251881Speter    CTLFLAG_RW | CTLFLAG_TUN, &check_integrity, 1,
119251881Speter    "Enable integrity checking");
120251881Speter
121251881Speter/*
122251881Speter * The GEOM partitioning class.
123251881Speter */
124251881Speterstatic g_ctl_req_t g_part_ctlreq;
125251881Speterstatic g_ctl_destroy_geom_t g_part_destroy_geom;
126251881Speterstatic g_fini_t g_part_fini;
127251881Speterstatic g_init_t g_part_init;
128251881Speterstatic g_taste_t g_part_taste;
129251881Speter
130251881Speterstatic g_access_t g_part_access;
131251881Speterstatic g_dumpconf_t g_part_dumpconf;
132251881Speterstatic g_orphan_t g_part_orphan;
133251881Speterstatic g_spoiled_t g_part_spoiled;
134251881Speterstatic g_start_t g_part_start;
135251881Speterstatic g_resize_t g_part_resize;
136251881Speter
137251881Speterstatic struct g_class g_part_class = {
138251881Speter	.name = "PART",
139251881Speter	.version = G_VERSION,
140251881Speter	/* Class methods. */
141251881Speter	.ctlreq = g_part_ctlreq,
142251881Speter	.destroy_geom = g_part_destroy_geom,
143251881Speter	.fini = g_part_fini,
144251881Speter	.init = g_part_init,
145251881Speter	.taste = g_part_taste,
146251881Speter	/* Geom methods. */
147251881Speter	.access = g_part_access,
148251881Speter	.dumpconf = g_part_dumpconf,
149251881Speter	.orphan = g_part_orphan,
150251881Speter	.spoiled = g_part_spoiled,
151251881Speter	.start = g_part_start,
152251881Speter	.resize = g_part_resize
153251881Speter};
154251881Speter
155251881SpeterDECLARE_GEOM_CLASS(g_part_class, g_part);
156251881SpeterMODULE_VERSION(g_part, 0);
157251881Speter
158251881Speter/*
159251881Speter * Support functions.
160251881Speter */
161251881Speter
162251881Speterstatic void g_part_wither(struct g_geom *, int);
163251881Speter
164251881Speterconst char *
165251881Speterg_part_alias_name(enum g_part_alias alias)
166251881Speter{
167251881Speter	int i;
168251881Speter
169251881Speter	for (i = 0; i < G_PART_ALIAS_COUNT; i++) {
170251881Speter		if (g_part_alias_list[i].alias != alias)
171251881Speter			continue;
172251881Speter		return (g_part_alias_list[i].lexeme);
173251881Speter	}
174251881Speter
175251881Speter	return (NULL);
176251881Speter}
177251881Speter
178251881Spetervoid
179251881Speterg_part_geometry_heads(off_t blocks, u_int sectors, off_t *bestchs,
180251881Speter    u_int *bestheads)
181251881Speter{
182251881Speter	static u_int candidate_heads[] = { 1, 2, 16, 32, 64, 128, 255, 0 };
183251881Speter	off_t chs, cylinders;
184251881Speter	u_int heads;
185251881Speter	int idx;
186251881Speter
187251881Speter	*bestchs = 0;
188251881Speter	*bestheads = 0;
189251881Speter	for (idx = 0; candidate_heads[idx] != 0; idx++) {
190251881Speter		heads = candidate_heads[idx];
191251881Speter		cylinders = blocks / heads / sectors;
192251881Speter		if (cylinders < heads || cylinders < sectors)
193251881Speter			break;
194251881Speter		if (cylinders > 1023)
195251881Speter			continue;
196251881Speter		chs = cylinders * heads * sectors;
197251881Speter		if (chs > *bestchs || (chs == *bestchs && *bestheads == 1)) {
198251881Speter			*bestchs = chs;
199251881Speter			*bestheads = heads;
200251881Speter		}
201251881Speter	}
202251881Speter}
203251881Speter
204251881Speterstatic void
205251881Speterg_part_geometry(struct g_part_table *table, struct g_consumer *cp,
206251881Speter    off_t blocks)
207251881Speter{
208251881Speter	static u_int candidate_sectors[] = { 1, 9, 17, 33, 63, 0 };
209251881Speter	off_t chs, bestchs;
210251881Speter	u_int heads, sectors;
211251881Speter	int idx;
212251881Speter
213251881Speter	if (g_getattr("GEOM::fwsectors", cp, &sectors) != 0 || sectors == 0 ||
214251881Speter	    g_getattr("GEOM::fwheads", cp, &heads) != 0 || heads == 0) {
215251881Speter		table->gpt_fixgeom = 0;
216251881Speter		table->gpt_heads = 0;
217251881Speter		table->gpt_sectors = 0;
218251881Speter		bestchs = 0;
219251881Speter		for (idx = 0; candidate_sectors[idx] != 0; idx++) {
220251881Speter			sectors = candidate_sectors[idx];
221251881Speter			g_part_geometry_heads(blocks, sectors, &chs, &heads);
222251881Speter			if (chs == 0)
223251881Speter				continue;
224251881Speter			/*
225251881Speter			 * Prefer a geometry with sectors > 1, but only if
226251881Speter			 * it doesn't bump down the number of heads to 1.
227251881Speter			 */
228251881Speter			if (chs > bestchs || (chs == bestchs && heads > 1 &&
229251881Speter			    table->gpt_sectors == 1)) {
230251881Speter				bestchs = chs;
231251881Speter				table->gpt_heads = heads;
232251881Speter				table->gpt_sectors = sectors;
233251881Speter			}
234251881Speter		}
235251881Speter		/*
236251881Speter		 * If we didn't find a geometry at all, then the disk is
237251881Speter		 * too big. This means we can use the maximum number of
238251881Speter		 * heads and sectors.
239251881Speter		 */
240251881Speter		if (bestchs == 0) {
241251881Speter			table->gpt_heads = 255;
242251881Speter			table->gpt_sectors = 63;
243251881Speter		}
244251881Speter	} else {
245289180Speter		table->gpt_fixgeom = 1;
246251881Speter		table->gpt_heads = heads;
247251881Speter		table->gpt_sectors = sectors;
248251881Speter	}
249251881Speter}
250251881Speter
251251881Speter#define	DPRINTF(...)	if (bootverbose) {	\
252251881Speter	printf("GEOM_PART: " __VA_ARGS__);	\
253251881Speter}
254251881Speter
255251881Speterstatic int
256251881Speterg_part_check_integrity(struct g_part_table *table, struct g_consumer *cp)
257251881Speter{
258251881Speter	struct g_part_entry *e1, *e2;
259251881Speter	struct g_provider *pp;
260251881Speter	off_t offset;
261251881Speter	int failed;
262289180Speter
263251881Speter	failed = 0;
264251881Speter	pp = cp->provider;
265251881Speter	if (table->gpt_last < table->gpt_first) {
266251881Speter		DPRINTF("last LBA is below first LBA: %jd < %jd\n",
267251881Speter		    (intmax_t)table->gpt_last, (intmax_t)table->gpt_first);
268251881Speter		failed++;
269251881Speter	}
270251881Speter	if (table->gpt_last > pp->mediasize / pp->sectorsize - 1) {
271251881Speter		DPRINTF("last LBA extends beyond mediasize: "
272251881Speter		    "%jd > %jd\n", (intmax_t)table->gpt_last,
273251881Speter		    (intmax_t)pp->mediasize / pp->sectorsize - 1);
274251881Speter		failed++;
275251881Speter	}
276251881Speter	LIST_FOREACH(e1, &table->gpt_entry, gpe_entry) {
277251881Speter		if (e1->gpe_deleted || e1->gpe_internal)
278251881Speter			continue;
279251881Speter		if (e1->gpe_start < table->gpt_first) {
280251881Speter			DPRINTF("partition %d has start offset below first "
281251881Speter			    "LBA: %jd < %jd\n", e1->gpe_index,
282251881Speter			    (intmax_t)e1->gpe_start,
283251881Speter			    (intmax_t)table->gpt_first);
284251881Speter			failed++;
285251881Speter		}
286251881Speter		if (e1->gpe_start > table->gpt_last) {
287251881Speter			DPRINTF("partition %d has start offset beyond last "
288251881Speter			    "LBA: %jd > %jd\n", e1->gpe_index,
289251881Speter			    (intmax_t)e1->gpe_start,
290251881Speter			    (intmax_t)table->gpt_last);
291251881Speter			failed++;
292251881Speter		}
293251881Speter		if (e1->gpe_end < e1->gpe_start) {
294251881Speter			DPRINTF("partition %d has end offset below start "
295251881Speter			    "offset: %jd < %jd\n", e1->gpe_index,
296251881Speter			    (intmax_t)e1->gpe_end,
297251881Speter			    (intmax_t)e1->gpe_start);
298251881Speter			failed++;
299251881Speter		}
300251881Speter		if (e1->gpe_end > table->gpt_last) {
301251881Speter			DPRINTF("partition %d has end offset beyond last "
302251881Speter			    "LBA: %jd > %jd\n", e1->gpe_index,
303251881Speter			    (intmax_t)e1->gpe_end,
304251881Speter			    (intmax_t)table->gpt_last);
305251881Speter			failed++;
306251881Speter		}
307251881Speter		if (pp->stripesize > 0) {
308251881Speter			offset = e1->gpe_start * pp->sectorsize;
309251881Speter			if (e1->gpe_offset > offset)
310251881Speter				offset = e1->gpe_offset;
311251881Speter			if ((offset + pp->stripeoffset) % pp->stripesize) {
312251881Speter				DPRINTF("partition %d is not aligned on %u "
313251881Speter				    "bytes\n", e1->gpe_index, pp->stripesize);
314251881Speter				/* Don't treat this as a critical failure */
315251881Speter			}
316251881Speter		}
317251881Speter		e2 = e1;
318251881Speter		while ((e2 = LIST_NEXT(e2, gpe_entry)) != NULL) {
319251881Speter			if (e2->gpe_deleted || e2->gpe_internal)
320251881Speter				continue;
321251881Speter			if (e1->gpe_start >= e2->gpe_start &&
322251881Speter			    e1->gpe_start <= e2->gpe_end) {
323251881Speter				DPRINTF("partition %d has start offset inside "
324251881Speter				    "partition %d: start[%d] %jd >= start[%d] "
325251881Speter				    "%jd <= end[%d] %jd\n",
326251881Speter				    e1->gpe_index, e2->gpe_index,
327251881Speter				    e2->gpe_index, (intmax_t)e2->gpe_start,
328251881Speter				    e1->gpe_index, (intmax_t)e1->gpe_start,
329251881Speter				    e2->gpe_index, (intmax_t)e2->gpe_end);
330251881Speter				failed++;
331251881Speter			}
332251881Speter			if (e1->gpe_end >= e2->gpe_start &&
333251881Speter			    e1->gpe_end <= e2->gpe_end) {
334251881Speter				DPRINTF("partition %d has end offset inside "
335251881Speter				    "partition %d: start[%d] %jd >= end[%d] "
336251881Speter				    "%jd <= end[%d] %jd\n",
337251881Speter				    e1->gpe_index, e2->gpe_index,
338251881Speter				    e2->gpe_index, (intmax_t)e2->gpe_start,
339251881Speter				    e1->gpe_index, (intmax_t)e1->gpe_end,
340251881Speter				    e2->gpe_index, (intmax_t)e2->gpe_end);
341251881Speter				failed++;
342251881Speter			}
343251881Speter			if (e1->gpe_start < e2->gpe_start &&
344251881Speter			    e1->gpe_end > e2->gpe_end) {
345251881Speter				DPRINTF("partition %d contains partition %d: "
346251881Speter				    "start[%d] %jd > start[%d] %jd, end[%d] "
347251881Speter				    "%jd < end[%d] %jd\n",
348251881Speter				    e1->gpe_index, e2->gpe_index,
349251881Speter				    e1->gpe_index, (intmax_t)e1->gpe_start,
350251881Speter				    e2->gpe_index, (intmax_t)e2->gpe_start,
351251881Speter				    e2->gpe_index, (intmax_t)e2->gpe_end,
352251881Speter				    e1->gpe_index, (intmax_t)e1->gpe_end);
353251881Speter				failed++;
354251881Speter			}
355251881Speter		}
356251881Speter	}
357251881Speter	if (failed != 0) {
358251881Speter		printf("GEOM_PART: integrity check failed (%s, %s)\n",
359251881Speter		    pp->name, table->gpt_scheme->name);
360251881Speter		if (check_integrity != 0)
361251881Speter			return (EINVAL);
362251881Speter		table->gpt_corrupt = 1;
363251881Speter	}
364251881Speter	return (0);
365251881Speter}
366251881Speter#undef	DPRINTF
367251881Speter
368251881Speterstruct g_part_entry *
369251881Speterg_part_new_entry(struct g_part_table *table, int index, quad_t start,
370251881Speter    quad_t end)
371251881Speter{
372251881Speter	struct g_part_entry *entry, *last;
373251881Speter
374251881Speter	last = NULL;
375251881Speter	LIST_FOREACH(entry, &table->gpt_entry, gpe_entry) {
376251881Speter		if (entry->gpe_index == index)
377251881Speter			break;
378251881Speter		if (entry->gpe_index > index) {
379251881Speter			entry = NULL;
380251881Speter			break;
381251881Speter		}
382251881Speter		last = entry;
383251881Speter	}
384251881Speter	if (entry == NULL) {
385251881Speter		entry = g_malloc(table->gpt_scheme->gps_entrysz,
386251881Speter		    M_WAITOK | M_ZERO);
387251881Speter		entry->gpe_index = index;
388251881Speter		if (last == NULL)
389251881Speter			LIST_INSERT_HEAD(&table->gpt_entry, entry, gpe_entry);
390251881Speter		else
391251881Speter			LIST_INSERT_AFTER(last, entry, gpe_entry);
392251881Speter	} else
393251881Speter		entry->gpe_offset = 0;
394251881Speter	entry->gpe_start = start;
395251881Speter	entry->gpe_end = end;
396251881Speter	return (entry);
397251881Speter}
398251881Speter
399251881Speterstatic void
400251881Speterg_part_new_provider(struct g_geom *gp, struct g_part_table *table,
401251881Speter    struct g_part_entry *entry)
402251881Speter{
403251881Speter	struct g_consumer *cp;
404251881Speter	struct g_provider *pp;
405251881Speter	struct sbuf *sb;
406251881Speter	off_t offset;
407251881Speter
408251881Speter	cp = LIST_FIRST(&gp->consumer);
409251881Speter	pp = cp->provider;
410286506Speter
411251881Speter	offset = entry->gpe_start * pp->sectorsize;
412251881Speter	if (entry->gpe_offset < offset)
413251881Speter		entry->gpe_offset = offset;
414251881Speter
415251881Speter	if (entry->gpe_pp == NULL) {
416251881Speter		sb = sbuf_new_auto();
417251881Speter		G_PART_FULLNAME(table, entry, sb, gp->name);
418251881Speter		sbuf_finish(sb);
419251881Speter		entry->gpe_pp = g_new_providerf(gp, "%s", sbuf_data(sb));
420251881Speter		sbuf_delete(sb);
421251881Speter		entry->gpe_pp->flags |= G_PF_DIRECT_SEND | G_PF_DIRECT_RECEIVE;
422251881Speter		entry->gpe_pp->private = entry;		/* Close the circle. */
423251881Speter	}
424251881Speter	entry->gpe_pp->index = entry->gpe_index - 1;	/* index is 1-based. */
425251881Speter	entry->gpe_pp->mediasize = (entry->gpe_end - entry->gpe_start + 1) *
426251881Speter	    pp->sectorsize;
427251881Speter	entry->gpe_pp->mediasize -= entry->gpe_offset - offset;
428251881Speter	entry->gpe_pp->sectorsize = pp->sectorsize;
429251881Speter	entry->gpe_pp->stripesize = pp->stripesize;
430251881Speter	entry->gpe_pp->stripeoffset = pp->stripeoffset + entry->gpe_offset;
431251881Speter	if (pp->stripesize > 0)
432251881Speter		entry->gpe_pp->stripeoffset %= pp->stripesize;
433251881Speter	entry->gpe_pp->flags |= pp->flags & G_PF_ACCEPT_UNMAPPED;
434251881Speter	g_error_provider(entry->gpe_pp, 0);
435251881Speter}
436251881Speter
437257936Speterstatic struct g_geom*
438251881Speterg_part_find_geom(const char *name)
439257936Speter{
440251881Speter	struct g_geom *gp;
441251881Speter	LIST_FOREACH(gp, &g_part_class.geom, geom) {
442251881Speter		if (!strcmp(name, gp->name))
443257936Speter			break;
444257936Speter	}
445257936Speter	return (gp);
446257936Speter}
447257936Speter
448257936Speterstatic int
449251881Speterg_part_parm_geom(struct gctl_req *req, const char *name, struct g_geom **v)
450251881Speter{
451251881Speter	struct g_geom *gp;
452251881Speter	const char *gname;
453251881Speter
454251881Speter	gname = gctl_get_asciiparam(req, name);
455251881Speter	if (gname == NULL)
456251881Speter		return (ENOATTR);
457251881Speter	if (strncmp(gname, _PATH_DEV, sizeof(_PATH_DEV) - 1) == 0)
458251881Speter		gname += sizeof(_PATH_DEV) - 1;
459251881Speter	gp = g_part_find_geom(gname);
460251881Speter	if (gp == NULL) {
461251881Speter		gctl_error(req, "%d %s '%s'", EINVAL, name, gname);
462251881Speter		return (EINVAL);
463251881Speter	}
464251881Speter	if ((gp->flags & G_GEOM_WITHER) != 0) {
465251881Speter		gctl_error(req, "%d %s", ENXIO, gname);
466251881Speter		return (ENXIO);
467251881Speter	}
468251881Speter	*v = gp;
469251881Speter	return (0);
470251881Speter}
471251881Speter
472251881Speterstatic int
473251881Speterg_part_parm_provider(struct gctl_req *req, const char *name,
474251881Speter    struct g_provider **v)
475251881Speter{
476251881Speter	struct g_provider *pp;
477251881Speter	const char *pname;
478251881Speter
479251881Speter	pname = gctl_get_asciiparam(req, name);
480251881Speter	if (pname == NULL)
481251881Speter		return (ENOATTR);
482251881Speter	if (strncmp(pname, _PATH_DEV, sizeof(_PATH_DEV) - 1) == 0)
483251881Speter		pname += sizeof(_PATH_DEV) - 1;
484251881Speter	pp = g_provider_by_name(pname);
485251881Speter	if (pp == NULL) {
486251881Speter		gctl_error(req, "%d %s '%s'", EINVAL, name, pname);
487251881Speter		return (EINVAL);
488251881Speter	}
489251881Speter	*v = pp;
490251881Speter	return (0);
491251881Speter}
492251881Speter
493251881Speterstatic int
494251881Speterg_part_parm_quad(struct gctl_req *req, const char *name, quad_t *v)
495251881Speter{
496251881Speter	const char *p;
497251881Speter	char *x;
498251881Speter	quad_t q;
499251881Speter
500251881Speter	p = gctl_get_asciiparam(req, name);
501251881Speter	if (p == NULL)
502251881Speter		return (ENOATTR);
503251881Speter	q = strtoq(p, &x, 0);
504251881Speter	if (*x != '\0' || q < 0) {
505251881Speter		gctl_error(req, "%d %s '%s'", EINVAL, name, p);
506251881Speter		return (EINVAL);
507251881Speter	}
508251881Speter	*v = q;
509251881Speter	return (0);
510251881Speter}
511251881Speter
512251881Speterstatic int
513251881Speterg_part_parm_scheme(struct gctl_req *req, const char *name,
514251881Speter    struct g_part_scheme **v)
515251881Speter{
516251881Speter	struct g_part_scheme *s;
517251881Speter	const char *p;
518251881Speter
519251881Speter	p = gctl_get_asciiparam(req, name);
520251881Speter	if (p == NULL)
521251881Speter		return (ENOATTR);
522251881Speter	TAILQ_FOREACH(s, &g_part_schemes, scheme_list) {
523251881Speter		if (s == &g_part_null_scheme)
524251881Speter			continue;
525251881Speter		if (!strcasecmp(s->name, p))
526251881Speter			break;
527251881Speter	}
528251881Speter	if (s == NULL) {
529251881Speter		gctl_error(req, "%d %s '%s'", EINVAL, name, p);
530251881Speter		return (EINVAL);
531251881Speter	}
532251881Speter	*v = s;
533251881Speter	return (0);
534251881Speter}
535251881Speter
536251881Speterstatic int
537251881Speterg_part_parm_str(struct gctl_req *req, const char *name, const char **v)
538251881Speter{
539251881Speter	const char *p;
540251881Speter
541251881Speter	p = gctl_get_asciiparam(req, name);
542251881Speter	if (p == NULL)
543251881Speter		return (ENOATTR);
544251881Speter	/* An empty label is always valid. */
545251881Speter	if (strcmp(name, "label") != 0 && p[0] == '\0') {
546251881Speter		gctl_error(req, "%d %s '%s'", EINVAL, name, p);
547251881Speter		return (EINVAL);
548251881Speter	}
549251881Speter	*v = p;
550251881Speter	return (0);
551251881Speter}
552251881Speter
553251881Speterstatic int
554251881Speterg_part_parm_intmax(struct gctl_req *req, const char *name, u_int *v)
555251881Speter{
556251881Speter	const intmax_t *p;
557251881Speter	int size;
558289180Speter
559251881Speter	p = gctl_get_param(req, name, &size);
560251881Speter	if (p == NULL)
561251881Speter		return (ENOATTR);
562251881Speter	if (size != sizeof(*p) || *p < 0 || *p > INT_MAX) {
563251881Speter		gctl_error(req, "%d %s '%jd'", EINVAL, name, *p);
564251881Speter		return (EINVAL);
565251881Speter	}
566251881Speter	*v = (u_int)*p;
567251881Speter	return (0);
568251881Speter}
569251881Speter
570251881Speterstatic int
571251881Speterg_part_parm_uint32(struct gctl_req *req, const char *name, u_int *v)
572251881Speter{
573251881Speter	const uint32_t *p;
574251881Speter	int size;
575251881Speter
576251881Speter	p = gctl_get_param(req, name, &size);
577251881Speter	if (p == NULL)
578251881Speter		return (ENOATTR);
579251881Speter	if (size != sizeof(*p) || *p > INT_MAX) {
580251881Speter		gctl_error(req, "%d %s '%u'", EINVAL, name, (unsigned int)*p);
581251881Speter		return (EINVAL);
582251881Speter	}
583251881Speter	*v = (u_int)*p;
584251881Speter	return (0);
585251881Speter}
586251881Speter
587251881Speterstatic int
588251881Speterg_part_parm_bootcode(struct gctl_req *req, const char *name, const void **v,
589251881Speter    unsigned int *s)
590251881Speter{
591251881Speter	const void *p;
592251881Speter	int size;
593251881Speter
594251881Speter	p = gctl_get_param(req, name, &size);
595251881Speter	if (p == NULL)
596251881Speter		return (ENOATTR);
597251881Speter	*v = p;
598251881Speter	*s = size;
599251881Speter	return (0);
600251881Speter}
601251881Speter
602251881Speterstatic int
603251881Speterg_part_probe(struct g_geom *gp, struct g_consumer *cp, int depth)
604251881Speter{
605251881Speter	struct g_part_scheme *iter, *scheme;
606251881Speter	struct g_part_table *table;
607251881Speter	int pri, probe;
608251881Speter
609251881Speter	table = gp->softc;
610251881Speter	scheme = (table != NULL) ? table->gpt_scheme : NULL;
611251881Speter	pri = (scheme != NULL) ? G_PART_PROBE(table, cp) : INT_MIN;
612251881Speter	if (pri == 0)
613251881Speter		goto done;
614251881Speter	if (pri > 0) {	/* error */
615251881Speter		scheme = NULL;
616251881Speter		pri = INT_MIN;
617251881Speter	}
618251881Speter
619251881Speter	TAILQ_FOREACH(iter, &g_part_schemes, scheme_list) {
620251881Speter		if (iter == &g_part_null_scheme)
621251881Speter			continue;
622251881Speter		table = (void *)kobj_create((kobj_class_t)iter, M_GEOM,
623251881Speter		    M_WAITOK);
624251881Speter		table->gpt_gp = gp;
625251881Speter		table->gpt_scheme = iter;
626251881Speter		table->gpt_depth = depth;
627251881Speter		probe = G_PART_PROBE(table, cp);
628251881Speter		if (probe <= 0 && probe > pri) {
629251881Speter			pri = probe;
630251881Speter			scheme = iter;
631251881Speter			if (gp->softc != NULL)
632251881Speter				kobj_delete((kobj_t)gp->softc, M_GEOM);
633251881Speter			gp->softc = table;
634251881Speter			if (pri == 0)
635251881Speter				goto done;
636251881Speter		} else
637251881Speter			kobj_delete((kobj_t)table, M_GEOM);
638251881Speter	}
639251881Speter
640251881Speterdone:
641251881Speter	return ((scheme == NULL) ? ENXIO : 0);
642251881Speter}
643251881Speter
644251881Speter/*
645251881Speter * Control request functions.
646251881Speter */
647251881Speter
648289180Speterstatic int
649251881Speterg_part_ctl_add(struct gctl_req *req, struct g_part_parms *gpp)
650251881Speter{
651251881Speter	struct g_geom *gp;
652251881Speter	struct g_provider *pp;
653251881Speter	struct g_part_entry *delent, *last, *entry;
654251881Speter	struct g_part_table *table;
655251881Speter	struct sbuf *sb;
656251881Speter	quad_t end;
657251881Speter	unsigned int index;
658251881Speter	int error;
659251881Speter
660251881Speter	gp = gpp->gpp_geom;
661251881Speter	G_PART_TRACE((G_T_TOPOLOGY, "%s(%s)", __func__, gp->name));
662251881Speter	g_topology_assert();
663251881Speter
664251881Speter	pp = LIST_FIRST(&gp->consumer)->provider;
665251881Speter	table = gp->softc;
666251881Speter	end = gpp->gpp_start + gpp->gpp_size - 1;
667251881Speter
668251881Speter	if (gpp->gpp_start < table->gpt_first ||
669251881Speter	    gpp->gpp_start > table->gpt_last) {
670251881Speter		gctl_error(req, "%d start '%jd'", EINVAL,
671251881Speter		    (intmax_t)gpp->gpp_start);
672251881Speter		return (EINVAL);
673251881Speter	}
674251881Speter	if (end < gpp->gpp_start || end > table->gpt_last) {
675251881Speter		gctl_error(req, "%d size '%jd'", EINVAL,
676251881Speter		    (intmax_t)gpp->gpp_size);
677251881Speter		return (EINVAL);
678251881Speter	}
679251881Speter	if (gpp->gpp_index > table->gpt_entries) {
680251881Speter		gctl_error(req, "%d index '%d'", EINVAL, gpp->gpp_index);
681251881Speter		return (EINVAL);
682251881Speter	}
683251881Speter
684251881Speter	delent = last = NULL;
685251881Speter	index = (gpp->gpp_index > 0) ? gpp->gpp_index : 1;
686251881Speter	LIST_FOREACH(entry, &table->gpt_entry, gpe_entry) {
687251881Speter		if (entry->gpe_deleted) {
688251881Speter			if (entry->gpe_index == index)
689251881Speter				delent = entry;
690251881Speter			continue;
691251881Speter		}
692251881Speter		if (entry->gpe_index == index)
693251881Speter			index = entry->gpe_index + 1;
694251881Speter		if (entry->gpe_index < index)
695251881Speter			last = entry;
696251881Speter		if (entry->gpe_internal)
697289180Speter			continue;
698289180Speter		if (gpp->gpp_start >= entry->gpe_start &&
699289180Speter		    gpp->gpp_start <= entry->gpe_end) {
700251881Speter			gctl_error(req, "%d start '%jd'", ENOSPC,
701251881Speter			    (intmax_t)gpp->gpp_start);
702251881Speter			return (ENOSPC);
703251881Speter		}
704251881Speter		if (end >= entry->gpe_start && end <= entry->gpe_end) {
705251881Speter			gctl_error(req, "%d end '%jd'", ENOSPC, (intmax_t)end);
706251881Speter			return (ENOSPC);
707251881Speter		}
708251881Speter		if (gpp->gpp_start < entry->gpe_start && end > entry->gpe_end) {
709251881Speter			gctl_error(req, "%d size '%jd'", ENOSPC,
710251881Speter			    (intmax_t)gpp->gpp_size);
711251881Speter			return (ENOSPC);
712251881Speter		}
713251881Speter	}
714251881Speter	if (gpp->gpp_index > 0 && index != gpp->gpp_index) {
715251881Speter		gctl_error(req, "%d index '%d'", EEXIST, gpp->gpp_index);
716251881Speter		return (EEXIST);
717251881Speter	}
718251881Speter	if (index > table->gpt_entries) {
719251881Speter		gctl_error(req, "%d index '%d'", ENOSPC, index);
720251881Speter		return (ENOSPC);
721251881Speter	}
722251881Speter
723251881Speter	entry = (delent == NULL) ? g_malloc(table->gpt_scheme->gps_entrysz,
724251881Speter	    M_WAITOK | M_ZERO) : delent;
725251881Speter	entry->gpe_index = index;
726251881Speter	entry->gpe_start = gpp->gpp_start;
727251881Speter	entry->gpe_end = end;
728251881Speter	error = G_PART_ADD(table, entry, gpp);
729251881Speter	if (error) {
730251881Speter		gctl_error(req, "%d", error);
731251881Speter		if (delent == NULL)
732251881Speter			g_free(entry);
733251881Speter		return (error);
734251881Speter	}
735251881Speter	if (delent == NULL) {
736251881Speter		if (last == NULL)
737251881Speter			LIST_INSERT_HEAD(&table->gpt_entry, entry, gpe_entry);
738251881Speter		else
739251881Speter			LIST_INSERT_AFTER(last, entry, gpe_entry);
740251881Speter		entry->gpe_created = 1;
741251881Speter	} else {
742251881Speter		entry->gpe_deleted = 0;
743251881Speter		entry->gpe_modified = 1;
744251881Speter	}
745251881Speter	g_part_new_provider(gp, table, entry);
746251881Speter
747251881Speter	/* Provide feedback if so requested. */
748251881Speter	if (gpp->gpp_parms & G_PART_PARM_OUTPUT) {
749251881Speter		sb = sbuf_new_auto();
750251881Speter		G_PART_FULLNAME(table, entry, sb, gp->name);
751251881Speter		if (pp->stripesize > 0 && entry->gpe_pp->stripeoffset != 0)
752251881Speter			sbuf_printf(sb, " added, but partition is not "
753251881Speter			    "aligned on %u bytes\n", pp->stripesize);
754251881Speter		else
755251881Speter			sbuf_cat(sb, " added\n");
756251881Speter		sbuf_finish(sb);
757251881Speter		gctl_set_param(req, "output", sbuf_data(sb), sbuf_len(sb) + 1);
758251881Speter		sbuf_delete(sb);
759251881Speter	}
760251881Speter	return (0);
761251881Speter}
762251881Speter
763251881Speterstatic int
764251881Speterg_part_ctl_bootcode(struct gctl_req *req, struct g_part_parms *gpp)
765251881Speter{
766251881Speter	struct g_geom *gp;
767251881Speter	struct g_part_table *table;
768251881Speter	struct sbuf *sb;
769251881Speter	int error, sz;
770251881Speter
771251881Speter	gp = gpp->gpp_geom;
772251881Speter	G_PART_TRACE((G_T_TOPOLOGY, "%s(%s)", __func__, gp->name));
773251881Speter	g_topology_assert();
774251881Speter
775251881Speter	table = gp->softc;
776251881Speter	sz = table->gpt_scheme->gps_bootcodesz;
777251881Speter	if (sz == 0) {
778251881Speter		error = ENODEV;
779251881Speter		goto fail;
780251881Speter	}
781251881Speter	if (gpp->gpp_codesize > sz) {
782251881Speter		error = EFBIG;
783251881Speter		goto fail;
784251881Speter	}
785251881Speter
786251881Speter	error = G_PART_BOOTCODE(table, gpp);
787251881Speter	if (error)
788251881Speter		goto fail;
789251881Speter
790251881Speter	/* Provide feedback if so requested. */
791251881Speter	if (gpp->gpp_parms & G_PART_PARM_OUTPUT) {
792251881Speter		sb = sbuf_new_auto();
793251881Speter		sbuf_printf(sb, "bootcode written to %s\n", gp->name);
794251881Speter		sbuf_finish(sb);
795251881Speter		gctl_set_param(req, "output", sbuf_data(sb), sbuf_len(sb) + 1);
796251881Speter		sbuf_delete(sb);
797251881Speter	}
798251881Speter	return (0);
799251881Speter
800251881Speter fail:
801251881Speter	gctl_error(req, "%d", error);
802251881Speter	return (error);
803251881Speter}
804251881Speter
805251881Speterstatic int
806251881Speterg_part_ctl_commit(struct gctl_req *req, struct g_part_parms *gpp)
807251881Speter{
808251881Speter	struct g_consumer *cp;
809251881Speter	struct g_geom *gp;
810251881Speter	struct g_provider *pp;
811251881Speter	struct g_part_entry *entry, *tmp;
812251881Speter	struct g_part_table *table;
813251881Speter	char *buf;
814251881Speter	int error, i;
815251881Speter
816251881Speter	gp = gpp->gpp_geom;
817251881Speter	G_PART_TRACE((G_T_TOPOLOGY, "%s(%s)", __func__, gp->name));
818251881Speter	g_topology_assert();
819251881Speter
820251881Speter	table = gp->softc;
821251881Speter	if (!table->gpt_opened) {
822251881Speter		gctl_error(req, "%d", EPERM);
823251881Speter		return (EPERM);
824251881Speter	}
825251881Speter
826251881Speter	g_topology_unlock();
827251881Speter
828251881Speter	cp = LIST_FIRST(&gp->consumer);
829251881Speter	if ((table->gpt_smhead | table->gpt_smtail) != 0) {
830251881Speter		pp = cp->provider;
831251881Speter		buf = g_malloc(pp->sectorsize, M_WAITOK | M_ZERO);
832251881Speter		while (table->gpt_smhead != 0) {
833286506Speter			i = ffs(table->gpt_smhead) - 1;
834286506Speter			error = g_write_data(cp, i * pp->sectorsize, buf,
835251881Speter			    pp->sectorsize);
836251881Speter			if (error) {
837251881Speter				g_free(buf);
838251881Speter				goto fail;
839251881Speter			}
840251881Speter			table->gpt_smhead &= ~(1 << i);
841251881Speter		}
842251881Speter		while (table->gpt_smtail != 0) {
843251881Speter			i = ffs(table->gpt_smtail) - 1;
844251881Speter			error = g_write_data(cp, pp->mediasize - (i + 1) *
845251881Speter			    pp->sectorsize, buf, pp->sectorsize);
846251881Speter			if (error) {
847251881Speter				g_free(buf);
848251881Speter				goto fail;
849251881Speter			}
850251881Speter			table->gpt_smtail &= ~(1 << i);
851251881Speter		}
852251881Speter		g_free(buf);
853251881Speter	}
854251881Speter
855251881Speter	if (table->gpt_scheme == &g_part_null_scheme) {
856251881Speter		g_topology_lock();
857251881Speter		g_access(cp, -1, -1, -1);
858251881Speter		g_part_wither(gp, ENXIO);
859251881Speter		return (0);
860251881Speter	}
861251881Speter
862251881Speter	error = G_PART_WRITE(table, cp);
863251881Speter	if (error)
864251881Speter		goto fail;
865251881Speter
866251881Speter	LIST_FOREACH_SAFE(entry, &table->gpt_entry, gpe_entry, tmp) {
867251881Speter		if (!entry->gpe_deleted) {
868251881Speter			entry->gpe_created = 0;
869251881Speter			entry->gpe_modified = 0;
870251881Speter			continue;
871251881Speter		}
872251881Speter		LIST_REMOVE(entry, gpe_entry);
873251881Speter		g_free(entry);
874251881Speter	}
875251881Speter	table->gpt_created = 0;
876251881Speter	table->gpt_opened = 0;
877251881Speter
878251881Speter	g_topology_lock();
879251881Speter	g_access(cp, -1, -1, -1);
880251881Speter	return (0);
881251881Speter
882251881Speterfail:
883251881Speter	g_topology_lock();
884251881Speter	gctl_error(req, "%d", error);
885251881Speter	return (error);
886251881Speter}
887251881Speter
888251881Speterstatic int
889251881Speterg_part_ctl_create(struct gctl_req *req, struct g_part_parms *gpp)
890251881Speter{
891251881Speter	struct g_consumer *cp;
892251881Speter	struct g_geom *gp;
893251881Speter	struct g_provider *pp;
894251881Speter	struct g_part_scheme *scheme;
895251881Speter	struct g_part_table *null, *table;
896251881Speter	struct sbuf *sb;
897251881Speter	int attr, error;
898251881Speter
899251881Speter	pp = gpp->gpp_provider;
900251881Speter	scheme = gpp->gpp_scheme;
901251881Speter	G_PART_TRACE((G_T_TOPOLOGY, "%s(%s)", __func__, pp->name));
902251881Speter	g_topology_assert();
903251881Speter
904251881Speter	/* Check that there isn't already a g_part geom on the provider. */
905286506Speter	gp = g_part_find_geom(pp->name);
906251881Speter	if (gp != NULL) {
907251881Speter		null = gp->softc;
908251881Speter		if (null->gpt_scheme != &g_part_null_scheme) {
909251881Speter			gctl_error(req, "%d geom '%s'", EEXIST, pp->name);
910251881Speter			return (EEXIST);
911251881Speter		}
912251881Speter	} else
913251881Speter		null = NULL;
914251881Speter
915251881Speter	if ((gpp->gpp_parms & G_PART_PARM_ENTRIES) &&
916251881Speter	    (gpp->gpp_entries < scheme->gps_minent ||
917251881Speter	     gpp->gpp_entries > scheme->gps_maxent)) {
918251881Speter		gctl_error(req, "%d entries '%d'", EINVAL, gpp->gpp_entries);
919251881Speter		return (EINVAL);
920251881Speter	}
921251881Speter
922251881Speter	if (null == NULL)
923251881Speter		gp = g_new_geomf(&g_part_class, "%s", pp->name);
924251881Speter	gp->softc = kobj_create((kobj_class_t)gpp->gpp_scheme, M_GEOM,
925251881Speter	    M_WAITOK);
926251881Speter	table = gp->softc;
927251881Speter	table->gpt_gp = gp;
928251881Speter	table->gpt_scheme = gpp->gpp_scheme;
929251881Speter	table->gpt_entries = (gpp->gpp_parms & G_PART_PARM_ENTRIES) ?
930251881Speter	    gpp->gpp_entries : scheme->gps_minent;
931251881Speter	LIST_INIT(&table->gpt_entry);
932251881Speter	if (null == NULL) {
933251881Speter		cp = g_new_consumer(gp);
934251881Speter		cp->flags |= G_CF_DIRECT_SEND | G_CF_DIRECT_RECEIVE;
935251881Speter		error = g_attach(cp, pp);
936251881Speter		if (error == 0)
937251881Speter			error = g_access(cp, 1, 1, 1);
938251881Speter		if (error != 0) {
939251881Speter			g_part_wither(gp, error);
940251881Speter			gctl_error(req, "%d geom '%s'", error, pp->name);
941251881Speter			return (error);
942251881Speter		}
943251881Speter		table->gpt_opened = 1;
944251881Speter	} else {
945251881Speter		cp = LIST_FIRST(&gp->consumer);
946251881Speter		table->gpt_opened = null->gpt_opened;
947251881Speter		table->gpt_smhead = null->gpt_smhead;
948251881Speter		table->gpt_smtail = null->gpt_smtail;
949251881Speter	}
950251881Speter
951251881Speter	g_topology_unlock();
952251881Speter
953251881Speter	/* Make sure the provider has media. */
954251881Speter	if (pp->mediasize == 0 || pp->sectorsize == 0) {
955251881Speter		error = ENODEV;
956251881Speter		goto fail;
957251881Speter	}
958251881Speter
959251881Speter	/* Make sure we can nest and if so, determine our depth. */
960251881Speter	error = g_getattr("PART::isleaf", cp, &attr);
961251881Speter	if (!error && attr) {
962251881Speter		error = ENODEV;
963251881Speter		goto fail;
964251881Speter	}
965251881Speter	error = g_getattr("PART::depth", cp, &attr);
966251881Speter	table->gpt_depth = (!error) ? attr + 1 : 0;
967251881Speter
968251881Speter	/*
969251881Speter	 * Synthesize a disk geometry. Some partitioning schemes
970251881Speter	 * depend on it and since some file systems need it even
971251881Speter	 * when the partitition scheme doesn't, we do it here in
972251881Speter	 * scheme-independent code.
973251881Speter	 */
974251881Speter	g_part_geometry(table, cp, pp->mediasize / pp->sectorsize);
975251881Speter
976251881Speter	error = G_PART_CREATE(table, gpp);
977251881Speter	if (error)
978251881Speter		goto fail;
979251881Speter
980251881Speter	g_topology_lock();
981251881Speter
982251881Speter	table->gpt_created = 1;
983251881Speter	if (null != NULL)
984251881Speter		kobj_delete((kobj_t)null, M_GEOM);
985251881Speter
986251881Speter	/*
987251881Speter	 * Support automatic commit by filling in the gpp_geom
988251881Speter	 * parameter.
989251881Speter	 */
990251881Speter	gpp->gpp_parms |= G_PART_PARM_GEOM;
991251881Speter	gpp->gpp_geom = gp;
992251881Speter
993251881Speter	/* Provide feedback if so requested. */
994251881Speter	if (gpp->gpp_parms & G_PART_PARM_OUTPUT) {
995251881Speter		sb = sbuf_new_auto();
996251881Speter		sbuf_printf(sb, "%s created\n", gp->name);
997251881Speter		sbuf_finish(sb);
998251881Speter		gctl_set_param(req, "output", sbuf_data(sb), sbuf_len(sb) + 1);
999251881Speter		sbuf_delete(sb);
1000251881Speter	}
1001251881Speter	return (0);
1002251881Speter
1003251881Speterfail:
1004251881Speter	g_topology_lock();
1005251881Speter	if (null == NULL) {
1006251881Speter		g_access(cp, -1, -1, -1);
1007251881Speter		g_part_wither(gp, error);
1008251881Speter	} else {
1009251881Speter		kobj_delete((kobj_t)gp->softc, M_GEOM);
1010251881Speter		gp->softc = null;
1011251881Speter	}
1012251881Speter	gctl_error(req, "%d provider", error);
1013251881Speter	return (error);
1014251881Speter}
1015251881Speter
1016251881Speterstatic int
1017251881Speterg_part_ctl_delete(struct gctl_req *req, struct g_part_parms *gpp)
1018251881Speter{
1019251881Speter	struct g_geom *gp;
1020251881Speter	struct g_provider *pp;
1021251881Speter	struct g_part_entry *entry;
1022251881Speter	struct g_part_table *table;
1023286506Speter	struct sbuf *sb;
1024286506Speter
1025286506Speter	gp = gpp->gpp_geom;
1026286506Speter	G_PART_TRACE((G_T_TOPOLOGY, "%s(%s)", __func__, gp->name));
1027286506Speter	g_topology_assert();
1028286506Speter
1029286506Speter	table = gp->softc;
1030286506Speter
1031251881Speter	LIST_FOREACH(entry, &table->gpt_entry, gpe_entry) {
1032251881Speter		if (entry->gpe_deleted || entry->gpe_internal)
1033251881Speter			continue;
1034251881Speter		if (entry->gpe_index == gpp->gpp_index)
1035251881Speter			break;
1036251881Speter	}
1037251881Speter	if (entry == NULL) {
1038251881Speter		gctl_error(req, "%d index '%d'", ENOENT, gpp->gpp_index);
1039251881Speter		return (ENOENT);
1040286506Speter	}
1041286506Speter
1042286506Speter	pp = entry->gpe_pp;
1043286506Speter	if (pp != NULL) {
1044286506Speter		if (pp->acr > 0 || pp->acw > 0 || pp->ace > 0) {
1045286506Speter			gctl_error(req, "%d", EBUSY);
1046286506Speter			return (EBUSY);
1047286506Speter		}
1048286506Speter
1049286506Speter		pp->private = NULL;
1050286506Speter		entry->gpe_pp = NULL;
1051286506Speter	}
1052286506Speter
1053286506Speter	if (pp != NULL)
1054286506Speter		g_wither_provider(pp, ENXIO);
1055286506Speter
1056286506Speter	/* Provide feedback if so requested. */
1057286506Speter	if (gpp->gpp_parms & G_PART_PARM_OUTPUT) {
1058286506Speter		sb = sbuf_new_auto();
1059286506Speter		G_PART_FULLNAME(table, entry, sb, gp->name);
1060286506Speter		sbuf_cat(sb, " deleted\n");
1061286506Speter		sbuf_finish(sb);
1062286506Speter		gctl_set_param(req, "output", sbuf_data(sb), sbuf_len(sb) + 1);
1063286506Speter		sbuf_delete(sb);
1064286506Speter	}
1065286506Speter
1066286506Speter	if (entry->gpe_created) {
1067286506Speter		LIST_REMOVE(entry, gpe_entry);
1068286506Speter		g_free(entry);
1069286506Speter	} else {
1070286506Speter		entry->gpe_modified = 0;
1071286506Speter		entry->gpe_deleted = 1;
1072286506Speter	}
1073286506Speter	return (0);
1074286506Speter}
1075286506Speter
1076286506Speterstatic int
1077286506Speterg_part_ctl_destroy(struct gctl_req *req, struct g_part_parms *gpp)
1078286506Speter{
1079286506Speter	struct g_consumer *cp;
1080286506Speter	struct g_geom *gp;
1081251881Speter	struct g_provider *pp;
1082251881Speter	struct g_part_entry *entry, *tmp;
1083251881Speter	struct g_part_table *null, *table;
1084251881Speter	struct sbuf *sb;
1085251881Speter	int error;
1086251881Speter
1087251881Speter	gp = gpp->gpp_geom;
1088286506Speter	G_PART_TRACE((G_T_TOPOLOGY, "%s(%s)", __func__, gp->name));
1089251881Speter	g_topology_assert();
1090251881Speter
1091251881Speter	table = gp->softc;
1092251881Speter	/* Check for busy providers. */
1093289180Speter	LIST_FOREACH(entry, &table->gpt_entry, gpe_entry) {
1094289180Speter		if (entry->gpe_deleted || entry->gpe_internal)
1095289180Speter			continue;
1096289180Speter		if (gpp->gpp_force) {
1097289180Speter			pp = entry->gpe_pp;
1098289180Speter			if (pp == NULL)
1099251881Speter				continue;
1100289180Speter			if (pp->acr == 0 && pp->acw == 0 && pp->ace == 0)
1101289180Speter				continue;
1102251881Speter		}
1103289180Speter		gctl_error(req, "%d", EBUSY);
1104289180Speter		return (EBUSY);
1105289180Speter	}
1106289180Speter
1107251881Speter	if (gpp->gpp_force) {
1108251881Speter		/* Destroy all providers. */
1109289180Speter		LIST_FOREACH_SAFE(entry, &table->gpt_entry, gpe_entry, tmp) {
1110289180Speter			pp = entry->gpe_pp;
1111251881Speter			if (pp != NULL) {
1112289180Speter				pp->private = NULL;
1113289180Speter				g_wither_provider(pp, ENXIO);
1114289180Speter			}
1115289180Speter			LIST_REMOVE(entry, gpe_entry);
1116289180Speter			g_free(entry);
1117289180Speter		}
1118289180Speter	}
1119251881Speter
1120289180Speter	error = G_PART_DESTROY(table, gpp);
1121251881Speter	if (error) {
1122289180Speter		gctl_error(req, "%d", error);
1123289180Speter		return (error);
1124251881Speter	}
1125289180Speter
1126251881Speter	gp->softc = kobj_create((kobj_class_t)&g_part_null_scheme, M_GEOM,
1127289180Speter	    M_WAITOK);
1128289180Speter	null = gp->softc;
1129289180Speter	null->gpt_gp = gp;
1130289180Speter	null->gpt_scheme = &g_part_null_scheme;
1131251881Speter	LIST_INIT(&null->gpt_entry);
1132289180Speter
1133289180Speter	cp = LIST_FIRST(&gp->consumer);
1134289180Speter	pp = cp->provider;
1135289180Speter	null->gpt_last = pp->mediasize / pp->sectorsize - 1;
1136251881Speter
1137289180Speter	null->gpt_depth = table->gpt_depth;
1138289180Speter	null->gpt_opened = table->gpt_opened;
1139289180Speter	null->gpt_smhead = table->gpt_smhead;
1140251881Speter	null->gpt_smtail = table->gpt_smtail;
1141289180Speter
1142289180Speter	while ((entry = LIST_FIRST(&table->gpt_entry)) != NULL) {
1143289180Speter		LIST_REMOVE(entry, gpe_entry);
1144289180Speter		g_free(entry);
1145289180Speter	}
1146289180Speter	kobj_delete((kobj_t)table, M_GEOM);
1147289180Speter
1148289180Speter	/* Provide feedback if so requested. */
1149289180Speter	if (gpp->gpp_parms & G_PART_PARM_OUTPUT) {
1150289180Speter		sb = sbuf_new_auto();
1151289180Speter		sbuf_printf(sb, "%s destroyed\n", gp->name);
1152289180Speter		sbuf_finish(sb);
1153289180Speter		gctl_set_param(req, "output", sbuf_data(sb), sbuf_len(sb) + 1);
1154289180Speter		sbuf_delete(sb);
1155289180Speter	}
1156289180Speter	return (0);
1157289180Speter}
1158289180Speter
1159289180Speterstatic int
1160289180Speterg_part_ctl_modify(struct gctl_req *req, struct g_part_parms *gpp)
1161289180Speter{
1162289180Speter	struct g_geom *gp;
1163289180Speter	struct g_part_entry *entry;
1164289180Speter	struct g_part_table *table;
1165289180Speter	struct sbuf *sb;
1166289180Speter	int error;
1167251881Speter
1168251881Speter	gp = gpp->gpp_geom;
1169251881Speter	G_PART_TRACE((G_T_TOPOLOGY, "%s(%s)", __func__, gp->name));
1170251881Speter	g_topology_assert();
1171251881Speter
1172251881Speter	table = gp->softc;
1173251881Speter
1174286506Speter	LIST_FOREACH(entry, &table->gpt_entry, gpe_entry) {
1175286506Speter		if (entry->gpe_deleted || entry->gpe_internal)
1176286506Speter			continue;
1177286506Speter		if (entry->gpe_index == gpp->gpp_index)
1178251881Speter			break;
1179286506Speter	}
1180251881Speter	if (entry == NULL) {
1181251881Speter		gctl_error(req, "%d index '%d'", ENOENT, gpp->gpp_index);
1182286506Speter		return (ENOENT);
1183251881Speter	}
1184286506Speter
1185286506Speter	error = G_PART_MODIFY(table, entry, gpp);
1186286506Speter	if (error) {
1187251881Speter		gctl_error(req, "%d", error);
1188251881Speter		return (error);
1189251881Speter	}
1190251881Speter
1191251881Speter	if (!entry->gpe_created)
1192251881Speter		entry->gpe_modified = 1;
1193251881Speter
1194251881Speter	/* Provide feedback if so requested. */
1195251881Speter	if (gpp->gpp_parms & G_PART_PARM_OUTPUT) {
1196251881Speter		sb = sbuf_new_auto();
1197251881Speter		G_PART_FULLNAME(table, entry, sb, gp->name);
1198251881Speter		sbuf_cat(sb, " modified\n");
1199251881Speter		sbuf_finish(sb);
1200251881Speter		gctl_set_param(req, "output", sbuf_data(sb), sbuf_len(sb) + 1);
1201251881Speter		sbuf_delete(sb);
1202251881Speter	}
1203251881Speter	return (0);
1204251881Speter}
1205251881Speter
1206251881Speterstatic int
1207251881Speterg_part_ctl_move(struct gctl_req *req, struct g_part_parms *gpp)
1208251881Speter{
1209251881Speter	gctl_error(req, "%d verb 'move'", ENOSYS);
1210251881Speter	return (ENOSYS);
1211251881Speter}
1212251881Speter
1213251881Speterstatic int
1214251881Speterg_part_ctl_recover(struct gctl_req *req, struct g_part_parms *gpp)
1215251881Speter{
1216251881Speter	struct g_part_table *table;
1217251881Speter	struct g_geom *gp;
1218251881Speter	struct sbuf *sb;
1219251881Speter	int error, recovered;
1220251881Speter
1221251881Speter	gp = gpp->gpp_geom;
1222251881Speter	G_PART_TRACE((G_T_TOPOLOGY, "%s(%s)", __func__, gp->name));
1223251881Speter	g_topology_assert();
1224251881Speter	table = gp->softc;
1225251881Speter	error = recovered = 0;
1226251881Speter
1227251881Speter	if (table->gpt_corrupt) {
1228251881Speter		error = G_PART_RECOVER(table);
1229251881Speter		if (error == 0)
1230251881Speter			error = g_part_check_integrity(table,
1231251881Speter			    LIST_FIRST(&gp->consumer));
1232251881Speter		if (error) {
1233251881Speter			gctl_error(req, "%d recovering '%s' failed",
1234251881Speter			    error, gp->name);
1235251881Speter			return (error);
1236251881Speter		}
1237251881Speter		recovered = 1;
1238251881Speter	}
1239251881Speter	/* Provide feedback if so requested. */
1240251881Speter	if (gpp->gpp_parms & G_PART_PARM_OUTPUT) {
1241251881Speter		sb = sbuf_new_auto();
1242251881Speter		if (recovered)
1243251881Speter			sbuf_printf(sb, "%s recovered\n", gp->name);
1244251881Speter		else
1245251881Speter			sbuf_printf(sb, "%s recovering is not needed\n",
1246251881Speter			    gp->name);
1247251881Speter		sbuf_finish(sb);
1248251881Speter		gctl_set_param(req, "output", sbuf_data(sb), sbuf_len(sb) + 1);
1249251881Speter		sbuf_delete(sb);
1250251881Speter	}
1251251881Speter	return (0);
1252251881Speter}
1253251881Speter
1254251881Speterstatic int
1255251881Speterg_part_ctl_resize(struct gctl_req *req, struct g_part_parms *gpp)
1256251881Speter{
1257251881Speter	struct g_geom *gp;
1258251881Speter	struct g_provider *pp;
1259251881Speter	struct g_part_entry *pe, *entry;
1260251881Speter	struct g_part_table *table;
1261251881Speter	struct sbuf *sb;
1262251881Speter	quad_t end;
1263251881Speter	int error;
1264251881Speter	off_t mediasize;
1265251881Speter
1266251881Speter	gp = gpp->gpp_geom;
1267251881Speter	G_PART_TRACE((G_T_TOPOLOGY, "%s(%s)", __func__, gp->name));
1268251881Speter	g_topology_assert();
1269251881Speter	table = gp->softc;
1270251881Speter
1271251881Speter	/* check gpp_index */
1272251881Speter	LIST_FOREACH(entry, &table->gpt_entry, gpe_entry) {
1273251881Speter		if (entry->gpe_deleted || entry->gpe_internal)
1274251881Speter			continue;
1275251881Speter		if (entry->gpe_index == gpp->gpp_index)
1276251881Speter			break;
1277251881Speter	}
1278251881Speter	if (entry == NULL) {
1279251881Speter		gctl_error(req, "%d index '%d'", ENOENT, gpp->gpp_index);
1280251881Speter		return (ENOENT);
1281251881Speter	}
1282251881Speter
1283251881Speter	/* check gpp_size */
1284251881Speter	end = entry->gpe_start + gpp->gpp_size - 1;
1285251881Speter	if (gpp->gpp_size < 1 || end > table->gpt_last) {
1286251881Speter		gctl_error(req, "%d size '%jd'", EINVAL,
1287251881Speter		    (intmax_t)gpp->gpp_size);
1288251881Speter		return (EINVAL);
1289251881Speter	}
1290251881Speter
1291251881Speter	LIST_FOREACH(pe, &table->gpt_entry, gpe_entry) {
1292251881Speter		if (pe->gpe_deleted || pe->gpe_internal || pe == entry)
1293251881Speter			continue;
1294251881Speter		if (end >= pe->gpe_start && end <= pe->gpe_end) {
1295251881Speter			gctl_error(req, "%d end '%jd'", ENOSPC,
1296251881Speter			    (intmax_t)end);
1297251881Speter			return (ENOSPC);
1298251881Speter		}
1299251881Speter		if (entry->gpe_start < pe->gpe_start && end > pe->gpe_end) {
1300251881Speter			gctl_error(req, "%d size '%jd'", ENOSPC,
1301251881Speter			    (intmax_t)gpp->gpp_size);
1302251881Speter			return (ENOSPC);
1303251881Speter		}
1304251881Speter	}
1305251881Speter
1306251881Speter	pp = entry->gpe_pp;
1307251881Speter	if ((g_debugflags & 16) == 0 &&
1308251881Speter	    (pp->acr > 0 || pp->acw > 0 || pp->ace > 0)) {
1309251881Speter		if (entry->gpe_end - entry->gpe_start + 1 > gpp->gpp_size) {
1310251881Speter			/* Deny shrinking of an opened partition. */
1311251881Speter			gctl_error(req, "%d", EBUSY);
1312251881Speter			return (EBUSY);
1313251881Speter		}
1314251881Speter	}
1315251881Speter
1316251881Speter	error = G_PART_RESIZE(table, entry, gpp);
1317251881Speter	if (error) {
1318251881Speter		gctl_error(req, "%d", error);
1319251881Speter		return (error);
1320251881Speter	}
1321251881Speter
1322251881Speter	if (!entry->gpe_created)
1323251881Speter		entry->gpe_modified = 1;
1324251881Speter
1325251881Speter	/* update mediasize of changed provider */
1326251881Speter	mediasize = (entry->gpe_end - entry->gpe_start + 1) *
1327251881Speter		pp->sectorsize;
1328251881Speter	g_resize_provider(pp, mediasize);
1329251881Speter
1330251881Speter	/* Provide feedback if so requested. */
1331251881Speter	if (gpp->gpp_parms & G_PART_PARM_OUTPUT) {
1332251881Speter		sb = sbuf_new_auto();
1333251881Speter		G_PART_FULLNAME(table, entry, sb, gp->name);
1334251881Speter		sbuf_cat(sb, " resized\n");
1335251881Speter		sbuf_finish(sb);
1336251881Speter		gctl_set_param(req, "output", sbuf_data(sb), sbuf_len(sb) + 1);
1337251881Speter		sbuf_delete(sb);
1338251881Speter	}
1339251881Speter	return (0);
1340251881Speter}
1341251881Speter
1342251881Speterstatic int
1343251881Speterg_part_ctl_setunset(struct gctl_req *req, struct g_part_parms *gpp,
1344251881Speter    unsigned int set)
1345251881Speter{
1346251881Speter	struct g_geom *gp;
1347251881Speter	struct g_part_entry *entry;
1348251881Speter	struct g_part_table *table;
1349251881Speter	struct sbuf *sb;
1350251881Speter	int error;
1351251881Speter
1352251881Speter	gp = gpp->gpp_geom;
1353251881Speter	G_PART_TRACE((G_T_TOPOLOGY, "%s(%s)", __func__, gp->name));
1354251881Speter	g_topology_assert();
1355251881Speter
1356251881Speter	table = gp->softc;
1357251881Speter
1358251881Speter	if (gpp->gpp_parms & G_PART_PARM_INDEX) {
1359251881Speter		LIST_FOREACH(entry, &table->gpt_entry, gpe_entry) {
1360251881Speter			if (entry->gpe_deleted || entry->gpe_internal)
1361251881Speter				continue;
1362251881Speter			if (entry->gpe_index == gpp->gpp_index)
1363251881Speter				break;
1364251881Speter		}
1365251881Speter		if (entry == NULL) {
1366251881Speter			gctl_error(req, "%d index '%d'", ENOENT,
1367251881Speter			    gpp->gpp_index);
1368251881Speter			return (ENOENT);
1369251881Speter		}
1370251881Speter	} else
1371251881Speter		entry = NULL;
1372251881Speter
1373251881Speter	error = G_PART_SETUNSET(table, entry, gpp->gpp_attrib, set);
1374251881Speter	if (error) {
1375251881Speter		gctl_error(req, "%d attrib '%s'", error, gpp->gpp_attrib);
1376251881Speter		return (error);
1377251881Speter	}
1378251881Speter
1379251881Speter	/* Provide feedback if so requested. */
1380251881Speter	if (gpp->gpp_parms & G_PART_PARM_OUTPUT) {
1381251881Speter		sb = sbuf_new_auto();
1382251881Speter		sbuf_printf(sb, "%s %sset on ", gpp->gpp_attrib,
1383251881Speter		    (set) ? "" : "un");
1384251881Speter		if (entry)
1385251881Speter			G_PART_FULLNAME(table, entry, sb, gp->name);
1386251881Speter		else
1387251881Speter			sbuf_cat(sb, gp->name);
1388251881Speter		sbuf_cat(sb, "\n");
1389251881Speter		sbuf_finish(sb);
1390251881Speter		gctl_set_param(req, "output", sbuf_data(sb), sbuf_len(sb) + 1);
1391251881Speter		sbuf_delete(sb);
1392251881Speter	}
1393251881Speter	return (0);
1394251881Speter}
1395251881Speter
1396251881Speterstatic int
1397251881Speterg_part_ctl_undo(struct gctl_req *req, struct g_part_parms *gpp)
1398251881Speter{
1399251881Speter	struct g_consumer *cp;
1400251881Speter	struct g_provider *pp;
1401251881Speter	struct g_geom *gp;
1402251881Speter	struct g_part_entry *entry, *tmp;
1403251881Speter	struct g_part_table *table;
1404251881Speter	int error, reprobe;
1405251881Speter
1406251881Speter	gp = gpp->gpp_geom;
1407251881Speter	G_PART_TRACE((G_T_TOPOLOGY, "%s(%s)", __func__, gp->name));
1408251881Speter	g_topology_assert();
1409251881Speter
1410251881Speter	table = gp->softc;
1411251881Speter	if (!table->gpt_opened) {
1412251881Speter		gctl_error(req, "%d", EPERM);
1413251881Speter		return (EPERM);
1414251881Speter	}
1415251881Speter
1416251881Speter	cp = LIST_FIRST(&gp->consumer);
1417251881Speter	LIST_FOREACH_SAFE(entry, &table->gpt_entry, gpe_entry, tmp) {
1418251881Speter		entry->gpe_modified = 0;
1419251881Speter		if (entry->gpe_created) {
1420251881Speter			pp = entry->gpe_pp;
1421251881Speter			if (pp != NULL) {
1422251881Speter				pp->private = NULL;
1423251881Speter				entry->gpe_pp = NULL;
1424251881Speter				g_wither_provider(pp, ENXIO);
1425251881Speter			}
1426251881Speter			entry->gpe_deleted = 1;
1427251881Speter		}
1428251881Speter		if (entry->gpe_deleted) {
1429251881Speter			LIST_REMOVE(entry, gpe_entry);
1430251881Speter			g_free(entry);
1431251881Speter		}
1432251881Speter	}
1433251881Speter
1434251881Speter	g_topology_unlock();
1435251881Speter
1436251881Speter	reprobe = (table->gpt_scheme == &g_part_null_scheme ||
1437251881Speter	    table->gpt_created) ? 1 : 0;
1438251881Speter
1439251881Speter	if (reprobe) {
1440251881Speter		LIST_FOREACH(entry, &table->gpt_entry, gpe_entry) {
1441251881Speter			if (entry->gpe_internal)
1442251881Speter				continue;
1443251881Speter			error = EBUSY;
1444251881Speter			goto fail;
1445251881Speter		}
1446251881Speter		while ((entry = LIST_FIRST(&table->gpt_entry)) != NULL) {
1447251881Speter			LIST_REMOVE(entry, gpe_entry);
1448251881Speter			g_free(entry);
1449251881Speter		}
1450251881Speter		error = g_part_probe(gp, cp, table->gpt_depth);
1451251881Speter		if (error) {
1452251881Speter			g_topology_lock();
1453251881Speter			g_access(cp, -1, -1, -1);
1454251881Speter			g_part_wither(gp, error);
1455251881Speter			return (0);
1456251881Speter		}
1457251881Speter		table = gp->softc;
1458251881Speter
1459251881Speter		/*
1460251881Speter		 * Synthesize a disk geometry. Some partitioning schemes
1461251881Speter		 * depend on it and since some file systems need it even
1462251881Speter		 * when the partitition scheme doesn't, we do it here in
1463251881Speter		 * scheme-independent code.
1464251881Speter		 */
1465251881Speter		pp = cp->provider;
1466251881Speter		g_part_geometry(table, cp, pp->mediasize / pp->sectorsize);
1467251881Speter	}
1468251881Speter
1469251881Speter	error = G_PART_READ(table, cp);
1470251881Speter	if (error)
1471251881Speter		goto fail;
1472251881Speter	error = g_part_check_integrity(table, cp);
1473251881Speter	if (error)
1474251881Speter		goto fail;
1475251881Speter
1476251881Speter	g_topology_lock();
1477251881Speter	LIST_FOREACH(entry, &table->gpt_entry, gpe_entry) {
1478251881Speter		if (!entry->gpe_internal)
1479251881Speter			g_part_new_provider(gp, table, entry);
1480251881Speter	}
1481251881Speter
1482251881Speter	table->gpt_opened = 0;
1483251881Speter	g_access(cp, -1, -1, -1);
1484251881Speter	return (0);
1485251881Speter
1486251881Speterfail:
1487251881Speter	g_topology_lock();
1488251881Speter	gctl_error(req, "%d", error);
1489251881Speter	return (error);
1490251881Speter}
1491251881Speter
1492251881Speterstatic void
1493251881Speterg_part_wither(struct g_geom *gp, int error)
1494251881Speter{
1495251881Speter	struct g_part_entry *entry;
1496251881Speter	struct g_part_table *table;
1497251881Speter
1498251881Speter	table = gp->softc;
1499251881Speter	if (table != NULL) {
1500251881Speter		G_PART_DESTROY(table, NULL);
1501251881Speter		while ((entry = LIST_FIRST(&table->gpt_entry)) != NULL) {
1502251881Speter			LIST_REMOVE(entry, gpe_entry);
1503251881Speter			g_free(entry);
1504251881Speter		}
1505251881Speter		if (gp->softc != NULL) {
1506251881Speter			kobj_delete((kobj_t)gp->softc, M_GEOM);
1507251881Speter			gp->softc = NULL;
1508251881Speter		}
1509251881Speter	}
1510251881Speter	g_wither_geom(gp, error);
1511251881Speter}
1512251881Speter
1513251881Speter/*
1514251881Speter * Class methods.
1515251881Speter */
1516251881Speter
1517251881Speterstatic void
1518251881Speterg_part_ctlreq(struct gctl_req *req, struct g_class *mp, const char *verb)
1519251881Speter{
1520251881Speter	struct g_part_parms gpp;
1521251881Speter	struct g_part_table *table;
1522251881Speter	struct gctl_req_arg *ap;
1523251881Speter	enum g_part_ctl ctlreq;
1524251881Speter	unsigned int i, mparms, oparms, parm;
1525251881Speter	int auto_commit, close_on_error;
1526251881Speter	int error, modifies;
1527251881Speter
1528251881Speter	G_PART_TRACE((G_T_TOPOLOGY, "%s(%s,%s)", __func__, mp->name, verb));
1529251881Speter	g_topology_assert();
1530251881Speter
1531251881Speter	ctlreq = G_PART_CTL_NONE;
1532251881Speter	modifies = 1;
1533251881Speter	mparms = 0;
1534251881Speter	oparms = G_PART_PARM_FLAGS | G_PART_PARM_OUTPUT | G_PART_PARM_VERSION;
1535251881Speter	switch (*verb) {
1536251881Speter	case 'a':
1537251881Speter		if (!strcmp(verb, "add")) {
1538251881Speter			ctlreq = G_PART_CTL_ADD;
1539251881Speter			mparms |= G_PART_PARM_GEOM | G_PART_PARM_SIZE |
1540251881Speter			    G_PART_PARM_START | G_PART_PARM_TYPE;
1541251881Speter			oparms |= G_PART_PARM_INDEX | G_PART_PARM_LABEL;
1542251881Speter		}
1543251881Speter		break;
1544251881Speter	case 'b':
1545251881Speter		if (!strcmp(verb, "bootcode")) {
1546251881Speter			ctlreq = G_PART_CTL_BOOTCODE;
1547251881Speter			mparms |= G_PART_PARM_GEOM | G_PART_PARM_BOOTCODE;
1548251881Speter		}
1549251881Speter		break;
1550251881Speter	case 'c':
1551251881Speter		if (!strcmp(verb, "commit")) {
1552251881Speter			ctlreq = G_PART_CTL_COMMIT;
1553251881Speter			mparms |= G_PART_PARM_GEOM;
1554251881Speter			modifies = 0;
1555251881Speter		} else if (!strcmp(verb, "create")) {
1556251881Speter			ctlreq = G_PART_CTL_CREATE;
1557251881Speter			mparms |= G_PART_PARM_PROVIDER | G_PART_PARM_SCHEME;
1558251881Speter			oparms |= G_PART_PARM_ENTRIES;
1559251881Speter		}
1560251881Speter		break;
1561251881Speter	case 'd':
1562251881Speter		if (!strcmp(verb, "delete")) {
1563251881Speter			ctlreq = G_PART_CTL_DELETE;
1564251881Speter			mparms |= G_PART_PARM_GEOM | G_PART_PARM_INDEX;
1565251881Speter		} else if (!strcmp(verb, "destroy")) {
1566251881Speter			ctlreq = G_PART_CTL_DESTROY;
1567251881Speter			mparms |= G_PART_PARM_GEOM;
1568251881Speter			oparms |= G_PART_PARM_FORCE;
1569251881Speter		}
1570251881Speter		break;
1571251881Speter	case 'm':
1572251881Speter		if (!strcmp(verb, "modify")) {
1573251881Speter			ctlreq = G_PART_CTL_MODIFY;
1574251881Speter			mparms |= G_PART_PARM_GEOM | G_PART_PARM_INDEX;
1575251881Speter			oparms |= G_PART_PARM_LABEL | G_PART_PARM_TYPE;
1576251881Speter		} else if (!strcmp(verb, "move")) {
1577251881Speter			ctlreq = G_PART_CTL_MOVE;
1578251881Speter			mparms |= G_PART_PARM_GEOM | G_PART_PARM_INDEX;
1579251881Speter		}
1580251881Speter		break;
1581251881Speter	case 'r':
1582251881Speter		if (!strcmp(verb, "recover")) {
1583251881Speter			ctlreq = G_PART_CTL_RECOVER;
1584251881Speter			mparms |= G_PART_PARM_GEOM;
1585251881Speter		} else if (!strcmp(verb, "resize")) {
1586251881Speter			ctlreq = G_PART_CTL_RESIZE;
1587251881Speter			mparms |= G_PART_PARM_GEOM | G_PART_PARM_INDEX |
1588251881Speter			    G_PART_PARM_SIZE;
1589251881Speter		}
1590251881Speter		break;
1591251881Speter	case 's':
1592251881Speter		if (!strcmp(verb, "set")) {
1593251881Speter			ctlreq = G_PART_CTL_SET;
1594251881Speter			mparms |= G_PART_PARM_ATTRIB | G_PART_PARM_GEOM;
1595251881Speter			oparms |= G_PART_PARM_INDEX;
1596251881Speter		}
1597251881Speter		break;
1598251881Speter	case 'u':
1599251881Speter		if (!strcmp(verb, "undo")) {
1600251881Speter			ctlreq = G_PART_CTL_UNDO;
1601251881Speter			mparms |= G_PART_PARM_GEOM;
1602251881Speter			modifies = 0;
1603251881Speter		} else if (!strcmp(verb, "unset")) {
1604251881Speter			ctlreq = G_PART_CTL_UNSET;
1605251881Speter			mparms |= G_PART_PARM_ATTRIB | G_PART_PARM_GEOM;
1606251881Speter			oparms |= G_PART_PARM_INDEX;
1607251881Speter		}
1608251881Speter		break;
1609251881Speter	}
1610251881Speter	if (ctlreq == G_PART_CTL_NONE) {
1611251881Speter		gctl_error(req, "%d verb '%s'", EINVAL, verb);
1612251881Speter		return;
1613251881Speter	}
1614251881Speter
1615251881Speter	bzero(&gpp, sizeof(gpp));
1616251881Speter	for (i = 0; i < req->narg; i++) {
1617251881Speter		ap = &req->arg[i];
1618251881Speter		parm = 0;
1619251881Speter		switch (ap->name[0]) {
1620251881Speter		case 'a':
1621251881Speter			if (!strcmp(ap->name, "arg0")) {
1622251881Speter				parm = mparms &
1623251881Speter				    (G_PART_PARM_GEOM | G_PART_PARM_PROVIDER);
1624251881Speter			}
1625251881Speter			if (!strcmp(ap->name, "attrib"))
1626251881Speter				parm = G_PART_PARM_ATTRIB;
1627251881Speter			break;
1628251881Speter		case 'b':
1629251881Speter			if (!strcmp(ap->name, "bootcode"))
1630251881Speter				parm = G_PART_PARM_BOOTCODE;
1631251881Speter			break;
1632251881Speter		case 'c':
1633251881Speter			if (!strcmp(ap->name, "class"))
1634251881Speter				continue;
1635251881Speter			break;
1636251881Speter		case 'e':
1637251881Speter			if (!strcmp(ap->name, "entries"))
1638251881Speter				parm = G_PART_PARM_ENTRIES;
1639251881Speter			break;
1640251881Speter		case 'f':
1641251881Speter			if (!strcmp(ap->name, "flags"))
1642251881Speter				parm = G_PART_PARM_FLAGS;
1643251881Speter			else if (!strcmp(ap->name, "force"))
1644251881Speter				parm = G_PART_PARM_FORCE;
1645251881Speter			break;
1646251881Speter		case 'i':
1647251881Speter			if (!strcmp(ap->name, "index"))
1648251881Speter				parm = G_PART_PARM_INDEX;
1649251881Speter			break;
1650251881Speter		case 'l':
1651251881Speter			if (!strcmp(ap->name, "label"))
1652251881Speter				parm = G_PART_PARM_LABEL;
1653251881Speter			break;
1654251881Speter		case 'o':
1655251881Speter			if (!strcmp(ap->name, "output"))
1656251881Speter				parm = G_PART_PARM_OUTPUT;
1657251881Speter			break;
1658251881Speter		case 's':
1659251881Speter			if (!strcmp(ap->name, "scheme"))
1660251881Speter				parm = G_PART_PARM_SCHEME;
1661251881Speter			else if (!strcmp(ap->name, "size"))
1662251881Speter				parm = G_PART_PARM_SIZE;
1663251881Speter			else if (!strcmp(ap->name, "start"))
1664251881Speter				parm = G_PART_PARM_START;
1665251881Speter			break;
1666251881Speter		case 't':
1667251881Speter			if (!strcmp(ap->name, "type"))
1668251881Speter				parm = G_PART_PARM_TYPE;
1669251881Speter			break;
1670251881Speter		case 'v':
1671251881Speter			if (!strcmp(ap->name, "verb"))
1672251881Speter				continue;
1673251881Speter			else if (!strcmp(ap->name, "version"))
1674251881Speter				parm = G_PART_PARM_VERSION;
1675251881Speter			break;
1676251881Speter		}
1677251881Speter		if ((parm & (mparms | oparms)) == 0) {
1678251881Speter			gctl_error(req, "%d param '%s'", EINVAL, ap->name);
1679251881Speter			return;
1680251881Speter		}
1681251881Speter		switch (parm) {
1682251881Speter		case G_PART_PARM_ATTRIB:
1683251881Speter			error = g_part_parm_str(req, ap->name,
1684251881Speter			    &gpp.gpp_attrib);
1685251881Speter			break;
1686251881Speter		case G_PART_PARM_BOOTCODE:
1687251881Speter			error = g_part_parm_bootcode(req, ap->name,
1688251881Speter			    &gpp.gpp_codeptr, &gpp.gpp_codesize);
1689251881Speter			break;
1690251881Speter		case G_PART_PARM_ENTRIES:
1691251881Speter			error = g_part_parm_intmax(req, ap->name,
1692251881Speter			    &gpp.gpp_entries);
1693251881Speter			break;
1694251881Speter		case G_PART_PARM_FLAGS:
1695251881Speter			error = g_part_parm_str(req, ap->name, &gpp.gpp_flags);
1696251881Speter			break;
1697251881Speter		case G_PART_PARM_FORCE:
1698251881Speter			error = g_part_parm_uint32(req, ap->name,
1699251881Speter			    &gpp.gpp_force);
1700251881Speter			break;
1701251881Speter		case G_PART_PARM_GEOM:
1702251881Speter			error = g_part_parm_geom(req, ap->name, &gpp.gpp_geom);
1703251881Speter			break;
1704251881Speter		case G_PART_PARM_INDEX:
1705251881Speter			error = g_part_parm_intmax(req, ap->name,
1706251881Speter			    &gpp.gpp_index);
1707251881Speter			break;
1708251881Speter		case G_PART_PARM_LABEL:
1709251881Speter			error = g_part_parm_str(req, ap->name, &gpp.gpp_label);
1710251881Speter			break;
1711251881Speter		case G_PART_PARM_OUTPUT:
1712251881Speter			error = 0;	/* Write-only parameter */
1713251881Speter			break;
1714251881Speter		case G_PART_PARM_PROVIDER:
1715251881Speter			error = g_part_parm_provider(req, ap->name,
1716251881Speter			    &gpp.gpp_provider);
1717251881Speter			break;
1718251881Speter		case G_PART_PARM_SCHEME:
1719251881Speter			error = g_part_parm_scheme(req, ap->name,
1720251881Speter			    &gpp.gpp_scheme);
1721251881Speter			break;
1722251881Speter		case G_PART_PARM_SIZE:
1723251881Speter			error = g_part_parm_quad(req, ap->name, &gpp.gpp_size);
1724251881Speter			break;
1725251881Speter		case G_PART_PARM_START:
1726251881Speter			error = g_part_parm_quad(req, ap->name,
1727251881Speter			    &gpp.gpp_start);
1728251881Speter			break;
1729251881Speter		case G_PART_PARM_TYPE:
1730251881Speter			error = g_part_parm_str(req, ap->name, &gpp.gpp_type);
1731251881Speter			break;
1732251881Speter		case G_PART_PARM_VERSION:
1733251881Speter			error = g_part_parm_uint32(req, ap->name,
1734251881Speter			    &gpp.gpp_version);
1735251881Speter			break;
1736251881Speter		default:
1737251881Speter			error = EDOOFUS;
1738251881Speter			gctl_error(req, "%d %s", error, ap->name);
1739251881Speter			break;
1740251881Speter		}
1741251881Speter		if (error != 0) {
1742251881Speter			if (error == ENOATTR) {
1743251881Speter				gctl_error(req, "%d param '%s'", error,
1744251881Speter				    ap->name);
1745251881Speter			}
1746251881Speter			return;
1747251881Speter		}
1748251881Speter		gpp.gpp_parms |= parm;
1749251881Speter	}
1750251881Speter	if ((gpp.gpp_parms & mparms) != mparms) {
1751251881Speter		parm = mparms - (gpp.gpp_parms & mparms);
1752251881Speter		gctl_error(req, "%d param '%x'", ENOATTR, parm);
1753251881Speter		return;
1754251881Speter	}
1755251881Speter
1756251881Speter	/* Obtain permissions if possible/necessary. */
1757251881Speter	close_on_error = 0;
1758251881Speter	table = NULL;
1759251881Speter	if (modifies && (gpp.gpp_parms & G_PART_PARM_GEOM)) {
1760251881Speter		table = gpp.gpp_geom->softc;
1761251881Speter		if (table != NULL && table->gpt_corrupt &&
1762251881Speter		    ctlreq != G_PART_CTL_DESTROY &&
1763251881Speter		    ctlreq != G_PART_CTL_RECOVER) {
1764251881Speter			gctl_error(req, "%d table '%s' is corrupt",
1765251881Speter			    EPERM, gpp.gpp_geom->name);
1766251881Speter			return;
1767251881Speter		}
1768251881Speter		if (table != NULL && !table->gpt_opened) {
1769251881Speter			error = g_access(LIST_FIRST(&gpp.gpp_geom->consumer),
1770251881Speter			    1, 1, 1);
1771251881Speter			if (error) {
1772251881Speter				gctl_error(req, "%d geom '%s'", error,
1773251881Speter				    gpp.gpp_geom->name);
1774251881Speter				return;
1775251881Speter			}
1776251881Speter			table->gpt_opened = 1;
1777251881Speter			close_on_error = 1;
1778251881Speter		}
1779251881Speter	}
1780251881Speter
1781251881Speter	/* Allow the scheme to check or modify the parameters. */
1782251881Speter	if (table != NULL) {
1783251881Speter		error = G_PART_PRECHECK(table, ctlreq, &gpp);
1784251881Speter		if (error) {
1785251881Speter			gctl_error(req, "%d pre-check failed", error);
1786251881Speter			goto out;
1787251881Speter		}
1788251881Speter	} else
1789251881Speter		error = EDOOFUS;	/* Prevent bogus uninit. warning. */
1790251881Speter
1791251881Speter	switch (ctlreq) {
1792251881Speter	case G_PART_CTL_NONE:
1793251881Speter		panic("%s", __func__);
1794251881Speter	case G_PART_CTL_ADD:
1795251881Speter		error = g_part_ctl_add(req, &gpp);
1796251881Speter		break;
1797251881Speter	case G_PART_CTL_BOOTCODE:
1798251881Speter		error = g_part_ctl_bootcode(req, &gpp);
1799251881Speter		break;
1800251881Speter	case G_PART_CTL_COMMIT:
1801251881Speter		error = g_part_ctl_commit(req, &gpp);
1802251881Speter		break;
1803251881Speter	case G_PART_CTL_CREATE:
1804251881Speter		error = g_part_ctl_create(req, &gpp);
1805251881Speter		break;
1806251881Speter	case G_PART_CTL_DELETE:
1807251881Speter		error = g_part_ctl_delete(req, &gpp);
1808251881Speter		break;
1809251881Speter	case G_PART_CTL_DESTROY:
1810251881Speter		error = g_part_ctl_destroy(req, &gpp);
1811251881Speter		break;
1812251881Speter	case G_PART_CTL_MODIFY:
1813251881Speter		error = g_part_ctl_modify(req, &gpp);
1814251881Speter		break;
1815251881Speter	case G_PART_CTL_MOVE:
1816251881Speter		error = g_part_ctl_move(req, &gpp);
1817251881Speter		break;
1818251881Speter	case G_PART_CTL_RECOVER:
1819251881Speter		error = g_part_ctl_recover(req, &gpp);
1820251881Speter		break;
1821251881Speter	case G_PART_CTL_RESIZE:
1822251881Speter		error = g_part_ctl_resize(req, &gpp);
1823251881Speter		break;
1824251881Speter	case G_PART_CTL_SET:
1825251881Speter		error = g_part_ctl_setunset(req, &gpp, 1);
1826251881Speter		break;
1827251881Speter	case G_PART_CTL_UNDO:
1828251881Speter		error = g_part_ctl_undo(req, &gpp);
1829251881Speter		break;
1830251881Speter	case G_PART_CTL_UNSET:
1831251881Speter		error = g_part_ctl_setunset(req, &gpp, 0);
1832251881Speter		break;
1833251881Speter	}
1834251881Speter
1835251881Speter	/* Implement automatic commit. */
1836251881Speter	if (!error) {
1837251881Speter		auto_commit = (modifies &&
1838251881Speter		    (gpp.gpp_parms & G_PART_PARM_FLAGS) &&
1839251881Speter		    strchr(gpp.gpp_flags, 'C') != NULL) ? 1 : 0;
1840251881Speter		if (auto_commit) {
1841251881Speter			KASSERT(gpp.gpp_parms & G_PART_PARM_GEOM, ("%s",
1842251881Speter			    __func__));
1843251881Speter			error = g_part_ctl_commit(req, &gpp);
1844251881Speter		}
1845251881Speter	}
1846251881Speter
1847251881Speter out:
1848251881Speter	if (error && close_on_error) {
1849251881Speter		g_access(LIST_FIRST(&gpp.gpp_geom->consumer), -1, -1, -1);
1850251881Speter		table->gpt_opened = 0;
1851251881Speter	}
1852251881Speter}
1853251881Speter
1854251881Speterstatic int
1855251881Speterg_part_destroy_geom(struct gctl_req *req, struct g_class *mp,
1856251881Speter    struct g_geom *gp)
1857251881Speter{
1858251881Speter
1859251881Speter	G_PART_TRACE((G_T_TOPOLOGY, "%s(%s,%s)", __func__, mp->name, gp->name));
1860251881Speter	g_topology_assert();
1861251881Speter
1862251881Speter	g_part_wither(gp, EINVAL);
1863251881Speter	return (0);
1864251881Speter}
1865251881Speter
1866251881Speterstatic struct g_geom *
1867251881Speterg_part_taste(struct g_class *mp, struct g_provider *pp, int flags __unused)
1868251881Speter{
1869251881Speter	struct g_consumer *cp;
1870251881Speter	struct g_geom *gp;
1871251881Speter	struct g_part_entry *entry;
1872251881Speter	struct g_part_table *table;
1873251881Speter	struct root_hold_token *rht;
1874251881Speter	int attr, depth;
1875251881Speter	int error;
1876251881Speter
1877251881Speter	G_PART_TRACE((G_T_TOPOLOGY, "%s(%s,%s)", __func__, mp->name, pp->name));
1878251881Speter	g_topology_assert();
1879251881Speter
1880251881Speter	/* Skip providers that are already open for writing. */
1881251881Speter	if (pp->acw > 0)
1882251881Speter		return (NULL);
1883251881Speter
1884251881Speter	/*
1885251881Speter	 * Create a GEOM with consumer and hook it up to the provider.
1886251881Speter	 * With that we become part of the topology. Optain read access
1887251881Speter	 * to the provider.
1888251881Speter	 */
1889251881Speter	gp = g_new_geomf(mp, "%s", pp->name);
1890251881Speter	cp = g_new_consumer(gp);
1891251881Speter	cp->flags |= G_CF_DIRECT_SEND | G_CF_DIRECT_RECEIVE;
1892251881Speter	error = g_attach(cp, pp);
1893251881Speter	if (error == 0)
1894251881Speter		error = g_access(cp, 1, 0, 0);
1895251881Speter	if (error != 0) {
1896251881Speter		if (cp->provider)
1897251881Speter			g_detach(cp);
1898251881Speter		g_destroy_consumer(cp);
1899251881Speter		g_destroy_geom(gp);
1900251881Speter		return (NULL);
1901251881Speter	}
1902251881Speter
1903251881Speter	rht = root_mount_hold(mp->name);
1904251881Speter	g_topology_unlock();
1905251881Speter
1906251881Speter	/*
1907251881Speter	 * Short-circuit the whole probing galore when there's no
1908251881Speter	 * media present.
1909251881Speter	 */
1910251881Speter	if (pp->mediasize == 0 || pp->sectorsize == 0) {
1911251881Speter		error = ENODEV;
1912251881Speter		goto fail;
1913251881Speter	}
1914251881Speter
1915251881Speter	/* Make sure we can nest and if so, determine our depth. */
1916251881Speter	error = g_getattr("PART::isleaf", cp, &attr);
1917251881Speter	if (!error && attr) {
1918251881Speter		error = ENODEV;
1919251881Speter		goto fail;
1920251881Speter	}
1921251881Speter	error = g_getattr("PART::depth", cp, &attr);
1922251881Speter	depth = (!error) ? attr + 1 : 0;
1923251881Speter
1924251881Speter	error = g_part_probe(gp, cp, depth);
1925251881Speter	if (error)
1926251881Speter		goto fail;
1927251881Speter
1928251881Speter	table = gp->softc;
1929251881Speter
1930251881Speter	/*
1931251881Speter	 * Synthesize a disk geometry. Some partitioning schemes
1932251881Speter	 * depend on it and since some file systems need it even
1933251881Speter	 * when the partitition scheme doesn't, we do it here in
1934251881Speter	 * scheme-independent code.
1935251881Speter	 */
1936251881Speter	g_part_geometry(table, cp, pp->mediasize / pp->sectorsize);
1937251881Speter
1938251881Speter	error = G_PART_READ(table, cp);
1939251881Speter	if (error)
1940251881Speter		goto fail;
1941251881Speter	error = g_part_check_integrity(table, cp);
1942251881Speter	if (error)
1943251881Speter		goto fail;
1944251881Speter
1945251881Speter	g_topology_lock();
1946251881Speter	LIST_FOREACH(entry, &table->gpt_entry, gpe_entry) {
1947251881Speter		if (!entry->gpe_internal)
1948251881Speter			g_part_new_provider(gp, table, entry);
1949251881Speter	}
1950251881Speter
1951251881Speter	root_mount_rel(rht);
1952251881Speter	g_access(cp, -1, 0, 0);
1953251881Speter	return (gp);
1954251881Speter
1955251881Speter fail:
1956251881Speter	g_topology_lock();
1957251881Speter	root_mount_rel(rht);
1958251881Speter	g_access(cp, -1, 0, 0);
1959251881Speter	g_detach(cp);
1960251881Speter	g_destroy_consumer(cp);
1961251881Speter	g_destroy_geom(gp);
1962251881Speter	return (NULL);
1963251881Speter}
1964251881Speter
1965251881Speter/*
1966251881Speter * Geom methods.
1967251881Speter */
1968251881Speter
1969251881Speterstatic int
1970251881Speterg_part_access(struct g_provider *pp, int dr, int dw, int de)
1971251881Speter{
1972251881Speter	struct g_consumer *cp;
1973251881Speter
1974251881Speter	G_PART_TRACE((G_T_ACCESS, "%s(%s,%d,%d,%d)", __func__, pp->name, dr,
1975251881Speter	    dw, de));
1976251881Speter
1977251881Speter	cp = LIST_FIRST(&pp->geom->consumer);
1978251881Speter
1979251881Speter	/* We always gain write-exclusive access. */
1980251881Speter	return (g_access(cp, dr, dw, dw + de));
1981251881Speter}
1982251881Speter
1983251881Speterstatic void
1984251881Speterg_part_dumpconf(struct sbuf *sb, const char *indent, struct g_geom *gp,
1985251881Speter    struct g_consumer *cp, struct g_provider *pp)
1986251881Speter{
1987251881Speter	char buf[64];
1988251881Speter	struct g_part_entry *entry;
1989251881Speter	struct g_part_table *table;
1990251881Speter
1991251881Speter	KASSERT(sb != NULL && gp != NULL, ("%s", __func__));
1992251881Speter	table = gp->softc;
1993251881Speter
1994251881Speter	if (indent == NULL) {
1995251881Speter		KASSERT(cp == NULL && pp != NULL, ("%s", __func__));
1996251881Speter		entry = pp->private;
1997251881Speter		if (entry == NULL)
1998251881Speter			return;
1999251881Speter		sbuf_printf(sb, " i %u o %ju ty %s", entry->gpe_index,
2000251881Speter		    (uintmax_t)entry->gpe_offset,
2001251881Speter		    G_PART_TYPE(table, entry, buf, sizeof(buf)));
2002251881Speter		/*
2003251881Speter		 * libdisk compatibility quirk - the scheme dumps the
2004251881Speter		 * slicer name and partition type in a way that is
2005251881Speter		 * compatible with libdisk. When libdisk is not used
2006251881Speter		 * anymore, this should go away.
2007251881Speter		 */
2008251881Speter		G_PART_DUMPCONF(table, entry, sb, indent);
2009251881Speter	} else if (cp != NULL) {	/* Consumer configuration. */
2010251881Speter		KASSERT(pp == NULL, ("%s", __func__));
2011251881Speter		/* none */
2012251881Speter	} else if (pp != NULL) {	/* Provider configuration. */
2013251881Speter		entry = pp->private;
2014251881Speter		if (entry == NULL)
2015251881Speter			return;
2016251881Speter		sbuf_printf(sb, "%s<start>%ju</start>\n", indent,
2017251881Speter		    (uintmax_t)entry->gpe_start);
2018251881Speter		sbuf_printf(sb, "%s<end>%ju</end>\n", indent,
2019251881Speter		    (uintmax_t)entry->gpe_end);
2020251881Speter		sbuf_printf(sb, "%s<index>%u</index>\n", indent,
2021251881Speter		    entry->gpe_index);
2022251881Speter		sbuf_printf(sb, "%s<type>%s</type>\n", indent,
2023251881Speter		    G_PART_TYPE(table, entry, buf, sizeof(buf)));
2024251881Speter		sbuf_printf(sb, "%s<offset>%ju</offset>\n", indent,
2025251881Speter		    (uintmax_t)entry->gpe_offset);
2026251881Speter		sbuf_printf(sb, "%s<length>%ju</length>\n", indent,
2027251881Speter		    (uintmax_t)pp->mediasize);
2028251881Speter		G_PART_DUMPCONF(table, entry, sb, indent);
2029251881Speter	} else {			/* Geom configuration. */
2030251881Speter		sbuf_printf(sb, "%s<scheme>%s</scheme>\n", indent,
2031251881Speter		    table->gpt_scheme->name);
2032251881Speter		sbuf_printf(sb, "%s<entries>%u</entries>\n", indent,
2033251881Speter		    table->gpt_entries);
2034251881Speter		sbuf_printf(sb, "%s<first>%ju</first>\n", indent,
2035251881Speter		    (uintmax_t)table->gpt_first);
2036251881Speter		sbuf_printf(sb, "%s<last>%ju</last>\n", indent,
2037251881Speter		    (uintmax_t)table->gpt_last);
2038251881Speter		sbuf_printf(sb, "%s<fwsectors>%u</fwsectors>\n", indent,
2039251881Speter		    table->gpt_sectors);
2040251881Speter		sbuf_printf(sb, "%s<fwheads>%u</fwheads>\n", indent,
2041286506Speter		    table->gpt_heads);
2042251881Speter		sbuf_printf(sb, "%s<state>%s</state>\n", indent,
2043286506Speter		    table->gpt_corrupt ? "CORRUPT": "OK");
2044286506Speter		sbuf_printf(sb, "%s<modified>%s</modified>\n", indent,
2045286506Speter		    table->gpt_opened ? "true": "false");
2046286506Speter		G_PART_DUMPCONF(table, NULL, sb, indent);
2047286506Speter	}
2048286506Speter}
2049286506Speter
2050251881Speterstatic void
2051251881Speterg_part_resize(struct g_consumer *cp)
2052251881Speter{
2053251881Speter	struct g_part_table *table;
2054251881Speter
2055251881Speter	G_PART_TRACE((G_T_TOPOLOGY, "%s(%s)", __func__, cp->provider->name));
2056251881Speter	g_topology_assert();
2057251881Speter
2058251881Speter	table = cp->geom->softc;
2059251881Speter	if (table->gpt_opened == 0) {
2060251881Speter		if (g_access(cp, 1, 1, 1) != 0)
2061251881Speter			return;
2062251881Speter		table->gpt_opened = 1;
2063251881Speter	}
2064251881Speter	if (G_PART_RESIZE(table, NULL, NULL) == 0)
2065251881Speter		printf("GEOM_PART: %s was automatically resized\n",
2066251881Speter		    cp->geom->name);
2067251881Speter	if (g_part_check_integrity(table, cp) != 0) {
2068251881Speter		g_access(cp, -1, -1, -1);
2069251881Speter		table->gpt_opened = 0;
2070251881Speter		g_part_wither(table->gpt_gp, ENXIO);
2071251881Speter	}
2072251881Speter}
2073251881Speter
2074251881Speterstatic void
2075251881Speterg_part_orphan(struct g_consumer *cp)
2076251881Speter{
2077251881Speter	struct g_provider *pp;
2078251881Speter	struct g_part_table *table;
2079251881Speter
2080251881Speter	pp = cp->provider;
2081251881Speter	KASSERT(pp != NULL, ("%s", __func__));
2082251881Speter	G_PART_TRACE((G_T_TOPOLOGY, "%s(%s)", __func__, pp->name));
2083251881Speter	g_topology_assert();
2084251881Speter
2085251881Speter	KASSERT(pp->error != 0, ("%s", __func__));
2086251881Speter	table = cp->geom->softc;
2087251881Speter	if (table != NULL && table->gpt_opened)
2088251881Speter		g_access(cp, -1, -1, -1);
2089251881Speter	g_part_wither(cp->geom, pp->error);
2090251881Speter}
2091251881Speter
2092251881Speterstatic void
2093251881Speterg_part_spoiled(struct g_consumer *cp)
2094251881Speter{
2095251881Speter
2096251881Speter	G_PART_TRACE((G_T_TOPOLOGY, "%s(%s)", __func__, cp->provider->name));
2097251881Speter	g_topology_assert();
2098251881Speter
2099251881Speter	cp->flags |= G_CF_ORPHAN;
2100251881Speter	g_part_wither(cp->geom, ENXIO);
2101251881Speter}
2102251881Speter
2103286506Speterstatic void
2104251881Speterg_part_start(struct bio *bp)
2105251881Speter{
2106251881Speter	struct bio *bp2;
2107251881Speter	struct g_consumer *cp;
2108251881Speter	struct g_geom *gp;
2109251881Speter	struct g_part_entry *entry;
2110251881Speter	struct g_part_table *table;
2111251881Speter	struct g_kerneldump *gkd;
2112251881Speter	struct g_provider *pp;
2113251881Speter	char buf[64];
2114251881Speter
2115251881Speter	pp = bp->bio_to;
2116251881Speter	gp = pp->geom;
2117251881Speter	table = gp->softc;
2118251881Speter	cp = LIST_FIRST(&gp->consumer);
2119251881Speter
2120251881Speter	G_PART_TRACE((G_T_BIO, "%s: cmd=%d, provider=%s", __func__, bp->bio_cmd,
2121251881Speter	    pp->name));
2122251881Speter
2123251881Speter	entry = pp->private;
2124251881Speter	if (entry == NULL) {
2125251881Speter		g_io_deliver(bp, ENXIO);
2126251881Speter		return;
2127251881Speter	}
2128251881Speter
2129251881Speter	switch(bp->bio_cmd) {
2130251881Speter	case BIO_DELETE:
2131251881Speter	case BIO_READ:
2132251881Speter	case BIO_WRITE:
2133251881Speter		if (bp->bio_offset >= pp->mediasize) {
2134251881Speter			g_io_deliver(bp, EIO);
2135251881Speter			return;
2136251881Speter		}
2137251881Speter		bp2 = g_clone_bio(bp);
2138251881Speter		if (bp2 == NULL) {
2139251881Speter			g_io_deliver(bp, ENOMEM);
2140251881Speter			return;
2141251881Speter		}
2142251881Speter		if (bp2->bio_offset + bp2->bio_length > pp->mediasize)
2143251881Speter			bp2->bio_length = pp->mediasize - bp2->bio_offset;
2144251881Speter		bp2->bio_done = g_std_done;
2145251881Speter		bp2->bio_offset += entry->gpe_offset;
2146251881Speter		g_io_request(bp2, cp);
2147251881Speter		return;
2148251881Speter	case BIO_FLUSH:
2149251881Speter		break;
2150251881Speter	case BIO_GETATTR:
2151251881Speter		if (g_handleattr_int(bp, "GEOM::fwheads", table->gpt_heads))
2152251881Speter			return;
2153251881Speter		if (g_handleattr_int(bp, "GEOM::fwsectors", table->gpt_sectors))
2154251881Speter			return;
2155251881Speter		if (g_handleattr_int(bp, "PART::isleaf", table->gpt_isleaf))
2156251881Speter			return;
2157251881Speter		if (g_handleattr_int(bp, "PART::depth", table->gpt_depth))
2158251881Speter			return;
2159251881Speter		if (g_handleattr_str(bp, "PART::scheme",
2160251881Speter		    table->gpt_scheme->name))
2161251881Speter			return;
2162251881Speter		if (g_handleattr_str(bp, "PART::type",
2163251881Speter		    G_PART_TYPE(table, entry, buf, sizeof(buf))))
2164251881Speter			return;
2165251881Speter		if (!strcmp("GEOM::kerneldump", bp->bio_attribute)) {
2166251881Speter			/*
2167251881Speter			 * Check that the partition is suitable for kernel
2168251881Speter			 * dumps. Typically only swap partitions should be
2169251881Speter			 * used. If the request comes from the nested scheme
2170251881Speter			 * we allow dumping there as well.
2171251881Speter			 */
2172251881Speter			if ((bp->bio_from == NULL ||
2173251881Speter			    bp->bio_from->geom->class != &g_part_class) &&
2174251881Speter			    G_PART_DUMPTO(table, entry) == 0) {
2175251881Speter				g_io_deliver(bp, ENODEV);
2176251881Speter				printf("GEOM_PART: Partition '%s' not suitable"
2177251881Speter				    " for kernel dumps (wrong type?)\n",
2178251881Speter				    pp->name);
2179251881Speter				return;
2180251881Speter			}
2181251881Speter			gkd = (struct g_kerneldump *)bp->bio_data;
2182251881Speter			if (gkd->offset >= pp->mediasize) {
2183251881Speter				g_io_deliver(bp, EIO);
2184251881Speter				return;
2185251881Speter			}
2186251881Speter			if (gkd->offset + gkd->length > pp->mediasize)
2187251881Speter				gkd->length = pp->mediasize - gkd->offset;
2188251881Speter			gkd->offset += entry->gpe_offset;
2189251881Speter		}
2190251881Speter		break;
2191251881Speter	default:
2192251881Speter		g_io_deliver(bp, EOPNOTSUPP);
2193251881Speter		return;
2194251881Speter	}
2195251881Speter
2196251881Speter	bp2 = g_clone_bio(bp);
2197251881Speter	if (bp2 == NULL) {
2198251881Speter		g_io_deliver(bp, ENOMEM);
2199289180Speter		return;
2200251881Speter	}
2201251881Speter	bp2->bio_done = g_std_done;
2202251881Speter	g_io_request(bp2, cp);
2203251881Speter}
2204251881Speter
2205251881Speterstatic void
2206251881Speterg_part_init(struct g_class *mp)
2207251881Speter{
2208251881Speter
2209251881Speter	TAILQ_INSERT_HEAD(&g_part_schemes, &g_part_null_scheme, scheme_list);
2210251881Speter}
2211251881Speter
2212251881Speterstatic void
2213251881Speterg_part_fini(struct g_class *mp)
2214251881Speter{
2215251881Speter
2216251881Speter	TAILQ_REMOVE(&g_part_schemes, &g_part_null_scheme, scheme_list);
2217251881Speter}
2218251881Speter
2219251881Speterstatic void
2220251881Speterg_part_unload_event(void *arg, int flag)
2221251881Speter{
2222251881Speter	struct g_consumer *cp;
2223251881Speter	struct g_geom *gp;
2224289180Speter	struct g_provider *pp;
2225251881Speter	struct g_part_scheme *scheme;
2226251881Speter	struct g_part_table *table;
2227251881Speter	uintptr_t *xchg;
2228251881Speter	int acc, error;
2229251881Speter
2230251881Speter	if (flag == EV_CANCEL)
2231251881Speter		return;
2232251881Speter
2233251881Speter	xchg = arg;
2234251881Speter	error = 0;
2235251881Speter	scheme = (void *)(*xchg);
2236251881Speter
2237251881Speter	g_topology_assert();
2238251881Speter
2239251881Speter	LIST_FOREACH(gp, &g_part_class.geom, geom) {
2240251881Speter		table = gp->softc;
2241251881Speter		if (table->gpt_scheme != scheme)
2242251881Speter			continue;
2243251881Speter
2244251881Speter		acc = 0;
2245251881Speter		LIST_FOREACH(pp, &gp->provider, provider)
2246251881Speter			acc += pp->acr + pp->acw + pp->ace;
2247251881Speter		LIST_FOREACH(cp, &gp->consumer, consumer)
2248251881Speter			acc += cp->acr + cp->acw + cp->ace;
2249251881Speter
2250251881Speter		if (!acc)
2251251881Speter			g_part_wither(gp, ENOSYS);
2252251881Speter		else
2253251881Speter			error = EBUSY;
2254251881Speter	}
2255251881Speter
2256251881Speter	if (!error)
2257251881Speter		TAILQ_REMOVE(&g_part_schemes, scheme, scheme_list);
2258251881Speter
2259251881Speter	*xchg = error;
2260251881Speter}
2261251881Speter
2262251881Speterint
2263251881Speterg_part_modevent(module_t mod, int type, struct g_part_scheme *scheme)
2264251881Speter{
2265251881Speter	struct g_part_scheme *iter;
2266251881Speter	uintptr_t arg;
2267289180Speter	int error;
2268251881Speter
2269251881Speter	error = 0;
2270251881Speter	switch (type) {
2271251881Speter	case MOD_LOAD:
2272251881Speter		TAILQ_FOREACH(iter, &g_part_schemes, scheme_list) {
2273251881Speter			if (scheme == iter) {
2274251881Speter				printf("GEOM_PART: scheme %s is already "
2275251881Speter				    "registered!\n", scheme->name);
2276251881Speter				break;
2277251881Speter			}
2278251881Speter		}
2279251881Speter		if (iter == NULL) {
2280251881Speter			TAILQ_INSERT_TAIL(&g_part_schemes, scheme,
2281251881Speter			    scheme_list);
2282251881Speter			g_retaste(&g_part_class);
2283286506Speter		}
2284286506Speter		break;
2285286506Speter	case MOD_UNLOAD:
2286286506Speter		arg = (uintptr_t)scheme;
2287286506Speter		error = g_waitfor_event(g_part_unload_event, &arg, M_WAITOK,
2288286506Speter		    NULL);
2289286506Speter		if (error == 0)
2290286506Speter			error = arg;
2291286506Speter		break;
2292286506Speter	default:
2293286506Speter		error = EOPNOTSUPP;
2294286506Speter		break;
2295251881Speter	}
2296251881Speter
2297251881Speter	return (error);
2298286506Speter}
2299289180Speter