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