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