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