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