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