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