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