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