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