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