geom_part.c revision 281303
1/*-
2 * Copyright (c) 2007, 2008 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 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 */
26
27#include <sys/cdefs.h>
28__FBSDID("$FreeBSD: stable/10/sbin/geom/class/part/geom_part.c 281303 2015-04-09 10:10:05Z mav $");
29
30#include <sys/stat.h>
31#include <sys/vtoc.h>
32
33#include <assert.h>
34#include <ctype.h>
35#include <err.h>
36#include <errno.h>
37#include <fcntl.h>
38#include <libgeom.h>
39#include <libutil.h>
40#include <paths.h>
41#include <signal.h>
42#include <stdint.h>
43#include <stdio.h>
44#include <stdlib.h>
45#include <limits.h>
46#include <inttypes.h>
47#include <string.h>
48#include <strings.h>
49#include <unistd.h>
50
51#include "core/geom.h"
52#include "misc/subr.h"
53
54#ifdef STATIC_GEOM_CLASSES
55#define	PUBSYM(x)	gpart_##x
56#else
57#define	PUBSYM(x)	x
58#endif
59
60uint32_t PUBSYM(lib_version) = G_LIB_VERSION;
61uint32_t PUBSYM(version) = 0;
62
63static char sstart[32];
64static char ssize[32];
65volatile sig_atomic_t undo_restore;
66
67#define	GPART_AUTOFILL	"*"
68#define	GPART_FLAGS	"C"
69
70#define	GPART_PARAM_BOOTCODE	"bootcode"
71#define	GPART_PARAM_INDEX	"index"
72#define	GPART_PARAM_PARTCODE	"partcode"
73
74static struct gclass *find_class(struct gmesh *, const char *);
75static struct ggeom * find_geom(struct gclass *, const char *);
76static const char *find_geomcfg(struct ggeom *, const char *);
77static const char *find_provcfg(struct gprovider *, const char *);
78static struct gprovider *find_provider(struct ggeom *, off_t);
79static const char *fmtsize(int64_t);
80static int gpart_autofill(struct gctl_req *);
81static int gpart_autofill_resize(struct gctl_req *);
82static void gpart_bootcode(struct gctl_req *, unsigned int);
83static void *gpart_bootfile_read(const char *, ssize_t *);
84static void gpart_issue(struct gctl_req *, unsigned int);
85static void gpart_show(struct gctl_req *, unsigned int);
86static void gpart_show_geom(struct ggeom *, const char *, int);
87static int gpart_show_hasopt(struct gctl_req *, const char *, const char *);
88static void gpart_write_partcode(struct ggeom *, int, void *, ssize_t);
89static void gpart_write_partcode_vtoc8(struct ggeom *, int, void *);
90static void gpart_print_error(const char *);
91static void gpart_backup(struct gctl_req *, unsigned int);
92static void gpart_restore(struct gctl_req *, unsigned int);
93
94struct g_command PUBSYM(class_commands)[] = {
95	{ "add", 0, gpart_issue, {
96		{ 'a', "alignment", GPART_AUTOFILL, G_TYPE_STRING },
97		{ 'b', "start", GPART_AUTOFILL, G_TYPE_STRING },
98		{ 's', "size", GPART_AUTOFILL, G_TYPE_STRING },
99		{ 't', "type", NULL, G_TYPE_STRING },
100		{ 'i', GPART_PARAM_INDEX, G_VAL_OPTIONAL, G_TYPE_NUMBER },
101		{ 'l', "label", G_VAL_OPTIONAL, G_TYPE_STRING },
102		{ 'f', "flags", GPART_FLAGS, G_TYPE_STRING },
103		G_OPT_SENTINEL },
104	    "-t type [-a alignment] [-b start] [-s size] [-i index] "
105		"[-l label] [-f flags] geom"
106	},
107	{ "backup", 0, gpart_backup, G_NULL_OPTS,
108	    "geom"
109	},
110	{ "bootcode", 0, gpart_bootcode, {
111		{ 'b', GPART_PARAM_BOOTCODE, G_VAL_OPTIONAL, G_TYPE_STRING },
112		{ 'p', GPART_PARAM_PARTCODE, G_VAL_OPTIONAL, G_TYPE_STRING },
113		{ 'i', GPART_PARAM_INDEX, G_VAL_OPTIONAL, G_TYPE_NUMBER },
114		{ 'f', "flags", GPART_FLAGS, G_TYPE_STRING },
115		G_OPT_SENTINEL },
116	    "[-b bootcode] [-p partcode -i index] [-f flags] geom"
117	},
118	{ "commit", 0, gpart_issue, G_NULL_OPTS,
119	    "geom"
120	},
121	{ "create", 0, gpart_issue, {
122		{ 's', "scheme", NULL, G_TYPE_STRING },
123		{ 'n', "entries", G_VAL_OPTIONAL, G_TYPE_NUMBER },
124		{ 'f', "flags", GPART_FLAGS, G_TYPE_STRING },
125		G_OPT_SENTINEL },
126	    "-s scheme [-n entries] [-f flags] provider"
127	},
128	{ "delete", 0, gpart_issue, {
129		{ 'i', GPART_PARAM_INDEX, NULL, G_TYPE_NUMBER },
130		{ 'f', "flags", GPART_FLAGS, G_TYPE_STRING },
131		G_OPT_SENTINEL },
132	    "-i index [-f flags] geom"
133	},
134	{ "destroy", 0, gpart_issue, {
135		{ 'F', "force", NULL, G_TYPE_BOOL },
136		{ 'f', "flags", GPART_FLAGS, G_TYPE_STRING },
137		G_OPT_SENTINEL },
138	    "[-F] [-f flags] geom"
139	},
140	{ "modify", 0, gpart_issue, {
141		{ 'i', GPART_PARAM_INDEX, NULL, G_TYPE_NUMBER },
142		{ 'l', "label", G_VAL_OPTIONAL, G_TYPE_STRING },
143		{ 't', "type", G_VAL_OPTIONAL, G_TYPE_STRING },
144		{ 'f', "flags", GPART_FLAGS, G_TYPE_STRING },
145		G_OPT_SENTINEL },
146	    "-i index [-l label] [-t type] [-f flags] geom"
147	},
148	{ "set", 0, gpart_issue, {
149		{ 'a', "attrib", NULL, G_TYPE_STRING },
150		{ 'i', GPART_PARAM_INDEX, G_VAL_OPTIONAL, G_TYPE_NUMBER },
151		{ 'f', "flags", GPART_FLAGS, G_TYPE_STRING },
152		G_OPT_SENTINEL },
153	    "-a attrib [-i index] [-f flags] geom"
154	},
155	{ "show", 0, gpart_show, {
156		{ 'l', "show_label", NULL, G_TYPE_BOOL },
157		{ 'r', "show_rawtype", NULL, G_TYPE_BOOL },
158		{ 'p', "show_providers", NULL, G_TYPE_BOOL },
159		G_OPT_SENTINEL },
160	    "[-l | -r] [-p] [geom ...]"
161	},
162	{ "undo", 0, gpart_issue, G_NULL_OPTS,
163	    "geom"
164	},
165	{ "unset", 0, gpart_issue, {
166		{ 'a', "attrib", NULL, G_TYPE_STRING },
167		{ 'i', GPART_PARAM_INDEX, G_VAL_OPTIONAL, G_TYPE_NUMBER },
168		{ 'f', "flags", GPART_FLAGS, G_TYPE_STRING },
169		G_OPT_SENTINEL },
170	    "-a attrib [-i index] [-f flags] geom"
171	},
172	{ "resize", 0, gpart_issue, {
173		{ 'a', "alignment", GPART_AUTOFILL, G_TYPE_STRING },
174		{ 's', "size", GPART_AUTOFILL, G_TYPE_STRING },
175		{ 'i', GPART_PARAM_INDEX, NULL, G_TYPE_NUMBER },
176		{ 'f', "flags", GPART_FLAGS, G_TYPE_STRING },
177		G_OPT_SENTINEL },
178	    "-i index [-a alignment] [-s size] [-f flags] geom"
179	},
180	{ "restore", 0, gpart_restore, {
181		{ 'F', "force", NULL, G_TYPE_BOOL },
182		{ 'l', "restore_labels", NULL, G_TYPE_BOOL },
183		{ 'f', "flags", GPART_FLAGS, G_TYPE_STRING },
184		G_OPT_SENTINEL },
185	    "[-lF] [-f flags] provider [...]"
186	},
187	{ "recover", 0, gpart_issue, {
188		{ 'f', "flags", GPART_FLAGS, G_TYPE_STRING },
189		G_OPT_SENTINEL },
190	    "[-f flags] geom"
191	},
192	G_CMD_SENTINEL
193};
194
195static struct gclass *
196find_class(struct gmesh *mesh, const char *name)
197{
198	struct gclass *classp;
199
200	LIST_FOREACH(classp, &mesh->lg_class, lg_class) {
201		if (strcmp(classp->lg_name, name) == 0)
202			return (classp);
203	}
204	return (NULL);
205}
206
207static struct ggeom *
208find_geom(struct gclass *classp, const char *name)
209{
210	struct ggeom *gp, *wgp;
211
212	if (strncmp(name, _PATH_DEV, sizeof(_PATH_DEV) - 1) == 0)
213		name += sizeof(_PATH_DEV) - 1;
214	wgp = NULL;
215	LIST_FOREACH(gp, &classp->lg_geom, lg_geom) {
216		if (strcmp(gp->lg_name, name) != 0)
217			continue;
218		if (find_geomcfg(gp, "wither") == NULL)
219			return (gp);
220		else
221			wgp = gp;
222	}
223	return (wgp);
224}
225
226static const char *
227find_geomcfg(struct ggeom *gp, const char *cfg)
228{
229	struct gconfig *gc;
230
231	LIST_FOREACH(gc, &gp->lg_config, lg_config) {
232		if (!strcmp(gc->lg_name, cfg))
233			return (gc->lg_val);
234	}
235	return (NULL);
236}
237
238static const char *
239find_provcfg(struct gprovider *pp, const char *cfg)
240{
241	struct gconfig *gc;
242
243	LIST_FOREACH(gc, &pp->lg_config, lg_config) {
244		if (!strcmp(gc->lg_name, cfg))
245			return (gc->lg_val);
246	}
247	return (NULL);
248}
249
250static struct gprovider *
251find_provider(struct ggeom *gp, off_t minsector)
252{
253	struct gprovider *pp, *bestpp;
254	const char *s;
255	off_t sector, bestsector;
256
257	bestpp = NULL;
258	bestsector = 0;
259	LIST_FOREACH(pp, &gp->lg_provider, lg_provider) {
260		s = find_provcfg(pp, "start");
261		sector = (off_t)strtoimax(s, NULL, 0);
262		if (sector < minsector)
263			continue;
264		if (bestpp != NULL && sector >= bestsector)
265			continue;
266
267		bestpp = pp;
268		bestsector = sector;
269	}
270	return (bestpp);
271}
272
273static const char *
274fmtsize(int64_t rawsz)
275{
276	static char buf[5];
277
278	humanize_number(buf, sizeof(buf), rawsz, "", HN_AUTOSCALE,
279	    HN_B | HN_NOSPACE | HN_DECIMAL);
280	return (buf);
281}
282
283static const char *
284fmtattrib(struct gprovider *pp)
285{
286	static char buf[128];
287	struct gconfig *gc;
288	u_int idx;
289
290	buf[0] = '\0';
291	idx = 0;
292	LIST_FOREACH(gc, &pp->lg_config, lg_config) {
293		if (strcmp(gc->lg_name, "attrib") != 0)
294			continue;
295		idx += snprintf(buf + idx, sizeof(buf) - idx, "%s%s",
296		    (idx == 0) ? " [" : ",", gc->lg_val);
297	}
298	if (idx > 0)
299		snprintf(buf + idx, sizeof(buf) - idx, "] ");
300	return (buf);
301}
302
303#define	ALIGNDOWN(d, a)	((d) - (d) % (a))
304#define	ALIGNUP(d, a)	((d) % (a) ? (d) - (d) % (a) + (a): (d))
305
306static int
307gpart_autofill_resize(struct gctl_req *req)
308{
309	struct gmesh mesh;
310	struct gclass *cp;
311	struct ggeom *gp;
312	struct gprovider *pp;
313	off_t last, size, start, new_size;
314	off_t lba, new_lba, alignment, offset;
315	const char *s;
316	int error, idx, has_alignment;
317
318	idx = (int)gctl_get_intmax(req, GPART_PARAM_INDEX);
319	if (idx < 1)
320		errx(EXIT_FAILURE, "invalid partition index");
321
322	error = geom_gettree(&mesh);
323	if (error)
324		return (error);
325	s = gctl_get_ascii(req, "class");
326	if (s == NULL)
327		abort();
328	cp = find_class(&mesh, s);
329	if (cp == NULL)
330		errx(EXIT_FAILURE, "Class %s not found.", s);
331	s = gctl_get_ascii(req, "arg0");
332	if (s == NULL)
333		abort();
334	gp = find_geom(cp, s);
335	if (gp == NULL)
336		errx(EXIT_FAILURE, "No such geom: %s.", s);
337	pp = LIST_FIRST(&gp->lg_consumer)->lg_provider;
338	if (pp == NULL)
339		errx(EXIT_FAILURE, "Provider for geom %s not found.", s);
340
341	s = gctl_get_ascii(req, "alignment");
342	has_alignment = (*s == '*') ? 0 : 1;
343	alignment = 1;
344	if (has_alignment) {
345		error = g_parse_lba(s, pp->lg_sectorsize, &alignment);
346		if (error)
347			errc(EXIT_FAILURE, error, "Invalid alignment param");
348		if (alignment == 0)
349			errx(EXIT_FAILURE, "Invalid alignment param");
350	} else {
351		lba = pp->lg_stripesize / pp->lg_sectorsize;
352		if (lba > 0)
353			alignment = lba;
354	}
355	error = gctl_delete_param(req, "alignment");
356	if (error)
357		errc(EXIT_FAILURE, error, "internal error");
358
359	s = gctl_get_ascii(req, "size");
360	if (*s == '*')
361		new_size = 0;
362	else {
363		error = g_parse_lba(s, pp->lg_sectorsize, &new_size);
364		if (error)
365			errc(EXIT_FAILURE, error, "Invalid size param");
366		/* no autofill necessary. */
367		if (has_alignment == 0)
368			goto done;
369	}
370
371	offset = (pp->lg_stripeoffset / pp->lg_sectorsize) % alignment;
372	s = find_geomcfg(gp, "last");
373	if (s == NULL)
374		errx(EXIT_FAILURE, "Final block not found for geom %s",
375		    gp->lg_name);
376	last = (off_t)strtoimax(s, NULL, 0);
377	LIST_FOREACH(pp, &gp->lg_provider, lg_provider) {
378		s = find_provcfg(pp, "index");
379		if (s == NULL)
380			continue;
381		if (atoi(s) == idx)
382			break;
383	}
384	if (pp == NULL)
385		errx(EXIT_FAILURE, "invalid partition index");
386
387	s = find_provcfg(pp, "start");
388	start = (off_t)strtoimax(s, NULL, 0);
389	s = find_provcfg(pp, "end");
390	lba = (off_t)strtoimax(s, NULL, 0);
391	size = lba - start + 1;
392
393	pp = find_provider(gp, lba + 1);
394	if (new_size > 0 && (new_size <= size || pp == NULL)) {
395		/* The start offset may be not aligned, so we align the end
396		 * offset and then calculate the size.
397		 */
398		new_size = ALIGNDOWN(start + offset + new_size,
399		    alignment) - start - offset;
400		goto done;
401	}
402	if (pp == NULL) {
403		new_size = ALIGNDOWN(last + offset + 1, alignment) -
404		    start - offset;
405		if (new_size < size)
406			return (ENOSPC);
407	} else {
408		s = find_provcfg(pp, "start");
409		new_lba = (off_t)strtoimax(s, NULL, 0);
410		/*
411		 * Is there any free space between current and
412		 * next providers?
413		 */
414		new_lba = ALIGNDOWN(new_lba + offset, alignment) - offset;
415		if (new_lba > lba)
416			new_size = new_lba - start;
417		else {
418			geom_deletetree(&mesh);
419			return (ENOSPC);
420		}
421	}
422done:
423	snprintf(ssize, sizeof(ssize), "%jd", (intmax_t)new_size);
424	gctl_change_param(req, "size", -1, ssize);
425	geom_deletetree(&mesh);
426	return (0);
427}
428
429static int
430gpart_autofill(struct gctl_req *req)
431{
432	struct gmesh mesh;
433	struct gclass *cp;
434	struct ggeom *gp;
435	struct gprovider *pp;
436	off_t first, last, a_first;
437	off_t size, start, a_lba;
438	off_t lba, len, alignment, offset;
439	uintmax_t grade;
440	const char *s;
441	int error, has_size, has_start, has_alignment;
442
443	s = gctl_get_ascii(req, "verb");
444	if (strcmp(s, "resize") == 0)
445		return gpart_autofill_resize(req);
446	if (strcmp(s, "add") != 0)
447		return (0);
448
449	error = geom_gettree(&mesh);
450	if (error)
451		return (error);
452	s = gctl_get_ascii(req, "class");
453	if (s == NULL)
454		abort();
455	cp = find_class(&mesh, s);
456	if (cp == NULL)
457		errx(EXIT_FAILURE, "Class %s not found.", s);
458	s = gctl_get_ascii(req, "arg0");
459	if (s == NULL)
460		abort();
461	gp = find_geom(cp, s);
462	if (gp == NULL)
463		errx(EXIT_FAILURE, "No such geom: %s.", s);
464	pp = LIST_FIRST(&gp->lg_consumer)->lg_provider;
465	if (pp == NULL)
466		errx(EXIT_FAILURE, "Provider for geom %s not found.", s);
467
468	s = gctl_get_ascii(req, "alignment");
469	has_alignment = (*s == '*') ? 0 : 1;
470	alignment = 1;
471	if (has_alignment) {
472		error = g_parse_lba(s, pp->lg_sectorsize, &alignment);
473		if (error)
474			errc(EXIT_FAILURE, error, "Invalid alignment param");
475		if (alignment == 0)
476			errx(EXIT_FAILURE, "Invalid alignment param");
477	}
478	error = gctl_delete_param(req, "alignment");
479	if (error)
480		errc(EXIT_FAILURE, error, "internal error");
481
482	s = gctl_get_ascii(req, "size");
483	has_size = (*s == '*') ? 0 : 1;
484	size = 0;
485	if (has_size) {
486		error = g_parse_lba(s, pp->lg_sectorsize, &size);
487		if (error)
488			errc(EXIT_FAILURE, error, "Invalid size param");
489	}
490
491	s = gctl_get_ascii(req, "start");
492	has_start = (*s == '*') ? 0 : 1;
493	start = 0ULL;
494	if (has_start) {
495		error = g_parse_lba(s, pp->lg_sectorsize, &start);
496		if (error)
497			errc(EXIT_FAILURE, error, "Invalid start param");
498	}
499
500	/* No autofill necessary. */
501	if (has_size && has_start && !has_alignment)
502		goto done;
503
504	len = pp->lg_stripesize / pp->lg_sectorsize;
505	if (len > 0 && !has_alignment)
506		alignment = len;
507
508	/* Adjust parameters to stripeoffset */
509	offset = (pp->lg_stripeoffset / pp->lg_sectorsize) % alignment;
510	start = ALIGNUP(start + offset, alignment);
511	if (size > alignment)
512		size = ALIGNDOWN(size, alignment);
513
514	s = find_geomcfg(gp, "first");
515	if (s == NULL)
516		errx(EXIT_FAILURE, "Starting block not found for geom %s",
517		    gp->lg_name);
518	first = (off_t)strtoimax(s, NULL, 0);
519	s = find_geomcfg(gp, "last");
520	if (s == NULL)
521		errx(EXIT_FAILURE, "Final block not found for geom %s",
522		    gp->lg_name);
523	last = (off_t)strtoimax(s, NULL, 0);
524	grade = ~0ULL;
525	a_first = ALIGNUP(first + offset, alignment);
526	last = ALIGNDOWN(last + offset, alignment);
527	if (a_first < start)
528		a_first = start;
529	while ((pp = find_provider(gp, first)) != NULL) {
530		s = find_provcfg(pp, "start");
531		lba = (off_t)strtoimax(s, NULL, 0);
532		a_lba = ALIGNDOWN(lba + offset, alignment);
533		if (first < a_lba && a_first < a_lba) {
534			/* Free space [first, lba> */
535			len = a_lba - a_first;
536			if (has_size) {
537				if (len >= size &&
538				    (uintmax_t)(len - size) < grade) {
539					start = a_first;
540					grade = len - size;
541				}
542			} else if (has_start) {
543				if (start >= a_first && start < a_lba) {
544					size = a_lba - start;
545					grade = start - a_first;
546				}
547			} else {
548				if (grade == ~0ULL || len > size) {
549					start = a_first;
550					size = len;
551					grade = 0;
552				}
553			}
554		}
555
556		s = find_provcfg(pp, "end");
557		first = (off_t)strtoimax(s, NULL, 0) + 1;
558		if (first + offset > a_first)
559			a_first = ALIGNUP(first + offset, alignment);
560	}
561	if (a_first <= last) {
562		/* Free space [first-last] */
563		len = ALIGNDOWN(last - a_first + 1, alignment);
564		if (has_size) {
565			if (len >= size &&
566			    (uintmax_t)(len - size) < grade) {
567				start = a_first;
568				grade = len - size;
569			}
570		} else if (has_start) {
571			if (start >= a_first && start <= last) {
572				size = ALIGNDOWN(last - start + 1, alignment);
573				grade = start - a_first;
574			}
575		} else {
576			if (grade == ~0ULL || len > size) {
577				start = a_first;
578				size = len;
579				grade = 0;
580			}
581		}
582	}
583	if (grade == ~0ULL) {
584		geom_deletetree(&mesh);
585		return (ENOSPC);
586	}
587	start -= offset;	/* Return back to real offset */
588done:
589	snprintf(ssize, sizeof(ssize), "%jd", (intmax_t)size);
590	gctl_change_param(req, "size", -1, ssize);
591	snprintf(sstart, sizeof(sstart), "%jd", (intmax_t)start);
592	gctl_change_param(req, "start", -1, sstart);
593	geom_deletetree(&mesh);
594	return (0);
595}
596
597static void
598gpart_show_geom(struct ggeom *gp, const char *element, int show_providers)
599{
600	struct gprovider *pp;
601	const char *s, *scheme;
602	off_t first, last, sector, end;
603	off_t length, secsz;
604	int idx, wblocks, wname, wmax;
605
606	if (find_geomcfg(gp, "wither"))
607		return;
608	scheme = find_geomcfg(gp, "scheme");
609	if (scheme == NULL)
610		errx(EXIT_FAILURE, "Scheme not found for geom %s", gp->lg_name);
611	s = find_geomcfg(gp, "first");
612	if (s == NULL)
613		errx(EXIT_FAILURE, "Starting block not found for geom %s",
614		    gp->lg_name);
615	first = (off_t)strtoimax(s, NULL, 0);
616	s = find_geomcfg(gp, "last");
617	if (s == NULL)
618		errx(EXIT_FAILURE, "Final block not found for geom %s",
619		    gp->lg_name);
620	last = (off_t)strtoimax(s, NULL, 0);
621	wblocks = strlen(s);
622	s = find_geomcfg(gp, "state");
623	if (s == NULL)
624		errx(EXIT_FAILURE, "State not found for geom %s", gp->lg_name);
625	if (s != NULL && *s != 'C')
626		s = NULL;
627	wmax = strlen(gp->lg_name);
628	if (show_providers) {
629		LIST_FOREACH(pp, &gp->lg_provider, lg_provider) {
630			wname = strlen(pp->lg_name);
631			if (wname > wmax)
632				wmax = wname;
633		}
634	}
635	wname = wmax;
636	pp = LIST_FIRST(&gp->lg_consumer)->lg_provider;
637	secsz = pp->lg_sectorsize;
638	printf("=>%*jd  %*jd  %*s  %s  (%s)%s\n",
639	    wblocks, (intmax_t)first, wblocks, (intmax_t)(last - first + 1),
640	    wname, gp->lg_name,
641	    scheme, fmtsize(pp->lg_mediasize),
642	    s ? " [CORRUPT]": "");
643
644	while ((pp = find_provider(gp, first)) != NULL) {
645		s = find_provcfg(pp, "start");
646		sector = (off_t)strtoimax(s, NULL, 0);
647
648		s = find_provcfg(pp, "end");
649		end = (off_t)strtoimax(s, NULL, 0);
650		length = end - sector + 1;
651
652		s = find_provcfg(pp, "index");
653		idx = atoi(s);
654		if (first < sector) {
655			printf("  %*jd  %*jd  %*s  - free -  (%s)\n",
656			    wblocks, (intmax_t)first, wblocks,
657			    (intmax_t)(sector - first), wname, "",
658			    fmtsize((sector - first) * secsz));
659		}
660		if (show_providers) {
661			printf("  %*jd  %*jd  %*s  %s %s (%s)\n",
662			    wblocks, (intmax_t)sector, wblocks,
663			    (intmax_t)length, wname, pp->lg_name,
664			    find_provcfg(pp, element), fmtattrib(pp),
665			    fmtsize(pp->lg_mediasize));
666		} else
667			printf("  %*jd  %*jd  %*d  %s %s (%s)\n",
668			    wblocks, (intmax_t)sector, wblocks,
669			    (intmax_t)length, wname, idx,
670			    find_provcfg(pp, element), fmtattrib(pp),
671			    fmtsize(pp->lg_mediasize));
672		first = end + 1;
673	}
674	if (first <= last) {
675		length = last - first + 1;
676		printf("  %*jd  %*jd  %*s  - free -  (%s)\n",
677		    wblocks, (intmax_t)first, wblocks, (intmax_t)length,
678		    wname, "",
679		    fmtsize(length * secsz));
680	}
681	printf("\n");
682}
683
684static int
685gpart_show_hasopt(struct gctl_req *req, const char *opt, const char *elt)
686{
687
688	if (!gctl_get_int(req, "%s", opt))
689		return (0);
690
691	if (elt != NULL)
692		errx(EXIT_FAILURE, "-l and -r are mutually exclusive");
693
694	return (1);
695}
696
697static void
698gpart_show(struct gctl_req *req, unsigned int fl __unused)
699{
700	struct gmesh mesh;
701	struct gclass *classp;
702	struct ggeom *gp;
703	const char *element, *name;
704	int error, i, nargs, show_providers;
705
706	element = NULL;
707	if (gpart_show_hasopt(req, "show_label", element))
708		element = "label";
709	if (gpart_show_hasopt(req, "show_rawtype", element))
710		element = "rawtype";
711	if (element == NULL)
712		element = "type";
713
714	name = gctl_get_ascii(req, "class");
715	if (name == NULL)
716		abort();
717	error = geom_gettree(&mesh);
718	if (error != 0)
719		errc(EXIT_FAILURE, error, "Cannot get GEOM tree");
720	classp = find_class(&mesh, name);
721	if (classp == NULL) {
722		geom_deletetree(&mesh);
723		errx(EXIT_FAILURE, "Class %s not found.", name);
724	}
725	show_providers = gctl_get_int(req, "show_providers");
726	nargs = gctl_get_int(req, "nargs");
727	if (nargs > 0) {
728		for (i = 0; i < nargs; i++) {
729			name = gctl_get_ascii(req, "arg%d", i);
730			gp = find_geom(classp, name);
731			if (gp != NULL)
732				gpart_show_geom(gp, element, show_providers);
733			else
734				errx(EXIT_FAILURE, "No such geom: %s.", name);
735		}
736	} else {
737		LIST_FOREACH(gp, &classp->lg_geom, lg_geom) {
738			gpart_show_geom(gp, element, show_providers);
739		}
740	}
741	geom_deletetree(&mesh);
742}
743
744static void
745gpart_backup(struct gctl_req *req, unsigned int fl __unused)
746{
747	struct gmesh mesh;
748	struct gclass *classp;
749	struct gprovider *pp;
750	struct ggeom *gp;
751	const char *s, *scheme;
752	off_t sector, end;
753	off_t length;
754	int error, i, windex, wblocks, wtype;
755
756	if (gctl_get_int(req, "nargs") != 1)
757		errx(EXIT_FAILURE, "Invalid number of arguments.");
758	error = geom_gettree(&mesh);
759	if (error != 0)
760		errc(EXIT_FAILURE, error, "Cannot get GEOM tree");
761	s = gctl_get_ascii(req, "class");
762	if (s == NULL)
763		abort();
764	classp = find_class(&mesh, s);
765	if (classp == NULL) {
766		geom_deletetree(&mesh);
767		errx(EXIT_FAILURE, "Class %s not found.", s);
768	}
769	s = gctl_get_ascii(req, "arg0");
770	if (s == NULL)
771		abort();
772	gp = find_geom(classp, s);
773	if (gp == NULL)
774		errx(EXIT_FAILURE, "No such geom: %s.", s);
775	scheme = find_geomcfg(gp, "scheme");
776	if (scheme == NULL)
777		abort();
778	pp = LIST_FIRST(&gp->lg_consumer)->lg_provider;
779	s = find_geomcfg(gp, "last");
780	if (s == NULL)
781		abort();
782	wblocks = strlen(s);
783	wtype = 0;
784	LIST_FOREACH(pp, &gp->lg_provider, lg_provider) {
785		s = find_provcfg(pp, "type");
786		i = strlen(s);
787		if (i > wtype)
788			wtype = i;
789	}
790	s = find_geomcfg(gp, "entries");
791	if (s == NULL)
792		abort();
793	windex = strlen(s);
794	printf("%s %s\n", scheme, s);
795	LIST_FOREACH(pp, &gp->lg_provider, lg_provider) {
796		s = find_provcfg(pp, "start");
797		sector = (off_t)strtoimax(s, NULL, 0);
798
799		s = find_provcfg(pp, "end");
800		end = (off_t)strtoimax(s, NULL, 0);
801		length = end - sector + 1;
802
803		s = find_provcfg(pp, "label");
804		printf("%-*s %*s %*jd %*jd %s %s\n",
805		    windex, find_provcfg(pp, "index"),
806		    wtype, find_provcfg(pp, "type"),
807		    wblocks, (intmax_t)sector,
808		    wblocks, (intmax_t)length,
809		    (s != NULL) ? s: "", fmtattrib(pp));
810	}
811	geom_deletetree(&mesh);
812}
813
814static int
815skip_line(const char *p)
816{
817
818	while (*p != '\0') {
819		if (*p == '#')
820			return (1);
821		if (isspace(*p) == 0)
822			return (0);
823		p++;
824	}
825	return (1);
826}
827
828static void
829gpart_sighndl(int sig __unused)
830{
831	undo_restore = 1;
832}
833
834static void
835gpart_restore(struct gctl_req *req, unsigned int fl __unused)
836{
837	struct gmesh mesh;
838	struct gclass *classp;
839	struct gctl_req *r;
840	struct ggeom *gp;
841	struct sigaction si_sa;
842	const char *s, *flags, *errstr, *label;
843	char **ap, *argv[6], line[BUFSIZ], *pline;
844	int error, forced, i, l, nargs, created, rl;
845	intmax_t n;
846
847	nargs = gctl_get_int(req, "nargs");
848	if (nargs < 1)
849		errx(EXIT_FAILURE, "Invalid number of arguments.");
850
851	forced = gctl_get_int(req, "force");
852	flags = gctl_get_ascii(req, "flags");
853	rl = gctl_get_int(req, "restore_labels");
854	s = gctl_get_ascii(req, "class");
855	if (s == NULL)
856		abort();
857	error = geom_gettree(&mesh);
858	if (error != 0)
859		errc(EXIT_FAILURE, error, "Cannot get GEOM tree");
860	classp = find_class(&mesh, s);
861	if (classp == NULL) {
862		geom_deletetree(&mesh);
863		errx(EXIT_FAILURE, "Class %s not found.", s);
864	}
865
866	sigemptyset(&si_sa.sa_mask);
867	si_sa.sa_flags = 0;
868	si_sa.sa_handler = gpart_sighndl;
869	if (sigaction(SIGINT, &si_sa, 0) == -1)
870		err(EXIT_FAILURE, "sigaction SIGINT");
871
872	if (forced) {
873		/* destroy existent partition table before restore */
874		for (i = 0; i < nargs; i++) {
875			s = gctl_get_ascii(req, "arg%d", i);
876			gp = find_geom(classp, s);
877			if (gp != NULL) {
878				r = gctl_get_handle();
879				gctl_ro_param(r, "class", -1,
880				    classp->lg_name);
881				gctl_ro_param(r, "verb", -1, "destroy");
882				gctl_ro_param(r, "flags", -1, "restore");
883				gctl_ro_param(r, "force", sizeof(forced),
884				    &forced);
885				gctl_ro_param(r, "arg0", -1, s);
886				errstr = gctl_issue(r);
887				if (errstr != NULL && errstr[0] != '\0') {
888					gpart_print_error(errstr);
889					gctl_free(r);
890					goto backout;
891				}
892				gctl_free(r);
893			}
894		}
895	}
896	created = 0;
897	while (undo_restore == 0 &&
898	    fgets(line, sizeof(line) - 1, stdin) != NULL) {
899		/* Format of backup entries:
900		 * <scheme name> <number of entries>
901		 * <index> <type> <start> <size> [label] ['['attrib[,attrib]']']
902		 */
903		pline = (char *)line;
904		pline[strlen(line) - 1] = 0;
905		if (skip_line(pline))
906			continue;
907		for (ap = argv;
908		    (*ap = strsep(&pline, " \t")) != NULL;)
909			if (**ap != '\0' && ++ap >= &argv[6])
910				break;
911		l = ap - &argv[0];
912		label = pline = NULL;
913		if (l == 1 || l == 2) { /* create table */
914			if (created)
915				errx(EXIT_FAILURE, "Incorrect backup format.");
916			if (l == 2)
917				n = strtoimax(argv[1], NULL, 0);
918			for (i = 0; i < nargs; i++) {
919				s = gctl_get_ascii(req, "arg%d", i);
920				r = gctl_get_handle();
921				gctl_ro_param(r, "class", -1,
922				    classp->lg_name);
923				gctl_ro_param(r, "verb", -1, "create");
924				gctl_ro_param(r, "scheme", -1, argv[0]);
925				if (l == 2)
926					gctl_ro_param(r, "entries",
927					    sizeof(n), &n);
928				gctl_ro_param(r, "flags", -1, "restore");
929				gctl_ro_param(r, "arg0", -1, s);
930				errstr = gctl_issue(r);
931				if (errstr != NULL && errstr[0] != '\0') {
932					gpart_print_error(errstr);
933					gctl_free(r);
934					goto backout;
935				}
936				gctl_free(r);
937			}
938			created = 1;
939			continue;
940		} else if (l < 4 || created == 0)
941			errx(EXIT_FAILURE, "Incorrect backup format.");
942		else if (l == 5) {
943			if (strchr(argv[4], '[') == NULL)
944				label = argv[4];
945			else
946				pline = argv[4];
947		} else if (l == 6) {
948			label = argv[4];
949			pline = argv[5];
950		}
951		/* Add partitions to each table */
952		for (i = 0; i < nargs; i++) {
953			s = gctl_get_ascii(req, "arg%d", i);
954			r = gctl_get_handle();
955			n = strtoimax(argv[0], NULL, 0);
956			gctl_ro_param(r, "class", -1, classp->lg_name);
957			gctl_ro_param(r, "verb", -1, "add");
958			gctl_ro_param(r, "flags", -1, "restore");
959			gctl_ro_param(r, GPART_PARAM_INDEX, sizeof(n), &n);
960			gctl_ro_param(r, "type", -1, argv[1]);
961			gctl_ro_param(r, "start", -1, argv[2]);
962			gctl_ro_param(r, "size", -1, argv[3]);
963			if (rl != 0 && label != NULL)
964				gctl_ro_param(r, "label", -1, argv[4]);
965			gctl_ro_param(r, "alignment", -1, GPART_AUTOFILL);
966			gctl_ro_param(r, "arg0", -1, s);
967			error = gpart_autofill(r);
968			if (error != 0)
969				errc(EXIT_FAILURE, error, "autofill");
970			errstr = gctl_issue(r);
971			if (errstr != NULL && errstr[0] != '\0') {
972				gpart_print_error(errstr);
973				gctl_free(r);
974				goto backout;
975			}
976			gctl_free(r);
977		}
978		if (pline == NULL || *pline != '[')
979			continue;
980		/* set attributes */
981		pline++;
982		for (ap = argv;
983		    (*ap = strsep(&pline, ",]")) != NULL;)
984			if (**ap != '\0' && ++ap >= &argv[6])
985				break;
986		for (i = 0; i < nargs; i++) {
987			l = ap - &argv[0];
988			s = gctl_get_ascii(req, "arg%d", i);
989			while (l > 0) {
990				r = gctl_get_handle();
991				gctl_ro_param(r, "class", -1, classp->lg_name);
992				gctl_ro_param(r, "verb", -1, "set");
993				gctl_ro_param(r, "flags", -1, "restore");
994				gctl_ro_param(r, GPART_PARAM_INDEX,
995				    sizeof(n), &n);
996				gctl_ro_param(r, "attrib", -1, argv[--l]);
997				gctl_ro_param(r, "arg0", -1, s);
998				errstr = gctl_issue(r);
999				if (errstr != NULL && errstr[0] != '\0') {
1000					gpart_print_error(errstr);
1001					gctl_free(r);
1002					goto backout;
1003				}
1004				gctl_free(r);
1005			}
1006		}
1007	}
1008	if (undo_restore)
1009		goto backout;
1010	/* commit changes if needed */
1011	if (strchr(flags, 'C') != NULL) {
1012		for (i = 0; i < nargs; i++) {
1013			s = gctl_get_ascii(req, "arg%d", i);
1014			r = gctl_get_handle();
1015			gctl_ro_param(r, "class", -1, classp->lg_name);
1016			gctl_ro_param(r, "verb", -1, "commit");
1017			gctl_ro_param(r, "arg0", -1, s);
1018			errstr = gctl_issue(r);
1019			if (errstr != NULL && errstr[0] != '\0') {
1020				gpart_print_error(errstr);
1021				gctl_free(r);
1022				goto backout;
1023			}
1024			gctl_free(r);
1025		}
1026	}
1027	gctl_free(req);
1028	geom_deletetree(&mesh);
1029	exit(EXIT_SUCCESS);
1030
1031backout:
1032	for (i = 0; i < nargs; i++) {
1033		s = gctl_get_ascii(req, "arg%d", i);
1034		r = gctl_get_handle();
1035		gctl_ro_param(r, "class", -1, classp->lg_name);
1036		gctl_ro_param(r, "verb", -1, "undo");
1037		gctl_ro_param(r, "arg0", -1, s);
1038		gctl_issue(r);
1039		gctl_free(r);
1040	}
1041	gctl_free(req);
1042	geom_deletetree(&mesh);
1043	exit(EXIT_FAILURE);
1044}
1045
1046static void *
1047gpart_bootfile_read(const char *bootfile, ssize_t *size)
1048{
1049	struct stat sb;
1050	void *code;
1051	int fd;
1052
1053	if (stat(bootfile, &sb) == -1)
1054		err(EXIT_FAILURE, "%s", bootfile);
1055	if (!S_ISREG(sb.st_mode))
1056		errx(EXIT_FAILURE, "%s: not a regular file", bootfile);
1057	if (sb.st_size == 0)
1058		errx(EXIT_FAILURE, "%s: empty file", bootfile);
1059	if (*size > 0 && sb.st_size > *size)
1060		errx(EXIT_FAILURE, "%s: file too big (%zu limit)", bootfile,
1061		    *size);
1062
1063	*size = sb.st_size;
1064
1065	fd = open(bootfile, O_RDONLY);
1066	if (fd == -1)
1067		err(EXIT_FAILURE, "%s", bootfile);
1068	code = malloc(*size);
1069	if (code == NULL)
1070		err(EXIT_FAILURE, NULL);
1071	if (read(fd, code, *size) != *size)
1072		err(EXIT_FAILURE, "%s", bootfile);
1073	close(fd);
1074
1075	return (code);
1076}
1077
1078static void
1079gpart_write_partcode(struct ggeom *gp, int idx, void *code, ssize_t size)
1080{
1081	char dsf[128];
1082	struct gprovider *pp;
1083	const char *s;
1084	char *buf;
1085	off_t bsize;
1086	int fd;
1087
1088	LIST_FOREACH(pp, &gp->lg_provider, lg_provider) {
1089		s = find_provcfg(pp, "index");
1090		if (s == NULL)
1091			continue;
1092		if (atoi(s) == idx)
1093			break;
1094	}
1095
1096	if (pp != NULL) {
1097		snprintf(dsf, sizeof(dsf), "/dev/%s", pp->lg_name);
1098		fd = open(dsf, O_WRONLY);
1099		if (fd == -1)
1100			err(EXIT_FAILURE, "%s", dsf);
1101		if (lseek(fd, size, SEEK_SET) != size)
1102			errx(EXIT_FAILURE, "%s: not enough space", dsf);
1103		if (lseek(fd, 0, SEEK_SET) != 0)
1104			err(EXIT_FAILURE, "%s", dsf);
1105
1106		/*
1107		 * When writing to a disk device, the write must be
1108		 * sector aligned and not write to any partial sectors,
1109		 * so round up the buffer size to the next sector and zero it.
1110		 */
1111		bsize = (size + pp->lg_sectorsize - 1) /
1112		    pp->lg_sectorsize * pp->lg_sectorsize;
1113		buf = calloc(1, bsize);
1114		if (buf == NULL)
1115			err(EXIT_FAILURE, "%s", dsf);
1116		bcopy(code, buf, size);
1117		if (write(fd, buf, bsize) != bsize)
1118			err(EXIT_FAILURE, "%s", dsf);
1119		free(buf);
1120		close(fd);
1121	} else
1122		errx(EXIT_FAILURE, "invalid partition index");
1123}
1124
1125static void
1126gpart_write_partcode_vtoc8(struct ggeom *gp, int idx, void *code)
1127{
1128	char dsf[128];
1129	struct gprovider *pp;
1130	const char *s;
1131	int installed, fd;
1132
1133	installed = 0;
1134	LIST_FOREACH(pp, &gp->lg_provider, lg_provider) {
1135		s = find_provcfg(pp, "index");
1136		if (s == NULL)
1137			continue;
1138		if (idx != 0 && atoi(s) != idx)
1139			continue;
1140		snprintf(dsf, sizeof(dsf), "/dev/%s", pp->lg_name);
1141		if (pp->lg_sectorsize != sizeof(struct vtoc8))
1142			errx(EXIT_FAILURE, "%s: unexpected sector "
1143			    "size (%d)\n", dsf, pp->lg_sectorsize);
1144		fd = open(dsf, O_WRONLY);
1145		if (fd == -1)
1146			err(EXIT_FAILURE, "%s", dsf);
1147		if (lseek(fd, VTOC_BOOTSIZE, SEEK_SET) != VTOC_BOOTSIZE)
1148			continue;
1149		/*
1150		 * We ignore the first VTOC_BOOTSIZE bytes of boot code in
1151		 * order to avoid overwriting the label.
1152		 */
1153		if (lseek(fd, sizeof(struct vtoc8), SEEK_SET) !=
1154		    sizeof(struct vtoc8))
1155			err(EXIT_FAILURE, "%s", dsf);
1156		if (write(fd, (caddr_t)code + sizeof(struct vtoc8),
1157		    VTOC_BOOTSIZE - sizeof(struct vtoc8)) != VTOC_BOOTSIZE -
1158		    sizeof(struct vtoc8))
1159			err(EXIT_FAILURE, "%s", dsf);
1160		installed++;
1161		close(fd);
1162		if (idx != 0 && atoi(s) == idx)
1163			break;
1164	}
1165	if (installed == 0)
1166		errx(EXIT_FAILURE, "%s: no partitions", gp->lg_name);
1167}
1168
1169static void
1170gpart_bootcode(struct gctl_req *req, unsigned int fl)
1171{
1172	struct gmesh mesh;
1173	struct gclass *classp;
1174	struct ggeom *gp;
1175	const char *s;
1176	void *bootcode, *partcode;
1177	size_t bootsize, partsize;
1178	int error, idx, vtoc8;
1179
1180	if (gctl_has_param(req, GPART_PARAM_BOOTCODE)) {
1181		s = gctl_get_ascii(req, GPART_PARAM_BOOTCODE);
1182		bootsize = 800 * 1024;		/* Arbitrary limit. */
1183		bootcode = gpart_bootfile_read(s, &bootsize);
1184		error = gctl_change_param(req, GPART_PARAM_BOOTCODE, bootsize,
1185		    bootcode);
1186		if (error)
1187			errc(EXIT_FAILURE, error, "internal error");
1188	} else {
1189		bootcode = NULL;
1190		bootsize = 0;
1191	}
1192
1193	s = gctl_get_ascii(req, "class");
1194	if (s == NULL)
1195		abort();
1196	error = geom_gettree(&mesh);
1197	if (error != 0)
1198		errc(EXIT_FAILURE, error, "Cannot get GEOM tree");
1199	classp = find_class(&mesh, s);
1200	if (classp == NULL) {
1201		geom_deletetree(&mesh);
1202		errx(EXIT_FAILURE, "Class %s not found.", s);
1203	}
1204	if (gctl_get_int(req, "nargs") != 1)
1205		errx(EXIT_FAILURE, "Invalid number of arguments.");
1206	s = gctl_get_ascii(req, "arg0");
1207	if (s == NULL)
1208		abort();
1209	gp = find_geom(classp, s);
1210	if (gp == NULL)
1211		errx(EXIT_FAILURE, "No such geom: %s.", s);
1212	s = find_geomcfg(gp, "scheme");
1213	if (s == NULL)
1214		errx(EXIT_FAILURE, "Scheme not found for geom %s", gp->lg_name);
1215	vtoc8 = 0;
1216	if (strcmp(s, "VTOC8") == 0)
1217		vtoc8 = 1;
1218
1219	if (gctl_has_param(req, GPART_PARAM_PARTCODE)) {
1220		s = gctl_get_ascii(req, GPART_PARAM_PARTCODE);
1221		partsize = vtoc8 != 0 ? VTOC_BOOTSIZE : bootsize * 1024;
1222		partcode = gpart_bootfile_read(s, &partsize);
1223		error = gctl_delete_param(req, GPART_PARAM_PARTCODE);
1224		if (error)
1225			errc(EXIT_FAILURE, error, "internal error");
1226	} else {
1227		partcode = NULL;
1228		partsize = 0;
1229	}
1230
1231	if (gctl_has_param(req, GPART_PARAM_INDEX)) {
1232		if (partcode == NULL)
1233			errx(EXIT_FAILURE, "-i is only valid with -p");
1234		idx = (int)gctl_get_intmax(req, GPART_PARAM_INDEX);
1235		if (idx < 1)
1236			errx(EXIT_FAILURE, "invalid partition index");
1237		error = gctl_delete_param(req, GPART_PARAM_INDEX);
1238		if (error)
1239			errc(EXIT_FAILURE, error, "internal error");
1240	} else
1241		idx = 0;
1242
1243	if (partcode != NULL) {
1244		if (vtoc8 == 0) {
1245			if (idx == 0)
1246				errx(EXIT_FAILURE, "missing -i option");
1247			gpart_write_partcode(gp, idx, partcode, partsize);
1248		} else {
1249			if (partsize != VTOC_BOOTSIZE)
1250				errx(EXIT_FAILURE, "invalid bootcode");
1251			gpart_write_partcode_vtoc8(gp, idx, partcode);
1252		}
1253	} else
1254		if (bootcode == NULL)
1255			errx(EXIT_FAILURE, "no -b nor -p");
1256
1257	if (bootcode != NULL)
1258		gpart_issue(req, fl);
1259
1260	geom_deletetree(&mesh);
1261}
1262
1263static void
1264gpart_print_error(const char *errstr)
1265{
1266	char *errmsg;
1267	int error;
1268
1269	error = strtol(errstr, &errmsg, 0);
1270	if (errmsg != errstr) {
1271		while (errmsg[0] == ' ')
1272			errmsg++;
1273		if (errmsg[0] != '\0')
1274			warnc(error, "%s", errmsg);
1275		else
1276			warnc(error, NULL);
1277	} else
1278		warnx("%s", errmsg);
1279}
1280
1281static void
1282gpart_issue(struct gctl_req *req, unsigned int fl __unused)
1283{
1284	char buf[4096];
1285	const char *errstr;
1286	int error, status;
1287
1288	if (gctl_get_int(req, "nargs") != 1)
1289		errx(EXIT_FAILURE, "Invalid number of arguments.");
1290	(void)gctl_delete_param(req, "nargs");
1291
1292	/* autofill parameters (if applicable). */
1293	error = gpart_autofill(req);
1294	if (error) {
1295		warnc(error, "autofill");
1296		status = EXIT_FAILURE;
1297		goto done;
1298	}
1299
1300	bzero(buf, sizeof(buf));
1301	gctl_rw_param(req, "output", sizeof(buf), buf);
1302	errstr = gctl_issue(req);
1303	if (errstr == NULL || errstr[0] == '\0') {
1304		if (buf[0] != '\0')
1305			printf("%s", buf);
1306		status = EXIT_SUCCESS;
1307		goto done;
1308	}
1309
1310	gpart_print_error(errstr);
1311	status = EXIT_FAILURE;
1312
1313 done:
1314	gctl_free(req);
1315	exit(status);
1316}
1317