g_part.c revision 210746
1/*-
2 * Copyright (c) 2002, 2005-2009 Marcel Moolenaar
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * 1. Redistributions of source code must retain the above copyright
10 *    notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 *    notice, this list of conditions and the following disclaimer in the
13 *    documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 */
26
27#include <sys/cdefs.h>
28__FBSDID("$FreeBSD: head/sys/geom/part/g_part.c 210746 2010-08-02 10:26:15Z ae $");
29
30#include <sys/param.h>
31#include <sys/bio.h>
32#include <sys/diskmbr.h>
33#include <sys/endian.h>
34#include <sys/kernel.h>
35#include <sys/kobj.h>
36#include <sys/limits.h>
37#include <sys/lock.h>
38#include <sys/malloc.h>
39#include <sys/mutex.h>
40#include <sys/queue.h>
41#include <sys/sbuf.h>
42#include <sys/systm.h>
43#include <sys/uuid.h>
44#include <geom/geom.h>
45#include <geom/geom_ctl.h>
46#include <geom/geom_int.h>
47#include <geom/part/g_part.h>
48
49#include "g_part_if.h"
50
51#ifndef _PATH_DEV
52#define _PATH_DEV "/dev/"
53#endif
54
55static kobj_method_t g_part_null_methods[] = {
56	{ 0, 0 }
57};
58
59static struct g_part_scheme g_part_null_scheme = {
60	"(none)",
61	g_part_null_methods,
62	sizeof(struct g_part_table),
63};
64
65TAILQ_HEAD(, g_part_scheme) g_part_schemes =
66    TAILQ_HEAD_INITIALIZER(g_part_schemes);
67
68struct g_part_alias_list {
69	const char *lexeme;
70	enum g_part_alias alias;
71} g_part_alias_list[G_PART_ALIAS_COUNT] = {
72	{ "apple-boot", G_PART_ALIAS_APPLE_BOOT },
73	{ "apple-hfs", G_PART_ALIAS_APPLE_HFS },
74	{ "apple-label", G_PART_ALIAS_APPLE_LABEL },
75	{ "apple-raid", G_PART_ALIAS_APPLE_RAID },
76	{ "apple-raid-offline", G_PART_ALIAS_APPLE_RAID_OFFLINE },
77	{ "apple-tv-recovery", G_PART_ALIAS_APPLE_TV_RECOVERY },
78	{ "apple-ufs", G_PART_ALIAS_APPLE_UFS },
79	{ "efi", G_PART_ALIAS_EFI },
80	{ "freebsd", G_PART_ALIAS_FREEBSD },
81	{ "freebsd-boot", G_PART_ALIAS_FREEBSD_BOOT },
82	{ "freebsd-swap", G_PART_ALIAS_FREEBSD_SWAP },
83	{ "freebsd-ufs", G_PART_ALIAS_FREEBSD_UFS },
84	{ "freebsd-vinum", G_PART_ALIAS_FREEBSD_VINUM },
85	{ "freebsd-zfs", G_PART_ALIAS_FREEBSD_ZFS },
86	{ "linux-data", G_PART_ALIAS_LINUX_DATA },
87	{ "linux-lvm", G_PART_ALIAS_LINUX_LVM },
88	{ "linux-raid", G_PART_ALIAS_LINUX_RAID },
89	{ "linux-swap", G_PART_ALIAS_LINUX_SWAP },
90	{ "ms-basic-data", G_PART_ALIAS_MS_BASIC_DATA },
91	{ "ms-ldm-data", G_PART_ALIAS_MS_LDM_DATA },
92	{ "ms-ldm-metadata", G_PART_ALIAS_MS_LDM_METADATA },
93	{ "ms-reserved", G_PART_ALIAS_MS_RESERVED },
94	{ "ntfs", G_PART_ALIAS_MS_NTFS },
95	{ "netbsd-ccd", G_PART_ALIAS_NETBSD_CCD },
96	{ "netbsd-cgd", G_PART_ALIAS_NETBSD_CGD },
97	{ "netbsd-ffs", G_PART_ALIAS_NETBSD_FFS },
98	{ "netbsd-lfs", G_PART_ALIAS_NETBSD_LFS },
99	{ "netbsd-raid", G_PART_ALIAS_NETBSD_RAID },
100	{ "netbsd-swap", G_PART_ALIAS_NETBSD_SWAP },
101	{ "mbr", G_PART_ALIAS_MBR }
102};
103
104/*
105 * The GEOM partitioning class.
106 */
107static g_ctl_req_t g_part_ctlreq;
108static g_ctl_destroy_geom_t g_part_destroy_geom;
109static g_fini_t g_part_fini;
110static g_init_t g_part_init;
111static g_taste_t g_part_taste;
112
113static g_access_t g_part_access;
114static g_dumpconf_t g_part_dumpconf;
115static g_orphan_t g_part_orphan;
116static g_spoiled_t g_part_spoiled;
117static g_start_t g_part_start;
118
119static struct g_class g_part_class = {
120	.name = "PART",
121	.version = G_VERSION,
122	/* Class methods. */
123	.ctlreq = g_part_ctlreq,
124	.destroy_geom = g_part_destroy_geom,
125	.fini = g_part_fini,
126	.init = g_part_init,
127	.taste = g_part_taste,
128	/* Geom methods. */
129	.access = g_part_access,
130	.dumpconf = g_part_dumpconf,
131	.orphan = g_part_orphan,
132	.spoiled = g_part_spoiled,
133	.start = g_part_start,
134};
135
136DECLARE_GEOM_CLASS(g_part_class, g_part);
137
138/*
139 * Support functions.
140 */
141
142static void g_part_wither(struct g_geom *, int);
143
144const char *
145g_part_alias_name(enum g_part_alias alias)
146{
147	int i;
148
149	for (i = 0; i < G_PART_ALIAS_COUNT; i++) {
150		if (g_part_alias_list[i].alias != alias)
151			continue;
152		return (g_part_alias_list[i].lexeme);
153	}
154
155	return (NULL);
156}
157
158void
159g_part_geometry_heads(off_t blocks, u_int sectors, off_t *bestchs,
160    u_int *bestheads)
161{
162	static u_int candidate_heads[] = { 1, 2, 16, 32, 64, 128, 255, 0 };
163	off_t chs, cylinders;
164	u_int heads;
165	int idx;
166
167	*bestchs = 0;
168	*bestheads = 0;
169	for (idx = 0; candidate_heads[idx] != 0; idx++) {
170		heads = candidate_heads[idx];
171		cylinders = blocks / heads / sectors;
172		if (cylinders < heads || cylinders < sectors)
173			break;
174		if (cylinders > 1023)
175			continue;
176		chs = cylinders * heads * sectors;
177		if (chs > *bestchs || (chs == *bestchs && *bestheads == 1)) {
178			*bestchs = chs;
179			*bestheads = heads;
180		}
181	}
182}
183
184static void
185g_part_geometry(struct g_part_table *table, struct g_consumer *cp,
186    off_t blocks)
187{
188	static u_int candidate_sectors[] = { 1, 9, 17, 33, 63, 0 };
189	off_t chs, bestchs;
190	u_int heads, sectors;
191	int idx;
192
193	if (g_getattr("GEOM::fwsectors", cp, &sectors) != 0 || sectors == 0 ||
194	    g_getattr("GEOM::fwheads", cp, &heads) != 0 || heads == 0) {
195		table->gpt_fixgeom = 0;
196		table->gpt_heads = 0;
197		table->gpt_sectors = 0;
198		bestchs = 0;
199		for (idx = 0; candidate_sectors[idx] != 0; idx++) {
200			sectors = candidate_sectors[idx];
201			g_part_geometry_heads(blocks, sectors, &chs, &heads);
202			if (chs == 0)
203				continue;
204			/*
205			 * Prefer a geometry with sectors > 1, but only if
206			 * it doesn't bump down the numbver of heads to 1.
207			 */
208			if (chs > bestchs || (chs == bestchs && heads > 1 &&
209			    table->gpt_sectors == 1)) {
210				bestchs = chs;
211				table->gpt_heads = heads;
212				table->gpt_sectors = sectors;
213			}
214		}
215		/*
216		 * If we didn't find a geometry at all, then the disk is
217		 * too big. This means we can use the maximum number of
218		 * heads and sectors.
219		 */
220		if (bestchs == 0) {
221			table->gpt_heads = 255;
222			table->gpt_sectors = 63;
223		}
224	} else {
225		table->gpt_fixgeom = 1;
226		table->gpt_heads = heads;
227		table->gpt_sectors = sectors;
228	}
229}
230
231struct g_part_entry *
232g_part_new_entry(struct g_part_table *table, int index, quad_t start,
233    quad_t end)
234{
235	struct g_part_entry *entry, *last;
236
237	last = NULL;
238	LIST_FOREACH(entry, &table->gpt_entry, gpe_entry) {
239		if (entry->gpe_index == index)
240			break;
241		if (entry->gpe_index > index) {
242			entry = NULL;
243			break;
244		}
245		last = entry;
246	}
247	if (entry == NULL) {
248		entry = g_malloc(table->gpt_scheme->gps_entrysz,
249		    M_WAITOK | M_ZERO);
250		entry->gpe_index = index;
251		if (last == NULL)
252			LIST_INSERT_HEAD(&table->gpt_entry, entry, gpe_entry);
253		else
254			LIST_INSERT_AFTER(last, entry, gpe_entry);
255	} else
256		entry->gpe_offset = 0;
257	entry->gpe_start = start;
258	entry->gpe_end = end;
259	return (entry);
260}
261
262static void
263g_part_new_provider(struct g_geom *gp, struct g_part_table *table,
264    struct g_part_entry *entry)
265{
266	struct g_consumer *cp;
267	struct g_provider *pp;
268	struct sbuf *sb;
269	off_t offset;
270
271	cp = LIST_FIRST(&gp->consumer);
272	pp = cp->provider;
273
274	offset = entry->gpe_start * pp->sectorsize;
275	if (entry->gpe_offset < offset)
276		entry->gpe_offset = offset;
277
278	if (entry->gpe_pp == NULL) {
279		sb = sbuf_new_auto();
280		G_PART_FULLNAME(table, entry, sb, gp->name);
281		sbuf_finish(sb);
282		entry->gpe_pp = g_new_providerf(gp, "%s", sbuf_data(sb));
283		sbuf_delete(sb);
284		entry->gpe_pp->private = entry;		/* Close the circle. */
285	}
286	entry->gpe_pp->index = entry->gpe_index - 1;	/* index is 1-based. */
287	entry->gpe_pp->mediasize = (entry->gpe_end - entry->gpe_start + 1) *
288	    pp->sectorsize;
289	entry->gpe_pp->mediasize -= entry->gpe_offset - offset;
290	entry->gpe_pp->sectorsize = pp->sectorsize;
291	entry->gpe_pp->flags = pp->flags & G_PF_CANDELETE;
292	entry->gpe_pp->stripesize = pp->stripesize;
293	entry->gpe_pp->stripeoffset = pp->stripeoffset + entry->gpe_offset;
294	if (pp->stripesize > 0)
295		entry->gpe_pp->stripeoffset %= pp->stripesize;
296	g_error_provider(entry->gpe_pp, 0);
297}
298
299static int
300g_part_parm_geom(const char *rawname, struct g_geom **v)
301{
302	struct g_geom *gp;
303	const char *pname;
304
305	if (strncmp(rawname, _PATH_DEV, strlen(_PATH_DEV)) == 0)
306		pname = rawname + strlen(_PATH_DEV);
307	else
308		pname = rawname;
309	LIST_FOREACH(gp, &g_part_class.geom, geom) {
310		if (!strcmp(pname, gp->name))
311			break;
312	}
313	if (gp == NULL)
314		return (EINVAL);
315	*v = gp;
316	return (0);
317}
318
319static int
320g_part_parm_provider(const char *pname, struct g_provider **v)
321{
322	struct g_provider *pp;
323
324	if (strncmp(pname, _PATH_DEV, strlen(_PATH_DEV)) == 0)
325		pp = g_provider_by_name(pname + strlen(_PATH_DEV));
326	else
327		pp = g_provider_by_name(pname);
328	if (pp == NULL)
329		return (EINVAL);
330	*v = pp;
331	return (0);
332}
333
334static int
335g_part_parm_quad(const char *p, quad_t *v)
336{
337	char *x;
338	quad_t q;
339
340	q = strtoq(p, &x, 0);
341	if (*x != '\0' || q < 0)
342		return (EINVAL);
343	*v = q;
344	return (0);
345}
346
347static int
348g_part_parm_scheme(const char *p, struct g_part_scheme **v)
349{
350	struct g_part_scheme *s;
351
352	TAILQ_FOREACH(s, &g_part_schemes, scheme_list) {
353		if (s == &g_part_null_scheme)
354			continue;
355		if (!strcasecmp(s->name, p))
356			break;
357	}
358	if (s == NULL)
359		return (EINVAL);
360	*v = s;
361	return (0);
362}
363
364static int
365g_part_parm_str(const char *p, const char **v)
366{
367
368	if (p[0] == '\0')
369		return (EINVAL);
370	*v = p;
371	return (0);
372}
373
374static int
375g_part_parm_uint(const char *p, u_int *v)
376{
377	char *x;
378	long l;
379
380	l = strtol(p, &x, 0);
381	if (*x != '\0' || l < 0 || l > INT_MAX)
382		return (EINVAL);
383	*v = (unsigned int)l;
384	return (0);
385}
386
387static int
388g_part_probe(struct g_geom *gp, struct g_consumer *cp, int depth)
389{
390	struct g_part_scheme *iter, *scheme;
391	struct g_part_table *table;
392	int pri, probe;
393
394	table = gp->softc;
395	scheme = (table != NULL) ? table->gpt_scheme : NULL;
396	pri = (scheme != NULL) ? G_PART_PROBE(table, cp) : INT_MIN;
397	if (pri == 0)
398		goto done;
399	if (pri > 0) {	/* error */
400		scheme = NULL;
401		pri = INT_MIN;
402	}
403
404	TAILQ_FOREACH(iter, &g_part_schemes, scheme_list) {
405		if (iter == &g_part_null_scheme)
406			continue;
407		table = (void *)kobj_create((kobj_class_t)iter, M_GEOM,
408		    M_WAITOK);
409		table->gpt_gp = gp;
410		table->gpt_scheme = iter;
411		table->gpt_depth = depth;
412		probe = G_PART_PROBE(table, cp);
413		if (probe <= 0 && probe > pri) {
414			pri = probe;
415			scheme = iter;
416			if (gp->softc != NULL)
417				kobj_delete((kobj_t)gp->softc, M_GEOM);
418			gp->softc = table;
419			if (pri == 0)
420				goto done;
421		} else
422			kobj_delete((kobj_t)table, M_GEOM);
423	}
424
425done:
426	return ((scheme == NULL) ? ENXIO : 0);
427}
428
429/*
430 * Control request functions.
431 */
432
433static int
434g_part_ctl_add(struct gctl_req *req, struct g_part_parms *gpp)
435{
436	struct g_geom *gp;
437	struct g_provider *pp;
438	struct g_part_entry *delent, *last, *entry;
439	struct g_part_table *table;
440	struct sbuf *sb;
441	quad_t end;
442	unsigned int index;
443	int error;
444
445	gp = gpp->gpp_geom;
446	G_PART_TRACE((G_T_TOPOLOGY, "%s(%s)", __func__, gp->name));
447	g_topology_assert();
448
449	pp = LIST_FIRST(&gp->consumer)->provider;
450	table = gp->softc;
451	end = gpp->gpp_start + gpp->gpp_size - 1;
452
453	if (gpp->gpp_start < table->gpt_first ||
454	    gpp->gpp_start > table->gpt_last) {
455		gctl_error(req, "%d start '%jd'", EINVAL,
456		    (intmax_t)gpp->gpp_start);
457		return (EINVAL);
458	}
459	if (end < gpp->gpp_start || end > table->gpt_last) {
460		gctl_error(req, "%d size '%jd'", EINVAL,
461		    (intmax_t)gpp->gpp_size);
462		return (EINVAL);
463	}
464	if (gpp->gpp_index > table->gpt_entries) {
465		gctl_error(req, "%d index '%d'", EINVAL, gpp->gpp_index);
466		return (EINVAL);
467	}
468
469	delent = last = NULL;
470	index = (gpp->gpp_index > 0) ? gpp->gpp_index : 1;
471	LIST_FOREACH(entry, &table->gpt_entry, gpe_entry) {
472		if (entry->gpe_deleted) {
473			if (entry->gpe_index == index)
474				delent = entry;
475			continue;
476		}
477		if (entry->gpe_index == index)
478			index = entry->gpe_index + 1;
479		if (entry->gpe_index < index)
480			last = entry;
481		if (entry->gpe_internal)
482			continue;
483		if (gpp->gpp_start >= entry->gpe_start &&
484		    gpp->gpp_start <= entry->gpe_end) {
485			gctl_error(req, "%d start '%jd'", ENOSPC,
486			    (intmax_t)gpp->gpp_start);
487			return (ENOSPC);
488		}
489		if (end >= entry->gpe_start && end <= entry->gpe_end) {
490			gctl_error(req, "%d end '%jd'", ENOSPC, (intmax_t)end);
491			return (ENOSPC);
492		}
493		if (gpp->gpp_start < entry->gpe_start && end > entry->gpe_end) {
494			gctl_error(req, "%d size '%jd'", ENOSPC,
495			    (intmax_t)gpp->gpp_size);
496			return (ENOSPC);
497		}
498	}
499	if (gpp->gpp_index > 0 && index != gpp->gpp_index) {
500		gctl_error(req, "%d index '%d'", EEXIST, gpp->gpp_index);
501		return (EEXIST);
502	}
503	if (index > table->gpt_entries) {
504		gctl_error(req, "%d index '%d'", ENOSPC, index);
505		return (ENOSPC);
506	}
507
508	entry = (delent == NULL) ? g_malloc(table->gpt_scheme->gps_entrysz,
509	    M_WAITOK | M_ZERO) : delent;
510	entry->gpe_index = index;
511	entry->gpe_start = gpp->gpp_start;
512	entry->gpe_end = end;
513	error = G_PART_ADD(table, entry, gpp);
514	if (error) {
515		gctl_error(req, "%d", error);
516		if (delent == NULL)
517			g_free(entry);
518		return (error);
519	}
520	if (delent == NULL) {
521		if (last == NULL)
522			LIST_INSERT_HEAD(&table->gpt_entry, entry, gpe_entry);
523		else
524			LIST_INSERT_AFTER(last, entry, gpe_entry);
525		entry->gpe_created = 1;
526	} else {
527		entry->gpe_deleted = 0;
528		entry->gpe_modified = 1;
529	}
530	g_part_new_provider(gp, table, entry);
531
532	/* Provide feedback if so requested. */
533	if (gpp->gpp_parms & G_PART_PARM_OUTPUT) {
534		sb = sbuf_new_auto();
535		G_PART_FULLNAME(table, entry, sb, gp->name);
536		sbuf_cat(sb, " added\n");
537		sbuf_finish(sb);
538		gctl_set_param(req, "output", sbuf_data(sb), sbuf_len(sb) + 1);
539		sbuf_delete(sb);
540	}
541	return (0);
542}
543
544static int
545g_part_ctl_bootcode(struct gctl_req *req, struct g_part_parms *gpp)
546{
547	struct g_geom *gp;
548	struct g_part_table *table;
549	struct sbuf *sb;
550	int error, sz;
551
552	gp = gpp->gpp_geom;
553	G_PART_TRACE((G_T_TOPOLOGY, "%s(%s)", __func__, gp->name));
554	g_topology_assert();
555
556	table = gp->softc;
557	sz = table->gpt_scheme->gps_bootcodesz;
558	if (sz == 0) {
559		error = ENODEV;
560		goto fail;
561	}
562	if (gpp->gpp_codesize > sz) {
563		error = EFBIG;
564		goto fail;
565	}
566
567	error = G_PART_BOOTCODE(table, gpp);
568	if (error)
569		goto fail;
570
571	/* Provide feedback if so requested. */
572	if (gpp->gpp_parms & G_PART_PARM_OUTPUT) {
573		sb = sbuf_new_auto();
574		sbuf_printf(sb, "%s has bootcode\n", gp->name);
575		sbuf_finish(sb);
576		gctl_set_param(req, "output", sbuf_data(sb), sbuf_len(sb) + 1);
577		sbuf_delete(sb);
578	}
579	return (0);
580
581 fail:
582	gctl_error(req, "%d", error);
583	return (error);
584}
585
586static int
587g_part_ctl_commit(struct gctl_req *req, struct g_part_parms *gpp)
588{
589	struct g_consumer *cp;
590	struct g_geom *gp;
591	struct g_provider *pp;
592	struct g_part_entry *entry, *tmp;
593	struct g_part_table *table;
594	char *buf;
595	int error, i;
596
597	gp = gpp->gpp_geom;
598	G_PART_TRACE((G_T_TOPOLOGY, "%s(%s)", __func__, gp->name));
599	g_topology_assert();
600
601	table = gp->softc;
602	if (!table->gpt_opened) {
603		gctl_error(req, "%d", EPERM);
604		return (EPERM);
605	}
606
607	g_topology_unlock();
608
609	cp = LIST_FIRST(&gp->consumer);
610	if ((table->gpt_smhead | table->gpt_smtail) != 0) {
611		pp = cp->provider;
612		buf = g_malloc(pp->sectorsize, M_WAITOK | M_ZERO);
613		while (table->gpt_smhead != 0) {
614			i = ffs(table->gpt_smhead) - 1;
615			error = g_write_data(cp, i * pp->sectorsize, buf,
616			    pp->sectorsize);
617			if (error) {
618				g_free(buf);
619				goto fail;
620			}
621			table->gpt_smhead &= ~(1 << i);
622		}
623		while (table->gpt_smtail != 0) {
624			i = ffs(table->gpt_smtail) - 1;
625			error = g_write_data(cp, pp->mediasize - (i + 1) *
626			    pp->sectorsize, buf, pp->sectorsize);
627			if (error) {
628				g_free(buf);
629				goto fail;
630			}
631			table->gpt_smtail &= ~(1 << i);
632		}
633		g_free(buf);
634	}
635
636	if (table->gpt_scheme == &g_part_null_scheme) {
637		g_topology_lock();
638		g_access(cp, -1, -1, -1);
639		g_part_wither(gp, ENXIO);
640		return (0);
641	}
642
643	error = G_PART_WRITE(table, cp);
644	if (error)
645		goto fail;
646
647	LIST_FOREACH_SAFE(entry, &table->gpt_entry, gpe_entry, tmp) {
648		if (!entry->gpe_deleted) {
649			entry->gpe_created = 0;
650			entry->gpe_modified = 0;
651			continue;
652		}
653		LIST_REMOVE(entry, gpe_entry);
654		g_free(entry);
655	}
656	table->gpt_created = 0;
657	table->gpt_opened = 0;
658
659	g_topology_lock();
660	g_access(cp, -1, -1, -1);
661	return (0);
662
663fail:
664	g_topology_lock();
665	gctl_error(req, "%d", error);
666	return (error);
667}
668
669static int
670g_part_ctl_create(struct gctl_req *req, struct g_part_parms *gpp)
671{
672	struct g_consumer *cp;
673	struct g_geom *gp;
674	struct g_provider *pp;
675	struct g_part_scheme *scheme;
676	struct g_part_table *null, *table;
677	struct sbuf *sb;
678	int attr, error;
679
680	pp = gpp->gpp_provider;
681	scheme = gpp->gpp_scheme;
682	G_PART_TRACE((G_T_TOPOLOGY, "%s(%s)", __func__, pp->name));
683	g_topology_assert();
684
685	/* Check that there isn't already a g_part geom on the provider. */
686	error = g_part_parm_geom(pp->name, &gp);
687	if (!error) {
688		null = gp->softc;
689		if (null->gpt_scheme != &g_part_null_scheme) {
690			gctl_error(req, "%d geom '%s'", EEXIST, pp->name);
691			return (EEXIST);
692		}
693	} else
694		null = NULL;
695
696	if ((gpp->gpp_parms & G_PART_PARM_ENTRIES) &&
697	    (gpp->gpp_entries < scheme->gps_minent ||
698	     gpp->gpp_entries > scheme->gps_maxent)) {
699		gctl_error(req, "%d entries '%d'", EINVAL, gpp->gpp_entries);
700		return (EINVAL);
701	}
702
703	if (null == NULL)
704		gp = g_new_geomf(&g_part_class, "%s", pp->name);
705	gp->softc = kobj_create((kobj_class_t)gpp->gpp_scheme, M_GEOM,
706	    M_WAITOK);
707	table = gp->softc;
708	table->gpt_gp = gp;
709	table->gpt_scheme = gpp->gpp_scheme;
710	table->gpt_entries = (gpp->gpp_parms & G_PART_PARM_ENTRIES) ?
711	    gpp->gpp_entries : scheme->gps_minent;
712	LIST_INIT(&table->gpt_entry);
713	if (null == NULL) {
714		cp = g_new_consumer(gp);
715		error = g_attach(cp, pp);
716		if (error == 0)
717			error = g_access(cp, 1, 1, 1);
718		if (error != 0) {
719			g_part_wither(gp, error);
720			gctl_error(req, "%d geom '%s'", error, pp->name);
721			return (error);
722		}
723		table->gpt_opened = 1;
724	} else {
725		cp = LIST_FIRST(&gp->consumer);
726		table->gpt_opened = null->gpt_opened;
727		table->gpt_smhead = null->gpt_smhead;
728		table->gpt_smtail = null->gpt_smtail;
729	}
730
731	g_topology_unlock();
732
733	/* Make sure the provider has media. */
734	if (pp->mediasize == 0 || pp->sectorsize == 0) {
735		error = ENODEV;
736		goto fail;
737	}
738
739	/* Make sure we can nest and if so, determine our depth. */
740	error = g_getattr("PART::isleaf", cp, &attr);
741	if (!error && attr) {
742		error = ENODEV;
743		goto fail;
744	}
745	error = g_getattr("PART::depth", cp, &attr);
746	table->gpt_depth = (!error) ? attr + 1 : 0;
747
748	/*
749	 * Synthesize a disk geometry. Some partitioning schemes
750	 * depend on it and since some file systems need it even
751	 * when the partitition scheme doesn't, we do it here in
752	 * scheme-independent code.
753	 */
754	g_part_geometry(table, cp, pp->mediasize / pp->sectorsize);
755
756	error = G_PART_CREATE(table, gpp);
757	if (error)
758		goto fail;
759
760	g_topology_lock();
761
762	table->gpt_created = 1;
763	if (null != NULL)
764		kobj_delete((kobj_t)null, M_GEOM);
765
766	/*
767	 * Support automatic commit by filling in the gpp_geom
768	 * parameter.
769	 */
770	gpp->gpp_parms |= G_PART_PARM_GEOM;
771	gpp->gpp_geom = gp;
772
773	/* Provide feedback if so requested. */
774	if (gpp->gpp_parms & G_PART_PARM_OUTPUT) {
775		sb = sbuf_new_auto();
776		sbuf_printf(sb, "%s created\n", gp->name);
777		sbuf_finish(sb);
778		gctl_set_param(req, "output", sbuf_data(sb), sbuf_len(sb) + 1);
779		sbuf_delete(sb);
780	}
781	return (0);
782
783fail:
784	g_topology_lock();
785	if (null == NULL) {
786		g_access(cp, -1, -1, -1);
787		g_part_wither(gp, error);
788	} else {
789		kobj_delete((kobj_t)gp->softc, M_GEOM);
790		gp->softc = null;
791	}
792	gctl_error(req, "%d provider", error);
793	return (error);
794}
795
796static int
797g_part_ctl_delete(struct gctl_req *req, struct g_part_parms *gpp)
798{
799	struct g_geom *gp;
800	struct g_provider *pp;
801	struct g_part_entry *entry;
802	struct g_part_table *table;
803	struct sbuf *sb;
804
805	gp = gpp->gpp_geom;
806	G_PART_TRACE((G_T_TOPOLOGY, "%s(%s)", __func__, gp->name));
807	g_topology_assert();
808
809	table = gp->softc;
810
811	LIST_FOREACH(entry, &table->gpt_entry, gpe_entry) {
812		if (entry->gpe_deleted || entry->gpe_internal)
813			continue;
814		if (entry->gpe_index == gpp->gpp_index)
815			break;
816	}
817	if (entry == NULL) {
818		gctl_error(req, "%d index '%d'", ENOENT, gpp->gpp_index);
819		return (ENOENT);
820	}
821
822	pp = entry->gpe_pp;
823	if (pp != NULL) {
824		if (pp->acr > 0 || pp->acw > 0 || pp->ace > 0) {
825			gctl_error(req, "%d", EBUSY);
826			return (EBUSY);
827		}
828
829		pp->private = NULL;
830		entry->gpe_pp = NULL;
831	}
832
833	if (pp != NULL)
834		g_wither_provider(pp, ENXIO);
835
836	/* Provide feedback if so requested. */
837	if (gpp->gpp_parms & G_PART_PARM_OUTPUT) {
838		sb = sbuf_new_auto();
839		G_PART_FULLNAME(table, entry, sb, gp->name);
840		sbuf_cat(sb, " deleted\n");
841		sbuf_finish(sb);
842		gctl_set_param(req, "output", sbuf_data(sb), sbuf_len(sb) + 1);
843		sbuf_delete(sb);
844	}
845
846	if (entry->gpe_created) {
847		LIST_REMOVE(entry, gpe_entry);
848		g_free(entry);
849	} else {
850		entry->gpe_modified = 0;
851		entry->gpe_deleted = 1;
852	}
853	return (0);
854}
855
856static int
857g_part_ctl_destroy(struct gctl_req *req, struct g_part_parms *gpp)
858{
859	struct g_consumer *cp;
860	struct g_geom *gp;
861	struct g_provider *pp;
862	struct g_part_entry *entry;
863	struct g_part_table *null, *table;
864	struct sbuf *sb;
865	int error;
866
867	gp = gpp->gpp_geom;
868	G_PART_TRACE((G_T_TOPOLOGY, "%s(%s)", __func__, gp->name));
869	g_topology_assert();
870
871	table = gp->softc;
872	LIST_FOREACH(entry, &table->gpt_entry, gpe_entry) {
873		if (entry->gpe_deleted || entry->gpe_internal)
874			continue;
875		gctl_error(req, "%d", EBUSY);
876		return (EBUSY);
877	}
878
879	error = G_PART_DESTROY(table, gpp);
880	if (error) {
881		gctl_error(req, "%d", error);
882		return (error);
883	}
884
885	gp->softc = kobj_create((kobj_class_t)&g_part_null_scheme, M_GEOM,
886	    M_WAITOK);
887	null = gp->softc;
888	null->gpt_gp = gp;
889	null->gpt_scheme = &g_part_null_scheme;
890	LIST_INIT(&null->gpt_entry);
891
892	cp = LIST_FIRST(&gp->consumer);
893	pp = cp->provider;
894	null->gpt_last = pp->mediasize / pp->sectorsize - 1;
895
896	null->gpt_depth = table->gpt_depth;
897	null->gpt_opened = table->gpt_opened;
898	null->gpt_smhead = table->gpt_smhead;
899	null->gpt_smtail = table->gpt_smtail;
900
901	while ((entry = LIST_FIRST(&table->gpt_entry)) != NULL) {
902		LIST_REMOVE(entry, gpe_entry);
903		g_free(entry);
904	}
905	kobj_delete((kobj_t)table, M_GEOM);
906
907	/* Provide feedback if so requested. */
908	if (gpp->gpp_parms & G_PART_PARM_OUTPUT) {
909		sb = sbuf_new_auto();
910		sbuf_printf(sb, "%s destroyed\n", gp->name);
911		sbuf_finish(sb);
912		gctl_set_param(req, "output", sbuf_data(sb), sbuf_len(sb) + 1);
913		sbuf_delete(sb);
914	}
915	return (0);
916}
917
918static int
919g_part_ctl_modify(struct gctl_req *req, struct g_part_parms *gpp)
920{
921	struct g_geom *gp;
922	struct g_part_entry *entry;
923	struct g_part_table *table;
924	struct sbuf *sb;
925	int error;
926
927	gp = gpp->gpp_geom;
928	G_PART_TRACE((G_T_TOPOLOGY, "%s(%s)", __func__, gp->name));
929	g_topology_assert();
930
931	table = gp->softc;
932
933	LIST_FOREACH(entry, &table->gpt_entry, gpe_entry) {
934		if (entry->gpe_deleted || entry->gpe_internal)
935			continue;
936		if (entry->gpe_index == gpp->gpp_index)
937			break;
938	}
939	if (entry == NULL) {
940		gctl_error(req, "%d index '%d'", ENOENT, gpp->gpp_index);
941		return (ENOENT);
942	}
943
944	error = G_PART_MODIFY(table, entry, gpp);
945	if (error) {
946		gctl_error(req, "%d", error);
947		return (error);
948	}
949
950	if (!entry->gpe_created)
951		entry->gpe_modified = 1;
952
953	/* Provide feedback if so requested. */
954	if (gpp->gpp_parms & G_PART_PARM_OUTPUT) {
955		sb = sbuf_new_auto();
956		G_PART_FULLNAME(table, entry, sb, gp->name);
957		sbuf_cat(sb, " modified\n");
958		sbuf_finish(sb);
959		gctl_set_param(req, "output", sbuf_data(sb), sbuf_len(sb) + 1);
960		sbuf_delete(sb);
961	}
962	return (0);
963}
964
965static int
966g_part_ctl_move(struct gctl_req *req, struct g_part_parms *gpp)
967{
968	gctl_error(req, "%d verb 'move'", ENOSYS);
969	return (ENOSYS);
970}
971
972static int
973g_part_ctl_recover(struct gctl_req *req, struct g_part_parms *gpp)
974{
975	gctl_error(req, "%d verb 'recover'", ENOSYS);
976	return (ENOSYS);
977}
978
979static int
980g_part_ctl_resize(struct gctl_req *req, struct g_part_parms *gpp)
981{
982	struct g_geom *gp;
983	struct g_provider *pp;
984	struct g_part_entry *pe, *entry;
985	struct g_part_table *table;
986	struct sbuf *sb;
987	quad_t end;
988	int error;
989
990	gp = gpp->gpp_geom;
991	G_PART_TRACE((G_T_TOPOLOGY, "%s(%s)", __func__, gp->name));
992	g_topology_assert();
993	table = gp->softc;
994
995	/* check gpp_index */
996	LIST_FOREACH(entry, &table->gpt_entry, gpe_entry) {
997		if (entry->gpe_deleted || entry->gpe_internal)
998			continue;
999		if (entry->gpe_index == gpp->gpp_index)
1000			break;
1001	}
1002	if (entry == NULL) {
1003		gctl_error(req, "%d index '%d'", ENOENT, gpp->gpp_index);
1004		return (ENOENT);
1005	}
1006
1007	/* check gpp_size */
1008	end = entry->gpe_start + gpp->gpp_size - 1;
1009	if (gpp->gpp_size < 1 || end > table->gpt_last) {
1010		gctl_error(req, "%d size '%jd'", EINVAL,
1011		    (intmax_t)gpp->gpp_size);
1012		return (EINVAL);
1013	}
1014
1015	LIST_FOREACH(pe, &table->gpt_entry, gpe_entry) {
1016		if (pe->gpe_deleted || pe->gpe_internal || pe == entry)
1017			continue;
1018		if (end >= pe->gpe_start && end <= pe->gpe_end) {
1019			gctl_error(req, "%d end '%jd'", ENOSPC,
1020			    (intmax_t)end);
1021			return (ENOSPC);
1022		}
1023		if (entry->gpe_start < pe->gpe_start && end > pe->gpe_end) {
1024			gctl_error(req, "%d size '%jd'", ENOSPC,
1025			    (intmax_t)gpp->gpp_size);
1026			return (ENOSPC);
1027		}
1028	}
1029
1030	pp = entry->gpe_pp;
1031	if ((g_debugflags & 16) == 0 &&
1032	    (pp->acr > 0 || pp->acw > 0 || pp->ace > 0)) {
1033		gctl_error(req, "%d", EBUSY);
1034		return (EBUSY);
1035	}
1036
1037	error = G_PART_RESIZE(table, entry, gpp);
1038	if (error) {
1039		gctl_error(req, "%d", error);
1040		return (error);
1041	}
1042
1043	if (!entry->gpe_created)
1044		entry->gpe_modified = 1;
1045
1046	/* update mediasize of changed provider */
1047	pp->mediasize = (entry->gpe_end - entry->gpe_start + 1) *
1048		pp->sectorsize;
1049
1050	/* Provide feedback if so requested. */
1051	if (gpp->gpp_parms & G_PART_PARM_OUTPUT) {
1052		sb = sbuf_new_auto();
1053		G_PART_FULLNAME(table, entry, sb, gp->name);
1054		sbuf_cat(sb, " resized\n");
1055		sbuf_finish(sb);
1056		gctl_set_param(req, "output", sbuf_data(sb), sbuf_len(sb) + 1);
1057		sbuf_delete(sb);
1058	}
1059	return (0);
1060}
1061
1062static int
1063g_part_ctl_setunset(struct gctl_req *req, struct g_part_parms *gpp,
1064    unsigned int set)
1065{
1066	struct g_geom *gp;
1067	struct g_part_entry *entry;
1068	struct g_part_table *table;
1069	struct sbuf *sb;
1070	int error;
1071
1072	gp = gpp->gpp_geom;
1073	G_PART_TRACE((G_T_TOPOLOGY, "%s(%s)", __func__, gp->name));
1074	g_topology_assert();
1075
1076	table = gp->softc;
1077
1078	LIST_FOREACH(entry, &table->gpt_entry, gpe_entry) {
1079		if (entry->gpe_deleted || entry->gpe_internal)
1080			continue;
1081		if (entry->gpe_index == gpp->gpp_index)
1082			break;
1083	}
1084	if (entry == NULL) {
1085		gctl_error(req, "%d index '%d'", ENOENT, gpp->gpp_index);
1086		return (ENOENT);
1087	}
1088
1089	error = G_PART_SETUNSET(table, entry, gpp->gpp_attrib, set);
1090	if (error) {
1091		gctl_error(req, "%d attrib '%s'", error, gpp->gpp_attrib);
1092		return (error);
1093	}
1094
1095	/* Provide feedback if so requested. */
1096	if (gpp->gpp_parms & G_PART_PARM_OUTPUT) {
1097		sb = sbuf_new_auto();
1098		G_PART_FULLNAME(table, entry, sb, gp->name);
1099		sbuf_printf(sb, " has %s %sset\n", gpp->gpp_attrib,
1100		    (set) ? "" : "un");
1101		sbuf_finish(sb);
1102		gctl_set_param(req, "output", sbuf_data(sb), sbuf_len(sb) + 1);
1103		sbuf_delete(sb);
1104	}
1105	return (0);
1106}
1107
1108static int
1109g_part_ctl_undo(struct gctl_req *req, struct g_part_parms *gpp)
1110{
1111	struct g_consumer *cp;
1112	struct g_provider *pp;
1113	struct g_geom *gp;
1114	struct g_part_entry *entry, *tmp;
1115	struct g_part_table *table;
1116	int error, reprobe;
1117
1118	gp = gpp->gpp_geom;
1119	G_PART_TRACE((G_T_TOPOLOGY, "%s(%s)", __func__, gp->name));
1120	g_topology_assert();
1121
1122	table = gp->softc;
1123	if (!table->gpt_opened) {
1124		gctl_error(req, "%d", EPERM);
1125		return (EPERM);
1126	}
1127
1128	cp = LIST_FIRST(&gp->consumer);
1129	LIST_FOREACH_SAFE(entry, &table->gpt_entry, gpe_entry, tmp) {
1130		entry->gpe_modified = 0;
1131		if (entry->gpe_created) {
1132			pp = entry->gpe_pp;
1133			if (pp != NULL) {
1134				pp->private = NULL;
1135				entry->gpe_pp = NULL;
1136				g_wither_provider(pp, ENXIO);
1137			}
1138			entry->gpe_deleted = 1;
1139		}
1140		if (entry->gpe_deleted) {
1141			LIST_REMOVE(entry, gpe_entry);
1142			g_free(entry);
1143		}
1144	}
1145
1146	g_topology_unlock();
1147
1148	reprobe = (table->gpt_scheme == &g_part_null_scheme ||
1149	    table->gpt_created) ? 1 : 0;
1150
1151	if (reprobe) {
1152		LIST_FOREACH(entry, &table->gpt_entry, gpe_entry) {
1153			if (entry->gpe_internal)
1154				continue;
1155			error = EBUSY;
1156			goto fail;
1157		}
1158		while ((entry = LIST_FIRST(&table->gpt_entry)) != NULL) {
1159			LIST_REMOVE(entry, gpe_entry);
1160			g_free(entry);
1161		}
1162		error = g_part_probe(gp, cp, table->gpt_depth);
1163		if (error) {
1164			g_topology_lock();
1165			g_access(cp, -1, -1, -1);
1166			g_part_wither(gp, error);
1167			return (0);
1168		}
1169		table = gp->softc;
1170
1171		/*
1172		 * Synthesize a disk geometry. Some partitioning schemes
1173		 * depend on it and since some file systems need it even
1174		 * when the partitition scheme doesn't, we do it here in
1175		 * scheme-independent code.
1176		 */
1177		pp = cp->provider;
1178		g_part_geometry(table, cp, pp->mediasize / pp->sectorsize);
1179	}
1180
1181	error = G_PART_READ(table, cp);
1182	if (error)
1183		goto fail;
1184
1185	g_topology_lock();
1186
1187	LIST_FOREACH(entry, &table->gpt_entry, gpe_entry) {
1188		if (!entry->gpe_internal)
1189			g_part_new_provider(gp, table, entry);
1190	}
1191
1192	table->gpt_opened = 0;
1193	g_access(cp, -1, -1, -1);
1194	return (0);
1195
1196fail:
1197	g_topology_lock();
1198	gctl_error(req, "%d", error);
1199	return (error);
1200}
1201
1202static void
1203g_part_wither(struct g_geom *gp, int error)
1204{
1205	struct g_part_entry *entry;
1206	struct g_part_table *table;
1207
1208	table = gp->softc;
1209	if (table != NULL) {
1210		while ((entry = LIST_FIRST(&table->gpt_entry)) != NULL) {
1211			LIST_REMOVE(entry, gpe_entry);
1212			g_free(entry);
1213		}
1214		if (gp->softc != NULL) {
1215			kobj_delete((kobj_t)gp->softc, M_GEOM);
1216			gp->softc = NULL;
1217		}
1218	}
1219	g_wither_geom(gp, error);
1220}
1221
1222/*
1223 * Class methods.
1224 */
1225
1226static void
1227g_part_ctlreq(struct gctl_req *req, struct g_class *mp, const char *verb)
1228{
1229	struct g_part_parms gpp;
1230	struct g_part_table *table;
1231	struct gctl_req_arg *ap;
1232	const char *p;
1233	enum g_part_ctl ctlreq;
1234	unsigned int i, mparms, oparms, parm;
1235	int auto_commit, close_on_error;
1236	int error, len, modifies;
1237
1238	G_PART_TRACE((G_T_TOPOLOGY, "%s(%s,%s)", __func__, mp->name, verb));
1239	g_topology_assert();
1240
1241	ctlreq = G_PART_CTL_NONE;
1242	modifies = 1;
1243	mparms = 0;
1244	oparms = G_PART_PARM_FLAGS | G_PART_PARM_OUTPUT | G_PART_PARM_VERSION;
1245	switch (*verb) {
1246	case 'a':
1247		if (!strcmp(verb, "add")) {
1248			ctlreq = G_PART_CTL_ADD;
1249			mparms |= G_PART_PARM_GEOM | G_PART_PARM_SIZE |
1250			    G_PART_PARM_START | G_PART_PARM_TYPE;
1251			oparms |= G_PART_PARM_INDEX | G_PART_PARM_LABEL;
1252		}
1253		break;
1254	case 'b':
1255		if (!strcmp(verb, "bootcode")) {
1256			ctlreq = G_PART_CTL_BOOTCODE;
1257			mparms |= G_PART_PARM_GEOM | G_PART_PARM_BOOTCODE;
1258		}
1259		break;
1260	case 'c':
1261		if (!strcmp(verb, "commit")) {
1262			ctlreq = G_PART_CTL_COMMIT;
1263			mparms |= G_PART_PARM_GEOM;
1264			modifies = 0;
1265		} else if (!strcmp(verb, "create")) {
1266			ctlreq = G_PART_CTL_CREATE;
1267			mparms |= G_PART_PARM_PROVIDER | G_PART_PARM_SCHEME;
1268			oparms |= G_PART_PARM_ENTRIES;
1269		}
1270		break;
1271	case 'd':
1272		if (!strcmp(verb, "delete")) {
1273			ctlreq = G_PART_CTL_DELETE;
1274			mparms |= G_PART_PARM_GEOM | G_PART_PARM_INDEX;
1275		} else if (!strcmp(verb, "destroy")) {
1276			ctlreq = G_PART_CTL_DESTROY;
1277			mparms |= G_PART_PARM_GEOM;
1278		}
1279		break;
1280	case 'm':
1281		if (!strcmp(verb, "modify")) {
1282			ctlreq = G_PART_CTL_MODIFY;
1283			mparms |= G_PART_PARM_GEOM | G_PART_PARM_INDEX;
1284			oparms |= G_PART_PARM_LABEL | G_PART_PARM_TYPE;
1285		} else if (!strcmp(verb, "move")) {
1286			ctlreq = G_PART_CTL_MOVE;
1287			mparms |= G_PART_PARM_GEOM | G_PART_PARM_INDEX;
1288		}
1289		break;
1290	case 'r':
1291		if (!strcmp(verb, "recover")) {
1292			ctlreq = G_PART_CTL_RECOVER;
1293			mparms |= G_PART_PARM_GEOM;
1294		} else if (!strcmp(verb, "resize")) {
1295			ctlreq = G_PART_CTL_RESIZE;
1296			mparms |= G_PART_PARM_GEOM | G_PART_PARM_INDEX |
1297			    G_PART_PARM_SIZE;
1298		}
1299		break;
1300	case 's':
1301		if (!strcmp(verb, "set")) {
1302			ctlreq = G_PART_CTL_SET;
1303			mparms |= G_PART_PARM_ATTRIB | G_PART_PARM_GEOM |
1304			    G_PART_PARM_INDEX;
1305		}
1306		break;
1307	case 'u':
1308		if (!strcmp(verb, "undo")) {
1309			ctlreq = G_PART_CTL_UNDO;
1310			mparms |= G_PART_PARM_GEOM;
1311			modifies = 0;
1312		} else if (!strcmp(verb, "unset")) {
1313			ctlreq = G_PART_CTL_UNSET;
1314			mparms |= G_PART_PARM_ATTRIB | G_PART_PARM_GEOM |
1315			    G_PART_PARM_INDEX;
1316		}
1317		break;
1318	}
1319	if (ctlreq == G_PART_CTL_NONE) {
1320		gctl_error(req, "%d verb '%s'", EINVAL, verb);
1321		return;
1322	}
1323
1324	bzero(&gpp, sizeof(gpp));
1325	for (i = 0; i < req->narg; i++) {
1326		ap = &req->arg[i];
1327		parm = 0;
1328		switch (ap->name[0]) {
1329		case 'a':
1330			if (!strcmp(ap->name, "attrib"))
1331				parm = G_PART_PARM_ATTRIB;
1332			break;
1333		case 'b':
1334			if (!strcmp(ap->name, "bootcode"))
1335				parm = G_PART_PARM_BOOTCODE;
1336			break;
1337		case 'c':
1338			if (!strcmp(ap->name, "class"))
1339				continue;
1340			break;
1341		case 'e':
1342			if (!strcmp(ap->name, "entries"))
1343				parm = G_PART_PARM_ENTRIES;
1344			break;
1345		case 'f':
1346			if (!strcmp(ap->name, "flags"))
1347				parm = G_PART_PARM_FLAGS;
1348			break;
1349		case 'g':
1350			if (!strcmp(ap->name, "geom"))
1351				parm = G_PART_PARM_GEOM;
1352			break;
1353		case 'i':
1354			if (!strcmp(ap->name, "index"))
1355				parm = G_PART_PARM_INDEX;
1356			break;
1357		case 'l':
1358			if (!strcmp(ap->name, "label"))
1359				parm = G_PART_PARM_LABEL;
1360			break;
1361		case 'o':
1362			if (!strcmp(ap->name, "output"))
1363				parm = G_PART_PARM_OUTPUT;
1364			break;
1365		case 'p':
1366			if (!strcmp(ap->name, "provider"))
1367				parm = G_PART_PARM_PROVIDER;
1368			break;
1369		case 's':
1370			if (!strcmp(ap->name, "scheme"))
1371				parm = G_PART_PARM_SCHEME;
1372			else if (!strcmp(ap->name, "size"))
1373				parm = G_PART_PARM_SIZE;
1374			else if (!strcmp(ap->name, "start"))
1375				parm = G_PART_PARM_START;
1376			break;
1377		case 't':
1378			if (!strcmp(ap->name, "type"))
1379				parm = G_PART_PARM_TYPE;
1380			break;
1381		case 'v':
1382			if (!strcmp(ap->name, "verb"))
1383				continue;
1384			else if (!strcmp(ap->name, "version"))
1385				parm = G_PART_PARM_VERSION;
1386			break;
1387		}
1388		if ((parm & (mparms | oparms)) == 0) {
1389			gctl_error(req, "%d param '%s'", EINVAL, ap->name);
1390			return;
1391		}
1392		if (parm == G_PART_PARM_BOOTCODE)
1393			p = gctl_get_param(req, ap->name, &len);
1394		else
1395			p = gctl_get_asciiparam(req, ap->name);
1396		if (p == NULL) {
1397			gctl_error(req, "%d param '%s'", ENOATTR, ap->name);
1398			return;
1399		}
1400		switch (parm) {
1401		case G_PART_PARM_ATTRIB:
1402			error = g_part_parm_str(p, &gpp.gpp_attrib);
1403			break;
1404		case G_PART_PARM_BOOTCODE:
1405			gpp.gpp_codeptr = p;
1406			gpp.gpp_codesize = len;
1407			error = 0;
1408			break;
1409		case G_PART_PARM_ENTRIES:
1410			error = g_part_parm_uint(p, &gpp.gpp_entries);
1411			break;
1412		case G_PART_PARM_FLAGS:
1413			if (p[0] == '\0')
1414				continue;
1415			error = g_part_parm_str(p, &gpp.gpp_flags);
1416			break;
1417		case G_PART_PARM_GEOM:
1418			error = g_part_parm_geom(p, &gpp.gpp_geom);
1419			break;
1420		case G_PART_PARM_INDEX:
1421			error = g_part_parm_uint(p, &gpp.gpp_index);
1422			break;
1423		case G_PART_PARM_LABEL:
1424			/* An empty label is always valid. */
1425			gpp.gpp_label = p;
1426			error = 0;
1427			break;
1428		case G_PART_PARM_OUTPUT:
1429			error = 0;	/* Write-only parameter */
1430			break;
1431		case G_PART_PARM_PROVIDER:
1432			error = g_part_parm_provider(p, &gpp.gpp_provider);
1433			break;
1434		case G_PART_PARM_SCHEME:
1435			error = g_part_parm_scheme(p, &gpp.gpp_scheme);
1436			break;
1437		case G_PART_PARM_SIZE:
1438			error = g_part_parm_quad(p, &gpp.gpp_size);
1439			break;
1440		case G_PART_PARM_START:
1441			error = g_part_parm_quad(p, &gpp.gpp_start);
1442			break;
1443		case G_PART_PARM_TYPE:
1444			error = g_part_parm_str(p, &gpp.gpp_type);
1445			break;
1446		case G_PART_PARM_VERSION:
1447			error = g_part_parm_uint(p, &gpp.gpp_version);
1448			break;
1449		default:
1450			error = EDOOFUS;
1451			break;
1452		}
1453		if (error) {
1454			gctl_error(req, "%d %s '%s'", error, ap->name, p);
1455			return;
1456		}
1457		gpp.gpp_parms |= parm;
1458	}
1459	if ((gpp.gpp_parms & mparms) != mparms) {
1460		parm = mparms - (gpp.gpp_parms & mparms);
1461		gctl_error(req, "%d param '%x'", ENOATTR, parm);
1462		return;
1463	}
1464
1465	/* Obtain permissions if possible/necessary. */
1466	close_on_error = 0;
1467	table = NULL;
1468	if (modifies && (gpp.gpp_parms & G_PART_PARM_GEOM)) {
1469		table = gpp.gpp_geom->softc;
1470		if (table != NULL && !table->gpt_opened) {
1471			error = g_access(LIST_FIRST(&gpp.gpp_geom->consumer),
1472			    1, 1, 1);
1473			if (error) {
1474				gctl_error(req, "%d geom '%s'", error,
1475				    gpp.gpp_geom->name);
1476				return;
1477			}
1478			table->gpt_opened = 1;
1479			close_on_error = 1;
1480		}
1481	}
1482
1483	/* Allow the scheme to check or modify the parameters. */
1484	if (table != NULL) {
1485		error = G_PART_PRECHECK(table, ctlreq, &gpp);
1486		if (error) {
1487			gctl_error(req, "%d pre-check failed", error);
1488			goto out;
1489		}
1490	} else
1491		error = EDOOFUS;	/* Prevent bogus uninit. warning. */
1492
1493	switch (ctlreq) {
1494	case G_PART_CTL_NONE:
1495		panic("%s", __func__);
1496	case G_PART_CTL_ADD:
1497		error = g_part_ctl_add(req, &gpp);
1498		break;
1499	case G_PART_CTL_BOOTCODE:
1500		error = g_part_ctl_bootcode(req, &gpp);
1501		break;
1502	case G_PART_CTL_COMMIT:
1503		error = g_part_ctl_commit(req, &gpp);
1504		break;
1505	case G_PART_CTL_CREATE:
1506		error = g_part_ctl_create(req, &gpp);
1507		break;
1508	case G_PART_CTL_DELETE:
1509		error = g_part_ctl_delete(req, &gpp);
1510		break;
1511	case G_PART_CTL_DESTROY:
1512		error = g_part_ctl_destroy(req, &gpp);
1513		break;
1514	case G_PART_CTL_MODIFY:
1515		error = g_part_ctl_modify(req, &gpp);
1516		break;
1517	case G_PART_CTL_MOVE:
1518		error = g_part_ctl_move(req, &gpp);
1519		break;
1520	case G_PART_CTL_RECOVER:
1521		error = g_part_ctl_recover(req, &gpp);
1522		break;
1523	case G_PART_CTL_RESIZE:
1524		error = g_part_ctl_resize(req, &gpp);
1525		break;
1526	case G_PART_CTL_SET:
1527		error = g_part_ctl_setunset(req, &gpp, 1);
1528		break;
1529	case G_PART_CTL_UNDO:
1530		error = g_part_ctl_undo(req, &gpp);
1531		break;
1532	case G_PART_CTL_UNSET:
1533		error = g_part_ctl_setunset(req, &gpp, 0);
1534		break;
1535	}
1536
1537	/* Implement automatic commit. */
1538	if (!error) {
1539		auto_commit = (modifies &&
1540		    (gpp.gpp_parms & G_PART_PARM_FLAGS) &&
1541		    strchr(gpp.gpp_flags, 'C') != NULL) ? 1 : 0;
1542		if (auto_commit) {
1543			KASSERT(gpp.gpp_parms & G_PART_PARM_GEOM, (__func__));
1544			error = g_part_ctl_commit(req, &gpp);
1545		}
1546	}
1547
1548 out:
1549	if (error && close_on_error) {
1550		g_access(LIST_FIRST(&gpp.gpp_geom->consumer), -1, -1, -1);
1551		table->gpt_opened = 0;
1552	}
1553}
1554
1555static int
1556g_part_destroy_geom(struct gctl_req *req, struct g_class *mp,
1557    struct g_geom *gp)
1558{
1559
1560	G_PART_TRACE((G_T_TOPOLOGY, "%s(%s,%s)", __func__, mp->name, gp->name));
1561	g_topology_assert();
1562
1563	g_part_wither(gp, EINVAL);
1564	return (0);
1565}
1566
1567static struct g_geom *
1568g_part_taste(struct g_class *mp, struct g_provider *pp, int flags __unused)
1569{
1570	struct g_consumer *cp;
1571	struct g_geom *gp;
1572	struct g_part_entry *entry;
1573	struct g_part_table *table;
1574	struct root_hold_token *rht;
1575	int attr, depth;
1576	int error;
1577
1578	G_PART_TRACE((G_T_TOPOLOGY, "%s(%s,%s)", __func__, mp->name, pp->name));
1579	g_topology_assert();
1580
1581	/* Skip providers that are already open for writing. */
1582	if (pp->acw > 0)
1583		return (NULL);
1584
1585	/*
1586	 * Create a GEOM with consumer and hook it up to the provider.
1587	 * With that we become part of the topology. Optain read access
1588	 * to the provider.
1589	 */
1590	gp = g_new_geomf(mp, "%s", pp->name);
1591	cp = g_new_consumer(gp);
1592	error = g_attach(cp, pp);
1593	if (error == 0)
1594		error = g_access(cp, 1, 0, 0);
1595	if (error != 0) {
1596		g_part_wither(gp, error);
1597		return (NULL);
1598	}
1599
1600	rht = root_mount_hold(mp->name);
1601	g_topology_unlock();
1602
1603	/*
1604	 * Short-circuit the whole probing galore when there's no
1605	 * media present.
1606	 */
1607	if (pp->mediasize == 0 || pp->sectorsize == 0) {
1608		error = ENODEV;
1609		goto fail;
1610	}
1611
1612	/* Make sure we can nest and if so, determine our depth. */
1613	error = g_getattr("PART::isleaf", cp, &attr);
1614	if (!error && attr) {
1615		error = ENODEV;
1616		goto fail;
1617	}
1618	error = g_getattr("PART::depth", cp, &attr);
1619	depth = (!error) ? attr + 1 : 0;
1620
1621	error = g_part_probe(gp, cp, depth);
1622	if (error)
1623		goto fail;
1624
1625	table = gp->softc;
1626
1627	/*
1628	 * Synthesize a disk geometry. Some partitioning schemes
1629	 * depend on it and since some file systems need it even
1630	 * when the partitition scheme doesn't, we do it here in
1631	 * scheme-independent code.
1632	 */
1633	g_part_geometry(table, cp, pp->mediasize / pp->sectorsize);
1634
1635	error = G_PART_READ(table, cp);
1636	if (error)
1637		goto fail;
1638
1639	g_topology_lock();
1640	LIST_FOREACH(entry, &table->gpt_entry, gpe_entry) {
1641		if (!entry->gpe_internal)
1642			g_part_new_provider(gp, table, entry);
1643	}
1644
1645	root_mount_rel(rht);
1646	g_access(cp, -1, 0, 0);
1647	return (gp);
1648
1649 fail:
1650	g_topology_lock();
1651	root_mount_rel(rht);
1652	g_access(cp, -1, 0, 0);
1653	g_part_wither(gp, error);
1654	return (NULL);
1655}
1656
1657/*
1658 * Geom methods.
1659 */
1660
1661static int
1662g_part_access(struct g_provider *pp, int dr, int dw, int de)
1663{
1664	struct g_consumer *cp;
1665
1666	G_PART_TRACE((G_T_ACCESS, "%s(%s,%d,%d,%d)", __func__, pp->name, dr,
1667	    dw, de));
1668
1669	cp = LIST_FIRST(&pp->geom->consumer);
1670
1671	/* We always gain write-exclusive access. */
1672	return (g_access(cp, dr, dw, dw + de));
1673}
1674
1675static void
1676g_part_dumpconf(struct sbuf *sb, const char *indent, struct g_geom *gp,
1677    struct g_consumer *cp, struct g_provider *pp)
1678{
1679	char buf[64];
1680	struct g_part_entry *entry;
1681	struct g_part_table *table;
1682
1683	KASSERT(sb != NULL && gp != NULL, (__func__));
1684	table = gp->softc;
1685
1686	if (indent == NULL) {
1687		KASSERT(cp == NULL && pp != NULL, (__func__));
1688		entry = pp->private;
1689		if (entry == NULL)
1690			return;
1691		sbuf_printf(sb, " i %u o %ju ty %s", entry->gpe_index,
1692		    (uintmax_t)entry->gpe_offset,
1693		    G_PART_TYPE(table, entry, buf, sizeof(buf)));
1694		/*
1695		 * libdisk compatibility quirk - the scheme dumps the
1696		 * slicer name and partition type in a way that is
1697		 * compatible with libdisk. When libdisk is not used
1698		 * anymore, this should go away.
1699		 */
1700		G_PART_DUMPCONF(table, entry, sb, indent);
1701	} else if (cp != NULL) {	/* Consumer configuration. */
1702		KASSERT(pp == NULL, (__func__));
1703		/* none */
1704	} else if (pp != NULL) {	/* Provider configuration. */
1705		entry = pp->private;
1706		if (entry == NULL)
1707			return;
1708		sbuf_printf(sb, "%s<start>%ju</start>\n", indent,
1709		    (uintmax_t)entry->gpe_start);
1710		sbuf_printf(sb, "%s<end>%ju</end>\n", indent,
1711		    (uintmax_t)entry->gpe_end);
1712		sbuf_printf(sb, "%s<index>%u</index>\n", indent,
1713		    entry->gpe_index);
1714		sbuf_printf(sb, "%s<type>%s</type>\n", indent,
1715		    G_PART_TYPE(table, entry, buf, sizeof(buf)));
1716		sbuf_printf(sb, "%s<offset>%ju</offset>\n", indent,
1717		    (uintmax_t)entry->gpe_offset);
1718		sbuf_printf(sb, "%s<length>%ju</length>\n", indent,
1719		    (uintmax_t)pp->mediasize);
1720		G_PART_DUMPCONF(table, entry, sb, indent);
1721	} else {			/* Geom configuration. */
1722		sbuf_printf(sb, "%s<scheme>%s</scheme>\n", indent,
1723		    table->gpt_scheme->name);
1724		sbuf_printf(sb, "%s<entries>%u</entries>\n", indent,
1725		    table->gpt_entries);
1726		sbuf_printf(sb, "%s<first>%ju</first>\n", indent,
1727		    (uintmax_t)table->gpt_first);
1728		sbuf_printf(sb, "%s<last>%ju</last>\n", indent,
1729		    (uintmax_t)table->gpt_last);
1730		sbuf_printf(sb, "%s<fwsectors>%u</fwsectors>\n", indent,
1731		    table->gpt_sectors);
1732		sbuf_printf(sb, "%s<fwheads>%u</fwheads>\n", indent,
1733		    table->gpt_heads);
1734		G_PART_DUMPCONF(table, NULL, sb, indent);
1735	}
1736}
1737
1738static void
1739g_part_orphan(struct g_consumer *cp)
1740{
1741	struct g_provider *pp;
1742	struct g_part_table *table;
1743
1744	pp = cp->provider;
1745	KASSERT(pp != NULL, (__func__));
1746	G_PART_TRACE((G_T_TOPOLOGY, "%s(%s)", __func__, pp->name));
1747	g_topology_assert();
1748
1749	KASSERT(pp->error != 0, (__func__));
1750	table = cp->geom->softc;
1751	if (table->gpt_opened)
1752		g_access(cp, -1, -1, -1);
1753	g_part_wither(cp->geom, pp->error);
1754}
1755
1756static void
1757g_part_spoiled(struct g_consumer *cp)
1758{
1759
1760	G_PART_TRACE((G_T_TOPOLOGY, "%s(%s)", __func__, cp->provider->name));
1761	g_topology_assert();
1762
1763	g_part_wither(cp->geom, ENXIO);
1764}
1765
1766static void
1767g_part_start(struct bio *bp)
1768{
1769	struct bio *bp2;
1770	struct g_consumer *cp;
1771	struct g_geom *gp;
1772	struct g_part_entry *entry;
1773	struct g_part_table *table;
1774	struct g_kerneldump *gkd;
1775	struct g_provider *pp;
1776
1777	pp = bp->bio_to;
1778	gp = pp->geom;
1779	table = gp->softc;
1780	cp = LIST_FIRST(&gp->consumer);
1781
1782	G_PART_TRACE((G_T_BIO, "%s: cmd=%d, provider=%s", __func__, bp->bio_cmd,
1783	    pp->name));
1784
1785	entry = pp->private;
1786	if (entry == NULL) {
1787		g_io_deliver(bp, ENXIO);
1788		return;
1789	}
1790
1791	switch(bp->bio_cmd) {
1792	case BIO_DELETE:
1793	case BIO_READ:
1794	case BIO_WRITE:
1795		if (bp->bio_offset >= pp->mediasize) {
1796			g_io_deliver(bp, EIO);
1797			return;
1798		}
1799		bp2 = g_clone_bio(bp);
1800		if (bp2 == NULL) {
1801			g_io_deliver(bp, ENOMEM);
1802			return;
1803		}
1804		if (bp2->bio_offset + bp2->bio_length > pp->mediasize)
1805			bp2->bio_length = pp->mediasize - bp2->bio_offset;
1806		bp2->bio_done = g_std_done;
1807		bp2->bio_offset += entry->gpe_offset;
1808		g_io_request(bp2, cp);
1809		return;
1810	case BIO_FLUSH:
1811		break;
1812	case BIO_GETATTR:
1813		if (g_handleattr_int(bp, "GEOM::fwheads", table->gpt_heads))
1814			return;
1815		if (g_handleattr_int(bp, "GEOM::fwsectors", table->gpt_sectors))
1816			return;
1817		if (g_handleattr_int(bp, "PART::isleaf", table->gpt_isleaf))
1818			return;
1819		if (g_handleattr_int(bp, "PART::depth", table->gpt_depth))
1820			return;
1821		if (g_handleattr_str(bp, "PART::scheme",
1822		    table->gpt_scheme->name))
1823			return;
1824		if (!strcmp("GEOM::kerneldump", bp->bio_attribute)) {
1825			/*
1826			 * Check that the partition is suitable for kernel
1827			 * dumps. Typically only swap partitions should be
1828			 * used.
1829			 */
1830			if (!G_PART_DUMPTO(table, entry)) {
1831				g_io_deliver(bp, ENODEV);
1832				printf("GEOM_PART: Partition '%s' not suitable"
1833				    " for kernel dumps (wrong type?)\n",
1834				    pp->name);
1835				return;
1836			}
1837			gkd = (struct g_kerneldump *)bp->bio_data;
1838			if (gkd->offset >= pp->mediasize) {
1839				g_io_deliver(bp, EIO);
1840				return;
1841			}
1842			if (gkd->offset + gkd->length > pp->mediasize)
1843				gkd->length = pp->mediasize - gkd->offset;
1844			gkd->offset += entry->gpe_offset;
1845		}
1846		break;
1847	default:
1848		g_io_deliver(bp, EOPNOTSUPP);
1849		return;
1850	}
1851
1852	bp2 = g_clone_bio(bp);
1853	if (bp2 == NULL) {
1854		g_io_deliver(bp, ENOMEM);
1855		return;
1856	}
1857	bp2->bio_done = g_std_done;
1858	g_io_request(bp2, cp);
1859}
1860
1861static void
1862g_part_init(struct g_class *mp)
1863{
1864
1865	TAILQ_INSERT_HEAD(&g_part_schemes, &g_part_null_scheme, scheme_list);
1866}
1867
1868static void
1869g_part_fini(struct g_class *mp)
1870{
1871
1872	TAILQ_REMOVE(&g_part_schemes, &g_part_null_scheme, scheme_list);
1873}
1874
1875static void
1876g_part_unload_event(void *arg, int flag)
1877{
1878	struct g_consumer *cp;
1879	struct g_geom *gp;
1880	struct g_provider *pp;
1881	struct g_part_scheme *scheme;
1882	struct g_part_table *table;
1883	uintptr_t *xchg;
1884	int acc, error;
1885
1886	if (flag == EV_CANCEL)
1887		return;
1888
1889	xchg = arg;
1890	error = 0;
1891	scheme = (void *)(*xchg);
1892
1893	g_topology_assert();
1894
1895	LIST_FOREACH(gp, &g_part_class.geom, geom) {
1896		table = gp->softc;
1897		if (table->gpt_scheme != scheme)
1898			continue;
1899
1900		acc = 0;
1901		LIST_FOREACH(pp, &gp->provider, provider)
1902			acc += pp->acr + pp->acw + pp->ace;
1903		LIST_FOREACH(cp, &gp->consumer, consumer)
1904			acc += cp->acr + cp->acw + cp->ace;
1905
1906		if (!acc)
1907			g_part_wither(gp, ENOSYS);
1908		else
1909			error = EBUSY;
1910	}
1911
1912	if (!error)
1913		TAILQ_REMOVE(&g_part_schemes, scheme, scheme_list);
1914
1915	*xchg = error;
1916}
1917
1918int
1919g_part_modevent(module_t mod, int type, struct g_part_scheme *scheme)
1920{
1921	uintptr_t arg;
1922	int error;
1923
1924	switch (type) {
1925	case MOD_LOAD:
1926		TAILQ_INSERT_TAIL(&g_part_schemes, scheme, scheme_list);
1927
1928		error = g_retaste(&g_part_class);
1929		if (error)
1930			TAILQ_REMOVE(&g_part_schemes, scheme, scheme_list);
1931		break;
1932	case MOD_UNLOAD:
1933		arg = (uintptr_t)scheme;
1934		error = g_waitfor_event(g_part_unload_event, &arg, M_WAITOK,
1935		    NULL);
1936		if (!error)
1937			error = (arg == (uintptr_t)scheme) ? EDOOFUS : arg;
1938		break;
1939	default:
1940		error = EOPNOTSUPP;
1941		break;
1942	}
1943
1944	return (error);
1945}
1946