geom_part.c revision 221363
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: head/sbin/geom/class/part/geom_part.c 221363 2011-05-03 07:33:39Z ae $");
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	    "[-a alignment] [-b start] [-s size] -t type [-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, NULL, 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	    "[-lrp] [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, NULL, 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	    "[-a alignment] [-s size] -i index [-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;
211
212	if (strncmp(name, _PATH_DEV, sizeof(_PATH_DEV) - 1) == 0)
213		name += sizeof(_PATH_DEV) - 1;
214	LIST_FOREACH(gp, &classp->lg_geom, lg_geom) {
215		if (strcmp(gp->lg_name, name) == 0)
216			return (gp);
217	}
218	return (NULL);
219}
220
221static const char *
222find_geomcfg(struct ggeom *gp, const char *cfg)
223{
224	struct gconfig *gc;
225
226	LIST_FOREACH(gc, &gp->lg_config, lg_config) {
227		if (!strcmp(gc->lg_name, cfg))
228			return (gc->lg_val);
229	}
230	return (NULL);
231}
232
233static const char *
234find_provcfg(struct gprovider *pp, const char *cfg)
235{
236	struct gconfig *gc;
237
238	LIST_FOREACH(gc, &pp->lg_config, lg_config) {
239		if (!strcmp(gc->lg_name, cfg))
240			return (gc->lg_val);
241	}
242	return (NULL);
243}
244
245static struct gprovider *
246find_provider(struct ggeom *gp, off_t minsector)
247{
248	struct gprovider *pp, *bestpp;
249	const char *s;
250	off_t sector, bestsector;
251
252	bestpp = NULL;
253	bestsector = 0;
254	LIST_FOREACH(pp, &gp->lg_provider, lg_provider) {
255		s = find_provcfg(pp, "start");
256		if (s == NULL) {
257			s = find_provcfg(pp, "offset");
258			sector =
259			    (off_t)strtoimax(s, NULL, 0) / pp->lg_sectorsize;
260		} else
261			sector = (off_t)strtoimax(s, NULL, 0);
262
263		if (sector < minsector)
264			continue;
265		if (bestpp != NULL && sector >= bestsector)
266			continue;
267
268		bestpp = pp;
269		bestsector = sector;
270	}
271	return (bestpp);
272}
273
274static const char *
275fmtsize(int64_t rawsz)
276{
277	static char buf[5];
278
279	humanize_number(buf, sizeof(buf), rawsz, "", HN_AUTOSCALE,
280	    HN_B | HN_NOSPACE | HN_DECIMAL);
281	return (buf);
282}
283
284static const char *
285fmtattrib(struct gprovider *pp)
286{
287	static char buf[128];
288	struct gconfig *gc;
289	u_int idx;
290
291	buf[0] = '\0';
292	idx = 0;
293	LIST_FOREACH(gc, &pp->lg_config, lg_config) {
294		if (strcmp(gc->lg_name, "attrib") != 0)
295			continue;
296		idx += snprintf(buf + idx, sizeof(buf) - idx, "%s%s",
297		    (idx == 0) ? " [" : ",", gc->lg_val);
298	}
299	if (idx > 0)
300		snprintf(buf + idx, sizeof(buf) - idx, "] ");
301	return (buf);
302}
303
304#define	ALIGNDOWN(d, a)		(-(a) & (d))
305#define	ALIGNUP(d, a)		(-(-(a) & -(d)))
306
307static int
308gpart_autofill_resize(struct gctl_req *req)
309{
310	struct gmesh mesh;
311	struct gclass *cp;
312	struct ggeom *gp;
313	struct gprovider *pp;
314	off_t last, size, start, new_size;
315	off_t lba, new_lba, alignment;
316	const char *s;
317	int error, idx;
318
319	idx = (int)gctl_get_intmax(req, GPART_PARAM_INDEX);
320	if (idx < 1)
321		errx(EXIT_FAILURE, "invalid partition index");
322
323	error = geom_gettree(&mesh);
324	if (error)
325		return (error);
326	s = gctl_get_ascii(req, "class");
327	if (s == NULL)
328		abort();
329	cp = find_class(&mesh, s);
330	if (cp == NULL)
331		errx(EXIT_FAILURE, "Class %s not found.", s);
332	s = gctl_get_ascii(req, "arg0");
333	if (s == NULL)
334		abort();
335	gp = find_geom(cp, s);
336	if (gp == NULL)
337		errx(EXIT_FAILURE, "No such geom: %s.", s);
338	pp = LIST_FIRST(&gp->lg_consumer)->lg_provider;
339	if (pp == NULL)
340		errx(EXIT_FAILURE, "Provider for geom %s not found.", s);
341
342	s = gctl_get_ascii(req, "alignment");
343	alignment = 1;
344	if (*s != '*') {
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	}
351	error = gctl_delete_param(req, "alignment");
352	if (error)
353		errc(EXIT_FAILURE, error, "internal error");
354
355	s = gctl_get_ascii(req, "size");
356	if (*s == '*')
357		new_size = 0;
358	else {
359		error = g_parse_lba(s, pp->lg_sectorsize, &new_size);
360		if (error)
361			errc(EXIT_FAILURE, error, "Invalid size param");
362		/* no autofill necessary. */
363		if (alignment == 1)
364			goto done;
365		if (new_size > alignment)
366			new_size = ALIGNDOWN(new_size, alignment);
367	}
368
369	last = (off_t)strtoimax(find_geomcfg(gp, "last"), NULL, 0);
370	last = ALIGNDOWN(last, alignment);
371	LIST_FOREACH(pp, &gp->lg_provider, lg_provider) {
372		s = find_provcfg(pp, "index");
373		if (s == NULL)
374			continue;
375		if (atoi(s) == idx)
376			break;
377	}
378	if (pp == NULL)
379		errx(EXIT_FAILURE, "invalid partition index");
380
381	s = find_provcfg(pp, "start");
382	if (s == NULL) {
383		s = find_provcfg(pp, "offset");
384		start = (off_t)strtoimax(s, NULL, 0) / pp->lg_sectorsize;
385	} else
386		start = (off_t)strtoimax(s, NULL, 0);
387	s = find_provcfg(pp, "end");
388	if (s == NULL) {
389		s = find_provcfg(pp, "length");
390		lba = start +
391		    (off_t)strtoimax(s, NULL, 0) / pp->lg_sectorsize;
392	} else
393		lba = (off_t)strtoimax(s, NULL, 0) + 1;
394
395	if (lba > last) {
396		geom_deletetree(&mesh);
397		return (ENOSPC);
398	}
399	size = lba - start;
400	pp = find_provider(gp, lba);
401	if (pp == NULL)
402		new_size = ALIGNDOWN(last - start + 1, alignment);
403	else {
404		s = find_provcfg(pp, "start");
405		if (s == NULL) {
406			s = find_provcfg(pp, "offset");
407			new_lba =
408			    (off_t)strtoimax(s, NULL, 0) / pp->lg_sectorsize;
409		} else
410			new_lba = (off_t)strtoimax(s, NULL, 0);
411		/*
412		 * Is there any free space between current and
413		 * next providers?
414		 */
415		new_lba = ALIGNUP(new_lba, alignment);
416		if (new_lba > lba)
417			new_size = new_lba - start;
418		else {
419			geom_deletetree(&mesh);
420			return (ENOSPC);
421		}
422	}
423done:
424	snprintf(ssize, sizeof(ssize), "%jd", (intmax_t)new_size);
425	gctl_change_param(req, "size", -1, ssize);
426	geom_deletetree(&mesh);
427	return (0);
428}
429
430static int
431gpart_autofill(struct gctl_req *req)
432{
433	struct gmesh mesh;
434	struct gclass *cp;
435	struct ggeom *gp;
436	struct gprovider *pp;
437	off_t first, last, a_first;
438	off_t size, start, a_lba;
439	off_t lba, len, alignment;
440	uintmax_t grade;
441	const char *s;
442	int error, has_size, has_start, has_alignment;
443
444	s = gctl_get_ascii(req, "verb");
445	if (strcmp(s, "resize") == 0)
446		return gpart_autofill_resize(req);
447	if (strcmp(s, "add") != 0)
448		return (0);
449
450	error = geom_gettree(&mesh);
451	if (error)
452		return (error);
453	s = gctl_get_ascii(req, "class");
454	if (s == NULL)
455		abort();
456	cp = find_class(&mesh, s);
457	if (cp == NULL)
458		errx(EXIT_FAILURE, "Class %s not found.", s);
459	s = gctl_get_ascii(req, "arg0");
460	if (s == NULL)
461		abort();
462	gp = find_geom(cp, s);
463	if (gp == NULL)
464		errx(EXIT_FAILURE, "No such geom: %s.", s);
465	pp = LIST_FIRST(&gp->lg_consumer)->lg_provider;
466	if (pp == NULL)
467		errx(EXIT_FAILURE, "Provider for geom %s not found.", s);
468
469	s = gctl_get_ascii(req, "alignment");
470	has_alignment = (*s == '*') ? 0 : 1;
471	alignment = 1;
472	if (has_alignment) {
473		error = g_parse_lba(s, pp->lg_sectorsize, &alignment);
474		if (error)
475			errc(EXIT_FAILURE, error, "Invalid alignment param");
476		if (alignment == 0)
477			errx(EXIT_FAILURE, "Invalid alignment param");
478	}
479	error = gctl_delete_param(req, "alignment");
480	if (error)
481		errc(EXIT_FAILURE, error, "internal error");
482
483	s = gctl_get_ascii(req, "size");
484	has_size = (*s == '*') ? 0 : 1;
485	size = 0;
486	if (has_size) {
487		error = g_parse_lba(s, pp->lg_sectorsize, &size);
488		if (error)
489			errc(EXIT_FAILURE, error, "Invalid size param");
490		if (size > alignment)
491			size = ALIGNDOWN(size, alignment);
492	}
493
494	s = gctl_get_ascii(req, "start");
495	has_start = (*s == '*') ? 0 : 1;
496	start = 0ULL;
497	if (has_start) {
498		error = g_parse_lba(s, pp->lg_sectorsize, &start);
499		if (error)
500			errc(EXIT_FAILURE, error, "Invalid start param");
501		start = ALIGNUP(start, alignment);
502	}
503
504	/* No autofill necessary. */
505	if (has_size && has_start && !has_alignment)
506		goto done;
507
508	first = (off_t)strtoimax(find_geomcfg(gp, "first"), NULL, 0);
509	last = (off_t)strtoimax(find_geomcfg(gp, "last"), NULL, 0);
510	grade = ~0ULL;
511	a_first = ALIGNUP(first, alignment);
512	last = ALIGNDOWN(last, alignment);
513	while ((pp = find_provider(gp, first)) != NULL) {
514		s = find_provcfg(pp, "start");
515		if (s == NULL) {
516			s = find_provcfg(pp, "offset");
517			lba = (off_t)strtoimax(s, NULL, 0) / pp->lg_sectorsize;
518		} else
519			lba = (off_t)strtoimax(s, NULL, 0);
520
521		a_lba = ALIGNDOWN(lba, alignment);
522		if (first < a_lba && a_first < a_lba) {
523			/* Free space [first, lba> */
524			len = a_lba - a_first;
525			if (has_size) {
526				if (len >= size &&
527				    (uintmax_t)(len - size) < grade) {
528					start = a_first;
529					grade = len - size;
530				}
531			} else if (has_start) {
532				if (start >= a_first && start < a_lba) {
533					size = a_lba - start;
534					grade = start - a_first;
535				}
536			} else {
537				if (grade == ~0ULL || len > size) {
538					start = a_first;
539					size = len;
540					grade = 0;
541				}
542			}
543		}
544
545		s = find_provcfg(pp, "end");
546		if (s == NULL) {
547			s = find_provcfg(pp, "length");
548			first = lba +
549			    (off_t)strtoimax(s, NULL, 0) / pp->lg_sectorsize;
550		} else
551			first = (off_t)strtoimax(s, NULL, 0) + 1;
552		a_first = ALIGNUP(first, alignment);
553	}
554	if (a_first <= last) {
555		/* Free space [first-last] */
556		len = ALIGNDOWN(last - a_first + 1, alignment);
557		if (has_size) {
558			if (len >= size &&
559			    (uintmax_t)(len - size) < grade) {
560				start = a_first;
561				grade = len - size;
562			}
563		} else if (has_start) {
564			if (start >= a_first && start <= last) {
565				size = ALIGNDOWN(last - start + 1, alignment);
566				grade = start - a_first;
567			}
568		} else {
569			if (grade == ~0ULL || len > size) {
570				start = a_first;
571				size = len;
572				grade = 0;
573			}
574		}
575	}
576
577	if (grade == ~0ULL) {
578		geom_deletetree(&mesh);
579		return (ENOSPC);
580	}
581
582done:
583	snprintf(ssize, sizeof(ssize), "%jd", (intmax_t)size);
584	gctl_change_param(req, "size", -1, ssize);
585	snprintf(sstart, sizeof(sstart), "%jd", (intmax_t)start);
586	gctl_change_param(req, "start", -1, sstart);
587	geom_deletetree(&mesh);
588	return (0);
589}
590
591static void
592gpart_show_geom(struct ggeom *gp, const char *element, int show_providers)
593{
594	struct gprovider *pp;
595	const char *s, *scheme;
596	off_t first, last, sector, end;
597	off_t length, secsz;
598	int idx, wblocks, wname, wmax;
599
600	scheme = find_geomcfg(gp, "scheme");
601	s = find_geomcfg(gp, "first");
602	first = (off_t)strtoimax(s, NULL, 0);
603	s = find_geomcfg(gp, "last");
604	last = (off_t)strtoimax(s, NULL, 0);
605	wblocks = strlen(s);
606	s = find_geomcfg(gp, "state");
607	if (s != NULL && *s != 'C')
608		s = NULL;
609	wmax = strlen(gp->lg_name);
610	if (show_providers) {
611		LIST_FOREACH(pp, &gp->lg_provider, lg_provider) {
612			wname = strlen(pp->lg_name);
613			if (wname > wmax)
614				wmax = wname;
615		}
616	}
617	wname = wmax;
618	pp = LIST_FIRST(&gp->lg_consumer)->lg_provider;
619	secsz = pp->lg_sectorsize;
620	printf("=>%*jd  %*jd  %*s  %s  (%s)%s\n",
621	    wblocks, (intmax_t)first, wblocks, (intmax_t)(last - first + 1),
622	    wname, gp->lg_name,
623	    scheme, fmtsize(pp->lg_mediasize),
624	    s ? " [CORRUPT]": "");
625
626	while ((pp = find_provider(gp, first)) != NULL) {
627		s = find_provcfg(pp, "start");
628		if (s == NULL) {
629			s = find_provcfg(pp, "offset");
630			sector = (off_t)strtoimax(s, NULL, 0) / secsz;
631		} else
632			sector = (off_t)strtoimax(s, NULL, 0);
633
634		s = find_provcfg(pp, "end");
635		if (s == NULL) {
636			s = find_provcfg(pp, "length");
637			length = (off_t)strtoimax(s, NULL, 0) / secsz;
638			end = sector + length - 1;
639		} else {
640			end = (off_t)strtoimax(s, NULL, 0);
641			length = end - sector + 1;
642		}
643		s = find_provcfg(pp, "index");
644		idx = atoi(s);
645		if (first < sector) {
646			printf("  %*jd  %*jd  %*s  - free -  (%s)\n",
647			    wblocks, (intmax_t)first, wblocks,
648			    (intmax_t)(sector - first), wname, "",
649			    fmtsize((sector - first) * secsz));
650		}
651		if (show_providers) {
652			printf("  %*jd  %*jd  %*s  %s %s (%s)\n",
653			    wblocks, (intmax_t)sector, wblocks,
654			    (intmax_t)length, wname, pp->lg_name,
655			    find_provcfg(pp, element), fmtattrib(pp),
656			    fmtsize(pp->lg_mediasize));
657		} else
658			printf("  %*jd  %*jd  %*d  %s %s (%s)\n",
659			    wblocks, (intmax_t)sector, wblocks,
660			    (intmax_t)length, wname, idx,
661			    find_provcfg(pp, element), fmtattrib(pp),
662			    fmtsize(pp->lg_mediasize));
663		first = end + 1;
664	}
665	if (first <= last) {
666		length = last - first + 1;
667		printf("  %*jd  %*jd  %*s  - free -  (%s)\n",
668		    wblocks, (intmax_t)first, wblocks, (intmax_t)length,
669		    wname, "",
670		    fmtsize(length * secsz));
671	}
672	printf("\n");
673}
674
675static int
676gpart_show_hasopt(struct gctl_req *req, const char *opt, const char *elt)
677{
678
679	if (!gctl_get_int(req, "%s", opt))
680		return (0);
681
682	if (elt != NULL)
683		errx(EXIT_FAILURE, "-l and -r are mutually exclusive");
684
685	return (1);
686}
687
688static void
689gpart_show(struct gctl_req *req, unsigned int fl __unused)
690{
691	struct gmesh mesh;
692	struct gclass *classp;
693	struct ggeom *gp;
694	const char *element, *name;
695	int error, i, nargs, show_providers;
696
697	element = NULL;
698	if (gpart_show_hasopt(req, "show_label", element))
699		element = "label";
700	if (gpart_show_hasopt(req, "show_rawtype", element))
701		element = "rawtype";
702	if (element == NULL)
703		element = "type";
704
705	name = gctl_get_ascii(req, "class");
706	if (name == NULL)
707		abort();
708	error = geom_gettree(&mesh);
709	if (error != 0)
710		errc(EXIT_FAILURE, error, "Cannot get GEOM tree");
711	classp = find_class(&mesh, name);
712	if (classp == NULL) {
713		geom_deletetree(&mesh);
714		errx(EXIT_FAILURE, "Class %s not found.", name);
715	}
716	show_providers = gctl_get_int(req, "show_providers");
717	nargs = gctl_get_int(req, "nargs");
718	if (nargs > 0) {
719		for (i = 0; i < nargs; i++) {
720			name = gctl_get_ascii(req, "arg%d", i);
721			gp = find_geom(classp, name);
722			if (gp != NULL)
723				gpart_show_geom(gp, element, show_providers);
724			else
725				errx(EXIT_FAILURE, "No such geom: %s.", name);
726		}
727	} else {
728		LIST_FOREACH(gp, &classp->lg_geom, lg_geom) {
729			gpart_show_geom(gp, element, show_providers);
730		}
731	}
732	geom_deletetree(&mesh);
733}
734
735static void
736gpart_backup(struct gctl_req *req, unsigned int fl __unused)
737{
738	struct gmesh mesh;
739	struct gclass *classp;
740	struct gprovider *pp;
741	struct ggeom *gp;
742	const char *s, *scheme;
743	off_t sector, end;
744	off_t length, secsz;
745	int error, i, windex, wblocks, wtype;
746
747	if (gctl_get_int(req, "nargs") != 1)
748		errx(EXIT_FAILURE, "Invalid number of arguments.");
749	error = geom_gettree(&mesh);
750	if (error != 0)
751		errc(EXIT_FAILURE, error, "Cannot get GEOM tree");
752	s = gctl_get_ascii(req, "class");
753	if (s == NULL)
754		abort();
755	classp = find_class(&mesh, s);
756	if (classp == NULL) {
757		geom_deletetree(&mesh);
758		errx(EXIT_FAILURE, "Class %s not found.", s);
759	}
760	s = gctl_get_ascii(req, "arg0");
761	if (s == NULL)
762		abort();
763	gp = find_geom(classp, s);
764	if (gp == NULL)
765		errx(EXIT_FAILURE, "No such geom: %s.", s);
766	scheme = find_geomcfg(gp, "scheme");
767	if (scheme == NULL)
768		abort();
769	pp = LIST_FIRST(&gp->lg_consumer)->lg_provider;
770	secsz = pp->lg_sectorsize;
771	s = find_geomcfg(gp, "last");
772	wblocks = strlen(s);
773	wtype = 0;
774	LIST_FOREACH(pp, &gp->lg_provider, lg_provider) {
775		s = find_provcfg(pp, "type");
776		i = strlen(s);
777		if (i > wtype)
778			wtype = i;
779	}
780	s = find_geomcfg(gp, "entries");
781	windex = strlen(s);
782	printf("%s %s\n", scheme, s);
783	LIST_FOREACH(pp, &gp->lg_provider, lg_provider) {
784		s = find_provcfg(pp, "start");
785		if (s == NULL) {
786			s = find_provcfg(pp, "offset");
787			sector = (off_t)strtoimax(s, NULL, 0) / secsz;
788		} else
789			sector = (off_t)strtoimax(s, NULL, 0);
790
791		s = find_provcfg(pp, "end");
792		if (s == NULL) {
793			s = find_provcfg(pp, "length");
794			length = (off_t)strtoimax(s, NULL, 0) / secsz;
795		} else {
796			end = (off_t)strtoimax(s, NULL, 0);
797			length = end - sector + 1;
798		}
799		s = find_provcfg(pp, "label");
800		printf("%-*s %*s %*jd %*jd %s %s\n",
801		    windex, find_provcfg(pp, "index"),
802		    wtype, find_provcfg(pp, "type"),
803		    wblocks, (intmax_t)sector,
804		    wblocks, (intmax_t)length,
805		    (s != NULL) ? s: "", fmtattrib(pp));
806	}
807	geom_deletetree(&mesh);
808}
809
810static int
811skip_line(const char *p)
812{
813
814	while (*p != '\0') {
815		if (*p == '#')
816			return (1);
817		if (isspace(*p) == 0)
818			return (0);
819		p++;
820	}
821	return (1);
822}
823
824static void
825gpart_sighndl(int sig __unused)
826{
827	undo_restore = 1;
828}
829
830static void
831gpart_restore(struct gctl_req *req, unsigned int fl __unused)
832{
833	struct gmesh mesh;
834	struct gclass *classp;
835	struct gctl_req *r;
836	struct ggeom *gp;
837	struct sigaction si_sa;
838	const char *s, *flags, *errstr, *label;
839	char **ap, *argv[6], line[BUFSIZ], *pline;
840	int error, forced, i, l, nargs, created, rl;
841	intmax_t n;
842
843	nargs = gctl_get_int(req, "nargs");
844	if (nargs < 1)
845		errx(EXIT_FAILURE, "Invalid number of arguments.");
846
847	forced = gctl_get_int(req, "force");
848	flags = gctl_get_ascii(req, "flags");
849	rl = gctl_get_int(req, "restore_labels");
850	s = gctl_get_ascii(req, "class");
851	if (s == NULL)
852		abort();
853	error = geom_gettree(&mesh);
854	if (error != 0)
855		errc(EXIT_FAILURE, error, "Cannot get GEOM tree");
856	classp = find_class(&mesh, s);
857	if (classp == NULL) {
858		geom_deletetree(&mesh);
859		errx(EXIT_FAILURE, "Class %s not found.", s);
860	}
861
862	sigemptyset(&si_sa.sa_mask);
863	si_sa.sa_flags = 0;
864	si_sa.sa_handler = gpart_sighndl;
865	if (sigaction(SIGINT, &si_sa, 0) == -1)
866		err(EXIT_FAILURE, "sigaction SIGINT");
867
868	if (forced) {
869		/* destroy existent partition table before restore */
870		for (i = 0; i < nargs; i++) {
871			s = gctl_get_ascii(req, "arg%d", i);
872			gp = find_geom(classp, s);
873			if (gp != NULL) {
874				r = gctl_get_handle();
875				gctl_ro_param(r, "class", -1,
876				    classp->lg_name);
877				gctl_ro_param(r, "verb", -1, "destroy");
878				gctl_ro_param(r, "flags", -1, "restore");
879				gctl_ro_param(r, "force", sizeof(forced),
880				    &forced);
881				gctl_ro_param(r, "arg0", -1, s);
882				errstr = gctl_issue(r);
883				if (errstr != NULL && errstr[0] != '\0') {
884					gpart_print_error(errstr);
885					gctl_free(r);
886					goto backout;
887				}
888				gctl_free(r);
889			}
890		}
891	}
892	created = 0;
893	while (undo_restore == 0 &&
894	    fgets(line, sizeof(line) - 1, stdin) != NULL) {
895		/* Format of backup entries:
896		 * <scheme name> <number of entries>
897		 * <index> <type> <start> <size> [label] ['['attrib[,attrib]']']
898		 */
899		pline = (char *)line;
900		pline[strlen(line) - 1] = 0;
901		if (skip_line(pline))
902			continue;
903		for (ap = argv;
904		    (*ap = strsep(&pline, " \t")) != NULL;)
905			if (**ap != '\0' && ++ap >= &argv[6])
906				break;
907		l = ap - &argv[0];
908		label = pline = NULL;
909		if (l == 1 || l == 2) { /* create table */
910			if (created)
911				errx(EXIT_FAILURE, "Incorrect backup format.");
912			if (l == 2)
913				n = strtoimax(argv[1], NULL, 0);
914			for (i = 0; i < nargs; i++) {
915				s = gctl_get_ascii(req, "arg%d", i);
916				r = gctl_get_handle();
917				gctl_ro_param(r, "class", -1,
918				    classp->lg_name);
919				gctl_ro_param(r, "verb", -1, "create");
920				gctl_ro_param(r, "scheme", -1, argv[0]);
921				if (l == 2)
922					gctl_ro_param(r, "entries",
923					    sizeof(n), &n);
924				gctl_ro_param(r, "flags", -1, "restore");
925				gctl_ro_param(r, "arg0", -1, s);
926				errstr = gctl_issue(r);
927				if (errstr != NULL && errstr[0] != '\0') {
928					gpart_print_error(errstr);
929					gctl_free(r);
930					goto backout;
931				}
932				gctl_free(r);
933			}
934			created = 1;
935			continue;
936		} else if (l < 4 || created == 0)
937			errx(EXIT_FAILURE, "Incorrect backup format.");
938		else if (l == 5) {
939			if (strchr(argv[4], '[') == NULL)
940				label = argv[4];
941			else
942				pline = argv[4];
943		} else if (l == 6) {
944			label = argv[4];
945			pline = argv[5];
946		}
947		/* Add partitions to each table */
948		for (i = 0; i < nargs; i++) {
949			s = gctl_get_ascii(req, "arg%d", i);
950			r = gctl_get_handle();
951			n = strtoimax(argv[0], NULL, 0);
952			gctl_ro_param(r, "class", -1, classp->lg_name);
953			gctl_ro_param(r, "verb", -1, "add");
954			gctl_ro_param(r, "flags", -1, "restore");
955			gctl_ro_param(r, GPART_PARAM_INDEX, sizeof(n), &n);
956			gctl_ro_param(r, "type", -1, argv[1]);
957			gctl_ro_param(r, "start", -1, argv[2]);
958			gctl_ro_param(r, "size", -1, argv[3]);
959			if (rl != 0 && label != NULL)
960				gctl_ro_param(r, "label", -1, argv[4]);
961			gctl_ro_param(r, "arg0", -1, s);
962			error = gpart_autofill(r);
963			if (error != 0)
964				errc(EXIT_FAILURE, error, "autofill");
965			errstr = gctl_issue(r);
966			if (errstr != NULL && errstr[0] != '\0') {
967				gpart_print_error(errstr);
968				gctl_free(r);
969				goto backout;
970			}
971			gctl_free(r);
972		}
973		if (pline == NULL || *pline != '[')
974			continue;
975		/* set attributes */
976		pline++;
977		for (ap = argv;
978		    (*ap = strsep(&pline, ",]")) != NULL;)
979			if (**ap != '\0' && ++ap >= &argv[6])
980				break;
981		for (i = 0; i < nargs; i++) {
982			l = ap - &argv[0];
983			s = gctl_get_ascii(req, "arg%d", i);
984			while (l > 0) {
985				r = gctl_get_handle();
986				gctl_ro_param(r, "class", -1, classp->lg_name);
987				gctl_ro_param(r, "verb", -1, "set");
988				gctl_ro_param(r, "flags", -1, "restore");
989				gctl_ro_param(r, GPART_PARAM_INDEX,
990				    sizeof(n), &n);
991				gctl_ro_param(r, "attrib", -1, argv[--l]);
992				gctl_ro_param(r, "arg0", -1, s);
993				errstr = gctl_issue(r);
994				if (errstr != NULL && errstr[0] != '\0') {
995					gpart_print_error(errstr);
996					gctl_free(r);
997					goto backout;
998				}
999				gctl_free(r);
1000			}
1001		}
1002	}
1003	if (undo_restore)
1004		goto backout;
1005	/* commit changes if needed */
1006	if (strchr(flags, 'C') != NULL) {
1007		for (i = 0; i < nargs; i++) {
1008			s = gctl_get_ascii(req, "arg%d", i);
1009			r = gctl_get_handle();
1010			gctl_ro_param(r, "class", -1, classp->lg_name);
1011			gctl_ro_param(r, "verb", -1, "commit");
1012			gctl_ro_param(r, "arg0", -1, s);
1013			errstr = gctl_issue(r);
1014			if (errstr != NULL && errstr[0] != '\0') {
1015				gpart_print_error(errstr);
1016				gctl_free(r);
1017				goto backout;
1018			}
1019			gctl_free(r);
1020		}
1021	}
1022	gctl_free(req);
1023	geom_deletetree(&mesh);
1024	exit(EXIT_SUCCESS);
1025
1026backout:
1027	for (i = 0; i < nargs; i++) {
1028		s = gctl_get_ascii(req, "arg%d", i);
1029		r = gctl_get_handle();
1030		gctl_ro_param(r, "class", -1, classp->lg_name);
1031		gctl_ro_param(r, "verb", -1, "undo");
1032		gctl_ro_param(r, "arg0", -1, s);
1033		gctl_issue(r);
1034		gctl_free(r);
1035	}
1036	gctl_free(req);
1037	geom_deletetree(&mesh);
1038	exit(EXIT_FAILURE);
1039}
1040
1041static void *
1042gpart_bootfile_read(const char *bootfile, ssize_t *size)
1043{
1044	struct stat sb;
1045	void *code;
1046	int fd;
1047
1048	if (stat(bootfile, &sb) == -1)
1049		err(EXIT_FAILURE, "%s", bootfile);
1050	if (!S_ISREG(sb.st_mode))
1051		errx(EXIT_FAILURE, "%s: not a regular file", bootfile);
1052	if (sb.st_size == 0)
1053		errx(EXIT_FAILURE, "%s: empty file", bootfile);
1054	if (*size > 0 && sb.st_size > *size)
1055		errx(EXIT_FAILURE, "%s: file too big (%zu limit)", bootfile,
1056		    *size);
1057
1058	*size = sb.st_size;
1059
1060	fd = open(bootfile, O_RDONLY);
1061	if (fd == -1)
1062		err(EXIT_FAILURE, "%s", bootfile);
1063	code = malloc(*size);
1064	if (code == NULL)
1065		err(EXIT_FAILURE, NULL);
1066	if (read(fd, code, *size) != *size)
1067		err(EXIT_FAILURE, "%s", bootfile);
1068	close(fd);
1069
1070	return (code);
1071}
1072
1073static void
1074gpart_write_partcode(struct ggeom *gp, int idx, void *code, ssize_t size)
1075{
1076	char dsf[128];
1077	struct gprovider *pp;
1078	const char *s;
1079	char *buf;
1080	off_t bsize;
1081	int fd;
1082
1083	LIST_FOREACH(pp, &gp->lg_provider, lg_provider) {
1084		s = find_provcfg(pp, "index");
1085		if (s == NULL)
1086			continue;
1087		if (atoi(s) == idx)
1088			break;
1089	}
1090
1091	if (pp != NULL) {
1092		snprintf(dsf, sizeof(dsf), "/dev/%s", pp->lg_name);
1093		fd = open(dsf, O_WRONLY);
1094		if (fd == -1)
1095			err(EXIT_FAILURE, "%s", dsf);
1096		if (lseek(fd, size, SEEK_SET) != size)
1097			errx(EXIT_FAILURE, "%s: not enough space", dsf);
1098		if (lseek(fd, 0, SEEK_SET) != 0)
1099			err(EXIT_FAILURE, "%s", dsf);
1100
1101		/*
1102		 * When writing to a disk device, the write must be
1103		 * sector aligned and not write to any partial sectors,
1104		 * so round up the buffer size to the next sector and zero it.
1105		 */
1106		bsize = (size + pp->lg_sectorsize - 1) /
1107		    pp->lg_sectorsize * pp->lg_sectorsize;
1108		buf = calloc(1, bsize);
1109		if (buf == NULL)
1110			err(EXIT_FAILURE, "%s", dsf);
1111		bcopy(code, buf, size);
1112		if (write(fd, buf, bsize) != bsize)
1113			err(EXIT_FAILURE, "%s", dsf);
1114		free(buf);
1115		close(fd);
1116	} else
1117		errx(EXIT_FAILURE, "invalid partition index");
1118}
1119
1120static void
1121gpart_write_partcode_vtoc8(struct ggeom *gp, int idx, void *code)
1122{
1123	char dsf[128];
1124	struct gprovider *pp;
1125	const char *s;
1126	int installed, fd;
1127
1128	installed = 0;
1129	LIST_FOREACH(pp, &gp->lg_provider, lg_provider) {
1130		s = find_provcfg(pp, "index");
1131		if (s == NULL)
1132			continue;
1133		if (idx != 0 && atoi(s) != idx)
1134			continue;
1135		snprintf(dsf, sizeof(dsf), "/dev/%s", pp->lg_name);
1136		if (pp->lg_sectorsize != sizeof(struct vtoc8))
1137			errx(EXIT_FAILURE, "%s: unexpected sector "
1138			    "size (%d)\n", dsf, pp->lg_sectorsize);
1139		fd = open(dsf, O_WRONLY);
1140		if (fd == -1)
1141			err(EXIT_FAILURE, "%s", dsf);
1142		if (lseek(fd, VTOC_BOOTSIZE, SEEK_SET) != VTOC_BOOTSIZE)
1143			continue;
1144		/*
1145		 * We ignore the first VTOC_BOOTSIZE bytes of boot code in
1146		 * order to avoid overwriting the label.
1147		 */
1148		if (lseek(fd, sizeof(struct vtoc8), SEEK_SET) !=
1149		    sizeof(struct vtoc8))
1150			err(EXIT_FAILURE, "%s", dsf);
1151		if (write(fd, (caddr_t)code + sizeof(struct vtoc8),
1152		    VTOC_BOOTSIZE - sizeof(struct vtoc8)) != VTOC_BOOTSIZE -
1153		    sizeof(struct vtoc8))
1154			err(EXIT_FAILURE, "%s", dsf);
1155		installed++;
1156		close(fd);
1157		if (idx != 0 && atoi(s) == idx)
1158			break;
1159	}
1160	if (installed == 0)
1161		errx(EXIT_FAILURE, "%s: no partitions", gp->lg_name);
1162}
1163
1164static void
1165gpart_bootcode(struct gctl_req *req, unsigned int fl)
1166{
1167	struct gmesh mesh;
1168	struct gclass *classp;
1169	struct ggeom *gp;
1170	const char *s;
1171	void *bootcode, *partcode;
1172	size_t bootsize, partsize;
1173	int error, idx, vtoc8;
1174
1175	if (gctl_has_param(req, GPART_PARAM_BOOTCODE)) {
1176		s = gctl_get_ascii(req, GPART_PARAM_BOOTCODE);
1177		bootsize = 800 * 1024;		/* Arbitrary limit. */
1178		bootcode = gpart_bootfile_read(s, &bootsize);
1179		error = gctl_change_param(req, GPART_PARAM_BOOTCODE, bootsize,
1180		    bootcode);
1181		if (error)
1182			errc(EXIT_FAILURE, error, "internal error");
1183	} else {
1184		bootcode = NULL;
1185		bootsize = 0;
1186	}
1187
1188	s = gctl_get_ascii(req, "class");
1189	if (s == NULL)
1190		abort();
1191	error = geom_gettree(&mesh);
1192	if (error != 0)
1193		errc(EXIT_FAILURE, error, "Cannot get GEOM tree");
1194	classp = find_class(&mesh, s);
1195	if (classp == NULL) {
1196		geom_deletetree(&mesh);
1197		errx(EXIT_FAILURE, "Class %s not found.", s);
1198	}
1199	if (gctl_get_int(req, "nargs") != 1)
1200		errx(EXIT_FAILURE, "Invalid number of arguments.");
1201	s = gctl_get_ascii(req, "arg0");
1202	if (s == NULL)
1203		abort();
1204	gp = find_geom(classp, s);
1205	if (gp == NULL)
1206		errx(EXIT_FAILURE, "No such geom: %s.", s);
1207	s = find_geomcfg(gp, "scheme");
1208	vtoc8 = 0;
1209	if (strcmp(s, "VTOC8") == 0)
1210		vtoc8 = 1;
1211
1212	if (gctl_has_param(req, GPART_PARAM_PARTCODE)) {
1213		s = gctl_get_ascii(req, GPART_PARAM_PARTCODE);
1214		partsize = vtoc8 != 0 ? VTOC_BOOTSIZE : bootsize * 1024;
1215		partcode = gpart_bootfile_read(s, &partsize);
1216		error = gctl_delete_param(req, GPART_PARAM_PARTCODE);
1217		if (error)
1218			errc(EXIT_FAILURE, error, "internal error");
1219	} else {
1220		partcode = NULL;
1221		partsize = 0;
1222	}
1223
1224	if (gctl_has_param(req, GPART_PARAM_INDEX)) {
1225		if (partcode == NULL)
1226			errx(EXIT_FAILURE, "-i is only valid with -p");
1227		idx = (int)gctl_get_intmax(req, GPART_PARAM_INDEX);
1228		if (idx < 1)
1229			errx(EXIT_FAILURE, "invalid partition index");
1230		error = gctl_delete_param(req, GPART_PARAM_INDEX);
1231		if (error)
1232			errc(EXIT_FAILURE, error, "internal error");
1233	} else
1234		idx = 0;
1235
1236	if (partcode != NULL) {
1237		if (vtoc8 == 0) {
1238			if (idx == 0)
1239				errx(EXIT_FAILURE, "missing -i option");
1240			gpart_write_partcode(gp, idx, partcode, partsize);
1241		} else
1242			gpart_write_partcode_vtoc8(gp, idx, partcode);
1243	} else
1244		if (bootcode == NULL)
1245			errx(EXIT_FAILURE, "no -b nor -p");
1246
1247	if (bootcode != NULL)
1248		gpart_issue(req, fl);
1249
1250	geom_deletetree(&mesh);
1251}
1252
1253static void
1254gpart_print_error(const char *errstr)
1255{
1256	char *errmsg;
1257	int error;
1258
1259	error = strtol(errstr, &errmsg, 0);
1260	if (errmsg != errstr) {
1261		while (errmsg[0] == ' ')
1262			errmsg++;
1263		if (errmsg[0] != '\0')
1264			warnc(error, "%s", errmsg);
1265		else
1266			warnc(error, NULL);
1267	} else
1268		warnx("%s", errmsg);
1269}
1270
1271static void
1272gpart_issue(struct gctl_req *req, unsigned int fl __unused)
1273{
1274	char buf[4096];
1275	const char *errstr;
1276	int error, status;
1277
1278	if (gctl_get_int(req, "nargs") != 1)
1279		errx(EXIT_FAILURE, "Invalid number of arguments.");
1280	(void)gctl_delete_param(req, "nargs");
1281
1282	/* autofill parameters (if applicable). */
1283	error = gpart_autofill(req);
1284	if (error) {
1285		warnc(error, "autofill");
1286		status = EXIT_FAILURE;
1287		goto done;
1288	}
1289
1290	bzero(buf, sizeof(buf));
1291	gctl_rw_param(req, "output", sizeof(buf), buf);
1292	errstr = gctl_issue(req);
1293	if (errstr == NULL || errstr[0] == '\0') {
1294		if (buf[0] != '\0')
1295			printf("%s", buf);
1296		status = EXIT_SUCCESS;
1297		goto done;
1298	}
1299
1300	gpart_print_error(errstr);
1301	status = EXIT_FAILURE;
1302
1303 done:
1304	gctl_free(req);
1305	exit(status);
1306}
1307