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