geom_part.c revision 198478
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 198478 2009-10-26 07:43:41Z lulf $"); 29 30#include <sys/stat.h> 31 32#include <assert.h> 33#include <err.h> 34#include <errno.h> 35#include <fcntl.h> 36#include <libgeom.h> 37#include <libutil.h> 38#include <paths.h> 39#include <stdint.h> 40#include <stdio.h> 41#include <stdlib.h> 42#include <string.h> 43#include <strings.h> 44#include <unistd.h> 45 46#include "core/geom.h" 47#include "misc/subr.h" 48 49#ifdef STATIC_GEOM_CLASSES 50#define PUBSYM(x) gpart_##x 51#else 52#define PUBSYM(x) x 53#endif 54 55uint32_t PUBSYM(lib_version) = G_LIB_VERSION; 56uint32_t PUBSYM(version) = 0; 57 58static char autofill[] = "*"; 59static char optional[] = ""; 60static char flags[] = "C"; 61 62static char bootcode_param[] = "bootcode"; 63static char index_param[] = "index"; 64static char partcode_param[] = "partcode"; 65 66static void gpart_bootcode(struct gctl_req *, unsigned int); 67static void gpart_issue(struct gctl_req *, unsigned int); 68static void gpart_show(struct gctl_req *, unsigned int); 69 70struct g_command PUBSYM(class_commands)[] = { 71 { "add", 0, gpart_issue, { 72 { 'b', "start", autofill, G_TYPE_ASCLBA }, 73 { 's', "size", autofill, G_TYPE_ASCLBA }, 74 { 't', "type", NULL, G_TYPE_STRING }, 75 { 'i', index_param, optional, G_TYPE_ASCNUM }, 76 { 'l', "label", optional, G_TYPE_STRING }, 77 { 'f', "flags", flags, G_TYPE_STRING }, 78 G_OPT_SENTINEL }, 79 "geom", NULL 80 }, 81 { "bootcode", 0, gpart_bootcode, { 82 { 'b', bootcode_param, optional, G_TYPE_STRING }, 83 { 'p', partcode_param, optional, G_TYPE_STRING }, 84 { 'i', index_param, optional, G_TYPE_ASCNUM }, 85 { 'f', "flags", flags, G_TYPE_STRING }, 86 G_OPT_SENTINEL }, 87 "geom", NULL 88 }, 89 { "commit", 0, gpart_issue, G_NULL_OPTS, "geom", NULL }, 90 { "create", 0, gpart_issue, { 91 { 's', "scheme", NULL, G_TYPE_STRING }, 92 { 'n', "entries", optional, G_TYPE_ASCNUM }, 93 { 'f', "flags", flags, G_TYPE_STRING }, 94 G_OPT_SENTINEL }, 95 "provider", NULL 96 }, 97 { "delete", 0, gpart_issue, { 98 { 'i', index_param, NULL, G_TYPE_ASCNUM }, 99 { 'f', "flags", flags, G_TYPE_STRING }, 100 G_OPT_SENTINEL }, 101 "geom", NULL 102 }, 103 { "destroy", 0, gpart_issue, { 104 { 'f', "flags", flags, G_TYPE_STRING }, 105 G_OPT_SENTINEL }, 106 "geom", NULL }, 107 { "modify", 0, gpart_issue, { 108 { 'i', index_param, NULL, G_TYPE_ASCNUM }, 109 { 'l', "label", optional, G_TYPE_STRING }, 110 { 't', "type", optional, G_TYPE_STRING }, 111 { 'f', "flags", flags, G_TYPE_STRING }, 112 G_OPT_SENTINEL }, 113 "geom", NULL 114 }, 115 { "set", 0, gpart_issue, { 116 { 'a', "attrib", NULL, G_TYPE_STRING }, 117 { 'i', index_param, NULL, G_TYPE_ASCNUM }, 118 { 'f', "flags", flags, G_TYPE_STRING }, 119 G_OPT_SENTINEL }, 120 "geom", NULL 121 }, 122 { "show", 0, gpart_show, { 123 { 'l', "show_label", NULL, G_TYPE_BOOL }, 124 { 'r', "show_rawtype", NULL, G_TYPE_BOOL }, 125 G_OPT_SENTINEL }, 126 NULL, "[-lr] [geom ...]" 127 }, 128 { "undo", 0, gpart_issue, G_NULL_OPTS, "geom", NULL }, 129 { "unset", 0, gpart_issue, { 130 { 'a', "attrib", NULL, G_TYPE_STRING }, 131 { 'i', index_param, NULL, G_TYPE_ASCNUM }, 132 { 'f', "flags", flags, G_TYPE_STRING }, 133 G_OPT_SENTINEL }, 134 "geom", NULL 135 }, 136 G_CMD_SENTINEL 137}; 138 139static struct gclass * 140find_class(struct gmesh *mesh, const char *name) 141{ 142 struct gclass *classp; 143 144 LIST_FOREACH(classp, &mesh->lg_class, lg_class) { 145 if (strcmp(classp->lg_name, name) == 0) 146 return (classp); 147 } 148 return (NULL); 149} 150 151static struct ggeom * 152find_geom(struct gclass *classp, const char *name) 153{ 154 struct ggeom *gp; 155 156 LIST_FOREACH(gp, &classp->lg_geom, lg_geom) { 157 if (strcmp(gp->lg_name, name) == 0) 158 return (gp); 159 } 160 return (NULL); 161} 162 163static const char * 164find_geomcfg(struct ggeom *gp, const char *cfg) 165{ 166 struct gconfig *gc; 167 168 LIST_FOREACH(gc, &gp->lg_config, lg_config) { 169 if (!strcmp(gc->lg_name, cfg)) 170 return (gc->lg_val); 171 } 172 return (NULL); 173} 174 175static const char * 176find_provcfg(struct gprovider *pp, const char *cfg) 177{ 178 struct gconfig *gc; 179 180 LIST_FOREACH(gc, &pp->lg_config, lg_config) { 181 if (!strcmp(gc->lg_name, cfg)) 182 return (gc->lg_val); 183 } 184 return (NULL); 185} 186 187static struct gprovider * 188find_provider(struct ggeom *gp, unsigned long long minsector) 189{ 190 struct gprovider *pp, *bestpp; 191 const char *s; 192 unsigned long long sector, bestsector; 193 194 bestpp = NULL; 195 bestsector = 0; 196 LIST_FOREACH(pp, &gp->lg_provider, lg_provider) { 197 s = find_provcfg(pp, "start"); 198 if (s == NULL) { 199 s = find_provcfg(pp, "offset"); 200 sector = atoll(s) / pp->lg_sectorsize; 201 } else 202 sector = atoll(s); 203 204 if (sector < minsector) 205 continue; 206 if (bestpp != NULL && sector >= bestsector) 207 continue; 208 209 bestpp = pp; 210 bestsector = sector; 211 } 212 return (bestpp); 213} 214 215static const char * 216fmtsize(int64_t rawsz) 217{ 218 static char buf[5]; 219 220 humanize_number(buf, sizeof(buf), rawsz, "", HN_AUTOSCALE, 221 HN_B | HN_NOSPACE | HN_DECIMAL); 222 return (buf); 223} 224 225static const char * 226fmtattrib(struct gprovider *pp) 227{ 228 static char buf[128]; 229 struct gconfig *gc; 230 u_int idx; 231 232 buf[0] = '\0'; 233 idx = 0; 234 LIST_FOREACH(gc, &pp->lg_config, lg_config) { 235 if (strcmp(gc->lg_name, "attrib") != 0) 236 continue; 237 idx += snprintf(buf + idx, sizeof(buf) - idx, "%s%s", 238 (idx == 0) ? " [" : ",", gc->lg_val); 239 } 240 if (idx > 0) 241 snprintf(buf + idx, sizeof(buf) - idx, "] "); 242 return (buf); 243} 244 245static int 246gpart_autofill(struct gctl_req *req) 247{ 248 struct gmesh mesh; 249 struct gclass *cp; 250 struct ggeom *gp; 251 struct gprovider *pp; 252 unsigned long long first, last; 253 unsigned long long size, start; 254 unsigned long long lba, len, grade; 255 const char *s; 256 char *val; 257 int error, has_size, has_start; 258 259 s = gctl_get_ascii(req, "verb"); 260 if (strcmp(s, "add") != 0) 261 return (0); 262 263 s = gctl_get_ascii(req, "size"); 264 has_size = (*s == '*') ? 0 : 1; 265 size = (has_size) ? (unsigned long long)atoll(s) : 0ULL; 266 267 s = gctl_get_ascii(req, "start"); 268 has_start = (*s == '*') ? 0 : 1; 269 start = (has_start) ? (unsigned long long)atoll(s) : ~0ULL; 270 271 /* No autofill necessary. */ 272 if (has_size && has_start) 273 return (0); 274 275 error = geom_gettree(&mesh); 276 if (error) 277 return (error); 278 s = gctl_get_ascii(req, "class"); 279 if (s == NULL) 280 abort(); 281 cp = find_class(&mesh, s); 282 if (cp == NULL) 283 errx(EXIT_FAILURE, "Class %s not found.", s); 284 s = gctl_get_ascii(req, "geom"); 285 if (s == NULL) 286 abort(); 287 gp = find_geom(cp, s); 288 if (gp == NULL) 289 errx(EXIT_FAILURE, "No such geom: %s.", s); 290 first = atoll(find_geomcfg(gp, "first")); 291 last = atoll(find_geomcfg(gp, "last")); 292 grade = ~0ULL; 293 while ((pp = find_provider(gp, first)) != NULL) { 294 s = find_provcfg(pp, "start"); 295 if (s == NULL) { 296 s = find_provcfg(pp, "offset"); 297 lba = atoll(s) / pp->lg_sectorsize; 298 } else 299 lba = atoll(s); 300 301 if (first < lba) { 302 /* Free space [first, lba> */ 303 len = lba - first; 304 if (has_size) { 305 if (len >= size && len - size < grade) { 306 start = first; 307 grade = len - size; 308 } 309 } else if (has_start) { 310 if (start >= first && start < lba) { 311 size = lba - start; 312 grade = start - first; 313 } 314 } else { 315 if (grade == ~0ULL || len > size) { 316 start = first; 317 size = len; 318 grade = 0; 319 } 320 } 321 } 322 323 s = find_provcfg(pp, "end"); 324 if (s == NULL) { 325 s = find_provcfg(pp, "length"); 326 first = lba + atoll(s) / pp->lg_sectorsize; 327 } else 328 first = atoll(s) + 1; 329 } 330 if (first <= last) { 331 /* Free space [first-last] */ 332 len = last - first + 1; 333 if (has_size) { 334 if (len >= size && len - size < grade) { 335 start = first; 336 grade = len - size; 337 } 338 } else if (has_start) { 339 if (start >= first && start <= last) { 340 size = last - start + 1; 341 grade = start - first; 342 } 343 } else { 344 if (grade == ~0ULL || len > size) { 345 start = first; 346 size = len; 347 grade = 0; 348 } 349 } 350 } 351 352 if (grade == ~0ULL) 353 return (ENOSPC); 354 355 if (!has_size) { 356 asprintf(&val, "%llu", size); 357 if (val == NULL) 358 return (ENOMEM); 359 gctl_change_param(req, "size", -1, val); 360 } 361 if (!has_start) { 362 asprintf(&val, "%llu", start); 363 if (val == NULL) 364 return (ENOMEM); 365 gctl_change_param(req, "start", -1, val); 366 } 367 return (0); 368} 369 370static void 371gpart_show_geom(struct ggeom *gp, const char *element) 372{ 373 struct gprovider *pp; 374 const char *s, *scheme; 375 unsigned long long first, last, sector, end; 376 unsigned long long length, secsz; 377 int idx, wblocks, wname; 378 379 scheme = find_geomcfg(gp, "scheme"); 380 s = find_geomcfg(gp, "first"); 381 first = atoll(s); 382 s = find_geomcfg(gp, "last"); 383 last = atoll(s); 384 wblocks = strlen(s); 385 wname = strlen(gp->lg_name); 386 pp = LIST_FIRST(&gp->lg_consumer)->lg_provider; 387 secsz = pp->lg_sectorsize; 388 printf("=>%*llu %*llu %*s %s (%s)\n", 389 wblocks, first, wblocks, (last - first + 1), 390 wname, gp->lg_name, 391 scheme, fmtsize(pp->lg_mediasize)); 392 393 while ((pp = find_provider(gp, first)) != NULL) { 394 s = find_provcfg(pp, "start"); 395 if (s == NULL) { 396 s = find_provcfg(pp, "offset"); 397 sector = atoll(s) / secsz; 398 } else 399 sector = atoll(s); 400 401 s = find_provcfg(pp, "end"); 402 if (s == NULL) { 403 s = find_provcfg(pp, "length"); 404 length = atoll(s) / secsz; 405 end = sector + length - 1; 406 } else { 407 end = atoll(s); 408 length = end - sector + 1; 409 } 410 s = find_provcfg(pp, "index"); 411 idx = atoi(s); 412 if (first < sector) { 413 printf(" %*llu %*llu %*s - free - (%s)\n", 414 wblocks, first, wblocks, sector - first, 415 wname, "", 416 fmtsize((sector - first) * secsz)); 417 } 418 printf(" %*llu %*llu %*d %s %s (%s)\n", 419 wblocks, sector, wblocks, length, 420 wname, idx, find_provcfg(pp, element), 421 fmtattrib(pp), fmtsize(pp->lg_mediasize)); 422 first = end + 1; 423 } 424 if (first <= last) { 425 length = last - first + 1; 426 printf(" %*llu %*llu %*s - free - (%s)\n", 427 wblocks, first, wblocks, length, 428 wname, "", 429 fmtsize(length * secsz)); 430 } 431 printf("\n"); 432} 433 434static int 435gpart_show_hasopt(struct gctl_req *req, const char *opt, const char *elt) 436{ 437 438 if (!gctl_get_int(req, opt)) 439 return (0); 440 441 if (elt != NULL) 442 errx(EXIT_FAILURE, "-l and -r are mutually exclusive"); 443 444 return (1); 445} 446 447static void 448gpart_show(struct gctl_req *req, unsigned int fl __unused) 449{ 450 struct gmesh mesh; 451 struct gclass *classp; 452 struct ggeom *gp; 453 const char *element, *name; 454 int error, i, nargs; 455 456 element = NULL; 457 if (gpart_show_hasopt(req, "show_label", element)) 458 element = "label"; 459 if (gpart_show_hasopt(req, "show_rawtype", element)) 460 element = "rawtype"; 461 if (element == NULL) 462 element = "type"; 463 464 name = gctl_get_ascii(req, "class"); 465 if (name == NULL) 466 abort(); 467 error = geom_gettree(&mesh); 468 if (error != 0) 469 errc(EXIT_FAILURE, error, "Cannot get GEOM tree"); 470 classp = find_class(&mesh, name); 471 if (classp == NULL) { 472 geom_deletetree(&mesh); 473 errx(EXIT_FAILURE, "Class %s not found.", name); 474 } 475 nargs = gctl_get_int(req, "nargs"); 476 if (nargs > 0) { 477 for (i = 0; i < nargs; i++) { 478 name = gctl_get_ascii(req, "arg%d", i); 479 gp = find_geom(classp, name); 480 if (gp != NULL) 481 gpart_show_geom(gp, element); 482 else 483 errx(EXIT_FAILURE, "No such geom: %s.", name); 484 } 485 } else { 486 LIST_FOREACH(gp, &classp->lg_geom, lg_geom) { 487 gpart_show_geom(gp, element); 488 } 489 } 490 geom_deletetree(&mesh); 491} 492 493static void * 494gpart_bootfile_read(const char *bootfile, ssize_t *size) 495{ 496 struct stat sb; 497 void *code; 498 int fd; 499 500 if (stat(bootfile, &sb) == -1) 501 err(EXIT_FAILURE, "%s", bootfile); 502 if (!S_ISREG(sb.st_mode)) 503 errx(EXIT_FAILURE, "%s: not a regular file", bootfile); 504 if (sb.st_size == 0) 505 errx(EXIT_FAILURE, "%s: empty file", bootfile); 506 if (*size > 0 && sb.st_size >= *size) 507 errx(EXIT_FAILURE, "%s: file too big (%zu limit)", bootfile, 508 *size); 509 510 *size = sb.st_size; 511 512 fd = open(bootfile, O_RDONLY); 513 if (fd == -1) 514 err(EXIT_FAILURE, "%s", bootfile); 515 code = malloc(*size); 516 if (code == NULL) 517 err(EXIT_FAILURE, NULL); 518 if (read(fd, code, *size) != *size) 519 err(EXIT_FAILURE, "%s", bootfile); 520 close(fd); 521 522 return (code); 523} 524 525static void 526gpart_write_partcode(struct gctl_req *req, int idx, void *code, ssize_t size) 527{ 528 char dsf[128]; 529 struct gmesh mesh; 530 struct gclass *classp; 531 struct ggeom *gp; 532 struct gprovider *pp; 533 const char *s; 534 char *buf; 535 off_t bsize; 536 int error, fd; 537 538 s = gctl_get_ascii(req, "class"); 539 if (s == NULL) 540 abort(); 541 error = geom_gettree(&mesh); 542 if (error != 0) 543 errc(EXIT_FAILURE, error, "Cannot get GEOM tree"); 544 classp = find_class(&mesh, s); 545 if (classp == NULL) { 546 geom_deletetree(&mesh); 547 errx(EXIT_FAILURE, "Class %s not found.", s); 548 } 549 s = gctl_get_ascii(req, "geom"); 550 if (s == NULL) 551 abort(); 552 gp = find_geom(classp, s); 553 if (gp == NULL) 554 errx(EXIT_FAILURE, "No such geom: %s.", s); 555 556 LIST_FOREACH(pp, &gp->lg_provider, lg_provider) { 557 s = find_provcfg(pp, "index"); 558 if (s == NULL) 559 continue; 560 if (atoi(s) == idx) 561 break; 562 } 563 564 if (pp != NULL) { 565 snprintf(dsf, sizeof(dsf), "/dev/%s", pp->lg_name); 566 fd = open(dsf, O_WRONLY); 567 if (fd == -1) 568 err(EXIT_FAILURE, "%s", dsf); 569 if (lseek(fd, size, SEEK_SET) != size) 570 errx(EXIT_FAILURE, "%s: not enough space", dsf); 571 if (lseek(fd, 0, SEEK_SET) != 0) 572 err(EXIT_FAILURE, "%s", dsf); 573 574 /* 575 * When writing to a disk device, the write must be 576 * sector aligned and not write to any partial sectors, 577 * so round up the buffer size to the next sector and zero it. 578 */ 579 bsize = (size + pp->lg_sectorsize - 1) / 580 pp->lg_sectorsize * pp->lg_sectorsize; 581 buf = calloc(1, bsize); 582 if (buf == NULL) 583 err(EXIT_FAILURE, "%s", dsf); 584 bcopy(code, buf, size); 585 if (write(fd, buf, bsize) != bsize) 586 err(EXIT_FAILURE, "%s", dsf); 587 free(buf); 588 close(fd); 589 } else 590 errx(EXIT_FAILURE, "invalid partition index"); 591 592 geom_deletetree(&mesh); 593} 594 595static void 596gpart_bootcode(struct gctl_req *req, unsigned int fl) 597{ 598 const char *s; 599 char *sp; 600 void *bootcode, *partcode; 601 size_t bootsize, partsize; 602 int error, idx; 603 604 if (gctl_has_param(req, bootcode_param)) { 605 s = gctl_get_ascii(req, bootcode_param); 606 bootsize = 64 * 1024; /* Arbitrary limit. */ 607 bootcode = gpart_bootfile_read(s, &bootsize); 608 error = gctl_change_param(req, bootcode_param, bootsize, 609 bootcode); 610 if (error) 611 errc(EXIT_FAILURE, error, "internal error"); 612 } else { 613 bootcode = NULL; 614 bootsize = 0; 615 } 616 617 if (gctl_has_param(req, partcode_param)) { 618 s = gctl_get_ascii(req, partcode_param); 619 partsize = bootsize * 1024; 620 partcode = gpart_bootfile_read(s, &partsize); 621 error = gctl_delete_param(req, partcode_param); 622 if (error) 623 errc(EXIT_FAILURE, error, "internal error"); 624 } else { 625 partcode = NULL; 626 partsize = 0; 627 } 628 629 if (gctl_has_param(req, index_param)) { 630 if (partcode == NULL) 631 errx(EXIT_FAILURE, "-i is only valid with -p"); 632 s = gctl_get_ascii(req, index_param); 633 idx = strtol(s, &sp, 10); 634 if (idx < 1 || *s == '\0' || *sp != '\0') 635 errx(EXIT_FAILURE, "invalid partition index"); 636 error = gctl_delete_param(req, index_param); 637 if (error) 638 errc(EXIT_FAILURE, error, "internal error"); 639 } else 640 idx = 0; 641 642 if (partcode != NULL) { 643 if (idx == 0) 644 errx(EXIT_FAILURE, "missing -i option"); 645 gpart_write_partcode(req, idx, partcode, partsize); 646 } else { 647 if (bootcode == NULL) 648 errx(EXIT_FAILURE, "no -b nor -p"); 649 } 650 651 if (bootcode != NULL) 652 gpart_issue(req, fl); 653} 654 655static void 656gpart_issue(struct gctl_req *req, unsigned int fl __unused) 657{ 658 char buf[4096]; 659 char *errmsg; 660 const char *errstr; 661 int error, status; 662 663 /* autofill parameters (if applicable). */ 664 error = gpart_autofill(req); 665 if (error) { 666 warnc(error, "autofill"); 667 status = EXIT_FAILURE; 668 goto done; 669 } 670 671 bzero(buf, sizeof(buf)); 672 gctl_rw_param(req, "output", sizeof(buf), buf); 673 errstr = gctl_issue(req); 674 if (errstr == NULL || errstr[0] == '\0') { 675 if (buf[0] != '\0') 676 printf("%s", buf); 677 status = EXIT_SUCCESS; 678 goto done; 679 } 680 681 error = strtol(errstr, &errmsg, 0); 682 if (errmsg != errstr) { 683 while (errmsg[0] == ' ') 684 errmsg++; 685 if (errmsg[0] != '\0') 686 warnc(error, "%s", errmsg); 687 else 688 warnc(error, NULL); 689 } else 690 warnx("%s", errmsg); 691 692 status = EXIT_FAILURE; 693 694 done: 695 gctl_free(req); 696 exit(status); 697} 698