geom_part.c revision 317440
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/11/sbin/geom/class/part/geom_part.c 317440 2017-04-26 14:50:06Z asomers $");
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 _Noreturn 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		if (g_device_path(s) == NULL) {
464			errx(EXIT_FAILURE, "No such geom %s.", s);
465		} else {
466			/*
467			 * We don't free memory allocated by g_device_path() as
468			 * we are about to exit.
469			 */
470			errx(EXIT_FAILURE,
471			    "No partitioning scheme found on geom %s. Create one first using 'gpart create'.",
472			    s);
473		}
474	}
475	pp = LIST_FIRST(&gp->lg_consumer)->lg_provider;
476	if (pp == NULL)
477		errx(EXIT_FAILURE, "Provider for geom %s not found.", s);
478
479	s = gctl_get_ascii(req, "alignment");
480	has_alignment = (*s == '*') ? 0 : 1;
481	alignment = 1;
482	if (has_alignment) {
483		error = g_parse_lba(s, pp->lg_sectorsize, &alignment);
484		if (error)
485			errc(EXIT_FAILURE, error, "Invalid alignment param");
486		if (alignment == 0)
487			errx(EXIT_FAILURE, "Invalid alignment param");
488	}
489	error = gctl_delete_param(req, "alignment");
490	if (error)
491		errc(EXIT_FAILURE, error, "internal error");
492
493	s = gctl_get_ascii(req, "size");
494	has_size = (*s == '*') ? 0 : 1;
495	size = 0;
496	if (has_size) {
497		error = g_parse_lba(s, pp->lg_sectorsize, &size);
498		if (error)
499			errc(EXIT_FAILURE, error, "Invalid size param");
500	}
501
502	s = gctl_get_ascii(req, "start");
503	has_start = (*s == '*') ? 0 : 1;
504	start = 0ULL;
505	if (has_start) {
506		error = g_parse_lba(s, pp->lg_sectorsize, &start);
507		if (error)
508			errc(EXIT_FAILURE, error, "Invalid start param");
509	}
510
511	/* No autofill necessary. */
512	if (has_size && has_start && !has_alignment)
513		goto done;
514
515	len = pp->lg_stripesize / pp->lg_sectorsize;
516	if (len > 0 && !has_alignment)
517		alignment = len;
518
519	/* Adjust parameters to stripeoffset */
520	offset = (pp->lg_stripeoffset / pp->lg_sectorsize) % alignment;
521	start = ALIGNUP(start + offset, alignment);
522	if (size > alignment)
523		size = ALIGNDOWN(size, alignment);
524
525	s = find_geomcfg(gp, "first");
526	if (s == NULL)
527		errx(EXIT_FAILURE, "Starting block not found for geom %s",
528		    gp->lg_name);
529	first = (off_t)strtoimax(s, NULL, 0);
530	s = find_geomcfg(gp, "last");
531	if (s == NULL)
532		errx(EXIT_FAILURE, "Final block not found for geom %s",
533		    gp->lg_name);
534	last = (off_t)strtoimax(s, NULL, 0);
535	grade = ~0ULL;
536	a_first = ALIGNUP(first + offset, alignment);
537	last = ALIGNDOWN(last + offset, alignment);
538	if (a_first < start)
539		a_first = start;
540	while ((pp = find_provider(gp, first)) != NULL) {
541		s = find_provcfg(pp, "start");
542		lba = (off_t)strtoimax(s, NULL, 0);
543		a_lba = ALIGNDOWN(lba + offset, alignment);
544		if (first < a_lba && a_first < a_lba) {
545			/* Free space [first, lba> */
546			len = a_lba - a_first;
547			if (has_size) {
548				if (len >= size &&
549				    (uintmax_t)(len - size) < grade) {
550					start = a_first;
551					grade = len - size;
552				}
553			} else if (has_start) {
554				if (start >= a_first && start < a_lba) {
555					size = a_lba - start;
556					grade = start - a_first;
557				}
558			} else {
559				if (grade == ~0ULL || len > size) {
560					start = a_first;
561					size = len;
562					grade = 0;
563				}
564			}
565		}
566
567		s = find_provcfg(pp, "end");
568		first = (off_t)strtoimax(s, NULL, 0) + 1;
569		if (first + offset > a_first)
570			a_first = ALIGNUP(first + offset, alignment);
571	}
572	if (a_first <= last) {
573		/* Free space [first-last] */
574		len = ALIGNDOWN(last - a_first + 1, alignment);
575		if (has_size) {
576			if (len >= size &&
577			    (uintmax_t)(len - size) < grade) {
578				start = a_first;
579				grade = len - size;
580			}
581		} else if (has_start) {
582			if (start >= a_first && start <= last) {
583				size = ALIGNDOWN(last - start + 1, alignment);
584				grade = start - a_first;
585			}
586		} else {
587			if (grade == ~0ULL || len > size) {
588				start = a_first;
589				size = len;
590				grade = 0;
591			}
592		}
593	}
594	if (grade == ~0ULL) {
595		geom_deletetree(&mesh);
596		return (ENOSPC);
597	}
598	start -= offset;	/* Return back to real offset */
599done:
600	snprintf(ssize, sizeof(ssize), "%jd", (intmax_t)size);
601	gctl_change_param(req, "size", -1, ssize);
602	snprintf(sstart, sizeof(sstart), "%jd", (intmax_t)start);
603	gctl_change_param(req, "start", -1, sstart);
604	geom_deletetree(&mesh);
605	return (0);
606}
607
608static void
609gpart_show_geom(struct ggeom *gp, const char *element, int show_providers)
610{
611	struct gprovider *pp;
612	const char *s, *scheme;
613	off_t first, last, sector, end;
614	off_t length, secsz;
615	int idx, wblocks, wname, wmax;
616
617	if (find_geomcfg(gp, "wither"))
618		return;
619	scheme = find_geomcfg(gp, "scheme");
620	if (scheme == NULL)
621		errx(EXIT_FAILURE, "Scheme not found for geom %s", gp->lg_name);
622	s = find_geomcfg(gp, "first");
623	if (s == NULL)
624		errx(EXIT_FAILURE, "Starting block not found for geom %s",
625		    gp->lg_name);
626	first = (off_t)strtoimax(s, NULL, 0);
627	s = find_geomcfg(gp, "last");
628	if (s == NULL)
629		errx(EXIT_FAILURE, "Final block not found for geom %s",
630		    gp->lg_name);
631	last = (off_t)strtoimax(s, NULL, 0);
632	wblocks = strlen(s);
633	s = find_geomcfg(gp, "state");
634	if (s == NULL)
635		errx(EXIT_FAILURE, "State not found for geom %s", gp->lg_name);
636	if (s != NULL && *s != 'C')
637		s = NULL;
638	wmax = strlen(gp->lg_name);
639	if (show_providers) {
640		LIST_FOREACH(pp, &gp->lg_provider, lg_provider) {
641			wname = strlen(pp->lg_name);
642			if (wname > wmax)
643				wmax = wname;
644		}
645	}
646	wname = wmax;
647	pp = LIST_FIRST(&gp->lg_consumer)->lg_provider;
648	secsz = pp->lg_sectorsize;
649	printf("=>%*jd  %*jd  %*s  %s  (%s)%s\n",
650	    wblocks, (intmax_t)first, wblocks, (intmax_t)(last - first + 1),
651	    wname, gp->lg_name,
652	    scheme, fmtsize(pp->lg_mediasize),
653	    s ? " [CORRUPT]": "");
654
655	while ((pp = find_provider(gp, first)) != NULL) {
656		s = find_provcfg(pp, "start");
657		sector = (off_t)strtoimax(s, NULL, 0);
658
659		s = find_provcfg(pp, "end");
660		end = (off_t)strtoimax(s, NULL, 0);
661		length = end - sector + 1;
662
663		s = find_provcfg(pp, "index");
664		idx = atoi(s);
665		if (first < sector) {
666			printf("  %*jd  %*jd  %*s  - free -  (%s)\n",
667			    wblocks, (intmax_t)first, wblocks,
668			    (intmax_t)(sector - first), wname, "",
669			    fmtsize((sector - first) * secsz));
670		}
671		if (show_providers) {
672			printf("  %*jd  %*jd  %*s  %s %s (%s)\n",
673			    wblocks, (intmax_t)sector, wblocks,
674			    (intmax_t)length, wname, pp->lg_name,
675			    find_provcfg(pp, element), fmtattrib(pp),
676			    fmtsize(pp->lg_mediasize));
677		} else
678			printf("  %*jd  %*jd  %*d  %s %s (%s)\n",
679			    wblocks, (intmax_t)sector, wblocks,
680			    (intmax_t)length, wname, idx,
681			    find_provcfg(pp, element), fmtattrib(pp),
682			    fmtsize(pp->lg_mediasize));
683		first = end + 1;
684	}
685	if (first <= last) {
686		length = last - first + 1;
687		printf("  %*jd  %*jd  %*s  - free -  (%s)\n",
688		    wblocks, (intmax_t)first, wblocks, (intmax_t)length,
689		    wname, "",
690		    fmtsize(length * secsz));
691	}
692	printf("\n");
693}
694
695static int
696gpart_show_hasopt(struct gctl_req *req, const char *opt, const char *elt)
697{
698
699	if (!gctl_get_int(req, "%s", opt))
700		return (0);
701
702	if (elt != NULL)
703		errx(EXIT_FAILURE, "-l and -r are mutually exclusive");
704
705	return (1);
706}
707
708static void
709gpart_show(struct gctl_req *req, unsigned int fl __unused)
710{
711	struct gmesh mesh;
712	struct gclass *classp;
713	struct ggeom *gp;
714	const char *element, *name;
715	int error, i, nargs, show_providers;
716
717	element = NULL;
718	if (gpart_show_hasopt(req, "show_label", element))
719		element = "label";
720	if (gpart_show_hasopt(req, "show_rawtype", element))
721		element = "rawtype";
722	if (element == NULL)
723		element = "type";
724
725	name = gctl_get_ascii(req, "class");
726	if (name == NULL)
727		abort();
728	error = geom_gettree(&mesh);
729	if (error != 0)
730		errc(EXIT_FAILURE, error, "Cannot get GEOM tree");
731	classp = find_class(&mesh, name);
732	if (classp == NULL) {
733		geom_deletetree(&mesh);
734		errx(EXIT_FAILURE, "Class %s not found.", name);
735	}
736	show_providers = gctl_get_int(req, "show_providers");
737	nargs = gctl_get_int(req, "nargs");
738	if (nargs > 0) {
739		for (i = 0; i < nargs; i++) {
740			name = gctl_get_ascii(req, "arg%d", i);
741			gp = find_geom(classp, name);
742			if (gp != NULL)
743				gpart_show_geom(gp, element, show_providers);
744			else
745				errx(EXIT_FAILURE, "No such geom: %s.", name);
746		}
747	} else {
748		LIST_FOREACH(gp, &classp->lg_geom, lg_geom) {
749			gpart_show_geom(gp, element, show_providers);
750		}
751	}
752	geom_deletetree(&mesh);
753}
754
755static void
756gpart_backup(struct gctl_req *req, unsigned int fl __unused)
757{
758	struct gmesh mesh;
759	struct gclass *classp;
760	struct gprovider *pp;
761	struct ggeom *gp;
762	const char *s, *scheme;
763	off_t sector, end;
764	off_t length;
765	int error, i, windex, wblocks, wtype;
766
767	if (gctl_get_int(req, "nargs") != 1)
768		errx(EXIT_FAILURE, "Invalid number of arguments.");
769	error = geom_gettree(&mesh);
770	if (error != 0)
771		errc(EXIT_FAILURE, error, "Cannot get GEOM tree");
772	s = gctl_get_ascii(req, "class");
773	if (s == NULL)
774		abort();
775	classp = find_class(&mesh, s);
776	if (classp == NULL) {
777		geom_deletetree(&mesh);
778		errx(EXIT_FAILURE, "Class %s not found.", s);
779	}
780	s = gctl_get_ascii(req, "arg0");
781	if (s == NULL)
782		abort();
783	gp = find_geom(classp, s);
784	if (gp == NULL)
785		errx(EXIT_FAILURE, "No such geom: %s.", s);
786	scheme = find_geomcfg(gp, "scheme");
787	if (scheme == NULL)
788		abort();
789	pp = LIST_FIRST(&gp->lg_consumer)->lg_provider;
790	s = find_geomcfg(gp, "last");
791	if (s == NULL)
792		abort();
793	wblocks = strlen(s);
794	wtype = 0;
795	LIST_FOREACH(pp, &gp->lg_provider, lg_provider) {
796		s = find_provcfg(pp, "type");
797		i = strlen(s);
798		if (i > wtype)
799			wtype = i;
800	}
801	s = find_geomcfg(gp, "entries");
802	if (s == NULL)
803		abort();
804	windex = strlen(s);
805	printf("%s %s\n", scheme, s);
806	LIST_FOREACH(pp, &gp->lg_provider, lg_provider) {
807		s = find_provcfg(pp, "start");
808		sector = (off_t)strtoimax(s, NULL, 0);
809
810		s = find_provcfg(pp, "end");
811		end = (off_t)strtoimax(s, NULL, 0);
812		length = end - sector + 1;
813
814		s = find_provcfg(pp, "label");
815		printf("%-*s %*s %*jd %*jd %s %s\n",
816		    windex, find_provcfg(pp, "index"),
817		    wtype, find_provcfg(pp, "type"),
818		    wblocks, (intmax_t)sector,
819		    wblocks, (intmax_t)length,
820		    (s != NULL) ? s: "", fmtattrib(pp));
821	}
822	geom_deletetree(&mesh);
823}
824
825static int
826skip_line(const char *p)
827{
828
829	while (*p != '\0') {
830		if (*p == '#')
831			return (1);
832		if (isspace(*p) == 0)
833			return (0);
834		p++;
835	}
836	return (1);
837}
838
839static void
840gpart_sighndl(int sig __unused)
841{
842	undo_restore = 1;
843}
844
845static void
846gpart_restore(struct gctl_req *req, unsigned int fl __unused)
847{
848	struct gmesh mesh;
849	struct gclass *classp;
850	struct gctl_req *r;
851	struct ggeom *gp;
852	struct sigaction si_sa;
853	const char *s, *flags, *errstr, *label;
854	char **ap, *argv[6], line[BUFSIZ], *pline;
855	int error, forced, i, l, nargs, created, rl;
856	intmax_t n;
857
858	nargs = gctl_get_int(req, "nargs");
859	if (nargs < 1)
860		errx(EXIT_FAILURE, "Invalid number of arguments.");
861
862	forced = gctl_get_int(req, "force");
863	flags = gctl_get_ascii(req, "flags");
864	rl = gctl_get_int(req, "restore_labels");
865	s = gctl_get_ascii(req, "class");
866	if (s == NULL)
867		abort();
868	error = geom_gettree(&mesh);
869	if (error != 0)
870		errc(EXIT_FAILURE, error, "Cannot get GEOM tree");
871	classp = find_class(&mesh, s);
872	if (classp == NULL) {
873		geom_deletetree(&mesh);
874		errx(EXIT_FAILURE, "Class %s not found.", s);
875	}
876
877	sigemptyset(&si_sa.sa_mask);
878	si_sa.sa_flags = 0;
879	si_sa.sa_handler = gpart_sighndl;
880	if (sigaction(SIGINT, &si_sa, 0) == -1)
881		err(EXIT_FAILURE, "sigaction SIGINT");
882
883	if (forced) {
884		/* destroy existent partition table before restore */
885		for (i = 0; i < nargs; i++) {
886			s = gctl_get_ascii(req, "arg%d", i);
887			gp = find_geom(classp, s);
888			if (gp != NULL) {
889				r = gctl_get_handle();
890				gctl_ro_param(r, "class", -1,
891				    classp->lg_name);
892				gctl_ro_param(r, "verb", -1, "destroy");
893				gctl_ro_param(r, "flags", -1, "restore");
894				gctl_ro_param(r, "force", sizeof(forced),
895				    &forced);
896				gctl_ro_param(r, "arg0", -1, s);
897				errstr = gctl_issue(r);
898				if (errstr != NULL && errstr[0] != '\0') {
899					gpart_print_error(errstr);
900					gctl_free(r);
901					goto backout;
902				}
903				gctl_free(r);
904			}
905		}
906	}
907	created = 0;
908	while (undo_restore == 0 &&
909	    fgets(line, sizeof(line) - 1, stdin) != NULL) {
910		/* Format of backup entries:
911		 * <scheme name> <number of entries>
912		 * <index> <type> <start> <size> [label] ['['attrib[,attrib]']']
913		 */
914		pline = (char *)line;
915		pline[strlen(line) - 1] = 0;
916		if (skip_line(pline))
917			continue;
918		for (ap = argv;
919		    (*ap = strsep(&pline, " \t")) != NULL;)
920			if (**ap != '\0' && ++ap >= &argv[6])
921				break;
922		l = ap - &argv[0];
923		label = pline = NULL;
924		if (l == 1 || l == 2) { /* create table */
925			if (created)
926				errx(EXIT_FAILURE, "Incorrect backup format.");
927			if (l == 2)
928				n = strtoimax(argv[1], NULL, 0);
929			for (i = 0; i < nargs; i++) {
930				s = gctl_get_ascii(req, "arg%d", i);
931				r = gctl_get_handle();
932				gctl_ro_param(r, "class", -1,
933				    classp->lg_name);
934				gctl_ro_param(r, "verb", -1, "create");
935				gctl_ro_param(r, "scheme", -1, argv[0]);
936				if (l == 2)
937					gctl_ro_param(r, "entries",
938					    sizeof(n), &n);
939				gctl_ro_param(r, "flags", -1, "restore");
940				gctl_ro_param(r, "arg0", -1, s);
941				errstr = gctl_issue(r);
942				if (errstr != NULL && errstr[0] != '\0') {
943					gpart_print_error(errstr);
944					gctl_free(r);
945					goto backout;
946				}
947				gctl_free(r);
948			}
949			created = 1;
950			continue;
951		} else if (l < 4 || created == 0)
952			errx(EXIT_FAILURE, "Incorrect backup format.");
953		else if (l == 5) {
954			if (strchr(argv[4], '[') == NULL)
955				label = argv[4];
956			else
957				pline = argv[4];
958		} else if (l == 6) {
959			label = argv[4];
960			pline = argv[5];
961		}
962		/* Add partitions to each table */
963		for (i = 0; i < nargs; i++) {
964			s = gctl_get_ascii(req, "arg%d", i);
965			r = gctl_get_handle();
966			n = strtoimax(argv[0], NULL, 0);
967			gctl_ro_param(r, "class", -1, classp->lg_name);
968			gctl_ro_param(r, "verb", -1, "add");
969			gctl_ro_param(r, "flags", -1, "restore");
970			gctl_ro_param(r, GPART_PARAM_INDEX, sizeof(n), &n);
971			gctl_ro_param(r, "type", -1, argv[1]);
972			gctl_ro_param(r, "start", -1, argv[2]);
973			gctl_ro_param(r, "size", -1, argv[3]);
974			if (rl != 0 && label != NULL)
975				gctl_ro_param(r, "label", -1, argv[4]);
976			gctl_ro_param(r, "alignment", -1, GPART_AUTOFILL);
977			gctl_ro_param(r, "arg0", -1, s);
978			error = gpart_autofill(r);
979			if (error != 0)
980				errc(EXIT_FAILURE, error, "autofill");
981			errstr = gctl_issue(r);
982			if (errstr != NULL && errstr[0] != '\0') {
983				gpart_print_error(errstr);
984				gctl_free(r);
985				goto backout;
986			}
987			gctl_free(r);
988		}
989		if (pline == NULL || *pline != '[')
990			continue;
991		/* set attributes */
992		pline++;
993		for (ap = argv;
994		    (*ap = strsep(&pline, ",]")) != NULL;)
995			if (**ap != '\0' && ++ap >= &argv[6])
996				break;
997		for (i = 0; i < nargs; i++) {
998			l = ap - &argv[0];
999			s = gctl_get_ascii(req, "arg%d", i);
1000			while (l > 0) {
1001				r = gctl_get_handle();
1002				gctl_ro_param(r, "class", -1, classp->lg_name);
1003				gctl_ro_param(r, "verb", -1, "set");
1004				gctl_ro_param(r, "flags", -1, "restore");
1005				gctl_ro_param(r, GPART_PARAM_INDEX,
1006				    sizeof(n), &n);
1007				gctl_ro_param(r, "attrib", -1, argv[--l]);
1008				gctl_ro_param(r, "arg0", -1, s);
1009				errstr = gctl_issue(r);
1010				if (errstr != NULL && errstr[0] != '\0') {
1011					gpart_print_error(errstr);
1012					gctl_free(r);
1013					goto backout;
1014				}
1015				gctl_free(r);
1016			}
1017		}
1018	}
1019	if (undo_restore)
1020		goto backout;
1021	/* commit changes if needed */
1022	if (strchr(flags, 'C') != NULL) {
1023		for (i = 0; i < nargs; i++) {
1024			s = gctl_get_ascii(req, "arg%d", i);
1025			r = gctl_get_handle();
1026			gctl_ro_param(r, "class", -1, classp->lg_name);
1027			gctl_ro_param(r, "verb", -1, "commit");
1028			gctl_ro_param(r, "arg0", -1, s);
1029			errstr = gctl_issue(r);
1030			if (errstr != NULL && errstr[0] != '\0') {
1031				gpart_print_error(errstr);
1032				gctl_free(r);
1033				goto backout;
1034			}
1035			gctl_free(r);
1036		}
1037	}
1038	gctl_free(req);
1039	geom_deletetree(&mesh);
1040	exit(EXIT_SUCCESS);
1041
1042backout:
1043	for (i = 0; i < nargs; i++) {
1044		s = gctl_get_ascii(req, "arg%d", i);
1045		r = gctl_get_handle();
1046		gctl_ro_param(r, "class", -1, classp->lg_name);
1047		gctl_ro_param(r, "verb", -1, "undo");
1048		gctl_ro_param(r, "arg0", -1, s);
1049		gctl_issue(r);
1050		gctl_free(r);
1051	}
1052	gctl_free(req);
1053	geom_deletetree(&mesh);
1054	exit(EXIT_FAILURE);
1055}
1056
1057static void *
1058gpart_bootfile_read(const char *bootfile, ssize_t *size)
1059{
1060	struct stat sb;
1061	void *code;
1062	int fd;
1063
1064	if (stat(bootfile, &sb) == -1)
1065		err(EXIT_FAILURE, "%s", bootfile);
1066	if (!S_ISREG(sb.st_mode))
1067		errx(EXIT_FAILURE, "%s: not a regular file", bootfile);
1068	if (sb.st_size == 0)
1069		errx(EXIT_FAILURE, "%s: empty file", bootfile);
1070	if (*size > 0 && sb.st_size > *size)
1071		errx(EXIT_FAILURE, "%s: file too big (%zu limit)", bootfile,
1072		    *size);
1073
1074	*size = sb.st_size;
1075
1076	fd = open(bootfile, O_RDONLY);
1077	if (fd == -1)
1078		err(EXIT_FAILURE, "%s", bootfile);
1079	code = malloc(*size);
1080	if (code == NULL)
1081		err(EXIT_FAILURE, NULL);
1082	if (read(fd, code, *size) != *size)
1083		err(EXIT_FAILURE, "%s", bootfile);
1084	close(fd);
1085
1086	return (code);
1087}
1088
1089static void
1090gpart_write_partcode(struct ggeom *gp, int idx, void *code, ssize_t size)
1091{
1092	char dsf[128];
1093	struct gprovider *pp;
1094	const char *s;
1095	char *buf;
1096	off_t bsize;
1097	int fd;
1098
1099	LIST_FOREACH(pp, &gp->lg_provider, lg_provider) {
1100		s = find_provcfg(pp, "index");
1101		if (s == NULL)
1102			continue;
1103		if (atoi(s) == idx)
1104			break;
1105	}
1106
1107	if (pp != NULL) {
1108		snprintf(dsf, sizeof(dsf), "/dev/%s", pp->lg_name);
1109		if (pp->lg_mediasize < size)
1110			errx(EXIT_FAILURE, "%s: not enough space", dsf);
1111		fd = open(dsf, O_WRONLY);
1112		if (fd == -1)
1113			err(EXIT_FAILURE, "%s", dsf);
1114		/*
1115		 * When writing to a disk device, the write must be
1116		 * sector aligned and not write to any partial sectors,
1117		 * so round up the buffer size to the next sector and zero it.
1118		 */
1119		bsize = (size + pp->lg_sectorsize - 1) /
1120		    pp->lg_sectorsize * pp->lg_sectorsize;
1121		buf = calloc(1, bsize);
1122		if (buf == NULL)
1123			err(EXIT_FAILURE, "%s", dsf);
1124		bcopy(code, buf, size);
1125		if (write(fd, buf, bsize) != bsize)
1126			err(EXIT_FAILURE, "%s", dsf);
1127		free(buf);
1128		close(fd);
1129		printf("partcode written to %s\n", pp->lg_name);
1130	} else
1131		errx(EXIT_FAILURE, "invalid partition index");
1132}
1133
1134static void
1135gpart_write_partcode_vtoc8(struct ggeom *gp, int idx, void *code)
1136{
1137	char dsf[128];
1138	struct gprovider *pp;
1139	const char *s;
1140	int installed, fd;
1141
1142	installed = 0;
1143	LIST_FOREACH(pp, &gp->lg_provider, lg_provider) {
1144		s = find_provcfg(pp, "index");
1145		if (s == NULL)
1146			continue;
1147		if (idx != 0 && atoi(s) != idx)
1148			continue;
1149		snprintf(dsf, sizeof(dsf), "/dev/%s", pp->lg_name);
1150		if (pp->lg_sectorsize != sizeof(struct vtoc8))
1151			errx(EXIT_FAILURE, "%s: unexpected sector "
1152			    "size (%d)\n", dsf, pp->lg_sectorsize);
1153		if (pp->lg_mediasize < VTOC_BOOTSIZE)
1154			continue;
1155		fd = open(dsf, O_WRONLY);
1156		if (fd == -1)
1157			err(EXIT_FAILURE, "%s", dsf);
1158		/*
1159		 * We ignore the first VTOC_BOOTSIZE bytes of boot code in
1160		 * order to avoid overwriting the label.
1161		 */
1162		if (lseek(fd, sizeof(struct vtoc8), SEEK_SET) !=
1163		    sizeof(struct vtoc8))
1164			err(EXIT_FAILURE, "%s", dsf);
1165		if (write(fd, (caddr_t)code + sizeof(struct vtoc8),
1166		    VTOC_BOOTSIZE - sizeof(struct vtoc8)) != VTOC_BOOTSIZE -
1167		    sizeof(struct vtoc8))
1168			err(EXIT_FAILURE, "%s", dsf);
1169		installed++;
1170		close(fd);
1171		if (idx != 0 && atoi(s) == idx)
1172			break;
1173	}
1174	if (installed == 0)
1175		errx(EXIT_FAILURE, "%s: no partitions", gp->lg_name);
1176	else
1177		printf("partcode written to %s\n",
1178		    idx != 0 ? pp->lg_name: gp->lg_name);
1179}
1180
1181static void
1182gpart_bootcode(struct gctl_req *req, unsigned int fl)
1183{
1184	struct gmesh mesh;
1185	struct gclass *classp;
1186	struct ggeom *gp;
1187	const char *s;
1188	void *bootcode, *partcode;
1189	size_t bootsize, partsize;
1190	int error, idx, vtoc8;
1191
1192	if (gctl_has_param(req, GPART_PARAM_BOOTCODE)) {
1193		s = gctl_get_ascii(req, GPART_PARAM_BOOTCODE);
1194		bootsize = 800 * 1024;		/* Arbitrary limit. */
1195		bootcode = gpart_bootfile_read(s, &bootsize);
1196		error = gctl_change_param(req, GPART_PARAM_BOOTCODE, bootsize,
1197		    bootcode);
1198		if (error)
1199			errc(EXIT_FAILURE, error, "internal error");
1200	} else
1201		bootcode = NULL;
1202
1203	s = gctl_get_ascii(req, "class");
1204	if (s == NULL)
1205		abort();
1206	error = geom_gettree(&mesh);
1207	if (error != 0)
1208		errc(EXIT_FAILURE, error, "Cannot get GEOM tree");
1209	classp = find_class(&mesh, s);
1210	if (classp == NULL) {
1211		geom_deletetree(&mesh);
1212		errx(EXIT_FAILURE, "Class %s not found.", s);
1213	}
1214	if (gctl_get_int(req, "nargs") != 1)
1215		errx(EXIT_FAILURE, "Invalid number of arguments.");
1216	s = gctl_get_ascii(req, "arg0");
1217	if (s == NULL)
1218		abort();
1219	gp = find_geom(classp, s);
1220	if (gp == NULL)
1221		errx(EXIT_FAILURE, "No such geom: %s.", s);
1222	s = find_geomcfg(gp, "scheme");
1223	if (s == NULL)
1224		errx(EXIT_FAILURE, "Scheme not found for geom %s", gp->lg_name);
1225	if (strcmp(s, "VTOC8") == 0)
1226		vtoc8 = 1;
1227	else
1228		vtoc8 = 0;
1229
1230	if (gctl_has_param(req, GPART_PARAM_PARTCODE)) {
1231		s = gctl_get_ascii(req, GPART_PARAM_PARTCODE);
1232		if (vtoc8 != 0)
1233			partsize = VTOC_BOOTSIZE;
1234		else
1235			partsize = 1024 * 1024;		/* Arbitrary limit. */
1236		partcode = gpart_bootfile_read(s, &partsize);
1237		error = gctl_delete_param(req, GPART_PARAM_PARTCODE);
1238		if (error)
1239			errc(EXIT_FAILURE, error, "internal error");
1240	} else
1241		partcode = NULL;
1242
1243	if (gctl_has_param(req, GPART_PARAM_INDEX)) {
1244		if (partcode == NULL)
1245			errx(EXIT_FAILURE, "-i is only valid with -p");
1246		idx = (int)gctl_get_intmax(req, GPART_PARAM_INDEX);
1247		if (idx < 1)
1248			errx(EXIT_FAILURE, "invalid partition index");
1249		error = gctl_delete_param(req, GPART_PARAM_INDEX);
1250		if (error)
1251			errc(EXIT_FAILURE, error, "internal error");
1252	} else
1253		idx = 0;
1254
1255	if (partcode != NULL) {
1256		if (vtoc8 == 0) {
1257			if (idx == 0)
1258				errx(EXIT_FAILURE, "missing -i option");
1259			gpart_write_partcode(gp, idx, partcode, partsize);
1260		} else {
1261			if (partsize != VTOC_BOOTSIZE)
1262				errx(EXIT_FAILURE, "invalid bootcode");
1263			gpart_write_partcode_vtoc8(gp, idx, partcode);
1264		}
1265	} else
1266		if (bootcode == NULL)
1267			errx(EXIT_FAILURE, "no -b nor -p");
1268
1269	if (bootcode != NULL)
1270		gpart_issue(req, fl);
1271
1272	geom_deletetree(&mesh);
1273	free(partcode);
1274}
1275
1276static void
1277gpart_print_error(const char *errstr)
1278{
1279	char *errmsg;
1280	int error;
1281
1282	error = strtol(errstr, &errmsg, 0);
1283	if (errmsg != errstr) {
1284		while (errmsg[0] == ' ')
1285			errmsg++;
1286		if (errmsg[0] != '\0')
1287			warnc(error, "%s", errmsg);
1288		else
1289			warnc(error, NULL);
1290	} else
1291		warnx("%s", errmsg);
1292}
1293
1294static _Noreturn void
1295gpart_issue(struct gctl_req *req, unsigned int fl __unused)
1296{
1297	char buf[4096];
1298	const char *errstr;
1299	int error, status;
1300
1301	if (gctl_get_int(req, "nargs") != 1)
1302		errx(EXIT_FAILURE, "Invalid number of arguments.");
1303	(void)gctl_delete_param(req, "nargs");
1304
1305	/* autofill parameters (if applicable). */
1306	error = gpart_autofill(req);
1307	if (error) {
1308		warnc(error, "autofill");
1309		status = EXIT_FAILURE;
1310		goto done;
1311	}
1312
1313	bzero(buf, sizeof(buf));
1314	gctl_rw_param(req, "output", sizeof(buf), buf);
1315	errstr = gctl_issue(req);
1316	if (errstr == NULL || errstr[0] == '\0') {
1317		if (buf[0] != '\0')
1318			printf("%s", buf);
1319		status = EXIT_SUCCESS;
1320		goto done;
1321	}
1322
1323	gpart_print_error(errstr);
1324	status = EXIT_FAILURE;
1325
1326 done:
1327	gctl_free(req);
1328	exit(status);
1329}
1330