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