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