1169586Smarcel/*-
2179854Smarcel * Copyright (c) 2007, 2008 Marcel Moolenaar
3169586Smarcel * All rights reserved.
4169586Smarcel *
5169586Smarcel * Redistribution and use in source and binary forms, with or without
6169586Smarcel * modification, are permitted provided that the following conditions
7169586Smarcel * are met:
8169586Smarcel * 1. Redistributions of source code must retain the above copyright
9169586Smarcel *    notice, this list of conditions and the following disclaimer.
10169586Smarcel * 2. Redistributions in binary form must reproduce the above copyright
11169586Smarcel *    notice, this list of conditions and the following disclaimer in the
12169586Smarcel *    documentation and/or other materials provided with the distribution.
13169586Smarcel *
14169586Smarcel * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
15169586Smarcel * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16169586Smarcel * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17169586Smarcel * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
18169586Smarcel * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19169586Smarcel * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20169586Smarcel * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21169586Smarcel * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22169586Smarcel * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23169586Smarcel * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24169586Smarcel * SUCH DAMAGE.
25169586Smarcel */
26169586Smarcel
27169586Smarcel#include <sys/cdefs.h>
28169586Smarcel__FBSDID("$FreeBSD$");
29169586Smarcel
30185044Smarcel#include <sys/stat.h>
31208777Smarius#include <sys/vtoc.h>
32185044Smarcel
33185044Smarcel#include <assert.h>
34215570Sae#include <ctype.h>
35185044Smarcel#include <err.h>
36185044Smarcel#include <errno.h>
37185044Smarcel#include <fcntl.h>
38185044Smarcel#include <libgeom.h>
39185046Smarcel#include <libutil.h>
40185044Smarcel#include <paths.h>
41215672Sae#include <signal.h>
42185044Smarcel#include <stdint.h>
43169586Smarcel#include <stdio.h>
44169586Smarcel#include <stdlib.h>
45209388Sae#include <limits.h>
46209388Sae#include <inttypes.h>
47169586Smarcel#include <string.h>
48169586Smarcel#include <strings.h>
49185044Smarcel#include <unistd.h>
50169586Smarcel
51169586Smarcel#include "core/geom.h"
52169586Smarcel#include "misc/subr.h"
53169586Smarcel
54179550Smarcel#ifdef STATIC_GEOM_CLASSES
55173313Smarcel#define	PUBSYM(x)	gpart_##x
56173313Smarcel#else
57173313Smarcel#define	PUBSYM(x)	x
58173313Smarcel#endif
59169586Smarcel
60173313Smarceluint32_t PUBSYM(lib_version) = G_LIB_VERSION;
61173313Smarceluint32_t PUBSYM(version) = 0;
62173313Smarcel
63209388Saestatic char sstart[32];
64209388Saestatic char ssize[32];
65215672Saevolatile sig_atomic_t undo_restore;
66209388Sae
67212554Spjd#define	GPART_AUTOFILL	"*"
68212554Spjd#define	GPART_FLAGS	"C"
69179629Smarcel
70212554Spjd#define	GPART_PARAM_BOOTCODE	"bootcode"
71212554Spjd#define	GPART_PARAM_INDEX	"index"
72212554Spjd#define	GPART_PARAM_PARTCODE	"partcode"
73212554Spjd
74208777Smariusstatic struct gclass *find_class(struct gmesh *, const char *);
75208777Smariusstatic struct ggeom * find_geom(struct gclass *, const char *);
76208777Smariusstatic const char *find_geomcfg(struct ggeom *, const char *);
77208777Smariusstatic const char *find_provcfg(struct gprovider *, const char *);
78209388Saestatic struct gprovider *find_provider(struct ggeom *, off_t);
79208777Smariusstatic const char *fmtsize(int64_t);
80208777Smariusstatic int gpart_autofill(struct gctl_req *);
81208777Smariusstatic int gpart_autofill_resize(struct gctl_req *);
82178180Smarcelstatic void gpart_bootcode(struct gctl_req *, unsigned int);
83208777Smariusstatic void *gpart_bootfile_read(const char *, ssize_t *);
84185454Smarcelstatic void gpart_issue(struct gctl_req *, unsigned int);
85178180Smarcelstatic void gpart_show(struct gctl_req *, unsigned int);
86219415Saestatic void gpart_show_geom(struct ggeom *, const char *, int);
87208777Smariusstatic int gpart_show_hasopt(struct gctl_req *, const char *, const char *);
88208777Smariusstatic void gpart_write_partcode(struct ggeom *, int, void *, ssize_t);
89208777Smariusstatic void gpart_write_partcode_vtoc8(struct ggeom *, int, void *);
90213097Saestatic void gpart_print_error(const char *);
91215570Saestatic void gpart_backup(struct gctl_req *, unsigned int);
92215570Saestatic void gpart_restore(struct gctl_req *, unsigned int);
93172837Smarcel
94173313Smarcelstruct g_command PUBSYM(class_commands)[] = {
95185454Smarcel	{ "add", 0, gpart_issue, {
96221363Sae		{ 'a', "alignment", GPART_AUTOFILL, G_TYPE_STRING },
97212554Spjd		{ 'b', "start", GPART_AUTOFILL, G_TYPE_STRING },
98212554Spjd		{ 's', "size", GPART_AUTOFILL, G_TYPE_STRING },
99169586Smarcel		{ 't', "type", NULL, G_TYPE_STRING },
100212614Spjd		{ 'i', GPART_PARAM_INDEX, G_VAL_OPTIONAL, G_TYPE_NUMBER },
101212606Spjd		{ 'l', "label", G_VAL_OPTIONAL, G_TYPE_STRING },
102212554Spjd		{ 'f', "flags", GPART_FLAGS, G_TYPE_STRING },
103169586Smarcel		G_OPT_SENTINEL },
104222357Sae	    "-t type [-a alignment] [-b start] [-s size] [-i index] "
105221363Sae		"[-l label] [-f flags] geom"
106169586Smarcel	},
107215671Sae	{ "backup", 0, gpart_backup, G_NULL_OPTS,
108215671Sae	    "geom"
109215570Sae	},
110178180Smarcel	{ "bootcode", 0, gpart_bootcode, {
111212606Spjd		{ 'b', GPART_PARAM_BOOTCODE, G_VAL_OPTIONAL, G_TYPE_STRING },
112212606Spjd		{ 'p', GPART_PARAM_PARTCODE, G_VAL_OPTIONAL, G_TYPE_STRING },
113212614Spjd		{ 'i', GPART_PARAM_INDEX, G_VAL_OPTIONAL, G_TYPE_NUMBER },
114212554Spjd		{ 'f', "flags", GPART_FLAGS, G_TYPE_STRING },
115178180Smarcel		G_OPT_SENTINEL },
116222357Sae	    "[-b bootcode] [-p partcode -i index] [-f flags] geom"
117178180Smarcel	},
118212554Spjd	{ "commit", 0, gpart_issue, G_NULL_OPTS,
119212554Spjd	    "geom"
120212554Spjd	},
121185454Smarcel	{ "create", 0, gpart_issue, {
122169586Smarcel		{ 's', "scheme", NULL, G_TYPE_STRING },
123212614Spjd		{ 'n', "entries", G_VAL_OPTIONAL, G_TYPE_NUMBER },
124212554Spjd		{ 'f', "flags", GPART_FLAGS, G_TYPE_STRING },
125169586Smarcel		G_OPT_SENTINEL },
126212554Spjd	    "-s scheme [-n entries] [-f flags] provider"
127169586Smarcel	},
128185454Smarcel	{ "delete", 0, gpart_issue, {
129212614Spjd		{ 'i', GPART_PARAM_INDEX, NULL, G_TYPE_NUMBER },
130212554Spjd		{ 'f', "flags", GPART_FLAGS, G_TYPE_STRING },
131169586Smarcel		G_OPT_SENTINEL },
132212554Spjd	    "-i index [-f flags] geom"
133169586Smarcel	},
134214352Sae	{ "destroy", 0, gpart_issue, {
135214352Sae		{ 'F', "force", NULL, G_TYPE_BOOL },
136212554Spjd		{ 'f', "flags", GPART_FLAGS, G_TYPE_STRING },
137169586Smarcel		G_OPT_SENTINEL },
138213097Sae	    "[-F] [-f flags] geom"
139212554Spjd	},
140185454Smarcel	{ "modify", 0, gpart_issue, {
141212614Spjd		{ 'i', GPART_PARAM_INDEX, NULL, G_TYPE_NUMBER },
142212606Spjd		{ 'l', "label", G_VAL_OPTIONAL, G_TYPE_STRING },
143212606Spjd		{ 't', "type", G_VAL_OPTIONAL, G_TYPE_STRING },
144212554Spjd		{ 'f', "flags", GPART_FLAGS, G_TYPE_STRING },
145169586Smarcel		G_OPT_SENTINEL },
146212554Spjd	    "-i index [-l label] [-t type] [-f flags] geom"
147169586Smarcel	},
148185454Smarcel	{ "set", 0, gpart_issue, {
149179854Smarcel		{ 'a', "attrib", NULL, G_TYPE_STRING },
150251588Smarcel		{ 'i', GPART_PARAM_INDEX, G_VAL_OPTIONAL, G_TYPE_NUMBER },
151212554Spjd		{ 'f', "flags", GPART_FLAGS, G_TYPE_STRING },
152179854Smarcel		G_OPT_SENTINEL },
153251588Smarcel	    "-a attrib [-i index] [-f flags] geom"
154179854Smarcel	},
155179769Smarcel	{ "show", 0, gpart_show, {
156179769Smarcel		{ 'l', "show_label", NULL, G_TYPE_BOOL },
157179769Smarcel		{ 'r', "show_rawtype", NULL, G_TYPE_BOOL },
158219415Sae		{ 'p', "show_providers", NULL, G_TYPE_BOOL },
159179769Smarcel		G_OPT_SENTINEL },
160222357Sae	    "[-l | -r] [-p] [geom ...]"
161179769Smarcel	},
162212554Spjd	{ "undo", 0, gpart_issue, G_NULL_OPTS,
163212554Spjd	    "geom"
164212554Spjd	},
165185454Smarcel	{ "unset", 0, gpart_issue, {
166179854Smarcel		{ 'a', "attrib", NULL, G_TYPE_STRING },
167251588Smarcel		{ 'i', GPART_PARAM_INDEX, G_VAL_OPTIONAL, G_TYPE_NUMBER },
168212554Spjd		{ 'f', "flags", GPART_FLAGS, G_TYPE_STRING },
169179854Smarcel		G_OPT_SENTINEL },
170251588Smarcel	    "-a attrib [-i index] [-f flags] geom"
171208777Smarius	},
172207095Smarcel	{ "resize", 0, gpart_issue, {
173221363Sae		{ 'a', "alignment", GPART_AUTOFILL, G_TYPE_STRING },
174212554Spjd		{ 's', "size", GPART_AUTOFILL, G_TYPE_STRING },
175212614Spjd		{ 'i', GPART_PARAM_INDEX, NULL, G_TYPE_NUMBER },
176212554Spjd		{ 'f', "flags", GPART_FLAGS, G_TYPE_STRING },
177207095Smarcel		G_OPT_SENTINEL },
178222357Sae	    "-i index [-a alignment] [-s size] [-f flags] geom"
179207095Smarcel	},
180215570Sae	{ "restore", 0, gpart_restore, {
181215570Sae		{ 'F', "force", NULL, G_TYPE_BOOL },
182215671Sae		{ 'l', "restore_labels", NULL, G_TYPE_BOOL },
183215570Sae		{ 'f', "flags", GPART_FLAGS, G_TYPE_STRING },
184215570Sae		G_OPT_SENTINEL },
185215671Sae	    "[-lF] [-f flags] provider [...]"
186215570Sae	},
187214352Sae	{ "recover", 0, gpart_issue, {
188214352Sae		{ 'f', "flags", GPART_FLAGS, G_TYPE_STRING },
189214352Sae		G_OPT_SENTINEL },
190214352Sae	    "[-f flags] geom"
191214352Sae	},
192169586Smarcel	G_CMD_SENTINEL
193169586Smarcel};
194172837Smarcel
195172837Smarcelstatic struct gclass *
196172837Smarcelfind_class(struct gmesh *mesh, const char *name)
197172837Smarcel{
198172837Smarcel	struct gclass *classp;
199172837Smarcel
200172837Smarcel	LIST_FOREACH(classp, &mesh->lg_class, lg_class) {
201172837Smarcel		if (strcmp(classp->lg_name, name) == 0)
202172837Smarcel			return (classp);
203172837Smarcel	}
204172837Smarcel	return (NULL);
205172837Smarcel}
206172837Smarcel
207172837Smarcelstatic struct ggeom *
208172837Smarcelfind_geom(struct gclass *classp, const char *name)
209172837Smarcel{
210172837Smarcel	struct ggeom *gp;
211172837Smarcel
212213662Sae	if (strncmp(name, _PATH_DEV, sizeof(_PATH_DEV) - 1) == 0)
213213662Sae		name += sizeof(_PATH_DEV) - 1;
214172837Smarcel	LIST_FOREACH(gp, &classp->lg_geom, lg_geom) {
215172837Smarcel		if (strcmp(gp->lg_name, name) == 0)
216172837Smarcel			return (gp);
217172837Smarcel	}
218172837Smarcel	return (NULL);
219172837Smarcel}
220172837Smarcel
221172837Smarcelstatic const char *
222172837Smarcelfind_geomcfg(struct ggeom *gp, const char *cfg)
223172837Smarcel{
224172837Smarcel	struct gconfig *gc;
225172837Smarcel
226172837Smarcel	LIST_FOREACH(gc, &gp->lg_config, lg_config) {
227172837Smarcel		if (!strcmp(gc->lg_name, cfg))
228172837Smarcel			return (gc->lg_val);
229172837Smarcel	}
230172837Smarcel	return (NULL);
231172837Smarcel}
232172837Smarcel
233172837Smarcelstatic const char *
234172837Smarcelfind_provcfg(struct gprovider *pp, const char *cfg)
235172837Smarcel{
236172837Smarcel	struct gconfig *gc;
237172837Smarcel
238172837Smarcel	LIST_FOREACH(gc, &pp->lg_config, lg_config) {
239172837Smarcel		if (!strcmp(gc->lg_name, cfg))
240172837Smarcel			return (gc->lg_val);
241172837Smarcel	}
242172837Smarcel	return (NULL);
243172837Smarcel}
244172837Smarcel
245172837Smarcelstatic struct gprovider *
246209388Saefind_provider(struct ggeom *gp, off_t minsector)
247172837Smarcel{
248172837Smarcel	struct gprovider *pp, *bestpp;
249188330Smarcel	const char *s;
250209388Sae	off_t sector, bestsector;
251172837Smarcel
252172837Smarcel	bestpp = NULL;
253198478Slulf	bestsector = 0;
254172837Smarcel	LIST_FOREACH(pp, &gp->lg_provider, lg_provider) {
255188330Smarcel		s = find_provcfg(pp, "start");
256221952Sae		sector = (off_t)strtoimax(s, NULL, 0);
257172837Smarcel		if (sector < minsector)
258172837Smarcel			continue;
259172837Smarcel		if (bestpp != NULL && sector >= bestsector)
260172837Smarcel			continue;
261188330Smarcel
262172837Smarcel		bestpp = pp;
263172837Smarcel		bestsector = sector;
264172837Smarcel	}
265172837Smarcel	return (bestpp);
266172837Smarcel}
267172837Smarcel
268172837Smarcelstatic const char *
269185046Smarcelfmtsize(int64_t rawsz)
270172837Smarcel{
271185046Smarcel	static char buf[5];
272172837Smarcel
273185046Smarcel	humanize_number(buf, sizeof(buf), rawsz, "", HN_AUTOSCALE,
274185046Smarcel	    HN_B | HN_NOSPACE | HN_DECIMAL);
275172837Smarcel	return (buf);
276172837Smarcel}
277172837Smarcel
278179854Smarcelstatic const char *
279179854Smarcelfmtattrib(struct gprovider *pp)
280179854Smarcel{
281184070Smarcel	static char buf[128];
282184070Smarcel	struct gconfig *gc;
283184070Smarcel	u_int idx;
284179854Smarcel
285184070Smarcel	buf[0] = '\0';
286184070Smarcel	idx = 0;
287184070Smarcel	LIST_FOREACH(gc, &pp->lg_config, lg_config) {
288184070Smarcel		if (strcmp(gc->lg_name, "attrib") != 0)
289184070Smarcel			continue;
290184070Smarcel		idx += snprintf(buf + idx, sizeof(buf) - idx, "%s%s",
291184070Smarcel		    (idx == 0) ? " [" : ",", gc->lg_val);
292184070Smarcel	}
293184070Smarcel	if (idx > 0)
294184070Smarcel		snprintf(buf + idx, sizeof(buf) - idx, "] ");
295179854Smarcel	return (buf);
296179854Smarcel}
297179854Smarcel
298222264Sae#define	ALIGNDOWN(d, a)	((d) - (d) % (a))
299222263Sae#define	ALIGNUP(d, a)	((d) % (a) ? (d) - (d) % (a) + (a): (d))
300221363Sae
301193673Smarcelstatic int
302207095Smarcelgpart_autofill_resize(struct gctl_req *req)
303207095Smarcel{
304207095Smarcel	struct gmesh mesh;
305207095Smarcel	struct gclass *cp;
306207095Smarcel	struct ggeom *gp;
307207095Smarcel	struct gprovider *pp;
308209388Sae	off_t last, size, start, new_size;
309222630Sae	off_t lba, new_lba, alignment, offset;
310207095Smarcel	const char *s;
311225445Sae	int error, idx, has_alignment;
312207095Smarcel
313212708Spjd	idx = (int)gctl_get_intmax(req, GPART_PARAM_INDEX);
314212708Spjd	if (idx < 1)
315207095Smarcel		errx(EXIT_FAILURE, "invalid partition index");
316207095Smarcel
317207095Smarcel	error = geom_gettree(&mesh);
318207095Smarcel	if (error)
319207095Smarcel		return (error);
320207095Smarcel	s = gctl_get_ascii(req, "class");
321207095Smarcel	if (s == NULL)
322207095Smarcel		abort();
323207095Smarcel	cp = find_class(&mesh, s);
324207095Smarcel	if (cp == NULL)
325207095Smarcel		errx(EXIT_FAILURE, "Class %s not found.", s);
326212613Spjd	s = gctl_get_ascii(req, "arg0");
327207095Smarcel	if (s == NULL)
328207095Smarcel		abort();
329207095Smarcel	gp = find_geom(cp, s);
330207095Smarcel	if (gp == NULL)
331207095Smarcel		errx(EXIT_FAILURE, "No such geom: %s.", s);
332209388Sae	pp = LIST_FIRST(&gp->lg_consumer)->lg_provider;
333209388Sae	if (pp == NULL)
334209388Sae		errx(EXIT_FAILURE, "Provider for geom %s not found.", s);
335207095Smarcel
336221363Sae	s = gctl_get_ascii(req, "alignment");
337225445Sae	has_alignment = (*s == '*') ? 0 : 1;
338221363Sae	alignment = 1;
339225445Sae	if (has_alignment) {
340221363Sae		error = g_parse_lba(s, pp->lg_sectorsize, &alignment);
341221363Sae		if (error)
342221363Sae			errc(EXIT_FAILURE, error, "Invalid alignment param");
343221363Sae		if (alignment == 0)
344221363Sae			errx(EXIT_FAILURE, "Invalid alignment param");
345222819Sae	} else {
346222630Sae		lba = pp->lg_stripesize / pp->lg_sectorsize;
347222631Sae		if (lba > 0)
348222819Sae			alignment = lba;
349221363Sae	}
350221363Sae	error = gctl_delete_param(req, "alignment");
351221363Sae	if (error)
352221363Sae		errc(EXIT_FAILURE, error, "internal error");
353221363Sae
354209388Sae	s = gctl_get_ascii(req, "size");
355209388Sae	if (*s == '*')
356209388Sae		new_size = 0;
357209388Sae	else {
358209388Sae		error = g_parse_lba(s, pp->lg_sectorsize, &new_size);
359209388Sae		if (error)
360209388Sae			errc(EXIT_FAILURE, error, "Invalid size param");
361209388Sae		/* no autofill necessary. */
362225445Sae		if (has_alignment == 0)
363221363Sae			goto done;
364209388Sae	}
365209388Sae
366223356Sdelphij	offset = (pp->lg_stripeoffset / pp->lg_sectorsize) % alignment;
367209388Sae	last = (off_t)strtoimax(find_geomcfg(gp, "last"), NULL, 0);
368207095Smarcel	LIST_FOREACH(pp, &gp->lg_provider, lg_provider) {
369207095Smarcel		s = find_provcfg(pp, "index");
370207095Smarcel		if (s == NULL)
371207095Smarcel			continue;
372207095Smarcel		if (atoi(s) == idx)
373207095Smarcel			break;
374207095Smarcel	}
375207095Smarcel	if (pp == NULL)
376207095Smarcel		errx(EXIT_FAILURE, "invalid partition index");
377207095Smarcel
378207095Smarcel	s = find_provcfg(pp, "start");
379221952Sae	start = (off_t)strtoimax(s, NULL, 0);
380207095Smarcel	s = find_provcfg(pp, "end");
381222630Sae	lba = (off_t)strtoimax(s, NULL, 0);
382222630Sae	size = lba - start + 1;
383207095Smarcel
384225445Sae	pp = find_provider(gp, lba + 1);
385225445Sae	if (new_size > 0 && (new_size <= size || pp == NULL)) {
386222630Sae		/* The start offset may be not aligned, so we align the end
387222630Sae		 * offset and then calculate the size.
388222630Sae		 */
389222630Sae		new_size = ALIGNDOWN(start + offset + new_size,
390222630Sae		    alignment) - start - offset;
391222630Sae		goto done;
392209388Sae	}
393222630Sae	if (pp == NULL) {
394222630Sae		new_size = ALIGNDOWN(last + offset + 1, alignment) -
395222630Sae		    start - offset;
396222630Sae		if (new_size < size)
397222630Sae			return (ENOSPC);
398222630Sae	} else {
399207095Smarcel		s = find_provcfg(pp, "start");
400221952Sae		new_lba = (off_t)strtoimax(s, NULL, 0);
401209388Sae		/*
402209388Sae		 * Is there any free space between current and
403207095Smarcel		 * next providers?
404207095Smarcel		 */
405222630Sae		new_lba = ALIGNDOWN(new_lba + offset, alignment) - offset;
406207095Smarcel		if (new_lba > lba)
407207095Smarcel			new_size = new_lba - start;
408209388Sae		else {
409209388Sae			geom_deletetree(&mesh);
410207095Smarcel			return (ENOSPC);
411209388Sae		}
412207095Smarcel	}
413209388Saedone:
414209388Sae	snprintf(ssize, sizeof(ssize), "%jd", (intmax_t)new_size);
415209388Sae	gctl_change_param(req, "size", -1, ssize);
416209388Sae	geom_deletetree(&mesh);
417207095Smarcel	return (0);
418207095Smarcel}
419207095Smarcel
420207095Smarcelstatic int
421193673Smarcelgpart_autofill(struct gctl_req *req)
422193673Smarcel{
423193673Smarcel	struct gmesh mesh;
424193673Smarcel	struct gclass *cp;
425193673Smarcel	struct ggeom *gp;
426193673Smarcel	struct gprovider *pp;
427221363Sae	off_t first, last, a_first;
428221363Sae	off_t size, start, a_lba;
429221967Sae	off_t lba, len, alignment, offset;
430209388Sae	uintmax_t grade;
431193673Smarcel	const char *s;
432221363Sae	int error, has_size, has_start, has_alignment;
433193673Smarcel
434193673Smarcel	s = gctl_get_ascii(req, "verb");
435207095Smarcel	if (strcmp(s, "resize") == 0)
436207095Smarcel		return gpart_autofill_resize(req);
437193673Smarcel	if (strcmp(s, "add") != 0)
438193673Smarcel		return (0);
439193673Smarcel
440193673Smarcel	error = geom_gettree(&mesh);
441193673Smarcel	if (error)
442193673Smarcel		return (error);
443196278Smarcel	s = gctl_get_ascii(req, "class");
444196278Smarcel	if (s == NULL)
445196278Smarcel		abort();
446196278Smarcel	cp = find_class(&mesh, s);
447196278Smarcel	if (cp == NULL)
448196278Smarcel		errx(EXIT_FAILURE, "Class %s not found.", s);
449212613Spjd	s = gctl_get_ascii(req, "arg0");
450196278Smarcel	if (s == NULL)
451196278Smarcel		abort();
452196278Smarcel	gp = find_geom(cp, s);
453196278Smarcel	if (gp == NULL)
454196278Smarcel		errx(EXIT_FAILURE, "No such geom: %s.", s);
455209388Sae	pp = LIST_FIRST(&gp->lg_consumer)->lg_provider;
456209388Sae	if (pp == NULL)
457209388Sae		errx(EXIT_FAILURE, "Provider for geom %s not found.", s);
458209388Sae
459221363Sae	s = gctl_get_ascii(req, "alignment");
460221363Sae	has_alignment = (*s == '*') ? 0 : 1;
461221363Sae	alignment = 1;
462221363Sae	if (has_alignment) {
463221363Sae		error = g_parse_lba(s, pp->lg_sectorsize, &alignment);
464221363Sae		if (error)
465221363Sae			errc(EXIT_FAILURE, error, "Invalid alignment param");
466221363Sae		if (alignment == 0)
467221363Sae			errx(EXIT_FAILURE, "Invalid alignment param");
468221363Sae	}
469221363Sae	error = gctl_delete_param(req, "alignment");
470221363Sae	if (error)
471221363Sae		errc(EXIT_FAILURE, error, "internal error");
472221363Sae
473209388Sae	s = gctl_get_ascii(req, "size");
474209388Sae	has_size = (*s == '*') ? 0 : 1;
475209388Sae	size = 0;
476209388Sae	if (has_size) {
477209388Sae		error = g_parse_lba(s, pp->lg_sectorsize, &size);
478209388Sae		if (error)
479209388Sae			errc(EXIT_FAILURE, error, "Invalid size param");
480209388Sae	}
481209388Sae
482209388Sae	s = gctl_get_ascii(req, "start");
483209388Sae	has_start = (*s == '*') ? 0 : 1;
484209388Sae	start = 0ULL;
485209388Sae	if (has_start) {
486209388Sae		error = g_parse_lba(s, pp->lg_sectorsize, &start);
487209388Sae		if (error)
488209388Sae			errc(EXIT_FAILURE, error, "Invalid start param");
489209388Sae	}
490209388Sae
491209388Sae	/* No autofill necessary. */
492221363Sae	if (has_size && has_start && !has_alignment)
493209388Sae		goto done;
494209388Sae
495222630Sae	len = pp->lg_stripesize / pp->lg_sectorsize;
496222819Sae	if (len > 0 && !has_alignment)
497222819Sae		alignment = len;
498222630Sae
499222630Sae	/* Adjust parameters to stripeoffset */
500223356Sdelphij	offset = (pp->lg_stripeoffset / pp->lg_sectorsize) % alignment;
501221967Sae	start = ALIGNUP(start + offset, alignment);
502223355Sae	if (size > alignment)
503223355Sae		size = ALIGNDOWN(size, alignment);
504221967Sae
505209388Sae	first = (off_t)strtoimax(find_geomcfg(gp, "first"), NULL, 0);
506209388Sae	last = (off_t)strtoimax(find_geomcfg(gp, "last"), NULL, 0);
507193673Smarcel	grade = ~0ULL;
508221967Sae	a_first = ALIGNUP(first + offset, alignment);
509221967Sae	last = ALIGNDOWN(last + offset, alignment);
510235033Sae	if (a_first < start)
511235033Sae		a_first = start;
512193673Smarcel	while ((pp = find_provider(gp, first)) != NULL) {
513193673Smarcel		s = find_provcfg(pp, "start");
514221952Sae		lba = (off_t)strtoimax(s, NULL, 0);
515221967Sae		a_lba = ALIGNDOWN(lba + offset, alignment);
516221363Sae		if (first < a_lba && a_first < a_lba) {
517193673Smarcel			/* Free space [first, lba> */
518221363Sae			len = a_lba - a_first;
519193673Smarcel			if (has_size) {
520209388Sae				if (len >= size &&
521209388Sae				    (uintmax_t)(len - size) < grade) {
522221363Sae					start = a_first;
523193673Smarcel					grade = len - size;
524193673Smarcel				}
525193673Smarcel			} else if (has_start) {
526221363Sae				if (start >= a_first && start < a_lba) {
527221363Sae					size = a_lba - start;
528221363Sae					grade = start - a_first;
529193673Smarcel				}
530193673Smarcel			} else {
531193673Smarcel				if (grade == ~0ULL || len > size) {
532221363Sae					start = a_first;
533193673Smarcel					size = len;
534193673Smarcel					grade = 0;
535193673Smarcel				}
536193673Smarcel			}
537193673Smarcel		}
538193673Smarcel
539193673Smarcel		s = find_provcfg(pp, "end");
540221952Sae		first = (off_t)strtoimax(s, NULL, 0) + 1;
541235033Sae		if (first > a_first)
542235033Sae			a_first = ALIGNUP(first + offset, alignment);
543193673Smarcel	}
544221363Sae	if (a_first <= last) {
545193673Smarcel		/* Free space [first-last] */
546221363Sae		len = ALIGNDOWN(last - a_first + 1, alignment);
547193673Smarcel		if (has_size) {
548209388Sae			if (len >= size &&
549209388Sae			    (uintmax_t)(len - size) < grade) {
550221363Sae				start = a_first;
551193673Smarcel				grade = len - size;
552193673Smarcel			}
553193673Smarcel		} else if (has_start) {
554221363Sae			if (start >= a_first && start <= last) {
555221363Sae				size = ALIGNDOWN(last - start + 1, alignment);
556221363Sae				grade = start - a_first;
557193673Smarcel			}
558193673Smarcel		} else {
559193673Smarcel			if (grade == ~0ULL || len > size) {
560221363Sae				start = a_first;
561193673Smarcel				size = len;
562193673Smarcel				grade = 0;
563193673Smarcel			}
564193673Smarcel		}
565193673Smarcel	}
566209388Sae	if (grade == ~0ULL) {
567209388Sae		geom_deletetree(&mesh);
568193673Smarcel		return (ENOSPC);
569209388Sae	}
570221967Sae	start -= offset;	/* Return back to real offset */
571209388Saedone:
572209388Sae	snprintf(ssize, sizeof(ssize), "%jd", (intmax_t)size);
573209388Sae	gctl_change_param(req, "size", -1, ssize);
574209388Sae	snprintf(sstart, sizeof(sstart), "%jd", (intmax_t)start);
575209388Sae	gctl_change_param(req, "start", -1, sstart);
576209388Sae	geom_deletetree(&mesh);
577193673Smarcel	return (0);
578193673Smarcel}
579193673Smarcel
580172837Smarcelstatic void
581219415Saegpart_show_geom(struct ggeom *gp, const char *element, int show_providers)
582172837Smarcel{
583172837Smarcel	struct gprovider *pp;
584172837Smarcel	const char *s, *scheme;
585209388Sae	off_t first, last, sector, end;
586209388Sae	off_t length, secsz;
587219415Sae	int idx, wblocks, wname, wmax;
588172837Smarcel
589172837Smarcel	scheme = find_geomcfg(gp, "scheme");
590172837Smarcel	s = find_geomcfg(gp, "first");
591209388Sae	first = (off_t)strtoimax(s, NULL, 0);
592172837Smarcel	s = find_geomcfg(gp, "last");
593209388Sae	last = (off_t)strtoimax(s, NULL, 0);
594172837Smarcel	wblocks = strlen(s);
595214352Sae	s = find_geomcfg(gp, "state");
596214352Sae	if (s != NULL && *s != 'C')
597214352Sae		s = NULL;
598219415Sae	wmax = strlen(gp->lg_name);
599219415Sae	if (show_providers) {
600219415Sae		LIST_FOREACH(pp, &gp->lg_provider, lg_provider) {
601219415Sae			wname = strlen(pp->lg_name);
602219415Sae			if (wname > wmax)
603219415Sae				wmax = wname;
604219415Sae		}
605219415Sae	}
606219415Sae	wname = wmax;
607172837Smarcel	pp = LIST_FIRST(&gp->lg_consumer)->lg_provider;
608172837Smarcel	secsz = pp->lg_sectorsize;
609214352Sae	printf("=>%*jd  %*jd  %*s  %s  (%s)%s\n",
610209388Sae	    wblocks, (intmax_t)first, wblocks, (intmax_t)(last - first + 1),
611172837Smarcel	    wname, gp->lg_name,
612214352Sae	    scheme, fmtsize(pp->lg_mediasize),
613214352Sae	    s ? " [CORRUPT]": "");
614172837Smarcel
615172837Smarcel	while ((pp = find_provider(gp, first)) != NULL) {
616188330Smarcel		s = find_provcfg(pp, "start");
617221952Sae		sector = (off_t)strtoimax(s, NULL, 0);
618188330Smarcel
619188330Smarcel		s = find_provcfg(pp, "end");
620221952Sae		end = (off_t)strtoimax(s, NULL, 0);
621221952Sae		length = end - sector + 1;
622221952Sae
623172837Smarcel		s = find_provcfg(pp, "index");
624172837Smarcel		idx = atoi(s);
625172837Smarcel		if (first < sector) {
626209388Sae			printf("  %*jd  %*jd  %*s  - free -  (%s)\n",
627209388Sae			    wblocks, (intmax_t)first, wblocks,
628209388Sae			    (intmax_t)(sector - first), wname, "",
629172837Smarcel			    fmtsize((sector - first) * secsz));
630172837Smarcel		}
631219415Sae		if (show_providers) {
632219415Sae			printf("  %*jd  %*jd  %*s  %s %s (%s)\n",
633219415Sae			    wblocks, (intmax_t)sector, wblocks,
634219415Sae			    (intmax_t)length, wname, pp->lg_name,
635219415Sae			    find_provcfg(pp, element), fmtattrib(pp),
636219415Sae			    fmtsize(pp->lg_mediasize));
637219415Sae		} else
638219415Sae			printf("  %*jd  %*jd  %*d  %s %s (%s)\n",
639219415Sae			    wblocks, (intmax_t)sector, wblocks,
640219415Sae			    (intmax_t)length, wname, idx,
641219415Sae			    find_provcfg(pp, element), fmtattrib(pp),
642219415Sae			    fmtsize(pp->lg_mediasize));
643188330Smarcel		first = end + 1;
644172837Smarcel	}
645172837Smarcel	if (first <= last) {
646188330Smarcel		length = last - first + 1;
647209388Sae		printf("  %*jd  %*jd  %*s  - free -  (%s)\n",
648209388Sae		    wblocks, (intmax_t)first, wblocks, (intmax_t)length,
649172837Smarcel		    wname, "",
650188330Smarcel		    fmtsize(length * secsz));
651172837Smarcel	}
652172837Smarcel	printf("\n");
653172837Smarcel}
654172837Smarcel
655179769Smarcelstatic int
656179769Smarcelgpart_show_hasopt(struct gctl_req *req, const char *opt, const char *elt)
657179769Smarcel{
658179769Smarcel
659215704Sbrucec	if (!gctl_get_int(req, "%s", opt))
660179769Smarcel		return (0);
661179769Smarcel
662179769Smarcel	if (elt != NULL)
663179769Smarcel		errx(EXIT_FAILURE, "-l and -r are mutually exclusive");
664179769Smarcel
665179769Smarcel	return (1);
666179769Smarcel}
667179769Smarcel
668172837Smarcelstatic void
669178180Smarcelgpart_show(struct gctl_req *req, unsigned int fl __unused)
670172837Smarcel{
671172837Smarcel	struct gmesh mesh;
672172837Smarcel	struct gclass *classp;
673172837Smarcel	struct ggeom *gp;
674179769Smarcel	const char *element, *name;
675219415Sae	int error, i, nargs, show_providers;
676172837Smarcel
677179769Smarcel	element = NULL;
678179769Smarcel	if (gpart_show_hasopt(req, "show_label", element))
679179769Smarcel		element = "label";
680179769Smarcel	if (gpart_show_hasopt(req, "show_rawtype", element))
681179769Smarcel		element = "rawtype";
682179769Smarcel	if (element == NULL)
683179769Smarcel		element = "type";
684179769Smarcel
685172837Smarcel	name = gctl_get_ascii(req, "class");
686172837Smarcel	if (name == NULL)
687172837Smarcel		abort();
688172837Smarcel	error = geom_gettree(&mesh);
689172837Smarcel	if (error != 0)
690172837Smarcel		errc(EXIT_FAILURE, error, "Cannot get GEOM tree");
691172837Smarcel	classp = find_class(&mesh, name);
692172837Smarcel	if (classp == NULL) {
693172837Smarcel		geom_deletetree(&mesh);
694172837Smarcel		errx(EXIT_FAILURE, "Class %s not found.", name);
695172837Smarcel	}
696219415Sae	show_providers = gctl_get_int(req, "show_providers");
697172837Smarcel	nargs = gctl_get_int(req, "nargs");
698172837Smarcel	if (nargs > 0) {
699172837Smarcel		for (i = 0; i < nargs; i++) {
700172837Smarcel			name = gctl_get_ascii(req, "arg%d", i);
701172837Smarcel			gp = find_geom(classp, name);
702172837Smarcel			if (gp != NULL)
703219415Sae				gpart_show_geom(gp, element, show_providers);
704172837Smarcel			else
705172837Smarcel				errx(EXIT_FAILURE, "No such geom: %s.", name);
706172837Smarcel		}
707172837Smarcel	} else {
708172837Smarcel		LIST_FOREACH(gp, &classp->lg_geom, lg_geom) {
709219415Sae			gpart_show_geom(gp, element, show_providers);
710172837Smarcel		}
711172837Smarcel	}
712172837Smarcel	geom_deletetree(&mesh);
713172837Smarcel}
714178180Smarcel
715215570Saestatic void
716215570Saegpart_backup(struct gctl_req *req, unsigned int fl __unused)
717215570Sae{
718215570Sae	struct gmesh mesh;
719215570Sae	struct gclass *classp;
720215570Sae	struct gprovider *pp;
721215570Sae	struct ggeom *gp;
722215570Sae	const char *s, *scheme;
723215570Sae	off_t sector, end;
724229916Seadler	off_t length;
725215671Sae	int error, i, windex, wblocks, wtype;
726215570Sae
727215570Sae	if (gctl_get_int(req, "nargs") != 1)
728215570Sae		errx(EXIT_FAILURE, "Invalid number of arguments.");
729215570Sae	error = geom_gettree(&mesh);
730215570Sae	if (error != 0)
731215570Sae		errc(EXIT_FAILURE, error, "Cannot get GEOM tree");
732215570Sae	s = gctl_get_ascii(req, "class");
733215570Sae	if (s == NULL)
734215570Sae		abort();
735215570Sae	classp = find_class(&mesh, s);
736215570Sae	if (classp == NULL) {
737215570Sae		geom_deletetree(&mesh);
738215570Sae		errx(EXIT_FAILURE, "Class %s not found.", s);
739215570Sae	}
740215570Sae	s = gctl_get_ascii(req, "arg0");
741215570Sae	if (s == NULL)
742215570Sae		abort();
743215570Sae	gp = find_geom(classp, s);
744215570Sae	if (gp == NULL)
745215570Sae		errx(EXIT_FAILURE, "No such geom: %s.", s);
746215570Sae	scheme = find_geomcfg(gp, "scheme");
747215570Sae	if (scheme == NULL)
748215570Sae		abort();
749215570Sae	pp = LIST_FIRST(&gp->lg_consumer)->lg_provider;
750215570Sae	s = find_geomcfg(gp, "last");
751215570Sae	wblocks = strlen(s);
752215570Sae	wtype = 0;
753215570Sae	LIST_FOREACH(pp, &gp->lg_provider, lg_provider) {
754215570Sae		s = find_provcfg(pp, "type");
755215570Sae		i = strlen(s);
756215570Sae		if (i > wtype)
757215570Sae			wtype = i;
758215570Sae	}
759215570Sae	s = find_geomcfg(gp, "entries");
760215570Sae	windex = strlen(s);
761215570Sae	printf("%s %s\n", scheme, s);
762215570Sae	LIST_FOREACH(pp, &gp->lg_provider, lg_provider) {
763215570Sae		s = find_provcfg(pp, "start");
764221952Sae		sector = (off_t)strtoimax(s, NULL, 0);
765215570Sae
766215570Sae		s = find_provcfg(pp, "end");
767221952Sae		end = (off_t)strtoimax(s, NULL, 0);
768221952Sae		length = end - sector + 1;
769221952Sae
770215570Sae		s = find_provcfg(pp, "label");
771215671Sae		printf("%-*s %*s %*jd %*jd %s %s\n",
772215570Sae		    windex, find_provcfg(pp, "index"),
773215570Sae		    wtype, find_provcfg(pp, "type"),
774215570Sae		    wblocks, (intmax_t)sector,
775215671Sae		    wblocks, (intmax_t)length,
776215671Sae		    (s != NULL) ? s: "", fmtattrib(pp));
777215570Sae	}
778215570Sae	geom_deletetree(&mesh);
779215570Sae}
780215570Sae
781215570Saestatic int
782215570Saeskip_line(const char *p)
783215570Sae{
784215570Sae
785215570Sae	while (*p != '\0') {
786215570Sae		if (*p == '#')
787215570Sae			return (1);
788215570Sae		if (isspace(*p) == 0)
789215570Sae			return (0);
790215570Sae		p++;
791215570Sae	}
792215570Sae	return (1);
793215570Sae}
794215570Sae
795215570Saestatic void
796215672Saegpart_sighndl(int sig __unused)
797215672Sae{
798215672Sae	undo_restore = 1;
799215672Sae}
800215672Sae
801215672Saestatic void
802215570Saegpart_restore(struct gctl_req *req, unsigned int fl __unused)
803215570Sae{
804215570Sae	struct gmesh mesh;
805215570Sae	struct gclass *classp;
806215570Sae	struct gctl_req *r;
807215570Sae	struct ggeom *gp;
808215672Sae	struct sigaction si_sa;
809215570Sae	const char *s, *flags, *errstr, *label;
810215570Sae	char **ap, *argv[6], line[BUFSIZ], *pline;
811215671Sae	int error, forced, i, l, nargs, created, rl;
812215570Sae	intmax_t n;
813215570Sae
814215570Sae	nargs = gctl_get_int(req, "nargs");
815215570Sae	if (nargs < 1)
816215570Sae		errx(EXIT_FAILURE, "Invalid number of arguments.");
817215570Sae
818215570Sae	forced = gctl_get_int(req, "force");
819215570Sae	flags = gctl_get_ascii(req, "flags");
820215671Sae	rl = gctl_get_int(req, "restore_labels");
821215570Sae	s = gctl_get_ascii(req, "class");
822215570Sae	if (s == NULL)
823215570Sae		abort();
824215570Sae	error = geom_gettree(&mesh);
825215570Sae	if (error != 0)
826215570Sae		errc(EXIT_FAILURE, error, "Cannot get GEOM tree");
827215570Sae	classp = find_class(&mesh, s);
828215570Sae	if (classp == NULL) {
829215570Sae		geom_deletetree(&mesh);
830215570Sae		errx(EXIT_FAILURE, "Class %s not found.", s);
831215570Sae	}
832215672Sae
833215672Sae	sigemptyset(&si_sa.sa_mask);
834215672Sae	si_sa.sa_flags = 0;
835215672Sae	si_sa.sa_handler = gpart_sighndl;
836215672Sae	if (sigaction(SIGINT, &si_sa, 0) == -1)
837215672Sae		err(EXIT_FAILURE, "sigaction SIGINT");
838215672Sae
839215570Sae	if (forced) {
840215570Sae		/* destroy existent partition table before restore */
841215570Sae		for (i = 0; i < nargs; i++) {
842215570Sae			s = gctl_get_ascii(req, "arg%d", i);
843215570Sae			gp = find_geom(classp, s);
844215570Sae			if (gp != NULL) {
845215570Sae				r = gctl_get_handle();
846215570Sae				gctl_ro_param(r, "class", -1,
847215570Sae				    classp->lg_name);
848215570Sae				gctl_ro_param(r, "verb", -1, "destroy");
849215570Sae				gctl_ro_param(r, "flags", -1, "restore");
850215570Sae				gctl_ro_param(r, "force", sizeof(forced),
851215570Sae				    &forced);
852215570Sae				gctl_ro_param(r, "arg0", -1, s);
853215570Sae				errstr = gctl_issue(r);
854215570Sae				if (errstr != NULL && errstr[0] != '\0') {
855215570Sae					gpart_print_error(errstr);
856215570Sae					gctl_free(r);
857215570Sae					goto backout;
858215570Sae				}
859215570Sae				gctl_free(r);
860215570Sae			}
861215570Sae		}
862215570Sae	}
863215570Sae	created = 0;
864215672Sae	while (undo_restore == 0 &&
865215672Sae	    fgets(line, sizeof(line) - 1, stdin) != NULL) {
866215570Sae		/* Format of backup entries:
867215570Sae		 * <scheme name> <number of entries>
868215570Sae		 * <index> <type> <start> <size> [label] ['['attrib[,attrib]']']
869215570Sae		 */
870215570Sae		pline = (char *)line;
871215570Sae		pline[strlen(line) - 1] = 0;
872215570Sae		if (skip_line(pline))
873215570Sae			continue;
874215570Sae		for (ap = argv;
875215570Sae		    (*ap = strsep(&pline, " \t")) != NULL;)
876215570Sae			if (**ap != '\0' && ++ap >= &argv[6])
877215570Sae				break;
878215570Sae		l = ap - &argv[0];
879215570Sae		label = pline = NULL;
880215671Sae		if (l == 1 || l == 2) { /* create table */
881215570Sae			if (created)
882215570Sae				errx(EXIT_FAILURE, "Incorrect backup format.");
883215671Sae			if (l == 2)
884215671Sae				n = strtoimax(argv[1], NULL, 0);
885215570Sae			for (i = 0; i < nargs; i++) {
886215570Sae				s = gctl_get_ascii(req, "arg%d", i);
887215570Sae				r = gctl_get_handle();
888215570Sae				gctl_ro_param(r, "class", -1,
889215570Sae				    classp->lg_name);
890215570Sae				gctl_ro_param(r, "verb", -1, "create");
891215570Sae				gctl_ro_param(r, "scheme", -1, argv[0]);
892215671Sae				if (l == 2)
893215671Sae					gctl_ro_param(r, "entries",
894215671Sae					    sizeof(n), &n);
895215570Sae				gctl_ro_param(r, "flags", -1, "restore");
896215570Sae				gctl_ro_param(r, "arg0", -1, s);
897215570Sae				errstr = gctl_issue(r);
898215570Sae				if (errstr != NULL && errstr[0] != '\0') {
899215570Sae					gpart_print_error(errstr);
900215570Sae					gctl_free(r);
901215570Sae					goto backout;
902215570Sae				}
903215570Sae				gctl_free(r);
904215570Sae			}
905215570Sae			created = 1;
906215570Sae			continue;
907215570Sae		} else if (l < 4 || created == 0)
908215570Sae			errx(EXIT_FAILURE, "Incorrect backup format.");
909215570Sae		else if (l == 5) {
910215570Sae			if (strchr(argv[4], '[') == NULL)
911215570Sae				label = argv[4];
912215570Sae			else
913215570Sae				pline = argv[4];
914215570Sae		} else if (l == 6) {
915215570Sae			label = argv[4];
916215570Sae			pline = argv[5];
917215570Sae		}
918215570Sae		/* Add partitions to each table */
919215570Sae		for (i = 0; i < nargs; i++) {
920215570Sae			s = gctl_get_ascii(req, "arg%d", i);
921215570Sae			r = gctl_get_handle();
922215570Sae			n = strtoimax(argv[0], NULL, 0);
923215570Sae			gctl_ro_param(r, "class", -1, classp->lg_name);
924215570Sae			gctl_ro_param(r, "verb", -1, "add");
925215570Sae			gctl_ro_param(r, "flags", -1, "restore");
926215570Sae			gctl_ro_param(r, GPART_PARAM_INDEX, sizeof(n), &n);
927215570Sae			gctl_ro_param(r, "type", -1, argv[1]);
928215570Sae			gctl_ro_param(r, "start", -1, argv[2]);
929215570Sae			gctl_ro_param(r, "size", -1, argv[3]);
930215671Sae			if (rl != 0 && label != NULL)
931215570Sae				gctl_ro_param(r, "label", -1, argv[4]);
932223158Sae			gctl_ro_param(r, "alignment", -1, GPART_AUTOFILL);
933215570Sae			gctl_ro_param(r, "arg0", -1, s);
934215570Sae			error = gpart_autofill(r);
935215570Sae			if (error != 0)
936215570Sae				errc(EXIT_FAILURE, error, "autofill");
937215570Sae			errstr = gctl_issue(r);
938215570Sae			if (errstr != NULL && errstr[0] != '\0') {
939215570Sae				gpart_print_error(errstr);
940215570Sae				gctl_free(r);
941215570Sae				goto backout;
942215570Sae			}
943215570Sae			gctl_free(r);
944215570Sae		}
945215570Sae		if (pline == NULL || *pline != '[')
946215570Sae			continue;
947215570Sae		/* set attributes */
948215570Sae		pline++;
949215570Sae		for (ap = argv;
950215570Sae		    (*ap = strsep(&pline, ",]")) != NULL;)
951215570Sae			if (**ap != '\0' && ++ap >= &argv[6])
952215570Sae				break;
953215570Sae		for (i = 0; i < nargs; i++) {
954215570Sae			l = ap - &argv[0];
955215570Sae			s = gctl_get_ascii(req, "arg%d", i);
956215570Sae			while (l > 0) {
957215570Sae				r = gctl_get_handle();
958215570Sae				gctl_ro_param(r, "class", -1, classp->lg_name);
959215570Sae				gctl_ro_param(r, "verb", -1, "set");
960215570Sae				gctl_ro_param(r, "flags", -1, "restore");
961215570Sae				gctl_ro_param(r, GPART_PARAM_INDEX,
962215570Sae				    sizeof(n), &n);
963215570Sae				gctl_ro_param(r, "attrib", -1, argv[--l]);
964215570Sae				gctl_ro_param(r, "arg0", -1, s);
965215570Sae				errstr = gctl_issue(r);
966215570Sae				if (errstr != NULL && errstr[0] != '\0') {
967215570Sae					gpart_print_error(errstr);
968215570Sae					gctl_free(r);
969215570Sae					goto backout;
970215570Sae				}
971215570Sae				gctl_free(r);
972215570Sae			}
973215570Sae		}
974215570Sae	}
975215672Sae	if (undo_restore)
976215672Sae		goto backout;
977215570Sae	/* commit changes if needed */
978215570Sae	if (strchr(flags, 'C') != NULL) {
979215570Sae		for (i = 0; i < nargs; i++) {
980215570Sae			s = gctl_get_ascii(req, "arg%d", i);
981215570Sae			r = gctl_get_handle();
982215570Sae			gctl_ro_param(r, "class", -1, classp->lg_name);
983215570Sae			gctl_ro_param(r, "verb", -1, "commit");
984215570Sae			gctl_ro_param(r, "arg0", -1, s);
985215570Sae			errstr = gctl_issue(r);
986215570Sae			if (errstr != NULL && errstr[0] != '\0') {
987215570Sae				gpart_print_error(errstr);
988215570Sae				gctl_free(r);
989215570Sae				goto backout;
990215570Sae			}
991215570Sae			gctl_free(r);
992215570Sae		}
993215570Sae	}
994215570Sae	gctl_free(req);
995215570Sae	geom_deletetree(&mesh);
996215570Sae	exit(EXIT_SUCCESS);
997215570Sae
998215570Saebackout:
999215570Sae	for (i = 0; i < nargs; i++) {
1000215570Sae		s = gctl_get_ascii(req, "arg%d", i);
1001215570Sae		r = gctl_get_handle();
1002215570Sae		gctl_ro_param(r, "class", -1, classp->lg_name);
1003215570Sae		gctl_ro_param(r, "verb", -1, "undo");
1004215570Sae		gctl_ro_param(r, "arg0", -1, s);
1005215570Sae		gctl_issue(r);
1006215570Sae		gctl_free(r);
1007215570Sae	}
1008215570Sae	gctl_free(req);
1009215570Sae	geom_deletetree(&mesh);
1010215570Sae	exit(EXIT_FAILURE);
1011215570Sae}
1012215570Sae
1013179629Smarcelstatic void *
1014179629Smarcelgpart_bootfile_read(const char *bootfile, ssize_t *size)
1015178180Smarcel{
1016178180Smarcel	struct stat sb;
1017178180Smarcel	void *code;
1018179629Smarcel	int fd;
1019178180Smarcel
1020179629Smarcel	if (stat(bootfile, &sb) == -1)
1021179629Smarcel		err(EXIT_FAILURE, "%s", bootfile);
1022178180Smarcel	if (!S_ISREG(sb.st_mode))
1023178180Smarcel		errx(EXIT_FAILURE, "%s: not a regular file", bootfile);
1024179629Smarcel	if (sb.st_size == 0)
1025179629Smarcel		errx(EXIT_FAILURE, "%s: empty file", bootfile);
1026208777Smarius	if (*size > 0 && sb.st_size > *size)
1027179629Smarcel		errx(EXIT_FAILURE, "%s: file too big (%zu limit)", bootfile,
1028179629Smarcel		    *size);
1029178180Smarcel
1030179629Smarcel	*size = sb.st_size;
1031178180Smarcel
1032178180Smarcel	fd = open(bootfile, O_RDONLY);
1033178180Smarcel	if (fd == -1)
1034179629Smarcel		err(EXIT_FAILURE, "%s", bootfile);
1035179629Smarcel	code = malloc(*size);
1036178180Smarcel	if (code == NULL)
1037179629Smarcel		err(EXIT_FAILURE, NULL);
1038179629Smarcel	if (read(fd, code, *size) != *size)
1039179629Smarcel		err(EXIT_FAILURE, "%s", bootfile);
1040178180Smarcel	close(fd);
1041178180Smarcel
1042179629Smarcel	return (code);
1043178180Smarcel}
1044179629Smarcel
1045179629Smarcelstatic void
1046208777Smariusgpart_write_partcode(struct ggeom *gp, int idx, void *code, ssize_t size)
1047179629Smarcel{
1048179629Smarcel	char dsf[128];
1049179629Smarcel	struct gprovider *pp;
1050179629Smarcel	const char *s;
1051185038Smarcel	char *buf;
1052185038Smarcel	off_t bsize;
1053208777Smarius	int fd;
1054179629Smarcel
1055179629Smarcel	LIST_FOREACH(pp, &gp->lg_provider, lg_provider) {
1056179629Smarcel		s = find_provcfg(pp, "index");
1057179629Smarcel		if (s == NULL)
1058179629Smarcel			continue;
1059179629Smarcel		if (atoi(s) == idx)
1060179629Smarcel			break;
1061179629Smarcel	}
1062179629Smarcel
1063179629Smarcel	if (pp != NULL) {
1064179629Smarcel		snprintf(dsf, sizeof(dsf), "/dev/%s", pp->lg_name);
1065179629Smarcel		fd = open(dsf, O_WRONLY);
1066179629Smarcel		if (fd == -1)
1067179629Smarcel			err(EXIT_FAILURE, "%s", dsf);
1068179629Smarcel		if (lseek(fd, size, SEEK_SET) != size)
1069179629Smarcel			errx(EXIT_FAILURE, "%s: not enough space", dsf);
1070179629Smarcel		if (lseek(fd, 0, SEEK_SET) != 0)
1071179629Smarcel			err(EXIT_FAILURE, "%s", dsf);
1072185038Smarcel
1073185038Smarcel		/*
1074185038Smarcel		 * When writing to a disk device, the write must be
1075185038Smarcel		 * sector aligned and not write to any partial sectors,
1076185038Smarcel		 * so round up the buffer size to the next sector and zero it.
1077185038Smarcel		 */
1078185038Smarcel		bsize = (size + pp->lg_sectorsize - 1) /
1079185038Smarcel		    pp->lg_sectorsize * pp->lg_sectorsize;
1080185038Smarcel		buf = calloc(1, bsize);
1081185038Smarcel		if (buf == NULL)
1082179629Smarcel			err(EXIT_FAILURE, "%s", dsf);
1083185038Smarcel		bcopy(code, buf, size);
1084185038Smarcel		if (write(fd, buf, bsize) != bsize)
1085185038Smarcel			err(EXIT_FAILURE, "%s", dsf);
1086185038Smarcel		free(buf);
1087179629Smarcel		close(fd);
1088179629Smarcel	} else
1089179629Smarcel		errx(EXIT_FAILURE, "invalid partition index");
1090208777Smarius}
1091179629Smarcel
1092208777Smariusstatic void
1093208777Smariusgpart_write_partcode_vtoc8(struct ggeom *gp, int idx, void *code)
1094208777Smarius{
1095208777Smarius	char dsf[128];
1096208777Smarius	struct gprovider *pp;
1097208777Smarius	const char *s;
1098208777Smarius	int installed, fd;
1099208777Smarius
1100208777Smarius	installed = 0;
1101208777Smarius	LIST_FOREACH(pp, &gp->lg_provider, lg_provider) {
1102208777Smarius		s = find_provcfg(pp, "index");
1103208777Smarius		if (s == NULL)
1104208777Smarius			continue;
1105208777Smarius		if (idx != 0 && atoi(s) != idx)
1106208777Smarius			continue;
1107208777Smarius		snprintf(dsf, sizeof(dsf), "/dev/%s", pp->lg_name);
1108208777Smarius		if (pp->lg_sectorsize != sizeof(struct vtoc8))
1109208777Smarius			errx(EXIT_FAILURE, "%s: unexpected sector "
1110208777Smarius			    "size (%d)\n", dsf, pp->lg_sectorsize);
1111208777Smarius		fd = open(dsf, O_WRONLY);
1112208777Smarius		if (fd == -1)
1113208777Smarius			err(EXIT_FAILURE, "%s", dsf);
1114208777Smarius		if (lseek(fd, VTOC_BOOTSIZE, SEEK_SET) != VTOC_BOOTSIZE)
1115208777Smarius			continue;
1116208777Smarius		/*
1117208777Smarius		 * We ignore the first VTOC_BOOTSIZE bytes of boot code in
1118208777Smarius		 * order to avoid overwriting the label.
1119208777Smarius		 */
1120208777Smarius		if (lseek(fd, sizeof(struct vtoc8), SEEK_SET) !=
1121208777Smarius		    sizeof(struct vtoc8))
1122208777Smarius			err(EXIT_FAILURE, "%s", dsf);
1123208777Smarius		if (write(fd, (caddr_t)code + sizeof(struct vtoc8),
1124208777Smarius		    VTOC_BOOTSIZE - sizeof(struct vtoc8)) != VTOC_BOOTSIZE -
1125208777Smarius		    sizeof(struct vtoc8))
1126208777Smarius			err(EXIT_FAILURE, "%s", dsf);
1127208777Smarius		installed++;
1128208777Smarius		close(fd);
1129208777Smarius		if (idx != 0 && atoi(s) == idx)
1130208777Smarius			break;
1131208777Smarius	}
1132208777Smarius	if (installed == 0)
1133208777Smarius		errx(EXIT_FAILURE, "%s: no partitions", gp->lg_name);
1134179629Smarcel}
1135179629Smarcel
1136179629Smarcelstatic void
1137185454Smarcelgpart_bootcode(struct gctl_req *req, unsigned int fl)
1138179629Smarcel{
1139208777Smarius	struct gmesh mesh;
1140208777Smarius	struct gclass *classp;
1141208777Smarius	struct ggeom *gp;
1142179629Smarcel	const char *s;
1143179629Smarcel	void *bootcode, *partcode;
1144179629Smarcel	size_t bootsize, partsize;
1145208777Smarius	int error, idx, vtoc8;
1146179629Smarcel
1147212554Spjd	if (gctl_has_param(req, GPART_PARAM_BOOTCODE)) {
1148212554Spjd		s = gctl_get_ascii(req, GPART_PARAM_BOOTCODE);
1149208173Snwhitehorn		bootsize = 800 * 1024;		/* Arbitrary limit. */
1150179629Smarcel		bootcode = gpart_bootfile_read(s, &bootsize);
1151212554Spjd		error = gctl_change_param(req, GPART_PARAM_BOOTCODE, bootsize,
1152179629Smarcel		    bootcode);
1153179629Smarcel		if (error)
1154179629Smarcel			errc(EXIT_FAILURE, error, "internal error");
1155179629Smarcel	} else {
1156179629Smarcel		bootcode = NULL;
1157179629Smarcel		bootsize = 0;
1158179629Smarcel	}
1159179629Smarcel
1160208777Smarius	s = gctl_get_ascii(req, "class");
1161208777Smarius	if (s == NULL)
1162208777Smarius		abort();
1163208777Smarius	error = geom_gettree(&mesh);
1164208777Smarius	if (error != 0)
1165208777Smarius		errc(EXIT_FAILURE, error, "Cannot get GEOM tree");
1166208777Smarius	classp = find_class(&mesh, s);
1167208777Smarius	if (classp == NULL) {
1168208777Smarius		geom_deletetree(&mesh);
1169208777Smarius		errx(EXIT_FAILURE, "Class %s not found.", s);
1170208777Smarius	}
1171216619Sae	if (gctl_get_int(req, "nargs") != 1)
1172216619Sae		errx(EXIT_FAILURE, "Invalid number of arguments.");
1173212554Spjd	s = gctl_get_ascii(req, "arg0");
1174208777Smarius	if (s == NULL)
1175208777Smarius		abort();
1176208777Smarius	gp = find_geom(classp, s);
1177208777Smarius	if (gp == NULL)
1178208777Smarius		errx(EXIT_FAILURE, "No such geom: %s.", s);
1179208777Smarius	s = find_geomcfg(gp, "scheme");
1180208777Smarius	vtoc8 = 0;
1181208777Smarius	if (strcmp(s, "VTOC8") == 0)
1182208777Smarius		vtoc8 = 1;
1183208777Smarius
1184212554Spjd	if (gctl_has_param(req, GPART_PARAM_PARTCODE)) {
1185212554Spjd		s = gctl_get_ascii(req, GPART_PARAM_PARTCODE);
1186208777Smarius		partsize = vtoc8 != 0 ? VTOC_BOOTSIZE : bootsize * 1024;
1187179629Smarcel		partcode = gpart_bootfile_read(s, &partsize);
1188212554Spjd		error = gctl_delete_param(req, GPART_PARAM_PARTCODE);
1189179629Smarcel		if (error)
1190179629Smarcel			errc(EXIT_FAILURE, error, "internal error");
1191179629Smarcel	} else {
1192179629Smarcel		partcode = NULL;
1193179629Smarcel		partsize = 0;
1194179629Smarcel	}
1195179629Smarcel
1196212554Spjd	if (gctl_has_param(req, GPART_PARAM_INDEX)) {
1197179629Smarcel		if (partcode == NULL)
1198179629Smarcel			errx(EXIT_FAILURE, "-i is only valid with -p");
1199212708Spjd		idx = (int)gctl_get_intmax(req, GPART_PARAM_INDEX);
1200212708Spjd		if (idx < 1)
1201179629Smarcel			errx(EXIT_FAILURE, "invalid partition index");
1202212554Spjd		error = gctl_delete_param(req, GPART_PARAM_INDEX);
1203179629Smarcel		if (error)
1204179629Smarcel			errc(EXIT_FAILURE, error, "internal error");
1205179629Smarcel	} else
1206179629Smarcel		idx = 0;
1207179629Smarcel
1208179629Smarcel	if (partcode != NULL) {
1209208777Smarius		if (vtoc8 == 0) {
1210208777Smarius			if (idx == 0)
1211208777Smarius				errx(EXIT_FAILURE, "missing -i option");
1212208777Smarius			gpart_write_partcode(gp, idx, partcode, partsize);
1213223364Sae		} else {
1214223364Sae			if (partsize != VTOC_BOOTSIZE)
1215223364Sae				errx(EXIT_FAILURE, "invalid bootcode");
1216208777Smarius			gpart_write_partcode_vtoc8(gp, idx, partcode);
1217223364Sae		}
1218208777Smarius	} else
1219179629Smarcel		if (bootcode == NULL)
1220179629Smarcel			errx(EXIT_FAILURE, "no -b nor -p");
1221179629Smarcel
1222185454Smarcel	if (bootcode != NULL)
1223185454Smarcel		gpart_issue(req, fl);
1224208777Smarius
1225208777Smarius	geom_deletetree(&mesh);
1226185454Smarcel}
1227185454Smarcel
1228185454Smarcelstatic void
1229213097Saegpart_print_error(const char *errstr)
1230213097Sae{
1231213097Sae	char *errmsg;
1232213097Sae	int error;
1233213097Sae
1234213097Sae	error = strtol(errstr, &errmsg, 0);
1235213097Sae	if (errmsg != errstr) {
1236213097Sae		while (errmsg[0] == ' ')
1237213097Sae			errmsg++;
1238213097Sae		if (errmsg[0] != '\0')
1239213097Sae			warnc(error, "%s", errmsg);
1240213097Sae		else
1241213097Sae			warnc(error, NULL);
1242213097Sae	} else
1243213097Sae		warnx("%s", errmsg);
1244213097Sae}
1245213097Sae
1246213097Saestatic void
1247185454Smarcelgpart_issue(struct gctl_req *req, unsigned int fl __unused)
1248185454Smarcel{
1249185454Smarcel	char buf[4096];
1250185454Smarcel	const char *errstr;
1251185495Smarcel	int error, status;
1252185454Smarcel
1253212554Spjd	if (gctl_get_int(req, "nargs") != 1)
1254212554Spjd		errx(EXIT_FAILURE, "Invalid number of arguments.");
1255212554Spjd	(void)gctl_delete_param(req, "nargs");
1256212554Spjd
1257193673Smarcel	/* autofill parameters (if applicable). */
1258193673Smarcel	error = gpart_autofill(req);
1259193673Smarcel	if (error) {
1260193673Smarcel		warnc(error, "autofill");
1261193673Smarcel		status = EXIT_FAILURE;
1262193673Smarcel		goto done;
1263193673Smarcel	}
1264193673Smarcel
1265185454Smarcel	bzero(buf, sizeof(buf));
1266185454Smarcel	gctl_rw_param(req, "output", sizeof(buf), buf);
1267185454Smarcel	errstr = gctl_issue(req);
1268185454Smarcel	if (errstr == NULL || errstr[0] == '\0') {
1269185454Smarcel		if (buf[0] != '\0')
1270185454Smarcel			printf("%s", buf);
1271185495Smarcel		status = EXIT_SUCCESS;
1272185495Smarcel		goto done;
1273179629Smarcel	}
1274185454Smarcel
1275213097Sae	gpart_print_error(errstr);
1276185495Smarcel	status = EXIT_FAILURE;
1277185495Smarcel
1278185495Smarcel done:
1279185495Smarcel	gctl_free(req);
1280185495Smarcel	exit(status);
1281179629Smarcel}
1282