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