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