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