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