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