geom_part.c revision 214352
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 214352 2010-10-25 16:23:35Z ae $"); 29 30#include <sys/stat.h> 31#include <sys/vtoc.h> 32 33#include <assert.h> 34#include <err.h> 35#include <errno.h> 36#include <fcntl.h> 37#include <libgeom.h> 38#include <libutil.h> 39#include <paths.h> 40#include <stdint.h> 41#include <stdio.h> 42#include <stdlib.h> 43#include <limits.h> 44#include <inttypes.h> 45#include <string.h> 46#include <strings.h> 47#include <unistd.h> 48 49#include "core/geom.h" 50#include "misc/subr.h" 51 52#ifdef STATIC_GEOM_CLASSES 53#define PUBSYM(x) gpart_##x 54#else 55#define PUBSYM(x) x 56#endif 57 58uint32_t PUBSYM(lib_version) = G_LIB_VERSION; 59uint32_t PUBSYM(version) = 0; 60 61static char sstart[32]; 62static char ssize[32]; 63 64#define GPART_AUTOFILL "*" 65#define GPART_FLAGS "C" 66 67#define GPART_PARAM_BOOTCODE "bootcode" 68#define GPART_PARAM_INDEX "index" 69#define GPART_PARAM_PARTCODE "partcode" 70 71static struct gclass *find_class(struct gmesh *, const char *); 72static struct ggeom * find_geom(struct gclass *, const char *); 73static const char *find_geomcfg(struct ggeom *, const char *); 74static const char *find_provcfg(struct gprovider *, const char *); 75static struct gprovider *find_provider(struct ggeom *, off_t); 76static const char *fmtsize(int64_t); 77static int gpart_autofill(struct gctl_req *); 78static int gpart_autofill_resize(struct gctl_req *); 79static void gpart_bootcode(struct gctl_req *, unsigned int); 80static void *gpart_bootfile_read(const char *, ssize_t *); 81static void gpart_issue(struct gctl_req *, unsigned int); 82static void gpart_show(struct gctl_req *, unsigned int); 83static void gpart_show_geom(struct ggeom *, const char *); 84static int gpart_show_hasopt(struct gctl_req *, const char *, const char *); 85static void gpart_write_partcode(struct ggeom *, int, void *, ssize_t); 86static void gpart_write_partcode_vtoc8(struct ggeom *, int, void *); 87static void gpart_print_error(const char *); 88 89struct g_command PUBSYM(class_commands)[] = { 90 { "add", 0, gpart_issue, { 91 { 'b', "start", GPART_AUTOFILL, G_TYPE_STRING }, 92 { 's', "size", GPART_AUTOFILL, G_TYPE_STRING }, 93 { 't', "type", NULL, G_TYPE_STRING }, 94 { 'i', GPART_PARAM_INDEX, G_VAL_OPTIONAL, G_TYPE_NUMBER }, 95 { 'l', "label", G_VAL_OPTIONAL, G_TYPE_STRING }, 96 { 'f', "flags", GPART_FLAGS, G_TYPE_STRING }, 97 G_OPT_SENTINEL }, 98 "[-b start] [-s size] -t type [-i index] [-l label] [-f flags] geom" 99 }, 100 { "bootcode", 0, gpart_bootcode, { 101 { 'b', GPART_PARAM_BOOTCODE, G_VAL_OPTIONAL, G_TYPE_STRING }, 102 { 'p', GPART_PARAM_PARTCODE, G_VAL_OPTIONAL, G_TYPE_STRING }, 103 { 'i', GPART_PARAM_INDEX, G_VAL_OPTIONAL, G_TYPE_NUMBER }, 104 { 'f', "flags", GPART_FLAGS, G_TYPE_STRING }, 105 G_OPT_SENTINEL }, 106 "[-b bootcode] [-p partcode] [-i index] [-f flags] geom" 107 }, 108 { "commit", 0, gpart_issue, G_NULL_OPTS, 109 "geom" 110 }, 111 { "create", 0, gpart_issue, { 112 { 's', "scheme", NULL, G_TYPE_STRING }, 113 { 'n', "entries", G_VAL_OPTIONAL, G_TYPE_NUMBER }, 114 { 'f', "flags", GPART_FLAGS, G_TYPE_STRING }, 115 G_OPT_SENTINEL }, 116 "-s scheme [-n entries] [-f flags] provider" 117 }, 118 { "delete", 0, gpart_issue, { 119 { 'i', GPART_PARAM_INDEX, NULL, G_TYPE_NUMBER }, 120 { 'f', "flags", GPART_FLAGS, G_TYPE_STRING }, 121 G_OPT_SENTINEL }, 122 "-i index [-f flags] geom" 123 }, 124 { "destroy", 0, gpart_issue, { 125 { 'F', "force", NULL, G_TYPE_BOOL }, 126 { 'f', "flags", GPART_FLAGS, G_TYPE_STRING }, 127 G_OPT_SENTINEL }, 128 "[-F] [-f flags] geom" 129 }, 130 { "modify", 0, gpart_issue, { 131 { 'i', GPART_PARAM_INDEX, NULL, G_TYPE_NUMBER }, 132 { 'l', "label", G_VAL_OPTIONAL, G_TYPE_STRING }, 133 { 't', "type", G_VAL_OPTIONAL, G_TYPE_STRING }, 134 { 'f', "flags", GPART_FLAGS, G_TYPE_STRING }, 135 G_OPT_SENTINEL }, 136 "-i index [-l label] [-t type] [-f flags] geom" 137 }, 138 { "set", 0, gpart_issue, { 139 { 'a', "attrib", NULL, G_TYPE_STRING }, 140 { 'i', GPART_PARAM_INDEX, NULL, G_TYPE_NUMBER }, 141 { 'f', "flags", GPART_FLAGS, G_TYPE_STRING }, 142 G_OPT_SENTINEL }, 143 "-a attrib -i index [-f flags] geom" 144 }, 145 { "show", 0, gpart_show, { 146 { 'l', "show_label", NULL, G_TYPE_BOOL }, 147 { 'r', "show_rawtype", NULL, G_TYPE_BOOL }, 148 G_OPT_SENTINEL }, 149 "[-lr] [geom ...]" 150 }, 151 { "undo", 0, gpart_issue, G_NULL_OPTS, 152 "geom" 153 }, 154 { "unset", 0, gpart_issue, { 155 { 'a', "attrib", NULL, G_TYPE_STRING }, 156 { 'i', GPART_PARAM_INDEX, NULL, G_TYPE_NUMBER }, 157 { 'f', "flags", GPART_FLAGS, G_TYPE_STRING }, 158 G_OPT_SENTINEL }, 159 "-a attrib -i index [-f flags] geom" 160 }, 161 { "resize", 0, gpart_issue, { 162 { 's', "size", GPART_AUTOFILL, G_TYPE_STRING }, 163 { 'i', GPART_PARAM_INDEX, NULL, G_TYPE_NUMBER }, 164 { 'f', "flags", GPART_FLAGS, G_TYPE_STRING }, 165 G_OPT_SENTINEL }, 166 "[-s size] -i index [-f flags] geom" 167 }, 168 { "recover", 0, gpart_issue, { 169 { 'f', "flags", GPART_FLAGS, G_TYPE_STRING }, 170 G_OPT_SENTINEL }, 171 "[-f flags] geom" 172 }, 173 G_CMD_SENTINEL 174}; 175 176static struct gclass * 177find_class(struct gmesh *mesh, const char *name) 178{ 179 struct gclass *classp; 180 181 LIST_FOREACH(classp, &mesh->lg_class, lg_class) { 182 if (strcmp(classp->lg_name, name) == 0) 183 return (classp); 184 } 185 return (NULL); 186} 187 188static struct ggeom * 189find_geom(struct gclass *classp, const char *name) 190{ 191 struct ggeom *gp; 192 193 if (strncmp(name, _PATH_DEV, sizeof(_PATH_DEV) - 1) == 0) 194 name += sizeof(_PATH_DEV) - 1; 195 LIST_FOREACH(gp, &classp->lg_geom, lg_geom) { 196 if (strcmp(gp->lg_name, name) == 0) 197 return (gp); 198 } 199 return (NULL); 200} 201 202static const char * 203find_geomcfg(struct ggeom *gp, const char *cfg) 204{ 205 struct gconfig *gc; 206 207 LIST_FOREACH(gc, &gp->lg_config, lg_config) { 208 if (!strcmp(gc->lg_name, cfg)) 209 return (gc->lg_val); 210 } 211 return (NULL); 212} 213 214static const char * 215find_provcfg(struct gprovider *pp, const char *cfg) 216{ 217 struct gconfig *gc; 218 219 LIST_FOREACH(gc, &pp->lg_config, lg_config) { 220 if (!strcmp(gc->lg_name, cfg)) 221 return (gc->lg_val); 222 } 223 return (NULL); 224} 225 226static struct gprovider * 227find_provider(struct ggeom *gp, off_t minsector) 228{ 229 struct gprovider *pp, *bestpp; 230 const char *s; 231 off_t sector, bestsector; 232 233 bestpp = NULL; 234 bestsector = 0; 235 LIST_FOREACH(pp, &gp->lg_provider, lg_provider) { 236 s = find_provcfg(pp, "start"); 237 if (s == NULL) { 238 s = find_provcfg(pp, "offset"); 239 sector = 240 (off_t)strtoimax(s, NULL, 0) / pp->lg_sectorsize; 241 } else 242 sector = (off_t)strtoimax(s, NULL, 0); 243 244 if (sector < minsector) 245 continue; 246 if (bestpp != NULL && sector >= bestsector) 247 continue; 248 249 bestpp = pp; 250 bestsector = sector; 251 } 252 return (bestpp); 253} 254 255static const char * 256fmtsize(int64_t rawsz) 257{ 258 static char buf[5]; 259 260 humanize_number(buf, sizeof(buf), rawsz, "", HN_AUTOSCALE, 261 HN_B | HN_NOSPACE | HN_DECIMAL); 262 return (buf); 263} 264 265static const char * 266fmtattrib(struct gprovider *pp) 267{ 268 static char buf[128]; 269 struct gconfig *gc; 270 u_int idx; 271 272 buf[0] = '\0'; 273 idx = 0; 274 LIST_FOREACH(gc, &pp->lg_config, lg_config) { 275 if (strcmp(gc->lg_name, "attrib") != 0) 276 continue; 277 idx += snprintf(buf + idx, sizeof(buf) - idx, "%s%s", 278 (idx == 0) ? " [" : ",", gc->lg_val); 279 } 280 if (idx > 0) 281 snprintf(buf + idx, sizeof(buf) - idx, "] "); 282 return (buf); 283} 284 285static int 286gpart_autofill_resize(struct gctl_req *req) 287{ 288 struct gmesh mesh; 289 struct gclass *cp; 290 struct ggeom *gp; 291 struct gprovider *pp; 292 off_t last, size, start, new_size; 293 off_t lba, new_lba; 294 const char *s; 295 int error, idx; 296 297 idx = (int)gctl_get_intmax(req, GPART_PARAM_INDEX); 298 if (idx < 1) 299 errx(EXIT_FAILURE, "invalid partition index"); 300 301 error = geom_gettree(&mesh); 302 if (error) 303 return (error); 304 s = gctl_get_ascii(req, "class"); 305 if (s == NULL) 306 abort(); 307 cp = find_class(&mesh, s); 308 if (cp == NULL) 309 errx(EXIT_FAILURE, "Class %s not found.", s); 310 s = gctl_get_ascii(req, "arg0"); 311 if (s == NULL) 312 abort(); 313 gp = find_geom(cp, s); 314 if (gp == NULL) 315 errx(EXIT_FAILURE, "No such geom: %s.", s); 316 pp = LIST_FIRST(&gp->lg_consumer)->lg_provider; 317 if (pp == NULL) 318 errx(EXIT_FAILURE, "Provider for geom %s not found.", s); 319 320 s = gctl_get_ascii(req, "size"); 321 if (*s == '*') 322 new_size = 0; 323 else { 324 error = g_parse_lba(s, pp->lg_sectorsize, &new_size); 325 if (error) 326 errc(EXIT_FAILURE, error, "Invalid size param"); 327 /* no autofill necessary. */ 328 goto done; 329 } 330 331 last = (off_t)strtoimax(find_geomcfg(gp, "last"), NULL, 0); 332 LIST_FOREACH(pp, &gp->lg_provider, lg_provider) { 333 s = find_provcfg(pp, "index"); 334 if (s == NULL) 335 continue; 336 if (atoi(s) == idx) 337 break; 338 } 339 if (pp == NULL) 340 errx(EXIT_FAILURE, "invalid partition index"); 341 342 s = find_provcfg(pp, "start"); 343 if (s == NULL) { 344 s = find_provcfg(pp, "offset"); 345 start = (off_t)strtoimax(s, NULL, 0) / pp->lg_sectorsize; 346 } else 347 start = (off_t)strtoimax(s, NULL, 0); 348 s = find_provcfg(pp, "end"); 349 if (s == NULL) { 350 s = find_provcfg(pp, "length"); 351 lba = start + 352 (off_t)strtoimax(s, NULL, 0) / pp->lg_sectorsize; 353 } else 354 lba = (off_t)strtoimax(s, NULL, 0) + 1; 355 356 if (lba > last) { 357 geom_deletetree(&mesh); 358 return (ENOSPC); 359 } 360 size = lba - start; 361 pp = find_provider(gp, lba); 362 if (pp == NULL) 363 new_size = last - start + 1; 364 else { 365 s = find_provcfg(pp, "start"); 366 if (s == NULL) { 367 s = find_provcfg(pp, "offset"); 368 new_lba = 369 (off_t)strtoimax(s, NULL, 0) / pp->lg_sectorsize; 370 } else 371 new_lba = (off_t)strtoimax(s, NULL, 0); 372 /* 373 * Is there any free space between current and 374 * next providers? 375 */ 376 if (new_lba > lba) 377 new_size = new_lba - start; 378 else { 379 geom_deletetree(&mesh); 380 return (ENOSPC); 381 } 382 } 383done: 384 snprintf(ssize, sizeof(ssize), "%jd", (intmax_t)new_size); 385 gctl_change_param(req, "size", -1, ssize); 386 geom_deletetree(&mesh); 387 return (0); 388} 389 390static int 391gpart_autofill(struct gctl_req *req) 392{ 393 struct gmesh mesh; 394 struct gclass *cp; 395 struct ggeom *gp; 396 struct gprovider *pp; 397 off_t first, last; 398 off_t size, start; 399 off_t lba, len; 400 uintmax_t grade; 401 const char *s; 402 int error, has_size, has_start; 403 404 s = gctl_get_ascii(req, "verb"); 405 if (strcmp(s, "resize") == 0) 406 return gpart_autofill_resize(req); 407 if (strcmp(s, "add") != 0) 408 return (0); 409 410 error = geom_gettree(&mesh); 411 if (error) 412 return (error); 413 s = gctl_get_ascii(req, "class"); 414 if (s == NULL) 415 abort(); 416 cp = find_class(&mesh, s); 417 if (cp == NULL) 418 errx(EXIT_FAILURE, "Class %s not found.", s); 419 s = gctl_get_ascii(req, "arg0"); 420 if (s == NULL) 421 abort(); 422 gp = find_geom(cp, s); 423 if (gp == NULL) 424 errx(EXIT_FAILURE, "No such geom: %s.", s); 425 pp = LIST_FIRST(&gp->lg_consumer)->lg_provider; 426 if (pp == NULL) 427 errx(EXIT_FAILURE, "Provider for geom %s not found.", s); 428 429 s = gctl_get_ascii(req, "size"); 430 has_size = (*s == '*') ? 0 : 1; 431 size = 0; 432 if (has_size) { 433 error = g_parse_lba(s, pp->lg_sectorsize, &size); 434 if (error) 435 errc(EXIT_FAILURE, error, "Invalid size param"); 436 } 437 438 s = gctl_get_ascii(req, "start"); 439 has_start = (*s == '*') ? 0 : 1; 440 start = 0ULL; 441 if (has_start) { 442 error = g_parse_lba(s, pp->lg_sectorsize, &start); 443 if (error) 444 errc(EXIT_FAILURE, error, "Invalid start param"); 445 } 446 447 /* No autofill necessary. */ 448 if (has_size && has_start) 449 goto done; 450 451 first = (off_t)strtoimax(find_geomcfg(gp, "first"), NULL, 0); 452 last = (off_t)strtoimax(find_geomcfg(gp, "last"), NULL, 0); 453 grade = ~0ULL; 454 while ((pp = find_provider(gp, first)) != NULL) { 455 s = find_provcfg(pp, "start"); 456 if (s == NULL) { 457 s = find_provcfg(pp, "offset"); 458 lba = (off_t)strtoimax(s, NULL, 0) / pp->lg_sectorsize; 459 } else 460 lba = (off_t)strtoimax(s, NULL, 0); 461 462 if (first < lba) { 463 /* Free space [first, lba> */ 464 len = lba - first; 465 if (has_size) { 466 if (len >= size && 467 (uintmax_t)(len - size) < grade) { 468 start = first; 469 grade = len - size; 470 } 471 } else if (has_start) { 472 if (start >= first && start < lba) { 473 size = lba - start; 474 grade = start - first; 475 } 476 } else { 477 if (grade == ~0ULL || len > size) { 478 start = first; 479 size = len; 480 grade = 0; 481 } 482 } 483 } 484 485 s = find_provcfg(pp, "end"); 486 if (s == NULL) { 487 s = find_provcfg(pp, "length"); 488 first = lba + 489 (off_t)strtoimax(s, NULL, 0) / pp->lg_sectorsize; 490 } else 491 first = (off_t)strtoimax(s, NULL, 0) + 1; 492 } 493 if (first <= last) { 494 /* Free space [first-last] */ 495 len = last - first + 1; 496 if (has_size) { 497 if (len >= size && 498 (uintmax_t)(len - size) < grade) { 499 start = first; 500 grade = len - size; 501 } 502 } else if (has_start) { 503 if (start >= first && start <= last) { 504 size = last - start + 1; 505 grade = start - first; 506 } 507 } else { 508 if (grade == ~0ULL || len > size) { 509 start = first; 510 size = len; 511 grade = 0; 512 } 513 } 514 } 515 516 if (grade == ~0ULL) { 517 geom_deletetree(&mesh); 518 return (ENOSPC); 519 } 520 521done: 522 snprintf(ssize, sizeof(ssize), "%jd", (intmax_t)size); 523 gctl_change_param(req, "size", -1, ssize); 524 snprintf(sstart, sizeof(sstart), "%jd", (intmax_t)start); 525 gctl_change_param(req, "start", -1, sstart); 526 geom_deletetree(&mesh); 527 return (0); 528} 529 530static void 531gpart_show_geom(struct ggeom *gp, const char *element) 532{ 533 struct gprovider *pp; 534 const char *s, *scheme; 535 off_t first, last, sector, end; 536 off_t length, secsz; 537 int idx, wblocks, wname; 538 539 scheme = find_geomcfg(gp, "scheme"); 540 s = find_geomcfg(gp, "first"); 541 first = (off_t)strtoimax(s, NULL, 0); 542 s = find_geomcfg(gp, "last"); 543 last = (off_t)strtoimax(s, NULL, 0); 544 wblocks = strlen(s); 545 s = find_geomcfg(gp, "state"); 546 if (s != NULL && *s != 'C') 547 s = NULL; 548 wname = strlen(gp->lg_name); 549 pp = LIST_FIRST(&gp->lg_consumer)->lg_provider; 550 secsz = pp->lg_sectorsize; 551 printf("=>%*jd %*jd %*s %s (%s)%s\n", 552 wblocks, (intmax_t)first, wblocks, (intmax_t)(last - first + 1), 553 wname, gp->lg_name, 554 scheme, fmtsize(pp->lg_mediasize), 555 s ? " [CORRUPT]": ""); 556 557 while ((pp = find_provider(gp, first)) != NULL) { 558 s = find_provcfg(pp, "start"); 559 if (s == NULL) { 560 s = find_provcfg(pp, "offset"); 561 sector = (off_t)strtoimax(s, NULL, 0) / secsz; 562 } else 563 sector = (off_t)strtoimax(s, NULL, 0); 564 565 s = find_provcfg(pp, "end"); 566 if (s == NULL) { 567 s = find_provcfg(pp, "length"); 568 length = (off_t)strtoimax(s, NULL, 0) / secsz; 569 end = sector + length - 1; 570 } else { 571 end = (off_t)strtoimax(s, NULL, 0); 572 length = end - sector + 1; 573 } 574 s = find_provcfg(pp, "index"); 575 idx = atoi(s); 576 if (first < sector) { 577 printf(" %*jd %*jd %*s - free - (%s)\n", 578 wblocks, (intmax_t)first, wblocks, 579 (intmax_t)(sector - first), wname, "", 580 fmtsize((sector - first) * secsz)); 581 } 582 printf(" %*jd %*jd %*d %s %s (%s)\n", 583 wblocks, (intmax_t)sector, wblocks, (intmax_t)length, 584 wname, idx, find_provcfg(pp, element), 585 fmtattrib(pp), fmtsize(pp->lg_mediasize)); 586 first = end + 1; 587 } 588 if (first <= last) { 589 length = last - first + 1; 590 printf(" %*jd %*jd %*s - free - (%s)\n", 591 wblocks, (intmax_t)first, wblocks, (intmax_t)length, 592 wname, "", 593 fmtsize(length * secsz)); 594 } 595 printf("\n"); 596} 597 598static int 599gpart_show_hasopt(struct gctl_req *req, const char *opt, const char *elt) 600{ 601 602 if (!gctl_get_int(req, opt)) 603 return (0); 604 605 if (elt != NULL) 606 errx(EXIT_FAILURE, "-l and -r are mutually exclusive"); 607 608 return (1); 609} 610 611static void 612gpart_show(struct gctl_req *req, unsigned int fl __unused) 613{ 614 struct gmesh mesh; 615 struct gclass *classp; 616 struct ggeom *gp; 617 const char *element, *name; 618 int error, i, nargs; 619 620 element = NULL; 621 if (gpart_show_hasopt(req, "show_label", element)) 622 element = "label"; 623 if (gpart_show_hasopt(req, "show_rawtype", element)) 624 element = "rawtype"; 625 if (element == NULL) 626 element = "type"; 627 628 name = gctl_get_ascii(req, "class"); 629 if (name == NULL) 630 abort(); 631 error = geom_gettree(&mesh); 632 if (error != 0) 633 errc(EXIT_FAILURE, error, "Cannot get GEOM tree"); 634 classp = find_class(&mesh, name); 635 if (classp == NULL) { 636 geom_deletetree(&mesh); 637 errx(EXIT_FAILURE, "Class %s not found.", name); 638 } 639 nargs = gctl_get_int(req, "nargs"); 640 if (nargs > 0) { 641 for (i = 0; i < nargs; i++) { 642 name = gctl_get_ascii(req, "arg%d", i); 643 gp = find_geom(classp, name); 644 if (gp != NULL) 645 gpart_show_geom(gp, element); 646 else 647 errx(EXIT_FAILURE, "No such geom: %s.", name); 648 } 649 } else { 650 LIST_FOREACH(gp, &classp->lg_geom, lg_geom) { 651 gpart_show_geom(gp, element); 652 } 653 } 654 geom_deletetree(&mesh); 655} 656 657static void * 658gpart_bootfile_read(const char *bootfile, ssize_t *size) 659{ 660 struct stat sb; 661 void *code; 662 int fd; 663 664 if (stat(bootfile, &sb) == -1) 665 err(EXIT_FAILURE, "%s", bootfile); 666 if (!S_ISREG(sb.st_mode)) 667 errx(EXIT_FAILURE, "%s: not a regular file", bootfile); 668 if (sb.st_size == 0) 669 errx(EXIT_FAILURE, "%s: empty file", bootfile); 670 if (*size > 0 && sb.st_size > *size) 671 errx(EXIT_FAILURE, "%s: file too big (%zu limit)", bootfile, 672 *size); 673 674 *size = sb.st_size; 675 676 fd = open(bootfile, O_RDONLY); 677 if (fd == -1) 678 err(EXIT_FAILURE, "%s", bootfile); 679 code = malloc(*size); 680 if (code == NULL) 681 err(EXIT_FAILURE, NULL); 682 if (read(fd, code, *size) != *size) 683 err(EXIT_FAILURE, "%s", bootfile); 684 close(fd); 685 686 return (code); 687} 688 689static void 690gpart_write_partcode(struct ggeom *gp, int idx, void *code, ssize_t size) 691{ 692 char dsf[128]; 693 struct gprovider *pp; 694 const char *s; 695 char *buf; 696 off_t bsize; 697 int fd; 698 699 LIST_FOREACH(pp, &gp->lg_provider, lg_provider) { 700 s = find_provcfg(pp, "index"); 701 if (s == NULL) 702 continue; 703 if (atoi(s) == idx) 704 break; 705 } 706 707 if (pp != NULL) { 708 snprintf(dsf, sizeof(dsf), "/dev/%s", pp->lg_name); 709 fd = open(dsf, O_WRONLY); 710 if (fd == -1) 711 err(EXIT_FAILURE, "%s", dsf); 712 if (lseek(fd, size, SEEK_SET) != size) 713 errx(EXIT_FAILURE, "%s: not enough space", dsf); 714 if (lseek(fd, 0, SEEK_SET) != 0) 715 err(EXIT_FAILURE, "%s", dsf); 716 717 /* 718 * When writing to a disk device, the write must be 719 * sector aligned and not write to any partial sectors, 720 * so round up the buffer size to the next sector and zero it. 721 */ 722 bsize = (size + pp->lg_sectorsize - 1) / 723 pp->lg_sectorsize * pp->lg_sectorsize; 724 buf = calloc(1, bsize); 725 if (buf == NULL) 726 err(EXIT_FAILURE, "%s", dsf); 727 bcopy(code, buf, size); 728 if (write(fd, buf, bsize) != bsize) 729 err(EXIT_FAILURE, "%s", dsf); 730 free(buf); 731 close(fd); 732 } else 733 errx(EXIT_FAILURE, "invalid partition index"); 734} 735 736static void 737gpart_write_partcode_vtoc8(struct ggeom *gp, int idx, void *code) 738{ 739 char dsf[128]; 740 struct gprovider *pp; 741 const char *s; 742 int installed, fd; 743 744 installed = 0; 745 LIST_FOREACH(pp, &gp->lg_provider, lg_provider) { 746 s = find_provcfg(pp, "index"); 747 if (s == NULL) 748 continue; 749 if (idx != 0 && atoi(s) != idx) 750 continue; 751 snprintf(dsf, sizeof(dsf), "/dev/%s", pp->lg_name); 752 if (pp->lg_sectorsize != sizeof(struct vtoc8)) 753 errx(EXIT_FAILURE, "%s: unexpected sector " 754 "size (%d)\n", dsf, pp->lg_sectorsize); 755 fd = open(dsf, O_WRONLY); 756 if (fd == -1) 757 err(EXIT_FAILURE, "%s", dsf); 758 if (lseek(fd, VTOC_BOOTSIZE, SEEK_SET) != VTOC_BOOTSIZE) 759 continue; 760 /* 761 * We ignore the first VTOC_BOOTSIZE bytes of boot code in 762 * order to avoid overwriting the label. 763 */ 764 if (lseek(fd, sizeof(struct vtoc8), SEEK_SET) != 765 sizeof(struct vtoc8)) 766 err(EXIT_FAILURE, "%s", dsf); 767 if (write(fd, (caddr_t)code + sizeof(struct vtoc8), 768 VTOC_BOOTSIZE - sizeof(struct vtoc8)) != VTOC_BOOTSIZE - 769 sizeof(struct vtoc8)) 770 err(EXIT_FAILURE, "%s", dsf); 771 installed++; 772 close(fd); 773 if (idx != 0 && atoi(s) == idx) 774 break; 775 } 776 if (installed == 0) 777 errx(EXIT_FAILURE, "%s: no partitions", gp->lg_name); 778} 779 780static void 781gpart_bootcode(struct gctl_req *req, unsigned int fl) 782{ 783 struct gmesh mesh; 784 struct gclass *classp; 785 struct ggeom *gp; 786 const char *s; 787 void *bootcode, *partcode; 788 size_t bootsize, partsize; 789 int error, idx, vtoc8; 790 791 if (gctl_has_param(req, GPART_PARAM_BOOTCODE)) { 792 s = gctl_get_ascii(req, GPART_PARAM_BOOTCODE); 793 bootsize = 800 * 1024; /* Arbitrary limit. */ 794 bootcode = gpart_bootfile_read(s, &bootsize); 795 error = gctl_change_param(req, GPART_PARAM_BOOTCODE, bootsize, 796 bootcode); 797 if (error) 798 errc(EXIT_FAILURE, error, "internal error"); 799 } else { 800 bootcode = NULL; 801 bootsize = 0; 802 } 803 804 s = gctl_get_ascii(req, "class"); 805 if (s == NULL) 806 abort(); 807 error = geom_gettree(&mesh); 808 if (error != 0) 809 errc(EXIT_FAILURE, error, "Cannot get GEOM tree"); 810 classp = find_class(&mesh, s); 811 if (classp == NULL) { 812 geom_deletetree(&mesh); 813 errx(EXIT_FAILURE, "Class %s not found.", s); 814 } 815 s = gctl_get_ascii(req, "arg0"); 816 if (s == NULL) 817 abort(); 818 gp = find_geom(classp, s); 819 if (gp == NULL) 820 errx(EXIT_FAILURE, "No such geom: %s.", s); 821 s = find_geomcfg(gp, "scheme"); 822 vtoc8 = 0; 823 if (strcmp(s, "VTOC8") == 0) 824 vtoc8 = 1; 825 826 if (gctl_has_param(req, GPART_PARAM_PARTCODE)) { 827 s = gctl_get_ascii(req, GPART_PARAM_PARTCODE); 828 partsize = vtoc8 != 0 ? VTOC_BOOTSIZE : bootsize * 1024; 829 partcode = gpart_bootfile_read(s, &partsize); 830 error = gctl_delete_param(req, GPART_PARAM_PARTCODE); 831 if (error) 832 errc(EXIT_FAILURE, error, "internal error"); 833 } else { 834 partcode = NULL; 835 partsize = 0; 836 } 837 838 if (gctl_has_param(req, GPART_PARAM_INDEX)) { 839 if (partcode == NULL) 840 errx(EXIT_FAILURE, "-i is only valid with -p"); 841 idx = (int)gctl_get_intmax(req, GPART_PARAM_INDEX); 842 if (idx < 1) 843 errx(EXIT_FAILURE, "invalid partition index"); 844 error = gctl_delete_param(req, GPART_PARAM_INDEX); 845 if (error) 846 errc(EXIT_FAILURE, error, "internal error"); 847 } else 848 idx = 0; 849 850 if (partcode != NULL) { 851 if (vtoc8 == 0) { 852 if (idx == 0) 853 errx(EXIT_FAILURE, "missing -i option"); 854 gpart_write_partcode(gp, idx, partcode, partsize); 855 } else 856 gpart_write_partcode_vtoc8(gp, idx, partcode); 857 } else 858 if (bootcode == NULL) 859 errx(EXIT_FAILURE, "no -b nor -p"); 860 861 if (bootcode != NULL) 862 gpart_issue(req, fl); 863 864 geom_deletetree(&mesh); 865} 866 867static void 868gpart_print_error(const char *errstr) 869{ 870 char *errmsg; 871 int error; 872 873 error = strtol(errstr, &errmsg, 0); 874 if (errmsg != errstr) { 875 while (errmsg[0] == ' ') 876 errmsg++; 877 if (errmsg[0] != '\0') 878 warnc(error, "%s", errmsg); 879 else 880 warnc(error, NULL); 881 } else 882 warnx("%s", errmsg); 883} 884 885static void 886gpart_issue(struct gctl_req *req, unsigned int fl __unused) 887{ 888 char buf[4096]; 889 const char *errstr; 890 int error, status; 891 892 if (gctl_get_int(req, "nargs") != 1) 893 errx(EXIT_FAILURE, "Invalid number of arguments."); 894 (void)gctl_delete_param(req, "nargs"); 895 896 /* autofill parameters (if applicable). */ 897 error = gpart_autofill(req); 898 if (error) { 899 warnc(error, "autofill"); 900 status = EXIT_FAILURE; 901 goto done; 902 } 903 904 bzero(buf, sizeof(buf)); 905 gctl_rw_param(req, "output", sizeof(buf), buf); 906 errstr = gctl_issue(req); 907 if (errstr == NULL || errstr[0] == '\0') { 908 if (buf[0] != '\0') 909 printf("%s", buf); 910 status = EXIT_SUCCESS; 911 goto done; 912 } 913 914 gpart_print_error(errstr); 915 status = EXIT_FAILURE; 916 917 done: 918 gctl_free(req); 919 exit(status); 920} 921