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