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: stable/10/sbin/geom/class/part/geom_part.c 319258 2017-05-30 22:33:24Z asomers $");
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 *);
84319258Sasomersstatic _Noreturn 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{
210281303Smav	struct ggeom *gp, *wgp;
211172837Smarcel
212213662Sae	if (strncmp(name, _PATH_DEV, sizeof(_PATH_DEV) - 1) == 0)
213213662Sae		name += sizeof(_PATH_DEV) - 1;
214281303Smav	wgp = NULL;
215172837Smarcel	LIST_FOREACH(gp, &classp->lg_geom, lg_geom) {
216281303Smav		if (strcmp(gp->lg_name, name) != 0)
217281303Smav			continue;
218281303Smav		if (find_geomcfg(gp, "wither") == NULL)
219172837Smarcel			return (gp);
220281303Smav		else
221281303Smav			wgp = gp;
222172837Smarcel	}
223281303Smav	return (wgp);
224172837Smarcel}
225172837Smarcel
226172837Smarcelstatic const char *
227172837Smarcelfind_geomcfg(struct ggeom *gp, const char *cfg)
228172837Smarcel{
229172837Smarcel	struct gconfig *gc;
230172837Smarcel
231172837Smarcel	LIST_FOREACH(gc, &gp->lg_config, lg_config) {
232172837Smarcel		if (!strcmp(gc->lg_name, cfg))
233172837Smarcel			return (gc->lg_val);
234172837Smarcel	}
235172837Smarcel	return (NULL);
236172837Smarcel}
237172837Smarcel
238172837Smarcelstatic const char *
239172837Smarcelfind_provcfg(struct gprovider *pp, const char *cfg)
240172837Smarcel{
241172837Smarcel	struct gconfig *gc;
242172837Smarcel
243172837Smarcel	LIST_FOREACH(gc, &pp->lg_config, lg_config) {
244172837Smarcel		if (!strcmp(gc->lg_name, cfg))
245172837Smarcel			return (gc->lg_val);
246172837Smarcel	}
247172837Smarcel	return (NULL);
248172837Smarcel}
249172837Smarcel
250172837Smarcelstatic struct gprovider *
251209388Saefind_provider(struct ggeom *gp, off_t minsector)
252172837Smarcel{
253172837Smarcel	struct gprovider *pp, *bestpp;
254188330Smarcel	const char *s;
255209388Sae	off_t sector, bestsector;
256172837Smarcel
257172837Smarcel	bestpp = NULL;
258198478Slulf	bestsector = 0;
259172837Smarcel	LIST_FOREACH(pp, &gp->lg_provider, lg_provider) {
260188330Smarcel		s = find_provcfg(pp, "start");
261221952Sae		sector = (off_t)strtoimax(s, NULL, 0);
262172837Smarcel		if (sector < minsector)
263172837Smarcel			continue;
264172837Smarcel		if (bestpp != NULL && sector >= bestsector)
265172837Smarcel			continue;
266188330Smarcel
267172837Smarcel		bestpp = pp;
268172837Smarcel		bestsector = sector;
269172837Smarcel	}
270172837Smarcel	return (bestpp);
271172837Smarcel}
272172837Smarcel
273172837Smarcelstatic const char *
274185046Smarcelfmtsize(int64_t rawsz)
275172837Smarcel{
276185046Smarcel	static char buf[5];
277172837Smarcel
278185046Smarcel	humanize_number(buf, sizeof(buf), rawsz, "", HN_AUTOSCALE,
279185046Smarcel	    HN_B | HN_NOSPACE | HN_DECIMAL);
280172837Smarcel	return (buf);
281172837Smarcel}
282172837Smarcel
283179854Smarcelstatic const char *
284179854Smarcelfmtattrib(struct gprovider *pp)
285179854Smarcel{
286184070Smarcel	static char buf[128];
287184070Smarcel	struct gconfig *gc;
288184070Smarcel	u_int idx;
289179854Smarcel
290184070Smarcel	buf[0] = '\0';
291184070Smarcel	idx = 0;
292184070Smarcel	LIST_FOREACH(gc, &pp->lg_config, lg_config) {
293184070Smarcel		if (strcmp(gc->lg_name, "attrib") != 0)
294184070Smarcel			continue;
295184070Smarcel		idx += snprintf(buf + idx, sizeof(buf) - idx, "%s%s",
296184070Smarcel		    (idx == 0) ? " [" : ",", gc->lg_val);
297184070Smarcel	}
298184070Smarcel	if (idx > 0)
299184070Smarcel		snprintf(buf + idx, sizeof(buf) - idx, "] ");
300179854Smarcel	return (buf);
301179854Smarcel}
302179854Smarcel
303222264Sae#define	ALIGNDOWN(d, a)	((d) - (d) % (a))
304222263Sae#define	ALIGNUP(d, a)	((d) % (a) ? (d) - (d) % (a) + (a): (d))
305221363Sae
306193673Smarcelstatic int
307207095Smarcelgpart_autofill_resize(struct gctl_req *req)
308207095Smarcel{
309207095Smarcel	struct gmesh mesh;
310207095Smarcel	struct gclass *cp;
311207095Smarcel	struct ggeom *gp;
312207095Smarcel	struct gprovider *pp;
313209388Sae	off_t last, size, start, new_size;
314222630Sae	off_t lba, new_lba, alignment, offset;
315207095Smarcel	const char *s;
316225445Sae	int error, idx, has_alignment;
317207095Smarcel
318212708Spjd	idx = (int)gctl_get_intmax(req, GPART_PARAM_INDEX);
319212708Spjd	if (idx < 1)
320207095Smarcel		errx(EXIT_FAILURE, "invalid partition index");
321207095Smarcel
322207095Smarcel	error = geom_gettree(&mesh);
323207095Smarcel	if (error)
324207095Smarcel		return (error);
325207095Smarcel	s = gctl_get_ascii(req, "class");
326207095Smarcel	if (s == NULL)
327207095Smarcel		abort();
328207095Smarcel	cp = find_class(&mesh, s);
329207095Smarcel	if (cp == NULL)
330207095Smarcel		errx(EXIT_FAILURE, "Class %s not found.", s);
331212613Spjd	s = gctl_get_ascii(req, "arg0");
332207095Smarcel	if (s == NULL)
333207095Smarcel		abort();
334207095Smarcel	gp = find_geom(cp, s);
335207095Smarcel	if (gp == NULL)
336207095Smarcel		errx(EXIT_FAILURE, "No such geom: %s.", s);
337209388Sae	pp = LIST_FIRST(&gp->lg_consumer)->lg_provider;
338209388Sae	if (pp == NULL)
339209388Sae		errx(EXIT_FAILURE, "Provider for geom %s not found.", s);
340207095Smarcel
341221363Sae	s = gctl_get_ascii(req, "alignment");
342225445Sae	has_alignment = (*s == '*') ? 0 : 1;
343221363Sae	alignment = 1;
344225445Sae	if (has_alignment) {
345221363Sae		error = g_parse_lba(s, pp->lg_sectorsize, &alignment);
346221363Sae		if (error)
347221363Sae			errc(EXIT_FAILURE, error, "Invalid alignment param");
348221363Sae		if (alignment == 0)
349221363Sae			errx(EXIT_FAILURE, "Invalid alignment param");
350222819Sae	} else {
351222630Sae		lba = pp->lg_stripesize / pp->lg_sectorsize;
352222631Sae		if (lba > 0)
353222819Sae			alignment = lba;
354221363Sae	}
355221363Sae	error = gctl_delete_param(req, "alignment");
356221363Sae	if (error)
357221363Sae		errc(EXIT_FAILURE, error, "internal error");
358221363Sae
359209388Sae	s = gctl_get_ascii(req, "size");
360209388Sae	if (*s == '*')
361209388Sae		new_size = 0;
362209388Sae	else {
363209388Sae		error = g_parse_lba(s, pp->lg_sectorsize, &new_size);
364209388Sae		if (error)
365209388Sae			errc(EXIT_FAILURE, error, "Invalid size param");
366209388Sae		/* no autofill necessary. */
367225445Sae		if (has_alignment == 0)
368221363Sae			goto done;
369209388Sae	}
370209388Sae
371223356Sdelphij	offset = (pp->lg_stripeoffset / pp->lg_sectorsize) % alignment;
372259311Sasomers	s = find_geomcfg(gp, "last");
373259311Sasomers	if (s == NULL)
374259311Sasomers		errx(EXIT_FAILURE, "Final block not found for geom %s",
375259311Sasomers		    gp->lg_name);
376259311Sasomers	last = (off_t)strtoimax(s, NULL, 0);
377207095Smarcel	LIST_FOREACH(pp, &gp->lg_provider, lg_provider) {
378207095Smarcel		s = find_provcfg(pp, "index");
379207095Smarcel		if (s == NULL)
380207095Smarcel			continue;
381207095Smarcel		if (atoi(s) == idx)
382207095Smarcel			break;
383207095Smarcel	}
384207095Smarcel	if (pp == NULL)
385207095Smarcel		errx(EXIT_FAILURE, "invalid partition index");
386207095Smarcel
387207095Smarcel	s = find_provcfg(pp, "start");
388221952Sae	start = (off_t)strtoimax(s, NULL, 0);
389207095Smarcel	s = find_provcfg(pp, "end");
390222630Sae	lba = (off_t)strtoimax(s, NULL, 0);
391222630Sae	size = lba - start + 1;
392207095Smarcel
393225445Sae	pp = find_provider(gp, lba + 1);
394225445Sae	if (new_size > 0 && (new_size <= size || pp == NULL)) {
395222630Sae		/* The start offset may be not aligned, so we align the end
396222630Sae		 * offset and then calculate the size.
397222630Sae		 */
398222630Sae		new_size = ALIGNDOWN(start + offset + new_size,
399222630Sae		    alignment) - start - offset;
400222630Sae		goto done;
401209388Sae	}
402222630Sae	if (pp == NULL) {
403222630Sae		new_size = ALIGNDOWN(last + offset + 1, alignment) -
404222630Sae		    start - offset;
405222630Sae		if (new_size < size)
406222630Sae			return (ENOSPC);
407222630Sae	} else {
408207095Smarcel		s = find_provcfg(pp, "start");
409221952Sae		new_lba = (off_t)strtoimax(s, NULL, 0);
410209388Sae		/*
411209388Sae		 * Is there any free space between current and
412207095Smarcel		 * next providers?
413207095Smarcel		 */
414222630Sae		new_lba = ALIGNDOWN(new_lba + offset, alignment) - offset;
415207095Smarcel		if (new_lba > lba)
416207095Smarcel			new_size = new_lba - start;
417209388Sae		else {
418209388Sae			geom_deletetree(&mesh);
419207095Smarcel			return (ENOSPC);
420209388Sae		}
421207095Smarcel	}
422209388Saedone:
423209388Sae	snprintf(ssize, sizeof(ssize), "%jd", (intmax_t)new_size);
424209388Sae	gctl_change_param(req, "size", -1, ssize);
425209388Sae	geom_deletetree(&mesh);
426207095Smarcel	return (0);
427207095Smarcel}
428207095Smarcel
429207095Smarcelstatic int
430193673Smarcelgpart_autofill(struct gctl_req *req)
431193673Smarcel{
432193673Smarcel	struct gmesh mesh;
433193673Smarcel	struct gclass *cp;
434193673Smarcel	struct ggeom *gp;
435193673Smarcel	struct gprovider *pp;
436221363Sae	off_t first, last, a_first;
437221363Sae	off_t size, start, a_lba;
438221967Sae	off_t lba, len, alignment, offset;
439209388Sae	uintmax_t grade;
440193673Smarcel	const char *s;
441221363Sae	int error, has_size, has_start, has_alignment;
442193673Smarcel
443193673Smarcel	s = gctl_get_ascii(req, "verb");
444207095Smarcel	if (strcmp(s, "resize") == 0)
445207095Smarcel		return gpart_autofill_resize(req);
446193673Smarcel	if (strcmp(s, "add") != 0)
447193673Smarcel		return (0);
448193673Smarcel
449193673Smarcel	error = geom_gettree(&mesh);
450193673Smarcel	if (error)
451193673Smarcel		return (error);
452196278Smarcel	s = gctl_get_ascii(req, "class");
453196278Smarcel	if (s == NULL)
454196278Smarcel		abort();
455196278Smarcel	cp = find_class(&mesh, s);
456196278Smarcel	if (cp == NULL)
457196278Smarcel		errx(EXIT_FAILURE, "Class %s not found.", s);
458212613Spjd	s = gctl_get_ascii(req, "arg0");
459196278Smarcel	if (s == NULL)
460196278Smarcel		abort();
461196278Smarcel	gp = find_geom(cp, s);
462196278Smarcel	if (gp == NULL)
463196278Smarcel		errx(EXIT_FAILURE, "No such geom: %s.", s);
464209388Sae	pp = LIST_FIRST(&gp->lg_consumer)->lg_provider;
465209388Sae	if (pp == NULL)
466209388Sae		errx(EXIT_FAILURE, "Provider for geom %s not found.", s);
467209388Sae
468221363Sae	s = gctl_get_ascii(req, "alignment");
469221363Sae	has_alignment = (*s == '*') ? 0 : 1;
470221363Sae	alignment = 1;
471221363Sae	if (has_alignment) {
472221363Sae		error = g_parse_lba(s, pp->lg_sectorsize, &alignment);
473221363Sae		if (error)
474221363Sae			errc(EXIT_FAILURE, error, "Invalid alignment param");
475221363Sae		if (alignment == 0)
476221363Sae			errx(EXIT_FAILURE, "Invalid alignment param");
477221363Sae	}
478221363Sae	error = gctl_delete_param(req, "alignment");
479221363Sae	if (error)
480221363Sae		errc(EXIT_FAILURE, error, "internal error");
481221363Sae
482209388Sae	s = gctl_get_ascii(req, "size");
483209388Sae	has_size = (*s == '*') ? 0 : 1;
484209388Sae	size = 0;
485209388Sae	if (has_size) {
486209388Sae		error = g_parse_lba(s, pp->lg_sectorsize, &size);
487209388Sae		if (error)
488209388Sae			errc(EXIT_FAILURE, error, "Invalid size param");
489209388Sae	}
490209388Sae
491209388Sae	s = gctl_get_ascii(req, "start");
492209388Sae	has_start = (*s == '*') ? 0 : 1;
493209388Sae	start = 0ULL;
494209388Sae	if (has_start) {
495209388Sae		error = g_parse_lba(s, pp->lg_sectorsize, &start);
496209388Sae		if (error)
497209388Sae			errc(EXIT_FAILURE, error, "Invalid start param");
498209388Sae	}
499209388Sae
500209388Sae	/* No autofill necessary. */
501221363Sae	if (has_size && has_start && !has_alignment)
502209388Sae		goto done;
503209388Sae
504222630Sae	len = pp->lg_stripesize / pp->lg_sectorsize;
505222819Sae	if (len > 0 && !has_alignment)
506222819Sae		alignment = len;
507222630Sae
508222630Sae	/* Adjust parameters to stripeoffset */
509223356Sdelphij	offset = (pp->lg_stripeoffset / pp->lg_sectorsize) % alignment;
510221967Sae	start = ALIGNUP(start + offset, alignment);
511223355Sae	if (size > alignment)
512223355Sae		size = ALIGNDOWN(size, alignment);
513221967Sae
514259311Sasomers	s = find_geomcfg(gp, "first");
515259311Sasomers	if (s == NULL)
516259311Sasomers		errx(EXIT_FAILURE, "Starting block not found for geom %s",
517259311Sasomers		    gp->lg_name);
518259311Sasomers	first = (off_t)strtoimax(s, NULL, 0);
519259311Sasomers	s = find_geomcfg(gp, "last");
520259311Sasomers	if (s == NULL)
521259311Sasomers		errx(EXIT_FAILURE, "Final block not found for geom %s",
522259311Sasomers		    gp->lg_name);
523259311Sasomers	last = (off_t)strtoimax(s, NULL, 0);
524193673Smarcel	grade = ~0ULL;
525221967Sae	a_first = ALIGNUP(first + offset, alignment);
526221967Sae	last = ALIGNDOWN(last + offset, alignment);
527235033Sae	if (a_first < start)
528235033Sae		a_first = start;
529193673Smarcel	while ((pp = find_provider(gp, first)) != NULL) {
530193673Smarcel		s = find_provcfg(pp, "start");
531221952Sae		lba = (off_t)strtoimax(s, NULL, 0);
532221967Sae		a_lba = ALIGNDOWN(lba + offset, alignment);
533221363Sae		if (first < a_lba && a_first < a_lba) {
534193673Smarcel			/* Free space [first, lba> */
535221363Sae			len = a_lba - a_first;
536193673Smarcel			if (has_size) {
537209388Sae				if (len >= size &&
538209388Sae				    (uintmax_t)(len - size) < grade) {
539221363Sae					start = a_first;
540193673Smarcel					grade = len - size;
541193673Smarcel				}
542193673Smarcel			} else if (has_start) {
543221363Sae				if (start >= a_first && start < a_lba) {
544221363Sae					size = a_lba - start;
545221363Sae					grade = start - a_first;
546193673Smarcel				}
547193673Smarcel			} else {
548193673Smarcel				if (grade == ~0ULL || len > size) {
549221363Sae					start = a_first;
550193673Smarcel					size = len;
551193673Smarcel					grade = 0;
552193673Smarcel				}
553193673Smarcel			}
554193673Smarcel		}
555193673Smarcel
556193673Smarcel		s = find_provcfg(pp, "end");
557221952Sae		first = (off_t)strtoimax(s, NULL, 0) + 1;
558279646Sae		if (first + offset > a_first)
559235033Sae			a_first = ALIGNUP(first + offset, alignment);
560193673Smarcel	}
561221363Sae	if (a_first <= last) {
562193673Smarcel		/* Free space [first-last] */
563221363Sae		len = ALIGNDOWN(last - a_first + 1, alignment);
564193673Smarcel		if (has_size) {
565209388Sae			if (len >= size &&
566209388Sae			    (uintmax_t)(len - size) < grade) {
567221363Sae				start = a_first;
568193673Smarcel				grade = len - size;
569193673Smarcel			}
570193673Smarcel		} else if (has_start) {
571221363Sae			if (start >= a_first && start <= last) {
572221363Sae				size = ALIGNDOWN(last - start + 1, alignment);
573221363Sae				grade = start - a_first;
574193673Smarcel			}
575193673Smarcel		} else {
576193673Smarcel			if (grade == ~0ULL || len > size) {
577221363Sae				start = a_first;
578193673Smarcel				size = len;
579193673Smarcel				grade = 0;
580193673Smarcel			}
581193673Smarcel		}
582193673Smarcel	}
583209388Sae	if (grade == ~0ULL) {
584209388Sae		geom_deletetree(&mesh);
585193673Smarcel		return (ENOSPC);
586209388Sae	}
587221967Sae	start -= offset;	/* Return back to real offset */
588209388Saedone:
589209388Sae	snprintf(ssize, sizeof(ssize), "%jd", (intmax_t)size);
590209388Sae	gctl_change_param(req, "size", -1, ssize);
591209388Sae	snprintf(sstart, sizeof(sstart), "%jd", (intmax_t)start);
592209388Sae	gctl_change_param(req, "start", -1, sstart);
593209388Sae	geom_deletetree(&mesh);
594193673Smarcel	return (0);
595193673Smarcel}
596193673Smarcel
597172837Smarcelstatic void
598219415Saegpart_show_geom(struct ggeom *gp, const char *element, int show_providers)
599172837Smarcel{
600172837Smarcel	struct gprovider *pp;
601172837Smarcel	const char *s, *scheme;
602209388Sae	off_t first, last, sector, end;
603209388Sae	off_t length, secsz;
604219415Sae	int idx, wblocks, wname, wmax;
605172837Smarcel
606281303Smav	if (find_geomcfg(gp, "wither"))
607281303Smav		return;
608172837Smarcel	scheme = find_geomcfg(gp, "scheme");
609259311Sasomers	if (scheme == NULL)
610259311Sasomers		errx(EXIT_FAILURE, "Scheme not found for geom %s", gp->lg_name);
611172837Smarcel	s = find_geomcfg(gp, "first");
612259311Sasomers	if (s == NULL)
613259311Sasomers		errx(EXIT_FAILURE, "Starting block not found for geom %s",
614259311Sasomers		    gp->lg_name);
615209388Sae	first = (off_t)strtoimax(s, NULL, 0);
616172837Smarcel	s = find_geomcfg(gp, "last");
617259311Sasomers	if (s == NULL)
618259311Sasomers		errx(EXIT_FAILURE, "Final block not found for geom %s",
619259311Sasomers		    gp->lg_name);
620209388Sae	last = (off_t)strtoimax(s, NULL, 0);
621172837Smarcel	wblocks = strlen(s);
622214352Sae	s = find_geomcfg(gp, "state");
623259311Sasomers	if (s == NULL)
624259311Sasomers		errx(EXIT_FAILURE, "State not found for geom %s", gp->lg_name);
625214352Sae	if (s != NULL && *s != 'C')
626214352Sae		s = NULL;
627219415Sae	wmax = strlen(gp->lg_name);
628219415Sae	if (show_providers) {
629219415Sae		LIST_FOREACH(pp, &gp->lg_provider, lg_provider) {
630219415Sae			wname = strlen(pp->lg_name);
631219415Sae			if (wname > wmax)
632219415Sae				wmax = wname;
633219415Sae		}
634219415Sae	}
635219415Sae	wname = wmax;
636172837Smarcel	pp = LIST_FIRST(&gp->lg_consumer)->lg_provider;
637172837Smarcel	secsz = pp->lg_sectorsize;
638214352Sae	printf("=>%*jd  %*jd  %*s  %s  (%s)%s\n",
639209388Sae	    wblocks, (intmax_t)first, wblocks, (intmax_t)(last - first + 1),
640172837Smarcel	    wname, gp->lg_name,
641214352Sae	    scheme, fmtsize(pp->lg_mediasize),
642214352Sae	    s ? " [CORRUPT]": "");
643172837Smarcel
644172837Smarcel	while ((pp = find_provider(gp, first)) != NULL) {
645188330Smarcel		s = find_provcfg(pp, "start");
646221952Sae		sector = (off_t)strtoimax(s, NULL, 0);
647188330Smarcel
648188330Smarcel		s = find_provcfg(pp, "end");
649221952Sae		end = (off_t)strtoimax(s, NULL, 0);
650221952Sae		length = end - sector + 1;
651221952Sae
652172837Smarcel		s = find_provcfg(pp, "index");
653172837Smarcel		idx = atoi(s);
654172837Smarcel		if (first < sector) {
655209388Sae			printf("  %*jd  %*jd  %*s  - free -  (%s)\n",
656209388Sae			    wblocks, (intmax_t)first, wblocks,
657209388Sae			    (intmax_t)(sector - first), wname, "",
658172837Smarcel			    fmtsize((sector - first) * secsz));
659172837Smarcel		}
660219415Sae		if (show_providers) {
661219415Sae			printf("  %*jd  %*jd  %*s  %s %s (%s)\n",
662219415Sae			    wblocks, (intmax_t)sector, wblocks,
663219415Sae			    (intmax_t)length, wname, pp->lg_name,
664219415Sae			    find_provcfg(pp, element), fmtattrib(pp),
665219415Sae			    fmtsize(pp->lg_mediasize));
666219415Sae		} else
667219415Sae			printf("  %*jd  %*jd  %*d  %s %s (%s)\n",
668219415Sae			    wblocks, (intmax_t)sector, wblocks,
669219415Sae			    (intmax_t)length, wname, idx,
670219415Sae			    find_provcfg(pp, element), fmtattrib(pp),
671219415Sae			    fmtsize(pp->lg_mediasize));
672188330Smarcel		first = end + 1;
673172837Smarcel	}
674172837Smarcel	if (first <= last) {
675188330Smarcel		length = last - first + 1;
676209388Sae		printf("  %*jd  %*jd  %*s  - free -  (%s)\n",
677209388Sae		    wblocks, (intmax_t)first, wblocks, (intmax_t)length,
678172837Smarcel		    wname, "",
679188330Smarcel		    fmtsize(length * secsz));
680172837Smarcel	}
681172837Smarcel	printf("\n");
682172837Smarcel}
683172837Smarcel
684179769Smarcelstatic int
685179769Smarcelgpart_show_hasopt(struct gctl_req *req, const char *opt, const char *elt)
686179769Smarcel{
687179769Smarcel
688215704Sbrucec	if (!gctl_get_int(req, "%s", opt))
689179769Smarcel		return (0);
690179769Smarcel
691179769Smarcel	if (elt != NULL)
692179769Smarcel		errx(EXIT_FAILURE, "-l and -r are mutually exclusive");
693179769Smarcel
694179769Smarcel	return (1);
695179769Smarcel}
696179769Smarcel
697172837Smarcelstatic void
698178180Smarcelgpart_show(struct gctl_req *req, unsigned int fl __unused)
699172837Smarcel{
700172837Smarcel	struct gmesh mesh;
701172837Smarcel	struct gclass *classp;
702172837Smarcel	struct ggeom *gp;
703179769Smarcel	const char *element, *name;
704219415Sae	int error, i, nargs, show_providers;
705172837Smarcel
706179769Smarcel	element = NULL;
707179769Smarcel	if (gpart_show_hasopt(req, "show_label", element))
708179769Smarcel		element = "label";
709179769Smarcel	if (gpart_show_hasopt(req, "show_rawtype", element))
710179769Smarcel		element = "rawtype";
711179769Smarcel	if (element == NULL)
712179769Smarcel		element = "type";
713179769Smarcel
714172837Smarcel	name = gctl_get_ascii(req, "class");
715172837Smarcel	if (name == NULL)
716172837Smarcel		abort();
717172837Smarcel	error = geom_gettree(&mesh);
718172837Smarcel	if (error != 0)
719172837Smarcel		errc(EXIT_FAILURE, error, "Cannot get GEOM tree");
720172837Smarcel	classp = find_class(&mesh, name);
721172837Smarcel	if (classp == NULL) {
722172837Smarcel		geom_deletetree(&mesh);
723172837Smarcel		errx(EXIT_FAILURE, "Class %s not found.", name);
724172837Smarcel	}
725219415Sae	show_providers = gctl_get_int(req, "show_providers");
726172837Smarcel	nargs = gctl_get_int(req, "nargs");
727172837Smarcel	if (nargs > 0) {
728172837Smarcel		for (i = 0; i < nargs; i++) {
729172837Smarcel			name = gctl_get_ascii(req, "arg%d", i);
730172837Smarcel			gp = find_geom(classp, name);
731172837Smarcel			if (gp != NULL)
732219415Sae				gpart_show_geom(gp, element, show_providers);
733172837Smarcel			else
734172837Smarcel				errx(EXIT_FAILURE, "No such geom: %s.", name);
735172837Smarcel		}
736172837Smarcel	} else {
737172837Smarcel		LIST_FOREACH(gp, &classp->lg_geom, lg_geom) {
738219415Sae			gpart_show_geom(gp, element, show_providers);
739172837Smarcel		}
740172837Smarcel	}
741172837Smarcel	geom_deletetree(&mesh);
742172837Smarcel}
743178180Smarcel
744215570Saestatic void
745215570Saegpart_backup(struct gctl_req *req, unsigned int fl __unused)
746215570Sae{
747215570Sae	struct gmesh mesh;
748215570Sae	struct gclass *classp;
749215570Sae	struct gprovider *pp;
750215570Sae	struct ggeom *gp;
751215570Sae	const char *s, *scheme;
752215570Sae	off_t sector, end;
753229916Seadler	off_t length;
754215671Sae	int error, i, windex, wblocks, wtype;
755215570Sae
756215570Sae	if (gctl_get_int(req, "nargs") != 1)
757215570Sae		errx(EXIT_FAILURE, "Invalid number of arguments.");
758215570Sae	error = geom_gettree(&mesh);
759215570Sae	if (error != 0)
760215570Sae		errc(EXIT_FAILURE, error, "Cannot get GEOM tree");
761215570Sae	s = gctl_get_ascii(req, "class");
762215570Sae	if (s == NULL)
763215570Sae		abort();
764215570Sae	classp = find_class(&mesh, s);
765215570Sae	if (classp == NULL) {
766215570Sae		geom_deletetree(&mesh);
767215570Sae		errx(EXIT_FAILURE, "Class %s not found.", s);
768215570Sae	}
769215570Sae	s = gctl_get_ascii(req, "arg0");
770215570Sae	if (s == NULL)
771215570Sae		abort();
772215570Sae	gp = find_geom(classp, s);
773215570Sae	if (gp == NULL)
774215570Sae		errx(EXIT_FAILURE, "No such geom: %s.", s);
775215570Sae	scheme = find_geomcfg(gp, "scheme");
776215570Sae	if (scheme == NULL)
777215570Sae		abort();
778215570Sae	pp = LIST_FIRST(&gp->lg_consumer)->lg_provider;
779215570Sae	s = find_geomcfg(gp, "last");
780259311Sasomers	if (s == NULL)
781259311Sasomers		abort();
782215570Sae	wblocks = strlen(s);
783215570Sae	wtype = 0;
784215570Sae	LIST_FOREACH(pp, &gp->lg_provider, lg_provider) {
785215570Sae		s = find_provcfg(pp, "type");
786215570Sae		i = strlen(s);
787215570Sae		if (i > wtype)
788215570Sae			wtype = i;
789215570Sae	}
790215570Sae	s = find_geomcfg(gp, "entries");
791259311Sasomers	if (s == NULL)
792259311Sasomers		abort();
793215570Sae	windex = strlen(s);
794215570Sae	printf("%s %s\n", scheme, s);
795215570Sae	LIST_FOREACH(pp, &gp->lg_provider, lg_provider) {
796215570Sae		s = find_provcfg(pp, "start");
797221952Sae		sector = (off_t)strtoimax(s, NULL, 0);
798215570Sae
799215570Sae		s = find_provcfg(pp, "end");
800221952Sae		end = (off_t)strtoimax(s, NULL, 0);
801221952Sae		length = end - sector + 1;
802221952Sae
803215570Sae		s = find_provcfg(pp, "label");
804215671Sae		printf("%-*s %*s %*jd %*jd %s %s\n",
805215570Sae		    windex, find_provcfg(pp, "index"),
806215570Sae		    wtype, find_provcfg(pp, "type"),
807215570Sae		    wblocks, (intmax_t)sector,
808215671Sae		    wblocks, (intmax_t)length,
809215671Sae		    (s != NULL) ? s: "", fmtattrib(pp));
810215570Sae	}
811215570Sae	geom_deletetree(&mesh);
812215570Sae}
813215570Sae
814215570Saestatic int
815215570Saeskip_line(const char *p)
816215570Sae{
817215570Sae
818215570Sae	while (*p != '\0') {
819215570Sae		if (*p == '#')
820215570Sae			return (1);
821215570Sae		if (isspace(*p) == 0)
822215570Sae			return (0);
823215570Sae		p++;
824215570Sae	}
825215570Sae	return (1);
826215570Sae}
827215570Sae
828215570Saestatic void
829215672Saegpart_sighndl(int sig __unused)
830215672Sae{
831215672Sae	undo_restore = 1;
832215672Sae}
833215672Sae
834215672Saestatic void
835215570Saegpart_restore(struct gctl_req *req, unsigned int fl __unused)
836215570Sae{
837215570Sae	struct gmesh mesh;
838215570Sae	struct gclass *classp;
839215570Sae	struct gctl_req *r;
840215570Sae	struct ggeom *gp;
841215672Sae	struct sigaction si_sa;
842215570Sae	const char *s, *flags, *errstr, *label;
843215570Sae	char **ap, *argv[6], line[BUFSIZ], *pline;
844215671Sae	int error, forced, i, l, nargs, created, rl;
845215570Sae	intmax_t n;
846215570Sae
847215570Sae	nargs = gctl_get_int(req, "nargs");
848215570Sae	if (nargs < 1)
849215570Sae		errx(EXIT_FAILURE, "Invalid number of arguments.");
850215570Sae
851215570Sae	forced = gctl_get_int(req, "force");
852215570Sae	flags = gctl_get_ascii(req, "flags");
853215671Sae	rl = gctl_get_int(req, "restore_labels");
854215570Sae	s = gctl_get_ascii(req, "class");
855215570Sae	if (s == NULL)
856215570Sae		abort();
857215570Sae	error = geom_gettree(&mesh);
858215570Sae	if (error != 0)
859215570Sae		errc(EXIT_FAILURE, error, "Cannot get GEOM tree");
860215570Sae	classp = find_class(&mesh, s);
861215570Sae	if (classp == NULL) {
862215570Sae		geom_deletetree(&mesh);
863215570Sae		errx(EXIT_FAILURE, "Class %s not found.", s);
864215570Sae	}
865215672Sae
866215672Sae	sigemptyset(&si_sa.sa_mask);
867215672Sae	si_sa.sa_flags = 0;
868215672Sae	si_sa.sa_handler = gpart_sighndl;
869215672Sae	if (sigaction(SIGINT, &si_sa, 0) == -1)
870215672Sae		err(EXIT_FAILURE, "sigaction SIGINT");
871215672Sae
872215570Sae	if (forced) {
873215570Sae		/* destroy existent partition table before restore */
874215570Sae		for (i = 0; i < nargs; i++) {
875215570Sae			s = gctl_get_ascii(req, "arg%d", i);
876215570Sae			gp = find_geom(classp, s);
877215570Sae			if (gp != NULL) {
878215570Sae				r = gctl_get_handle();
879215570Sae				gctl_ro_param(r, "class", -1,
880215570Sae				    classp->lg_name);
881215570Sae				gctl_ro_param(r, "verb", -1, "destroy");
882215570Sae				gctl_ro_param(r, "flags", -1, "restore");
883215570Sae				gctl_ro_param(r, "force", sizeof(forced),
884215570Sae				    &forced);
885215570Sae				gctl_ro_param(r, "arg0", -1, s);
886215570Sae				errstr = gctl_issue(r);
887215570Sae				if (errstr != NULL && errstr[0] != '\0') {
888215570Sae					gpart_print_error(errstr);
889215570Sae					gctl_free(r);
890215570Sae					goto backout;
891215570Sae				}
892215570Sae				gctl_free(r);
893215570Sae			}
894215570Sae		}
895215570Sae	}
896215570Sae	created = 0;
897215672Sae	while (undo_restore == 0 &&
898215672Sae	    fgets(line, sizeof(line) - 1, stdin) != NULL) {
899215570Sae		/* Format of backup entries:
900215570Sae		 * <scheme name> <number of entries>
901215570Sae		 * <index> <type> <start> <size> [label] ['['attrib[,attrib]']']
902215570Sae		 */
903215570Sae		pline = (char *)line;
904215570Sae		pline[strlen(line) - 1] = 0;
905215570Sae		if (skip_line(pline))
906215570Sae			continue;
907215570Sae		for (ap = argv;
908215570Sae		    (*ap = strsep(&pline, " \t")) != NULL;)
909215570Sae			if (**ap != '\0' && ++ap >= &argv[6])
910215570Sae				break;
911215570Sae		l = ap - &argv[0];
912215570Sae		label = pline = NULL;
913215671Sae		if (l == 1 || l == 2) { /* create table */
914215570Sae			if (created)
915215570Sae				errx(EXIT_FAILURE, "Incorrect backup format.");
916215671Sae			if (l == 2)
917215671Sae				n = strtoimax(argv[1], NULL, 0);
918215570Sae			for (i = 0; i < nargs; i++) {
919215570Sae				s = gctl_get_ascii(req, "arg%d", i);
920215570Sae				r = gctl_get_handle();
921215570Sae				gctl_ro_param(r, "class", -1,
922215570Sae				    classp->lg_name);
923215570Sae				gctl_ro_param(r, "verb", -1, "create");
924215570Sae				gctl_ro_param(r, "scheme", -1, argv[0]);
925215671Sae				if (l == 2)
926215671Sae					gctl_ro_param(r, "entries",
927215671Sae					    sizeof(n), &n);
928215570Sae				gctl_ro_param(r, "flags", -1, "restore");
929215570Sae				gctl_ro_param(r, "arg0", -1, s);
930215570Sae				errstr = gctl_issue(r);
931215570Sae				if (errstr != NULL && errstr[0] != '\0') {
932215570Sae					gpart_print_error(errstr);
933215570Sae					gctl_free(r);
934215570Sae					goto backout;
935215570Sae				}
936215570Sae				gctl_free(r);
937215570Sae			}
938215570Sae			created = 1;
939215570Sae			continue;
940215570Sae		} else if (l < 4 || created == 0)
941215570Sae			errx(EXIT_FAILURE, "Incorrect backup format.");
942215570Sae		else if (l == 5) {
943215570Sae			if (strchr(argv[4], '[') == NULL)
944215570Sae				label = argv[4];
945215570Sae			else
946215570Sae				pline = argv[4];
947215570Sae		} else if (l == 6) {
948215570Sae			label = argv[4];
949215570Sae			pline = argv[5];
950215570Sae		}
951215570Sae		/* Add partitions to each table */
952215570Sae		for (i = 0; i < nargs; i++) {
953215570Sae			s = gctl_get_ascii(req, "arg%d", i);
954215570Sae			r = gctl_get_handle();
955215570Sae			n = strtoimax(argv[0], NULL, 0);
956215570Sae			gctl_ro_param(r, "class", -1, classp->lg_name);
957215570Sae			gctl_ro_param(r, "verb", -1, "add");
958215570Sae			gctl_ro_param(r, "flags", -1, "restore");
959215570Sae			gctl_ro_param(r, GPART_PARAM_INDEX, sizeof(n), &n);
960215570Sae			gctl_ro_param(r, "type", -1, argv[1]);
961215570Sae			gctl_ro_param(r, "start", -1, argv[2]);
962215570Sae			gctl_ro_param(r, "size", -1, argv[3]);
963215671Sae			if (rl != 0 && label != NULL)
964215570Sae				gctl_ro_param(r, "label", -1, argv[4]);
965223158Sae			gctl_ro_param(r, "alignment", -1, GPART_AUTOFILL);
966215570Sae			gctl_ro_param(r, "arg0", -1, s);
967215570Sae			error = gpart_autofill(r);
968215570Sae			if (error != 0)
969215570Sae				errc(EXIT_FAILURE, error, "autofill");
970215570Sae			errstr = gctl_issue(r);
971215570Sae			if (errstr != NULL && errstr[0] != '\0') {
972215570Sae				gpart_print_error(errstr);
973215570Sae				gctl_free(r);
974215570Sae				goto backout;
975215570Sae			}
976215570Sae			gctl_free(r);
977215570Sae		}
978215570Sae		if (pline == NULL || *pline != '[')
979215570Sae			continue;
980215570Sae		/* set attributes */
981215570Sae		pline++;
982215570Sae		for (ap = argv;
983215570Sae		    (*ap = strsep(&pline, ",]")) != NULL;)
984215570Sae			if (**ap != '\0' && ++ap >= &argv[6])
985215570Sae				break;
986215570Sae		for (i = 0; i < nargs; i++) {
987215570Sae			l = ap - &argv[0];
988215570Sae			s = gctl_get_ascii(req, "arg%d", i);
989215570Sae			while (l > 0) {
990215570Sae				r = gctl_get_handle();
991215570Sae				gctl_ro_param(r, "class", -1, classp->lg_name);
992215570Sae				gctl_ro_param(r, "verb", -1, "set");
993215570Sae				gctl_ro_param(r, "flags", -1, "restore");
994215570Sae				gctl_ro_param(r, GPART_PARAM_INDEX,
995215570Sae				    sizeof(n), &n);
996215570Sae				gctl_ro_param(r, "attrib", -1, argv[--l]);
997215570Sae				gctl_ro_param(r, "arg0", -1, s);
998215570Sae				errstr = gctl_issue(r);
999215570Sae				if (errstr != NULL && errstr[0] != '\0') {
1000215570Sae					gpart_print_error(errstr);
1001215570Sae					gctl_free(r);
1002215570Sae					goto backout;
1003215570Sae				}
1004215570Sae				gctl_free(r);
1005215570Sae			}
1006215570Sae		}
1007215570Sae	}
1008215672Sae	if (undo_restore)
1009215672Sae		goto backout;
1010215570Sae	/* commit changes if needed */
1011215570Sae	if (strchr(flags, 'C') != NULL) {
1012215570Sae		for (i = 0; i < nargs; i++) {
1013215570Sae			s = gctl_get_ascii(req, "arg%d", i);
1014215570Sae			r = gctl_get_handle();
1015215570Sae			gctl_ro_param(r, "class", -1, classp->lg_name);
1016215570Sae			gctl_ro_param(r, "verb", -1, "commit");
1017215570Sae			gctl_ro_param(r, "arg0", -1, s);
1018215570Sae			errstr = gctl_issue(r);
1019215570Sae			if (errstr != NULL && errstr[0] != '\0') {
1020215570Sae				gpart_print_error(errstr);
1021215570Sae				gctl_free(r);
1022215570Sae				goto backout;
1023215570Sae			}
1024215570Sae			gctl_free(r);
1025215570Sae		}
1026215570Sae	}
1027215570Sae	gctl_free(req);
1028215570Sae	geom_deletetree(&mesh);
1029215570Sae	exit(EXIT_SUCCESS);
1030215570Sae
1031215570Saebackout:
1032215570Sae	for (i = 0; i < nargs; i++) {
1033215570Sae		s = gctl_get_ascii(req, "arg%d", i);
1034215570Sae		r = gctl_get_handle();
1035215570Sae		gctl_ro_param(r, "class", -1, classp->lg_name);
1036215570Sae		gctl_ro_param(r, "verb", -1, "undo");
1037215570Sae		gctl_ro_param(r, "arg0", -1, s);
1038215570Sae		gctl_issue(r);
1039215570Sae		gctl_free(r);
1040215570Sae	}
1041215570Sae	gctl_free(req);
1042215570Sae	geom_deletetree(&mesh);
1043215570Sae	exit(EXIT_FAILURE);
1044215570Sae}
1045215570Sae
1046179629Smarcelstatic void *
1047179629Smarcelgpart_bootfile_read(const char *bootfile, ssize_t *size)
1048178180Smarcel{
1049178180Smarcel	struct stat sb;
1050178180Smarcel	void *code;
1051179629Smarcel	int fd;
1052178180Smarcel
1053179629Smarcel	if (stat(bootfile, &sb) == -1)
1054179629Smarcel		err(EXIT_FAILURE, "%s", bootfile);
1055178180Smarcel	if (!S_ISREG(sb.st_mode))
1056178180Smarcel		errx(EXIT_FAILURE, "%s: not a regular file", bootfile);
1057179629Smarcel	if (sb.st_size == 0)
1058179629Smarcel		errx(EXIT_FAILURE, "%s: empty file", bootfile);
1059208777Smarius	if (*size > 0 && sb.st_size > *size)
1060179629Smarcel		errx(EXIT_FAILURE, "%s: file too big (%zu limit)", bootfile,
1061179629Smarcel		    *size);
1062178180Smarcel
1063179629Smarcel	*size = sb.st_size;
1064178180Smarcel
1065178180Smarcel	fd = open(bootfile, O_RDONLY);
1066178180Smarcel	if (fd == -1)
1067179629Smarcel		err(EXIT_FAILURE, "%s", bootfile);
1068179629Smarcel	code = malloc(*size);
1069178180Smarcel	if (code == NULL)
1070179629Smarcel		err(EXIT_FAILURE, NULL);
1071179629Smarcel	if (read(fd, code, *size) != *size)
1072179629Smarcel		err(EXIT_FAILURE, "%s", bootfile);
1073178180Smarcel	close(fd);
1074178180Smarcel
1075179629Smarcel	return (code);
1076178180Smarcel}
1077179629Smarcel
1078179629Smarcelstatic void
1079208777Smariusgpart_write_partcode(struct ggeom *gp, int idx, void *code, ssize_t size)
1080179629Smarcel{
1081179629Smarcel	char dsf[128];
1082179629Smarcel	struct gprovider *pp;
1083179629Smarcel	const char *s;
1084185038Smarcel	char *buf;
1085185038Smarcel	off_t bsize;
1086208777Smarius	int fd;
1087179629Smarcel
1088179629Smarcel	LIST_FOREACH(pp, &gp->lg_provider, lg_provider) {
1089179629Smarcel		s = find_provcfg(pp, "index");
1090179629Smarcel		if (s == NULL)
1091179629Smarcel			continue;
1092179629Smarcel		if (atoi(s) == idx)
1093179629Smarcel			break;
1094179629Smarcel	}
1095179629Smarcel
1096179629Smarcel	if (pp != NULL) {
1097179629Smarcel		snprintf(dsf, sizeof(dsf), "/dev/%s", pp->lg_name);
1098285936Sae		if (pp->lg_mediasize < size)
1099285936Sae			errx(EXIT_FAILURE, "%s: not enough space", dsf);
1100179629Smarcel		fd = open(dsf, O_WRONLY);
1101179629Smarcel		if (fd == -1)
1102179629Smarcel			err(EXIT_FAILURE, "%s", dsf);
1103185038Smarcel		/*
1104185038Smarcel		 * When writing to a disk device, the write must be
1105185038Smarcel		 * sector aligned and not write to any partial sectors,
1106185038Smarcel		 * so round up the buffer size to the next sector and zero it.
1107185038Smarcel		 */
1108185038Smarcel		bsize = (size + pp->lg_sectorsize - 1) /
1109185038Smarcel		    pp->lg_sectorsize * pp->lg_sectorsize;
1110185038Smarcel		buf = calloc(1, bsize);
1111185038Smarcel		if (buf == NULL)
1112179629Smarcel			err(EXIT_FAILURE, "%s", dsf);
1113185038Smarcel		bcopy(code, buf, size);
1114185038Smarcel		if (write(fd, buf, bsize) != bsize)
1115185038Smarcel			err(EXIT_FAILURE, "%s", dsf);
1116185038Smarcel		free(buf);
1117179629Smarcel		close(fd);
1118298628Sae		printf("partcode written to %s\n", pp->lg_name);
1119179629Smarcel	} else
1120179629Smarcel		errx(EXIT_FAILURE, "invalid partition index");
1121208777Smarius}
1122179629Smarcel
1123208777Smariusstatic void
1124208777Smariusgpart_write_partcode_vtoc8(struct ggeom *gp, int idx, void *code)
1125208777Smarius{
1126208777Smarius	char dsf[128];
1127208777Smarius	struct gprovider *pp;
1128208777Smarius	const char *s;
1129208777Smarius	int installed, fd;
1130208777Smarius
1131208777Smarius	installed = 0;
1132208777Smarius	LIST_FOREACH(pp, &gp->lg_provider, lg_provider) {
1133208777Smarius		s = find_provcfg(pp, "index");
1134208777Smarius		if (s == NULL)
1135208777Smarius			continue;
1136208777Smarius		if (idx != 0 && atoi(s) != idx)
1137208777Smarius			continue;
1138208777Smarius		snprintf(dsf, sizeof(dsf), "/dev/%s", pp->lg_name);
1139208777Smarius		if (pp->lg_sectorsize != sizeof(struct vtoc8))
1140208777Smarius			errx(EXIT_FAILURE, "%s: unexpected sector "
1141208777Smarius			    "size (%d)\n", dsf, pp->lg_sectorsize);
1142285936Sae		if (pp->lg_mediasize < VTOC_BOOTSIZE)
1143285936Sae			continue;
1144208777Smarius		fd = open(dsf, O_WRONLY);
1145208777Smarius		if (fd == -1)
1146208777Smarius			err(EXIT_FAILURE, "%s", dsf);
1147208777Smarius		/*
1148208777Smarius		 * We ignore the first VTOC_BOOTSIZE bytes of boot code in
1149208777Smarius		 * order to avoid overwriting the label.
1150208777Smarius		 */
1151208777Smarius		if (lseek(fd, sizeof(struct vtoc8), SEEK_SET) !=
1152208777Smarius		    sizeof(struct vtoc8))
1153208777Smarius			err(EXIT_FAILURE, "%s", dsf);
1154208777Smarius		if (write(fd, (caddr_t)code + sizeof(struct vtoc8),
1155208777Smarius		    VTOC_BOOTSIZE - sizeof(struct vtoc8)) != VTOC_BOOTSIZE -
1156208777Smarius		    sizeof(struct vtoc8))
1157208777Smarius			err(EXIT_FAILURE, "%s", dsf);
1158208777Smarius		installed++;
1159208777Smarius		close(fd);
1160208777Smarius		if (idx != 0 && atoi(s) == idx)
1161208777Smarius			break;
1162208777Smarius	}
1163208777Smarius	if (installed == 0)
1164208777Smarius		errx(EXIT_FAILURE, "%s: no partitions", gp->lg_name);
1165298628Sae	else
1166298628Sae		printf("partcode written to %s\n",
1167298628Sae		    idx != 0 ? pp->lg_name: gp->lg_name);
1168179629Smarcel}
1169179629Smarcel
1170179629Smarcelstatic void
1171185454Smarcelgpart_bootcode(struct gctl_req *req, unsigned int fl)
1172179629Smarcel{
1173208777Smarius	struct gmesh mesh;
1174208777Smarius	struct gclass *classp;
1175208777Smarius	struct ggeom *gp;
1176179629Smarcel	const char *s;
1177179629Smarcel	void *bootcode, *partcode;
1178179629Smarcel	size_t bootsize, partsize;
1179208777Smarius	int error, idx, vtoc8;
1180179629Smarcel
1181212554Spjd	if (gctl_has_param(req, GPART_PARAM_BOOTCODE)) {
1182212554Spjd		s = gctl_get_ascii(req, GPART_PARAM_BOOTCODE);
1183208173Snwhitehorn		bootsize = 800 * 1024;		/* Arbitrary limit. */
1184179629Smarcel		bootcode = gpart_bootfile_read(s, &bootsize);
1185212554Spjd		error = gctl_change_param(req, GPART_PARAM_BOOTCODE, bootsize,
1186179629Smarcel		    bootcode);
1187179629Smarcel		if (error)
1188179629Smarcel			errc(EXIT_FAILURE, error, "internal error");
1189298628Sae	} else
1190179629Smarcel		bootcode = NULL;
1191179629Smarcel
1192208777Smarius	s = gctl_get_ascii(req, "class");
1193208777Smarius	if (s == NULL)
1194208777Smarius		abort();
1195208777Smarius	error = geom_gettree(&mesh);
1196208777Smarius	if (error != 0)
1197208777Smarius		errc(EXIT_FAILURE, error, "Cannot get GEOM tree");
1198208777Smarius	classp = find_class(&mesh, s);
1199208777Smarius	if (classp == NULL) {
1200208777Smarius		geom_deletetree(&mesh);
1201208777Smarius		errx(EXIT_FAILURE, "Class %s not found.", s);
1202208777Smarius	}
1203216619Sae	if (gctl_get_int(req, "nargs") != 1)
1204216619Sae		errx(EXIT_FAILURE, "Invalid number of arguments.");
1205212554Spjd	s = gctl_get_ascii(req, "arg0");
1206208777Smarius	if (s == NULL)
1207208777Smarius		abort();
1208208777Smarius	gp = find_geom(classp, s);
1209208777Smarius	if (gp == NULL)
1210208777Smarius		errx(EXIT_FAILURE, "No such geom: %s.", s);
1211208777Smarius	s = find_geomcfg(gp, "scheme");
1212259311Sasomers	if (s == NULL)
1213259311Sasomers		errx(EXIT_FAILURE, "Scheme not found for geom %s", gp->lg_name);
1214208777Smarius	if (strcmp(s, "VTOC8") == 0)
1215208777Smarius		vtoc8 = 1;
1216298628Sae	else
1217298628Sae		vtoc8 = 0;
1218208777Smarius
1219212554Spjd	if (gctl_has_param(req, GPART_PARAM_PARTCODE)) {
1220212554Spjd		s = gctl_get_ascii(req, GPART_PARAM_PARTCODE);
1221298628Sae		if (vtoc8 != 0)
1222298628Sae			partsize = VTOC_BOOTSIZE;
1223298628Sae		else
1224298628Sae			partsize = 1024 * 1024;		/* Arbitrary limit. */
1225179629Smarcel		partcode = gpart_bootfile_read(s, &partsize);
1226212554Spjd		error = gctl_delete_param(req, GPART_PARAM_PARTCODE);
1227179629Smarcel		if (error)
1228179629Smarcel			errc(EXIT_FAILURE, error, "internal error");
1229298628Sae	} else
1230179629Smarcel		partcode = NULL;
1231179629Smarcel
1232212554Spjd	if (gctl_has_param(req, GPART_PARAM_INDEX)) {
1233179629Smarcel		if (partcode == NULL)
1234179629Smarcel			errx(EXIT_FAILURE, "-i is only valid with -p");
1235212708Spjd		idx = (int)gctl_get_intmax(req, GPART_PARAM_INDEX);
1236212708Spjd		if (idx < 1)
1237179629Smarcel			errx(EXIT_FAILURE, "invalid partition index");
1238212554Spjd		error = gctl_delete_param(req, GPART_PARAM_INDEX);
1239179629Smarcel		if (error)
1240179629Smarcel			errc(EXIT_FAILURE, error, "internal error");
1241179629Smarcel	} else
1242179629Smarcel		idx = 0;
1243179629Smarcel
1244179629Smarcel	if (partcode != NULL) {
1245208777Smarius		if (vtoc8 == 0) {
1246208777Smarius			if (idx == 0)
1247208777Smarius				errx(EXIT_FAILURE, "missing -i option");
1248208777Smarius			gpart_write_partcode(gp, idx, partcode, partsize);
1249223364Sae		} else {
1250223364Sae			if (partsize != VTOC_BOOTSIZE)
1251223364Sae				errx(EXIT_FAILURE, "invalid bootcode");
1252208777Smarius			gpart_write_partcode_vtoc8(gp, idx, partcode);
1253223364Sae		}
1254208777Smarius	} else
1255179629Smarcel		if (bootcode == NULL)
1256179629Smarcel			errx(EXIT_FAILURE, "no -b nor -p");
1257179629Smarcel
1258185454Smarcel	if (bootcode != NULL)
1259185454Smarcel		gpart_issue(req, fl);
1260208777Smarius
1261208777Smarius	geom_deletetree(&mesh);
1262319258Sasomers	free(partcode);
1263185454Smarcel}
1264185454Smarcel
1265185454Smarcelstatic void
1266213097Saegpart_print_error(const char *errstr)
1267213097Sae{
1268213097Sae	char *errmsg;
1269213097Sae	int error;
1270213097Sae
1271213097Sae	error = strtol(errstr, &errmsg, 0);
1272213097Sae	if (errmsg != errstr) {
1273213097Sae		while (errmsg[0] == ' ')
1274213097Sae			errmsg++;
1275213097Sae		if (errmsg[0] != '\0')
1276213097Sae			warnc(error, "%s", errmsg);
1277213097Sae		else
1278213097Sae			warnc(error, NULL);
1279213097Sae	} else
1280213097Sae		warnx("%s", errmsg);
1281213097Sae}
1282213097Sae
1283319258Sasomersstatic _Noreturn void
1284185454Smarcelgpart_issue(struct gctl_req *req, unsigned int fl __unused)
1285185454Smarcel{
1286185454Smarcel	char buf[4096];
1287185454Smarcel	const char *errstr;
1288185495Smarcel	int error, status;
1289185454Smarcel
1290212554Spjd	if (gctl_get_int(req, "nargs") != 1)
1291212554Spjd		errx(EXIT_FAILURE, "Invalid number of arguments.");
1292212554Spjd	(void)gctl_delete_param(req, "nargs");
1293212554Spjd
1294193673Smarcel	/* autofill parameters (if applicable). */
1295193673Smarcel	error = gpart_autofill(req);
1296193673Smarcel	if (error) {
1297193673Smarcel		warnc(error, "autofill");
1298193673Smarcel		status = EXIT_FAILURE;
1299193673Smarcel		goto done;
1300193673Smarcel	}
1301193673Smarcel
1302185454Smarcel	bzero(buf, sizeof(buf));
1303185454Smarcel	gctl_rw_param(req, "output", sizeof(buf), buf);
1304185454Smarcel	errstr = gctl_issue(req);
1305185454Smarcel	if (errstr == NULL || errstr[0] == '\0') {
1306185454Smarcel		if (buf[0] != '\0')
1307185454Smarcel			printf("%s", buf);
1308185495Smarcel		status = EXIT_SUCCESS;
1309185495Smarcel		goto done;
1310179629Smarcel	}
1311185454Smarcel
1312213097Sae	gpart_print_error(errstr);
1313185495Smarcel	status = EXIT_FAILURE;
1314185495Smarcel
1315185495Smarcel done:
1316185495Smarcel	gctl_free(req);
1317185495Smarcel	exit(status);
1318179629Smarcel}
1319