geom_part.c revision 215671
11541Srgrimes/*-
21541Srgrimes * Copyright (c) 2007, 2008 Marcel Moolenaar
31541Srgrimes * All rights reserved.
41541Srgrimes *
51541Srgrimes * Redistribution and use in source and binary forms, with or without
61541Srgrimes * modification, are permitted provided that the following conditions
71541Srgrimes * are met:
81541Srgrimes * 1. Redistributions of source code must retain the above copyright
91541Srgrimes *    notice, this list of conditions and the following disclaimer.
101541Srgrimes * 2. Redistributions in binary form must reproduce the above copyright
111541Srgrimes *    notice, this list of conditions and the following disclaimer in the
121541Srgrimes *    documentation and/or other materials provided with the distribution.
131541Srgrimes *
141541Srgrimes * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
151541Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
161541Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
171541Srgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
181541Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
191541Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
201541Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
211541Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
221541Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
231541Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
241541Srgrimes * SUCH DAMAGE.
251541Srgrimes */
261541Srgrimes
271541Srgrimes#include <sys/cdefs.h>
281541Srgrimes__FBSDID("$FreeBSD: head/sbin/geom/class/part/geom_part.c 215671 2010-11-22 10:08:33Z ae $");
291541Srgrimes
301541Srgrimes#include <sys/stat.h>
311541Srgrimes#include <sys/vtoc.h>
321541Srgrimes
331541Srgrimes#include <assert.h>
342165Spaul#include <ctype.h>
351541Srgrimes#include <err.h>
361541Srgrimes#include <errno.h>
372165Spaul#include <fcntl.h>
382165Spaul#include <libgeom.h>
392165Spaul#include <libutil.h>
401541Srgrimes#include <paths.h>
411541Srgrimes#include <stdint.h>
421541Srgrimes#include <stdio.h>
431541Srgrimes#include <stdlib.h>
441541Srgrimes#include <limits.h>
451541Srgrimes#include <inttypes.h>
461541Srgrimes#include <string.h>
471541Srgrimes#include <strings.h>
481541Srgrimes#include <unistd.h>
491541Srgrimes
501541Srgrimes#include "core/geom.h"
511541Srgrimes#include "misc/subr.h"
521541Srgrimes
531541Srgrimes#ifdef STATIC_GEOM_CLASSES
541541Srgrimes#define	PUBSYM(x)	gpart_##x
551541Srgrimes#else
561541Srgrimes#define	PUBSYM(x)	x
571541Srgrimes#endif
581541Srgrimes
591541Srgrimesuint32_t PUBSYM(lib_version) = G_LIB_VERSION;
601541Srgrimesuint32_t PUBSYM(version) = 0;
611541Srgrimes
621541Srgrimesstatic char sstart[32];
631541Srgrimesstatic char ssize[32];
641541Srgrimes
651541Srgrimes#define	GPART_AUTOFILL	"*"
661541Srgrimes#define	GPART_FLAGS	"C"
671541Srgrimes
681541Srgrimes#define	GPART_PARAM_BOOTCODE	"bootcode"
691541Srgrimes#define	GPART_PARAM_INDEX	"index"
701541Srgrimes#define	GPART_PARAM_PARTCODE	"partcode"
711541Srgrimes
721541Srgrimesstatic struct gclass *find_class(struct gmesh *, const char *);
731541Srgrimesstatic struct ggeom * find_geom(struct gclass *, const char *);
741541Srgrimesstatic const char *find_geomcfg(struct ggeom *, const char *);
751541Srgrimesstatic const char *find_provcfg(struct gprovider *, const char *);
761541Srgrimesstatic struct gprovider *find_provider(struct ggeom *, off_t);
771541Srgrimesstatic const char *fmtsize(int64_t);
781541Srgrimesstatic int gpart_autofill(struct gctl_req *);
791541Srgrimesstatic int gpart_autofill_resize(struct gctl_req *);
801541Srgrimesstatic void gpart_bootcode(struct gctl_req *, unsigned int);
811541Srgrimesstatic void *gpart_bootfile_read(const char *, ssize_t *);
821541Srgrimesstatic void gpart_issue(struct gctl_req *, unsigned int);
831541Srgrimesstatic void gpart_show(struct gctl_req *, unsigned int);
841541Srgrimesstatic void gpart_show_geom(struct ggeom *, const char *);
851541Srgrimesstatic int gpart_show_hasopt(struct gctl_req *, const char *, const char *);
861541Srgrimesstatic void gpart_write_partcode(struct ggeom *, int, void *, ssize_t);
871541Srgrimesstatic void gpart_write_partcode_vtoc8(struct ggeom *, int, void *);
881541Srgrimesstatic void gpart_print_error(const char *);
891541Srgrimesstatic void gpart_backup(struct gctl_req *, unsigned int);
901541Srgrimesstatic void gpart_restore(struct gctl_req *, unsigned int);
911541Srgrimes
921541Srgrimesstruct g_command PUBSYM(class_commands)[] = {
931541Srgrimes	{ "add", 0, gpart_issue, {
941541Srgrimes		{ 'b', "start", GPART_AUTOFILL, G_TYPE_STRING },
951541Srgrimes		{ 's', "size", GPART_AUTOFILL, G_TYPE_STRING },
961541Srgrimes		{ 't', "type", NULL, G_TYPE_STRING },
971541Srgrimes		{ 'i', GPART_PARAM_INDEX, G_VAL_OPTIONAL, G_TYPE_NUMBER },
981541Srgrimes		{ 'l', "label", G_VAL_OPTIONAL, G_TYPE_STRING },
991541Srgrimes		{ 'f', "flags", GPART_FLAGS, G_TYPE_STRING },
1001541Srgrimes		G_OPT_SENTINEL },
1011541Srgrimes	    "[-b start] [-s size] -t type [-i index] [-l label] [-f flags] geom"
1021541Srgrimes	},
1031541Srgrimes	{ "backup", 0, gpart_backup, G_NULL_OPTS,
1041541Srgrimes	    "geom"
1051541Srgrimes	},
1061541Srgrimes	{ "bootcode", 0, gpart_bootcode, {
1071541Srgrimes		{ 'b', GPART_PARAM_BOOTCODE, G_VAL_OPTIONAL, G_TYPE_STRING },
1081541Srgrimes		{ 'p', GPART_PARAM_PARTCODE, G_VAL_OPTIONAL, G_TYPE_STRING },
1091541Srgrimes		{ 'i', GPART_PARAM_INDEX, G_VAL_OPTIONAL, G_TYPE_NUMBER },
1101541Srgrimes		{ 'f', "flags", GPART_FLAGS, G_TYPE_STRING },
1111541Srgrimes		G_OPT_SENTINEL },
1121541Srgrimes	    "[-b bootcode] [-p partcode] [-i index] [-f flags] geom"
1131541Srgrimes	},
1141541Srgrimes	{ "commit", 0, gpart_issue, G_NULL_OPTS,
1151541Srgrimes	    "geom"
1161541Srgrimes	},
1171541Srgrimes	{ "create", 0, gpart_issue, {
1181541Srgrimes		{ 's', "scheme", NULL, G_TYPE_STRING },
1191541Srgrimes		{ 'n', "entries", G_VAL_OPTIONAL, G_TYPE_NUMBER },
1201541Srgrimes		{ 'f', "flags", GPART_FLAGS, G_TYPE_STRING },
1211541Srgrimes		G_OPT_SENTINEL },
1221541Srgrimes	    "-s scheme [-n entries] [-f flags] provider"
1231541Srgrimes	},
1241541Srgrimes	{ "delete", 0, gpart_issue, {
1251541Srgrimes		{ 'i', GPART_PARAM_INDEX, NULL, G_TYPE_NUMBER },
1261541Srgrimes		{ 'f', "flags", GPART_FLAGS, G_TYPE_STRING },
1271541Srgrimes		G_OPT_SENTINEL },
1281541Srgrimes	    "-i index [-f flags] geom"
1291541Srgrimes	},
1301541Srgrimes	{ "destroy", 0, gpart_issue, {
1311541Srgrimes		{ 'F', "force", NULL, G_TYPE_BOOL },
1321541Srgrimes		{ 'f', "flags", GPART_FLAGS, G_TYPE_STRING },
1331541Srgrimes		G_OPT_SENTINEL },
1341541Srgrimes	    "[-F] [-f flags] geom"
1351541Srgrimes	},
1361541Srgrimes	{ "modify", 0, gpart_issue, {
1371541Srgrimes		{ 'i', GPART_PARAM_INDEX, NULL, G_TYPE_NUMBER },
1381541Srgrimes		{ 'l', "label", G_VAL_OPTIONAL, G_TYPE_STRING },
1391541Srgrimes		{ 't', "type", G_VAL_OPTIONAL, G_TYPE_STRING },
1401541Srgrimes		{ 'f', "flags", GPART_FLAGS, G_TYPE_STRING },
1411541Srgrimes		G_OPT_SENTINEL },
1421541Srgrimes	    "-i index [-l label] [-t type] [-f flags] geom"
1431541Srgrimes	},
1441541Srgrimes	{ "set", 0, gpart_issue, {
1451541Srgrimes		{ 'a', "attrib", NULL, G_TYPE_STRING },
1461541Srgrimes		{ 'i', GPART_PARAM_INDEX, NULL, G_TYPE_NUMBER },
1471541Srgrimes		{ 'f', "flags", GPART_FLAGS, G_TYPE_STRING },
1481541Srgrimes		G_OPT_SENTINEL },
1491541Srgrimes	    "-a attrib -i index [-f flags] geom"
1501541Srgrimes	},
1511541Srgrimes	{ "show", 0, gpart_show, {
1521541Srgrimes		{ 'l', "show_label", NULL, G_TYPE_BOOL },
1531541Srgrimes		{ 'r', "show_rawtype", NULL, G_TYPE_BOOL },
1541541Srgrimes		G_OPT_SENTINEL },
1551541Srgrimes	    "[-lr] [geom ...]"
1561541Srgrimes	},
1571541Srgrimes	{ "undo", 0, gpart_issue, G_NULL_OPTS,
1581541Srgrimes	    "geom"
1591541Srgrimes	},
1601541Srgrimes	{ "unset", 0, gpart_issue, {
1611541Srgrimes		{ 'a', "attrib", NULL, G_TYPE_STRING },
1621541Srgrimes		{ 'i', GPART_PARAM_INDEX, NULL, G_TYPE_NUMBER },
1631541Srgrimes		{ 'f', "flags", GPART_FLAGS, G_TYPE_STRING },
1641541Srgrimes		G_OPT_SENTINEL },
1651541Srgrimes	    "-a attrib -i index [-f flags] geom"
1661541Srgrimes	},
1671541Srgrimes	{ "resize", 0, gpart_issue, {
1681541Srgrimes		{ 's', "size", GPART_AUTOFILL, G_TYPE_STRING },
1691541Srgrimes		{ 'i', GPART_PARAM_INDEX, NULL, G_TYPE_NUMBER },
1701541Srgrimes		{ 'f', "flags", GPART_FLAGS, G_TYPE_STRING },
1711541Srgrimes		G_OPT_SENTINEL },
1721541Srgrimes	    "[-s size] -i index [-f flags] geom"
1731541Srgrimes	},
1741541Srgrimes	{ "restore", 0, gpart_restore, {
1751541Srgrimes		{ 'F', "force", NULL, G_TYPE_BOOL },
1761541Srgrimes		{ 'l', "restore_labels", NULL, G_TYPE_BOOL },
1771541Srgrimes		{ 'f', "flags", GPART_FLAGS, G_TYPE_STRING },
1781541Srgrimes		G_OPT_SENTINEL },
1791541Srgrimes	    "[-lF] [-f flags] provider [...]"
1801541Srgrimes	},
1811541Srgrimes	{ "recover", 0, gpart_issue, {
1821541Srgrimes		{ 'f', "flags", GPART_FLAGS, G_TYPE_STRING },
1831541Srgrimes		G_OPT_SENTINEL },
1841541Srgrimes	    "[-f flags] geom"
1851541Srgrimes	},
1861541Srgrimes	G_CMD_SENTINEL
1871541Srgrimes};
1881541Srgrimes
1891541Srgrimesstatic struct gclass *
1901541Srgrimesfind_class(struct gmesh *mesh, const char *name)
1911541Srgrimes{
1921541Srgrimes	struct gclass *classp;
1931541Srgrimes
1941541Srgrimes	LIST_FOREACH(classp, &mesh->lg_class, lg_class) {
1952112Swollman		if (strcmp(classp->lg_name, name) == 0)
1961541Srgrimes			return (classp);
1971541Srgrimes	}
1981541Srgrimes	return (NULL);
1991541Srgrimes}
2001541Srgrimes
2011541Srgrimesstatic struct ggeom *
2021541Srgrimesfind_geom(struct gclass *classp, const char *name)
2031541Srgrimes{
2041541Srgrimes	struct ggeom *gp;
2051541Srgrimes
2061541Srgrimes	if (strncmp(name, _PATH_DEV, sizeof(_PATH_DEV) - 1) == 0)
2071541Srgrimes		name += sizeof(_PATH_DEV) - 1;
2081541Srgrimes	LIST_FOREACH(gp, &classp->lg_geom, lg_geom) {
2091541Srgrimes		if (strcmp(gp->lg_name, name) == 0)
2101541Srgrimes			return (gp);
2111541Srgrimes	}
2122165Spaul	return (NULL);
2132165Spaul}
214
215static const char *
216find_geomcfg(struct ggeom *gp, const char *cfg)
217{
218	struct gconfig *gc;
219
220	LIST_FOREACH(gc, &gp->lg_config, lg_config) {
221		if (!strcmp(gc->lg_name, cfg))
222			return (gc->lg_val);
223	}
224	return (NULL);
225}
226
227static const char *
228find_provcfg(struct gprovider *pp, const char *cfg)
229{
230	struct gconfig *gc;
231
232	LIST_FOREACH(gc, &pp->lg_config, lg_config) {
233		if (!strcmp(gc->lg_name, cfg))
234			return (gc->lg_val);
235	}
236	return (NULL);
237}
238
239static struct gprovider *
240find_provider(struct ggeom *gp, off_t minsector)
241{
242	struct gprovider *pp, *bestpp;
243	const char *s;
244	off_t sector, bestsector;
245
246	bestpp = NULL;
247	bestsector = 0;
248	LIST_FOREACH(pp, &gp->lg_provider, lg_provider) {
249		s = find_provcfg(pp, "start");
250		if (s == NULL) {
251			s = find_provcfg(pp, "offset");
252			sector =
253			    (off_t)strtoimax(s, NULL, 0) / pp->lg_sectorsize;
254		} else
255			sector = (off_t)strtoimax(s, NULL, 0);
256
257		if (sector < minsector)
258			continue;
259		if (bestpp != NULL && sector >= bestsector)
260			continue;
261
262		bestpp = pp;
263		bestsector = sector;
264	}
265	return (bestpp);
266}
267
268static const char *
269fmtsize(int64_t rawsz)
270{
271	static char buf[5];
272
273	humanize_number(buf, sizeof(buf), rawsz, "", HN_AUTOSCALE,
274	    HN_B | HN_NOSPACE | HN_DECIMAL);
275	return (buf);
276}
277
278static const char *
279fmtattrib(struct gprovider *pp)
280{
281	static char buf[128];
282	struct gconfig *gc;
283	u_int idx;
284
285	buf[0] = '\0';
286	idx = 0;
287	LIST_FOREACH(gc, &pp->lg_config, lg_config) {
288		if (strcmp(gc->lg_name, "attrib") != 0)
289			continue;
290		idx += snprintf(buf + idx, sizeof(buf) - idx, "%s%s",
291		    (idx == 0) ? " [" : ",", gc->lg_val);
292	}
293	if (idx > 0)
294		snprintf(buf + idx, sizeof(buf) - idx, "] ");
295	return (buf);
296}
297
298static int
299gpart_autofill_resize(struct gctl_req *req)
300{
301	struct gmesh mesh;
302	struct gclass *cp;
303	struct ggeom *gp;
304	struct gprovider *pp;
305	off_t last, size, start, new_size;
306	off_t lba, new_lba;
307	const char *s;
308	int error, idx;
309
310	idx = (int)gctl_get_intmax(req, GPART_PARAM_INDEX);
311	if (idx < 1)
312		errx(EXIT_FAILURE, "invalid partition index");
313
314	error = geom_gettree(&mesh);
315	if (error)
316		return (error);
317	s = gctl_get_ascii(req, "class");
318	if (s == NULL)
319		abort();
320	cp = find_class(&mesh, s);
321	if (cp == NULL)
322		errx(EXIT_FAILURE, "Class %s not found.", s);
323	s = gctl_get_ascii(req, "arg0");
324	if (s == NULL)
325		abort();
326	gp = find_geom(cp, s);
327	if (gp == NULL)
328		errx(EXIT_FAILURE, "No such geom: %s.", s);
329	pp = LIST_FIRST(&gp->lg_consumer)->lg_provider;
330	if (pp == NULL)
331		errx(EXIT_FAILURE, "Provider for geom %s not found.", s);
332
333	s = gctl_get_ascii(req, "size");
334	if (*s == '*')
335		new_size = 0;
336	else {
337		error = g_parse_lba(s, pp->lg_sectorsize, &new_size);
338		if (error)
339			errc(EXIT_FAILURE, error, "Invalid size param");
340		/* no autofill necessary. */
341		goto done;
342	}
343
344	last = (off_t)strtoimax(find_geomcfg(gp, "last"), NULL, 0);
345	LIST_FOREACH(pp, &gp->lg_provider, lg_provider) {
346		s = find_provcfg(pp, "index");
347		if (s == NULL)
348			continue;
349		if (atoi(s) == idx)
350			break;
351	}
352	if (pp == NULL)
353		errx(EXIT_FAILURE, "invalid partition index");
354
355	s = find_provcfg(pp, "start");
356	if (s == NULL) {
357		s = find_provcfg(pp, "offset");
358		start = (off_t)strtoimax(s, NULL, 0) / pp->lg_sectorsize;
359	} else
360		start = (off_t)strtoimax(s, NULL, 0);
361	s = find_provcfg(pp, "end");
362	if (s == NULL) {
363		s = find_provcfg(pp, "length");
364		lba = start +
365		    (off_t)strtoimax(s, NULL, 0) / pp->lg_sectorsize;
366	} else
367		lba = (off_t)strtoimax(s, NULL, 0) + 1;
368
369	if (lba > last) {
370		geom_deletetree(&mesh);
371		return (ENOSPC);
372	}
373	size = lba - start;
374	pp = find_provider(gp, lba);
375	if (pp == NULL)
376		new_size = last - start + 1;
377	else {
378		s = find_provcfg(pp, "start");
379		if (s == NULL) {
380			s = find_provcfg(pp, "offset");
381			new_lba =
382			    (off_t)strtoimax(s, NULL, 0) / pp->lg_sectorsize;
383		} else
384			new_lba = (off_t)strtoimax(s, NULL, 0);
385		/*
386		 * Is there any free space between current and
387		 * next providers?
388		 */
389		if (new_lba > lba)
390			new_size = new_lba - start;
391		else {
392			geom_deletetree(&mesh);
393			return (ENOSPC);
394		}
395	}
396done:
397	snprintf(ssize, sizeof(ssize), "%jd", (intmax_t)new_size);
398	gctl_change_param(req, "size", -1, ssize);
399	geom_deletetree(&mesh);
400	return (0);
401}
402
403static int
404gpart_autofill(struct gctl_req *req)
405{
406	struct gmesh mesh;
407	struct gclass *cp;
408	struct ggeom *gp;
409	struct gprovider *pp;
410	off_t first, last;
411	off_t size, start;
412	off_t lba, len;
413	uintmax_t grade;
414	const char *s;
415	int error, has_size, has_start;
416
417	s = gctl_get_ascii(req, "verb");
418	if (strcmp(s, "resize") == 0)
419		return gpart_autofill_resize(req);
420	if (strcmp(s, "add") != 0)
421		return (0);
422
423	error = geom_gettree(&mesh);
424	if (error)
425		return (error);
426	s = gctl_get_ascii(req, "class");
427	if (s == NULL)
428		abort();
429	cp = find_class(&mesh, s);
430	if (cp == NULL)
431		errx(EXIT_FAILURE, "Class %s not found.", s);
432	s = gctl_get_ascii(req, "arg0");
433	if (s == NULL)
434		abort();
435	gp = find_geom(cp, s);
436	if (gp == NULL)
437		errx(EXIT_FAILURE, "No such geom: %s.", s);
438	pp = LIST_FIRST(&gp->lg_consumer)->lg_provider;
439	if (pp == NULL)
440		errx(EXIT_FAILURE, "Provider for geom %s not found.", s);
441
442	s = gctl_get_ascii(req, "size");
443	has_size = (*s == '*') ? 0 : 1;
444	size = 0;
445	if (has_size) {
446		error = g_parse_lba(s, pp->lg_sectorsize, &size);
447		if (error)
448			errc(EXIT_FAILURE, error, "Invalid size param");
449	}
450
451	s = gctl_get_ascii(req, "start");
452	has_start = (*s == '*') ? 0 : 1;
453	start = 0ULL;
454	if (has_start) {
455		error = g_parse_lba(s, pp->lg_sectorsize, &start);
456		if (error)
457			errc(EXIT_FAILURE, error, "Invalid start param");
458	}
459
460	/* No autofill necessary. */
461	if (has_size && has_start)
462		goto done;
463
464	first = (off_t)strtoimax(find_geomcfg(gp, "first"), NULL, 0);
465	last = (off_t)strtoimax(find_geomcfg(gp, "last"), NULL, 0);
466	grade = ~0ULL;
467	while ((pp = find_provider(gp, first)) != NULL) {
468		s = find_provcfg(pp, "start");
469		if (s == NULL) {
470			s = find_provcfg(pp, "offset");
471			lba = (off_t)strtoimax(s, NULL, 0) / pp->lg_sectorsize;
472		} else
473			lba = (off_t)strtoimax(s, NULL, 0);
474
475		if (first < lba) {
476			/* Free space [first, lba> */
477			len = lba - first;
478			if (has_size) {
479				if (len >= size &&
480				    (uintmax_t)(len - size) < grade) {
481					start = first;
482					grade = len - size;
483				}
484			} else if (has_start) {
485				if (start >= first && start < lba) {
486					size = lba - start;
487					grade = start - first;
488				}
489			} else {
490				if (grade == ~0ULL || len > size) {
491					start = first;
492					size = len;
493					grade = 0;
494				}
495			}
496		}
497
498		s = find_provcfg(pp, "end");
499		if (s == NULL) {
500			s = find_provcfg(pp, "length");
501			first = lba +
502			    (off_t)strtoimax(s, NULL, 0) / pp->lg_sectorsize;
503		} else
504			first = (off_t)strtoimax(s, NULL, 0) + 1;
505	}
506	if (first <= last) {
507		/* Free space [first-last] */
508		len = last - first + 1;
509		if (has_size) {
510			if (len >= size &&
511			    (uintmax_t)(len - size) < grade) {
512				start = first;
513				grade = len - size;
514			}
515		} else if (has_start) {
516			if (start >= first && start <= last) {
517				size = last - start + 1;
518				grade = start - first;
519			}
520		} else {
521			if (grade == ~0ULL || len > size) {
522				start = first;
523				size = len;
524				grade = 0;
525			}
526		}
527	}
528
529	if (grade == ~0ULL) {
530		geom_deletetree(&mesh);
531		return (ENOSPC);
532	}
533
534done:
535	snprintf(ssize, sizeof(ssize), "%jd", (intmax_t)size);
536	gctl_change_param(req, "size", -1, ssize);
537	snprintf(sstart, sizeof(sstart), "%jd", (intmax_t)start);
538	gctl_change_param(req, "start", -1, sstart);
539	geom_deletetree(&mesh);
540	return (0);
541}
542
543static void
544gpart_show_geom(struct ggeom *gp, const char *element)
545{
546	struct gprovider *pp;
547	const char *s, *scheme;
548	off_t first, last, sector, end;
549	off_t length, secsz;
550	int idx, wblocks, wname;
551
552	scheme = find_geomcfg(gp, "scheme");
553	s = find_geomcfg(gp, "first");
554	first = (off_t)strtoimax(s, NULL, 0);
555	s = find_geomcfg(gp, "last");
556	last = (off_t)strtoimax(s, NULL, 0);
557	wblocks = strlen(s);
558	s = find_geomcfg(gp, "state");
559	if (s != NULL && *s != 'C')
560		s = NULL;
561	wname = strlen(gp->lg_name);
562	pp = LIST_FIRST(&gp->lg_consumer)->lg_provider;
563	secsz = pp->lg_sectorsize;
564	printf("=>%*jd  %*jd  %*s  %s  (%s)%s\n",
565	    wblocks, (intmax_t)first, wblocks, (intmax_t)(last - first + 1),
566	    wname, gp->lg_name,
567	    scheme, fmtsize(pp->lg_mediasize),
568	    s ? " [CORRUPT]": "");
569
570	while ((pp = find_provider(gp, first)) != NULL) {
571		s = find_provcfg(pp, "start");
572		if (s == NULL) {
573			s = find_provcfg(pp, "offset");
574			sector = (off_t)strtoimax(s, NULL, 0) / secsz;
575		} else
576			sector = (off_t)strtoimax(s, NULL, 0);
577
578		s = find_provcfg(pp, "end");
579		if (s == NULL) {
580			s = find_provcfg(pp, "length");
581			length = (off_t)strtoimax(s, NULL, 0) / secsz;
582			end = sector + length - 1;
583		} else {
584			end = (off_t)strtoimax(s, NULL, 0);
585			length = end - sector + 1;
586		}
587		s = find_provcfg(pp, "index");
588		idx = atoi(s);
589		if (first < sector) {
590			printf("  %*jd  %*jd  %*s  - free -  (%s)\n",
591			    wblocks, (intmax_t)first, wblocks,
592			    (intmax_t)(sector - first), wname, "",
593			    fmtsize((sector - first) * secsz));
594		}
595		printf("  %*jd  %*jd  %*d  %s %s (%s)\n",
596		    wblocks, (intmax_t)sector, wblocks, (intmax_t)length,
597		    wname, idx, find_provcfg(pp, element),
598		    fmtattrib(pp), fmtsize(pp->lg_mediasize));
599		first = end + 1;
600	}
601	if (first <= last) {
602		length = last - first + 1;
603		printf("  %*jd  %*jd  %*s  - free -  (%s)\n",
604		    wblocks, (intmax_t)first, wblocks, (intmax_t)length,
605		    wname, "",
606		    fmtsize(length * secsz));
607	}
608	printf("\n");
609}
610
611static int
612gpart_show_hasopt(struct gctl_req *req, const char *opt, const char *elt)
613{
614
615	if (!gctl_get_int(req, opt))
616		return (0);
617
618	if (elt != NULL)
619		errx(EXIT_FAILURE, "-l and -r are mutually exclusive");
620
621	return (1);
622}
623
624static void
625gpart_show(struct gctl_req *req, unsigned int fl __unused)
626{
627	struct gmesh mesh;
628	struct gclass *classp;
629	struct ggeom *gp;
630	const char *element, *name;
631	int error, i, nargs;
632
633	element = NULL;
634	if (gpart_show_hasopt(req, "show_label", element))
635		element = "label";
636	if (gpart_show_hasopt(req, "show_rawtype", element))
637		element = "rawtype";
638	if (element == NULL)
639		element = "type";
640
641	name = gctl_get_ascii(req, "class");
642	if (name == NULL)
643		abort();
644	error = geom_gettree(&mesh);
645	if (error != 0)
646		errc(EXIT_FAILURE, error, "Cannot get GEOM tree");
647	classp = find_class(&mesh, name);
648	if (classp == NULL) {
649		geom_deletetree(&mesh);
650		errx(EXIT_FAILURE, "Class %s not found.", name);
651	}
652	nargs = gctl_get_int(req, "nargs");
653	if (nargs > 0) {
654		for (i = 0; i < nargs; i++) {
655			name = gctl_get_ascii(req, "arg%d", i);
656			gp = find_geom(classp, name);
657			if (gp != NULL)
658				gpart_show_geom(gp, element);
659			else
660				errx(EXIT_FAILURE, "No such geom: %s.", name);
661		}
662	} else {
663		LIST_FOREACH(gp, &classp->lg_geom, lg_geom) {
664			gpart_show_geom(gp, element);
665		}
666	}
667	geom_deletetree(&mesh);
668}
669
670static void
671gpart_backup(struct gctl_req *req, unsigned int fl __unused)
672{
673	struct gmesh mesh;
674	struct gclass *classp;
675	struct gprovider *pp;
676	struct ggeom *gp;
677	const char *s, *scheme;
678	off_t sector, end;
679	off_t length, secsz;
680	int error, i, windex, wblocks, wtype;
681
682	if (gctl_get_int(req, "nargs") != 1)
683		errx(EXIT_FAILURE, "Invalid number of arguments.");
684	error = geom_gettree(&mesh);
685	if (error != 0)
686		errc(EXIT_FAILURE, error, "Cannot get GEOM tree");
687	s = gctl_get_ascii(req, "class");
688	if (s == NULL)
689		abort();
690	classp = find_class(&mesh, s);
691	if (classp == NULL) {
692		geom_deletetree(&mesh);
693		errx(EXIT_FAILURE, "Class %s not found.", s);
694	}
695	s = gctl_get_ascii(req, "arg0");
696	if (s == NULL)
697		abort();
698	gp = find_geom(classp, s);
699	if (gp == NULL)
700		errx(EXIT_FAILURE, "No such geom: %s.", s);
701	scheme = find_geomcfg(gp, "scheme");
702	if (scheme == NULL)
703		abort();
704	pp = LIST_FIRST(&gp->lg_consumer)->lg_provider;
705	secsz = pp->lg_sectorsize;
706	s = find_geomcfg(gp, "last");
707	wblocks = strlen(s);
708	wtype = 0;
709	LIST_FOREACH(pp, &gp->lg_provider, lg_provider) {
710		s = find_provcfg(pp, "type");
711		i = strlen(s);
712		if (i > wtype)
713			wtype = i;
714	}
715	s = find_geomcfg(gp, "entries");
716	windex = strlen(s);
717	printf("%s %s\n", scheme, s);
718	LIST_FOREACH(pp, &gp->lg_provider, lg_provider) {
719		s = find_provcfg(pp, "start");
720		if (s == NULL) {
721			s = find_provcfg(pp, "offset");
722			sector = (off_t)strtoimax(s, NULL, 0) / secsz;
723		} else
724			sector = (off_t)strtoimax(s, NULL, 0);
725
726		s = find_provcfg(pp, "end");
727		if (s == NULL) {
728			s = find_provcfg(pp, "length");
729			length = (off_t)strtoimax(s, NULL, 0) / secsz;
730		} else {
731			end = (off_t)strtoimax(s, NULL, 0);
732			length = end - sector + 1;
733		}
734		s = find_provcfg(pp, "label");
735		printf("%-*s %*s %*jd %*jd %s %s\n",
736		    windex, find_provcfg(pp, "index"),
737		    wtype, find_provcfg(pp, "type"),
738		    wblocks, (intmax_t)sector,
739		    wblocks, (intmax_t)length,
740		    (s != NULL) ? s: "", fmtattrib(pp));
741	}
742	geom_deletetree(&mesh);
743}
744
745static int
746skip_line(const char *p)
747{
748
749	while (*p != '\0') {
750		if (*p == '#')
751			return (1);
752		if (isspace(*p) == 0)
753			return (0);
754		p++;
755	}
756	return (1);
757}
758
759static void
760gpart_restore(struct gctl_req *req, unsigned int fl __unused)
761{
762	struct gmesh mesh;
763	struct gclass *classp;
764	struct gctl_req *r;
765	struct ggeom *gp;
766	const char *s, *flags, *errstr, *label;
767	char **ap, *argv[6], line[BUFSIZ], *pline;
768	int error, forced, i, l, nargs, created, rl;
769	intmax_t n;
770
771	nargs = gctl_get_int(req, "nargs");
772	if (nargs < 1)
773		errx(EXIT_FAILURE, "Invalid number of arguments.");
774
775	forced = gctl_get_int(req, "force");
776	flags = gctl_get_ascii(req, "flags");
777	rl = gctl_get_int(req, "restore_labels");
778	s = gctl_get_ascii(req, "class");
779	if (s == NULL)
780		abort();
781	error = geom_gettree(&mesh);
782	if (error != 0)
783		errc(EXIT_FAILURE, error, "Cannot get GEOM tree");
784	classp = find_class(&mesh, s);
785	if (classp == NULL) {
786		geom_deletetree(&mesh);
787		errx(EXIT_FAILURE, "Class %s not found.", s);
788	}
789	if (forced) {
790		/* destroy existent partition table before restore */
791		for (i = 0; i < nargs; i++) {
792			s = gctl_get_ascii(req, "arg%d", i);
793			gp = find_geom(classp, s);
794			if (gp != NULL) {
795				r = gctl_get_handle();
796				gctl_ro_param(r, "class", -1,
797				    classp->lg_name);
798				gctl_ro_param(r, "verb", -1, "destroy");
799				gctl_ro_param(r, "flags", -1, "restore");
800				gctl_ro_param(r, "force", sizeof(forced),
801				    &forced);
802				gctl_ro_param(r, "arg0", -1, s);
803				errstr = gctl_issue(r);
804				if (errstr != NULL && errstr[0] != '\0') {
805					gpart_print_error(errstr);
806					gctl_free(r);
807					goto backout;
808				}
809				gctl_free(r);
810			}
811		}
812	}
813	created = 0;
814	while (fgets(line, sizeof(line) - 1, stdin)) {
815		/* Format of backup entries:
816		 * <scheme name> <number of entries>
817		 * <index> <type> <start> <size> [label] ['['attrib[,attrib]']']
818		 */
819		pline = (char *)line;
820		pline[strlen(line) - 1] = 0;
821		if (skip_line(pline))
822			continue;
823		for (ap = argv;
824		    (*ap = strsep(&pline, " \t")) != NULL;)
825			if (**ap != '\0' && ++ap >= &argv[6])
826				break;
827		l = ap - &argv[0];
828		label = pline = NULL;
829		if (l == 1 || l == 2) { /* create table */
830			if (created)
831				errx(EXIT_FAILURE, "Incorrect backup format.");
832			if (l == 2)
833				n = strtoimax(argv[1], NULL, 0);
834			for (i = 0; i < nargs; i++) {
835				s = gctl_get_ascii(req, "arg%d", i);
836				r = gctl_get_handle();
837				gctl_ro_param(r, "class", -1,
838				    classp->lg_name);
839				gctl_ro_param(r, "verb", -1, "create");
840				gctl_ro_param(r, "scheme", -1, argv[0]);
841				if (l == 2)
842					gctl_ro_param(r, "entries",
843					    sizeof(n), &n);
844				gctl_ro_param(r, "flags", -1, "restore");
845				gctl_ro_param(r, "arg0", -1, s);
846				errstr = gctl_issue(r);
847				if (errstr != NULL && errstr[0] != '\0') {
848					gpart_print_error(errstr);
849					gctl_free(r);
850					goto backout;
851				}
852				gctl_free(r);
853			}
854			created = 1;
855			continue;
856		} else if (l < 4 || created == 0)
857			errx(EXIT_FAILURE, "Incorrect backup format.");
858		else if (l == 5) {
859			if (strchr(argv[4], '[') == NULL)
860				label = argv[4];
861			else
862				pline = argv[4];
863		} else if (l == 6) {
864			label = argv[4];
865			pline = argv[5];
866		}
867		/* Add partitions to each table */
868		for (i = 0; i < nargs; i++) {
869			s = gctl_get_ascii(req, "arg%d", i);
870			r = gctl_get_handle();
871			n = strtoimax(argv[0], NULL, 0);
872			gctl_ro_param(r, "class", -1, classp->lg_name);
873			gctl_ro_param(r, "verb", -1, "add");
874			gctl_ro_param(r, "flags", -1, "restore");
875			gctl_ro_param(r, GPART_PARAM_INDEX, sizeof(n), &n);
876			gctl_ro_param(r, "type", -1, argv[1]);
877			gctl_ro_param(r, "start", -1, argv[2]);
878			gctl_ro_param(r, "size", -1, argv[3]);
879			if (rl != 0 && label != NULL)
880				gctl_ro_param(r, "label", -1, argv[4]);
881			gctl_ro_param(r, "arg0", -1, s);
882			error = gpart_autofill(r);
883			if (error != 0)
884				errc(EXIT_FAILURE, error, "autofill");
885			errstr = gctl_issue(r);
886			if (errstr != NULL && errstr[0] != '\0') {
887				gpart_print_error(errstr);
888				gctl_free(r);
889				goto backout;
890			}
891			gctl_free(r);
892		}
893		if (pline == NULL || *pline != '[')
894			continue;
895		/* set attributes */
896		pline++;
897		for (ap = argv;
898		    (*ap = strsep(&pline, ",]")) != NULL;)
899			if (**ap != '\0' && ++ap >= &argv[6])
900				break;
901		for (i = 0; i < nargs; i++) {
902			l = ap - &argv[0];
903			s = gctl_get_ascii(req, "arg%d", i);
904			while (l > 0) {
905				r = gctl_get_handle();
906				gctl_ro_param(r, "class", -1, classp->lg_name);
907				gctl_ro_param(r, "verb", -1, "set");
908				gctl_ro_param(r, "flags", -1, "restore");
909				gctl_ro_param(r, GPART_PARAM_INDEX,
910				    sizeof(n), &n);
911				gctl_ro_param(r, "attrib", -1, argv[--l]);
912				gctl_ro_param(r, "arg0", -1, s);
913				errstr = gctl_issue(r);
914				if (errstr != NULL && errstr[0] != '\0') {
915					gpart_print_error(errstr);
916					gctl_free(r);
917					goto backout;
918				}
919				gctl_free(r);
920			}
921		}
922	}
923	/* commit changes if needed */
924	if (strchr(flags, 'C') != NULL) {
925		for (i = 0; i < nargs; i++) {
926			s = gctl_get_ascii(req, "arg%d", i);
927			r = gctl_get_handle();
928			gctl_ro_param(r, "class", -1, classp->lg_name);
929			gctl_ro_param(r, "verb", -1, "commit");
930			gctl_ro_param(r, "arg0", -1, s);
931			errstr = gctl_issue(r);
932			if (errstr != NULL && errstr[0] != '\0') {
933				gpart_print_error(errstr);
934				gctl_free(r);
935				goto backout;
936			}
937			gctl_free(r);
938		}
939	}
940	gctl_free(req);
941	geom_deletetree(&mesh);
942	exit(EXIT_SUCCESS);
943
944backout:
945	for (i = 0; i < nargs; i++) {
946		s = gctl_get_ascii(req, "arg%d", i);
947		r = gctl_get_handle();
948		gctl_ro_param(r, "class", -1, classp->lg_name);
949		gctl_ro_param(r, "verb", -1, "undo");
950		gctl_ro_param(r, "arg0", -1, s);
951		gctl_issue(r);
952		gctl_free(r);
953	}
954	gctl_free(req);
955	geom_deletetree(&mesh);
956	exit(EXIT_FAILURE);
957}
958
959static void *
960gpart_bootfile_read(const char *bootfile, ssize_t *size)
961{
962	struct stat sb;
963	void *code;
964	int fd;
965
966	if (stat(bootfile, &sb) == -1)
967		err(EXIT_FAILURE, "%s", bootfile);
968	if (!S_ISREG(sb.st_mode))
969		errx(EXIT_FAILURE, "%s: not a regular file", bootfile);
970	if (sb.st_size == 0)
971		errx(EXIT_FAILURE, "%s: empty file", bootfile);
972	if (*size > 0 && sb.st_size > *size)
973		errx(EXIT_FAILURE, "%s: file too big (%zu limit)", bootfile,
974		    *size);
975
976	*size = sb.st_size;
977
978	fd = open(bootfile, O_RDONLY);
979	if (fd == -1)
980		err(EXIT_FAILURE, "%s", bootfile);
981	code = malloc(*size);
982	if (code == NULL)
983		err(EXIT_FAILURE, NULL);
984	if (read(fd, code, *size) != *size)
985		err(EXIT_FAILURE, "%s", bootfile);
986	close(fd);
987
988	return (code);
989}
990
991static void
992gpart_write_partcode(struct ggeom *gp, int idx, void *code, ssize_t size)
993{
994	char dsf[128];
995	struct gprovider *pp;
996	const char *s;
997	char *buf;
998	off_t bsize;
999	int fd;
1000
1001	LIST_FOREACH(pp, &gp->lg_provider, lg_provider) {
1002		s = find_provcfg(pp, "index");
1003		if (s == NULL)
1004			continue;
1005		if (atoi(s) == idx)
1006			break;
1007	}
1008
1009	if (pp != NULL) {
1010		snprintf(dsf, sizeof(dsf), "/dev/%s", pp->lg_name);
1011		fd = open(dsf, O_WRONLY);
1012		if (fd == -1)
1013			err(EXIT_FAILURE, "%s", dsf);
1014		if (lseek(fd, size, SEEK_SET) != size)
1015			errx(EXIT_FAILURE, "%s: not enough space", dsf);
1016		if (lseek(fd, 0, SEEK_SET) != 0)
1017			err(EXIT_FAILURE, "%s", dsf);
1018
1019		/*
1020		 * When writing to a disk device, the write must be
1021		 * sector aligned and not write to any partial sectors,
1022		 * so round up the buffer size to the next sector and zero it.
1023		 */
1024		bsize = (size + pp->lg_sectorsize - 1) /
1025		    pp->lg_sectorsize * pp->lg_sectorsize;
1026		buf = calloc(1, bsize);
1027		if (buf == NULL)
1028			err(EXIT_FAILURE, "%s", dsf);
1029		bcopy(code, buf, size);
1030		if (write(fd, buf, bsize) != bsize)
1031			err(EXIT_FAILURE, "%s", dsf);
1032		free(buf);
1033		close(fd);
1034	} else
1035		errx(EXIT_FAILURE, "invalid partition index");
1036}
1037
1038static void
1039gpart_write_partcode_vtoc8(struct ggeom *gp, int idx, void *code)
1040{
1041	char dsf[128];
1042	struct gprovider *pp;
1043	const char *s;
1044	int installed, fd;
1045
1046	installed = 0;
1047	LIST_FOREACH(pp, &gp->lg_provider, lg_provider) {
1048		s = find_provcfg(pp, "index");
1049		if (s == NULL)
1050			continue;
1051		if (idx != 0 && atoi(s) != idx)
1052			continue;
1053		snprintf(dsf, sizeof(dsf), "/dev/%s", pp->lg_name);
1054		if (pp->lg_sectorsize != sizeof(struct vtoc8))
1055			errx(EXIT_FAILURE, "%s: unexpected sector "
1056			    "size (%d)\n", dsf, pp->lg_sectorsize);
1057		fd = open(dsf, O_WRONLY);
1058		if (fd == -1)
1059			err(EXIT_FAILURE, "%s", dsf);
1060		if (lseek(fd, VTOC_BOOTSIZE, SEEK_SET) != VTOC_BOOTSIZE)
1061			continue;
1062		/*
1063		 * We ignore the first VTOC_BOOTSIZE bytes of boot code in
1064		 * order to avoid overwriting the label.
1065		 */
1066		if (lseek(fd, sizeof(struct vtoc8), SEEK_SET) !=
1067		    sizeof(struct vtoc8))
1068			err(EXIT_FAILURE, "%s", dsf);
1069		if (write(fd, (caddr_t)code + sizeof(struct vtoc8),
1070		    VTOC_BOOTSIZE - sizeof(struct vtoc8)) != VTOC_BOOTSIZE -
1071		    sizeof(struct vtoc8))
1072			err(EXIT_FAILURE, "%s", dsf);
1073		installed++;
1074		close(fd);
1075		if (idx != 0 && atoi(s) == idx)
1076			break;
1077	}
1078	if (installed == 0)
1079		errx(EXIT_FAILURE, "%s: no partitions", gp->lg_name);
1080}
1081
1082static void
1083gpart_bootcode(struct gctl_req *req, unsigned int fl)
1084{
1085	struct gmesh mesh;
1086	struct gclass *classp;
1087	struct ggeom *gp;
1088	const char *s;
1089	void *bootcode, *partcode;
1090	size_t bootsize, partsize;
1091	int error, idx, vtoc8;
1092
1093	if (gctl_has_param(req, GPART_PARAM_BOOTCODE)) {
1094		s = gctl_get_ascii(req, GPART_PARAM_BOOTCODE);
1095		bootsize = 800 * 1024;		/* Arbitrary limit. */
1096		bootcode = gpart_bootfile_read(s, &bootsize);
1097		error = gctl_change_param(req, GPART_PARAM_BOOTCODE, bootsize,
1098		    bootcode);
1099		if (error)
1100			errc(EXIT_FAILURE, error, "internal error");
1101	} else {
1102		bootcode = NULL;
1103		bootsize = 0;
1104	}
1105
1106	s = gctl_get_ascii(req, "class");
1107	if (s == NULL)
1108		abort();
1109	error = geom_gettree(&mesh);
1110	if (error != 0)
1111		errc(EXIT_FAILURE, error, "Cannot get GEOM tree");
1112	classp = find_class(&mesh, s);
1113	if (classp == NULL) {
1114		geom_deletetree(&mesh);
1115		errx(EXIT_FAILURE, "Class %s not found.", s);
1116	}
1117	s = gctl_get_ascii(req, "arg0");
1118	if (s == NULL)
1119		abort();
1120	gp = find_geom(classp, s);
1121	if (gp == NULL)
1122		errx(EXIT_FAILURE, "No such geom: %s.", s);
1123	s = find_geomcfg(gp, "scheme");
1124	vtoc8 = 0;
1125	if (strcmp(s, "VTOC8") == 0)
1126		vtoc8 = 1;
1127
1128	if (gctl_has_param(req, GPART_PARAM_PARTCODE)) {
1129		s = gctl_get_ascii(req, GPART_PARAM_PARTCODE);
1130		partsize = vtoc8 != 0 ? VTOC_BOOTSIZE : bootsize * 1024;
1131		partcode = gpart_bootfile_read(s, &partsize);
1132		error = gctl_delete_param(req, GPART_PARAM_PARTCODE);
1133		if (error)
1134			errc(EXIT_FAILURE, error, "internal error");
1135	} else {
1136		partcode = NULL;
1137		partsize = 0;
1138	}
1139
1140	if (gctl_has_param(req, GPART_PARAM_INDEX)) {
1141		if (partcode == NULL)
1142			errx(EXIT_FAILURE, "-i is only valid with -p");
1143		idx = (int)gctl_get_intmax(req, GPART_PARAM_INDEX);
1144		if (idx < 1)
1145			errx(EXIT_FAILURE, "invalid partition index");
1146		error = gctl_delete_param(req, GPART_PARAM_INDEX);
1147		if (error)
1148			errc(EXIT_FAILURE, error, "internal error");
1149	} else
1150		idx = 0;
1151
1152	if (partcode != NULL) {
1153		if (vtoc8 == 0) {
1154			if (idx == 0)
1155				errx(EXIT_FAILURE, "missing -i option");
1156			gpart_write_partcode(gp, idx, partcode, partsize);
1157		} else
1158			gpart_write_partcode_vtoc8(gp, idx, partcode);
1159	} else
1160		if (bootcode == NULL)
1161			errx(EXIT_FAILURE, "no -b nor -p");
1162
1163	if (bootcode != NULL)
1164		gpart_issue(req, fl);
1165
1166	geom_deletetree(&mesh);
1167}
1168
1169static void
1170gpart_print_error(const char *errstr)
1171{
1172	char *errmsg;
1173	int error;
1174
1175	error = strtol(errstr, &errmsg, 0);
1176	if (errmsg != errstr) {
1177		while (errmsg[0] == ' ')
1178			errmsg++;
1179		if (errmsg[0] != '\0')
1180			warnc(error, "%s", errmsg);
1181		else
1182			warnc(error, NULL);
1183	} else
1184		warnx("%s", errmsg);
1185}
1186
1187static void
1188gpart_issue(struct gctl_req *req, unsigned int fl __unused)
1189{
1190	char buf[4096];
1191	const char *errstr;
1192	int error, status;
1193
1194	if (gctl_get_int(req, "nargs") != 1)
1195		errx(EXIT_FAILURE, "Invalid number of arguments.");
1196	(void)gctl_delete_param(req, "nargs");
1197
1198	/* autofill parameters (if applicable). */
1199	error = gpart_autofill(req);
1200	if (error) {
1201		warnc(error, "autofill");
1202		status = EXIT_FAILURE;
1203		goto done;
1204	}
1205
1206	bzero(buf, sizeof(buf));
1207	gctl_rw_param(req, "output", sizeof(buf), buf);
1208	errstr = gctl_issue(req);
1209	if (errstr == NULL || errstr[0] == '\0') {
1210		if (buf[0] != '\0')
1211			printf("%s", buf);
1212		status = EXIT_SUCCESS;
1213		goto done;
1214	}
1215
1216	gpart_print_error(errstr);
1217	status = EXIT_FAILURE;
1218
1219 done:
1220	gctl_free(req);
1221	exit(status);
1222}
1223