1 /*- 2 * Copyright (c) 2011 Nathan Whitehorn 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 AUTHOR 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 AUTHOR 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 * $FreeBSD$ 27 */ 28 29 #include <sys/param.h> 30 #include <errno.h> 31 #include <libutil.h> 32 #include <inttypes.h> 33 34 #include <libgeom.h> 35 #include <dialog.h> 36 #include <dlg_keys.h> 37 38 #include "partedit.h" 39 40 #define GPART_FLAGS "x" /* Do not commit changes by default */ 41 42 static void 43 gpart_show_error(const char *title, const char *explanation, const char *errstr) 44 { 45 char *errmsg; 46 char message[512]; 47 int error; 48 49 if (explanation == NULL) 50 explanation = ""; 51 52 error = strtol(errstr, &errmsg, 0); 53 if (errmsg != errstr) { 54 while (errmsg[0] == ' ') 55 errmsg++; 56 if (errmsg[0] != '\0') 57 sprintf(message, "%s%s. %s", explanation, 58 strerror(error), errmsg); 59 else 60 sprintf(message, "%s%s", explanation, strerror(error)); 61 } else { 62 sprintf(message, "%s%s", explanation, errmsg); 63 } 64 65 dialog_msgbox(title, message, 0, 0, TRUE); 66 } 67 68 static int 69 scheme_supports_labels(const char *scheme) 70 { 71 if (strcmp(scheme, "APM") == 0) 72 return (1); 73 if (strcmp(scheme, "GPT") == 0) 74 return (1); 75 if (strcmp(scheme, "PC98") == 0) 76 return (1); 77 78 return (0); 79 } 80 81 int 82 gpart_partition(const char *lg_name, const char *scheme) 83 { 84 int cancel, choice; 85 struct gctl_req *r; 86 const char *errstr; 87 88 DIALOG_LISTITEM items[] = { 89 {"APM", "Apple Partition Map", 90 "Bootable on PowerPC Apple Hardware", 0 }, 91 {"BSD", "BSD Labels", 92 "Bootable on most x86 systems", 0 }, 93 {"GPT", "GUID Partition Table", 94 "Bootable on most x86 systems", 0 }, 95 {"MBR", "DOS Partitions", 96 "Bootable on most x86 systems", 0 }, 97 {"PC98", "NEC PC9801 Partition Table", 98 "Bootable on NEC PC9801 systems", 0 }, 99 {"VTOC8", "Sun VTOC8 Partition Table", 100 "Bootable on Sun SPARC systems", 0 }, 101 }; 102 103 schememenu: 104 if (scheme == NULL) { 105 dialog_vars.default_item = __DECONST(char *, default_scheme()); 106 cancel = dlg_menu("Partition Scheme", 107 "Select a partition scheme for this volume:", 0, 0, 0, 108 sizeof(items) / sizeof(items[0]), items, &choice, NULL); 109 dialog_vars.default_item = NULL; 110 111 if (cancel) 112 return (-1); 113 114 if (!is_scheme_bootable(items[choice].name)) { 115 char message[512]; 116 sprintf(message, "This partition scheme (%s) is not " 117 "bootable on this platform. Are you sure you want " 118 "to proceed?", items[choice].name); 119 dialog_vars.defaultno = TRUE; 120 cancel = dialog_yesno("Warning", message, 0, 0); 121 dialog_vars.defaultno = FALSE; 122 if (cancel) /* cancel */ 123 goto schememenu; 124 } 125 126 scheme = items[choice].name; 127 } 128 129 r = gctl_get_handle(); 130 gctl_ro_param(r, "class", -1, "PART"); 131 gctl_ro_param(r, "arg0", -1, lg_name); 132 gctl_ro_param(r, "flags", -1, GPART_FLAGS); 133 gctl_ro_param(r, "scheme", -1, scheme); 134 gctl_ro_param(r, "verb", -1, "create"); 135 136 errstr = gctl_issue(r); 137 if (errstr != NULL && errstr[0] != '\0') { 138 gpart_show_error("Error", NULL, errstr); 139 gctl_free(r); 140 scheme = NULL; 141 goto schememenu; 142 } 143 gctl_free(r); 144 145 if (bootcode_path(scheme) != NULL) 146 get_part_metadata(lg_name, 1)->bootcode = 1; 147 return (0); 148 } 149 150 static void 151 gpart_activate(struct gprovider *pp) 152 { 153 struct gconfig *gc; 154 struct gctl_req *r; 155 const char *errstr, *scheme; 156 const char *attribute = NULL; 157 intmax_t idx; 158 159 /* 160 * Some partition schemes need this partition to be marked 'active' 161 * for it to be bootable. 162 */ 163 LIST_FOREACH(gc, &pp->lg_geom->lg_config, lg_config) { 164 if (strcmp(gc->lg_name, "scheme") == 0) { 165 scheme = gc->lg_val; 166 break; 167 } 168 } 169 170 if (strcmp(scheme, "MBR") == 0 || strcmp(scheme, "EBR") == 0 || 171 strcmp(scheme, "PC98") == 0) 172 attribute = "active"; 173 else 174 return; 175 176 LIST_FOREACH(gc, &pp->lg_config, lg_config) { 177 if (strcmp(gc->lg_name, "index") == 0) { 178 idx = atoi(gc->lg_val); 179 break; 180 } 181 } 182 183 r = gctl_get_handle(); 184 gctl_ro_param(r, "class", -1, "PART"); 185 gctl_ro_param(r, "arg0", -1, pp->lg_geom->lg_name); 186 gctl_ro_param(r, "verb", -1, "set"); 187 gctl_ro_param(r, "attrib", -1, attribute); 188 gctl_ro_param(r, "index", sizeof(idx), &idx); 189 190 errstr = gctl_issue(r); 191 if (errstr != NULL && errstr[0] != '\0') 192 gpart_show_error("Error", "Error marking partition active:", 193 errstr); 194 gctl_free(r); 195 } 196 197 static void 198 gpart_bootcode(struct ggeom *gp) 199 { 200 const char *bootcode; 201 struct gconfig *gc; 202 struct gctl_req *r; 203 const char *errstr, *scheme; 204 uint8_t *boot; 205 size_t bootsize, bytes; 206 int bootfd; 207 208 /* 209 * Write default bootcode to the newly partitioned disk, if that 210 * applies on this platform. 211 */ 212 LIST_FOREACH(gc, &gp->lg_config, lg_config) { 213 if (strcmp(gc->lg_name, "scheme") == 0) { 214 scheme = gc->lg_val; 215 break; 216 } 217 } 218 219 bootcode = bootcode_path(scheme); 220 if (bootcode == NULL) 221 return; 222 223 bootfd = open(bootcode, O_RDONLY); 224 if (bootfd <= 0) { 225 dialog_msgbox("Bootcode Error", strerror(errno), 0, 0, 226 TRUE); 227 return; 228 } 229 230 bootsize = lseek(bootfd, 0, SEEK_END); 231 boot = malloc(bootsize); 232 lseek(bootfd, 0, SEEK_SET); 233 bytes = 0; 234 while (bytes < bootsize) 235 bytes += read(bootfd, boot + bytes, bootsize - bytes); 236 close(bootfd); 237 238 r = gctl_get_handle(); 239 gctl_ro_param(r, "class", -1, "PART"); 240 gctl_ro_param(r, "arg0", -1, gp->lg_name); 241 gctl_ro_param(r, "verb", -1, "bootcode"); 242 gctl_ro_param(r, "bootcode", bootsize, boot); 243 244 errstr = gctl_issue(r); 245 if (errstr != NULL && errstr[0] != '\0') 246 gpart_show_error("Bootcode Error", NULL, errstr); 247 gctl_free(r); 248 free(boot); 249 } 250 251 static void 252 gpart_partcode(struct gprovider *pp) 253 { 254 struct gconfig *gc; 255 const char *scheme; 256 const char *indexstr; 257 char message[255], command[255]; 258 259 LIST_FOREACH(gc, &pp->lg_geom->lg_config, lg_config) { 260 if (strcmp(gc->lg_name, "scheme") == 0) { 261 scheme = gc->lg_val; 262 break; 263 } 264 } 265 266 /* Make sure this partition scheme needs partcode on this platform */ 267 if (partcode_path(scheme) == NULL) 268 return; 269 270 LIST_FOREACH(gc, &pp->lg_config, lg_config) { 271 if (strcmp(gc->lg_name, "index") == 0) { 272 indexstr = gc->lg_val; 273 break; 274 } 275 } 276 277 /* Shell out to gpart for partcode for now */ 278 sprintf(command, "gpart bootcode -p %s -i %s %s", 279 partcode_path(scheme), indexstr, pp->lg_geom->lg_name); 280 if (system(command) != 0) { 281 sprintf(message, "Error installing partcode on partition %s", 282 pp->lg_name); 283 dialog_msgbox("Error", message, 0, 0, TRUE); 284 } 285 } 286 287 void 288 gpart_destroy(struct ggeom *lg_geom, int force) 289 { 290 struct gprovider *pp; 291 struct gctl_req *r; 292 const char *errstr; 293 294 /* Begin with the hosing: delete all partitions */ 295 LIST_FOREACH(pp, &lg_geom->lg_provider, lg_provider) 296 gpart_delete(pp); 297 298 /* Now destroy the geom itself */ 299 r = gctl_get_handle(); 300 gctl_ro_param(r, "class", -1, "PART"); 301 gctl_ro_param(r, "arg0", -1, lg_geom->lg_name); 302 gctl_ro_param(r, "flags", -1, GPART_FLAGS); 303 gctl_ro_param(r, "verb", -1, "destroy"); 304 errstr = gctl_issue(r); 305 if (errstr != NULL && errstr[0] != '\0') 306 gpart_show_error("Error", NULL, errstr); 307 gctl_free(r); 308 309 /* If asked, commit the change */ 310 if (force) { 311 r = gctl_get_handle(); 312 gctl_ro_param(r, "class", -1, "PART"); 313 gctl_ro_param(r, "arg0", -1, lg_geom->lg_name); 314 gctl_ro_param(r, "verb", -1, "commit"); 315 errstr = gctl_issue(r); 316 if (errstr != NULL && errstr[0] != '\0') 317 gpart_show_error("Error", NULL, errstr); 318 gctl_free(r); 319 } 320 321 /* And any metadata associated with the partition scheme itself */ 322 delete_part_metadata(lg_geom->lg_name); 323 } 324 325 void 326 gpart_edit(struct gprovider *pp) 327 { 328 struct gctl_req *r; 329 struct gconfig *gc; 330 struct gconsumer *cp; 331 struct ggeom *geom; 332 const char *errstr, *oldtype, *scheme; 333 struct partition_metadata *md; 334 char sizestr[32]; 335 intmax_t idx; 336 int hadlabel, choice, junk, nitems; 337 unsigned i; 338 339 DIALOG_FORMITEM items[] = { 340 {0, "Type:", 5, 0, 0, FALSE, "", 11, 0, 12, 15, 0, 341 FALSE, "Filesystem type (e.g. freebsd-ufs, freebsd-swap)", 342 FALSE}, 343 {0, "Size:", 5, 1, 0, FALSE, "", 11, 1, 12, 0, 0, 344 FALSE, "Partition size. Append K, M, G for kilobytes, " 345 "megabytes or gigabytes.", FALSE}, 346 {0, "Mountpoint:", 11, 2, 0, FALSE, "", 11, 2, 12, 15, 0, 347 FALSE, "Path at which to mount this partition (leave blank " 348 "for swap, set to / for root filesystem)", FALSE}, 349 {0, "Label:", 7, 3, 0, FALSE, "", 11, 3, 12, 15, 0, FALSE, 350 "Partition name. Not all partition schemes support this.", 351 FALSE}, 352 }; 353 354 /* 355 * Find the PART geom we are manipulating. This may be a consumer of 356 * this provider, or its parent. Check the consumer case first. 357 */ 358 geom = NULL; 359 LIST_FOREACH(cp, &pp->lg_consumers, lg_consumers) 360 if (strcmp(cp->lg_geom->lg_class->lg_name, "PART") == 0) { 361 char message[512]; 362 /* 363 * The PART object is a consumer, so the user wants to 364 * edit the partition table. gpart doesn't really 365 * support this, so we have to hose the whole table 366 * first. 367 */ 368 369 sprintf(message, "Changing the partition scheme on " 370 "this disk (%s) requires deleting all existing " 371 "partitions on this drive. This will PERMANENTLY " 372 "ERASE any data stored here. Are you sure you want " 373 "to proceed?", cp->lg_geom->lg_name); 374 dialog_vars.defaultno = TRUE; 375 choice = dialog_yesno("Warning", message, 0, 0); 376 dialog_vars.defaultno = FALSE; 377 378 if (choice == 1) /* cancel */ 379 return; 380 381 /* Destroy the geom and all sub-partitions */ 382 gpart_destroy(cp->lg_geom, 0); 383 384 /* Now re-partition and return */ 385 gpart_partition(cp->lg_geom->lg_name, NULL); 386 return; 387 } 388 389 if (geom == NULL && strcmp(pp->lg_geom->lg_class->lg_name, "PART") == 0) 390 geom = pp->lg_geom; 391 392 if (geom == NULL) { 393 /* Disk not partitioned, so partition it */ 394 gpart_partition(pp->lg_geom->lg_name, NULL); 395 return; 396 } 397 398 LIST_FOREACH(gc, &geom->lg_config, lg_config) { 399 if (strcmp(gc->lg_name, "scheme") == 0) { 400 scheme = gc->lg_val; 401 break; 402 } 403 } 404 405 nitems = scheme_supports_labels(scheme) ? 4 : 3; 406 407 /* Edit editable parameters of a partition */ 408 hadlabel = 0; 409 LIST_FOREACH(gc, &pp->lg_config, lg_config) { 410 if (strcmp(gc->lg_name, "type") == 0) { 411 oldtype = gc->lg_val; 412 items[0].text = gc->lg_val; 413 } 414 if (strcmp(gc->lg_name, "label") == 0 && gc->lg_val != NULL) { 415 hadlabel = 1; 416 items[3].text = gc->lg_val; 417 } 418 if (strcmp(gc->lg_name, "index") == 0) 419 idx = atoi(gc->lg_val); 420 } 421 422 TAILQ_FOREACH(md, &part_metadata, metadata) { 423 if (md->name != NULL && strcmp(md->name, pp->lg_name) == 0) { 424 if (md->fstab != NULL) 425 items[2].text = md->fstab->fs_file; 426 break; 427 } 428 } 429 430 humanize_number(sizestr, 7, pp->lg_mediasize, "B", HN_AUTOSCALE, 431 HN_NOSPACE | HN_DECIMAL); 432 items[1].text = sizestr; 433 434 editpart: 435 choice = dlg_form("Edit Partition", "", 0, 0, 0, nitems, items, &junk); 436 437 if (choice) /* Cancel pressed */ 438 return; 439 440 /* Check if the label has a / in it */ 441 if (strchr(items[3].text, '/') != NULL) { 442 dialog_msgbox("Error", "Label contains a /, which is not an " 443 "allowed character.", 0, 0, TRUE); 444 goto editpart; 445 } 446 447 if (strncmp(items[0].text, "freebsd-", 8) != 0 && 448 items[0].text[0] != '\0') { 449 char message[512]; 450 451 sprintf(message, "Cannot mount unknown file system %s!\n", 452 items[0].text); 453 dialog_msgbox("Error", message, 0, 0, TRUE); 454 goto editpart; 455 } 456 457 r = gctl_get_handle(); 458 gctl_ro_param(r, "class", -1, "PART"); 459 gctl_ro_param(r, "arg0", -1, geom->lg_name); 460 gctl_ro_param(r, "flags", -1, GPART_FLAGS); 461 gctl_ro_param(r, "verb", -1, "modify"); 462 gctl_ro_param(r, "index", sizeof(idx), &idx); 463 if (hadlabel || items[3].text[0] != '\0') 464 gctl_ro_param(r, "label", -1, items[3].text); 465 gctl_ro_param(r, "type", -1, items[0].text); 466 errstr = gctl_issue(r); 467 if (errstr != NULL && errstr[0] != '\0') { 468 gpart_show_error("Error", NULL, errstr); 469 gctl_free(r); 470 goto editpart; 471 } 472 gctl_free(r); 473 474 set_default_part_metadata(pp->lg_name, scheme, items[0].text, 475 items[2].text, strcmp(oldtype, items[0].text) != 0); 476 477 for (i = 0; i < (sizeof(items) / sizeof(items[0])); i++) 478 if (items[i].text_free) 479 free(items[i].text); 480 } 481 482 void 483 set_default_part_metadata(const char *name, const char *scheme, 484 const char *type, const char *mountpoint, int newfs) 485 { 486 struct partition_metadata *md; 487 488 /* Set part metadata */ 489 md = get_part_metadata(name, 1); 490 491 if (newfs) { 492 if (md->newfs != NULL) { 493 free(md->newfs); 494 md->newfs = NULL; 495 } 496 497 if (strcmp(type, "freebsd-ufs") == 0) { 498 md->newfs = malloc(255); 499 sprintf(md->newfs, "newfs /dev/%s", name); 500 } 501 } 502 503 if (strcmp(type, "freebsd-swap") == 0) 504 mountpoint = "none"; 505 if (strcmp(type, "freebsd-boot") == 0) 506 md->bootcode = 1; 507 508 /* VTOC8 needs partcode in UFS partitions */ 509 if (strcmp(scheme, "VTOC8") == 0 && strcmp(type, "freebsd-ufs") == 0) 510 md->bootcode = 1; 511 512 if (mountpoint == NULL || mountpoint[0] == '\0') { 513 if (md->fstab != NULL) { 514 free(md->fstab->fs_spec); 515 free(md->fstab->fs_file); 516 free(md->fstab->fs_vfstype); 517 free(md->fstab->fs_mntops); 518 free(md->fstab->fs_type); 519 free(md->fstab); 520 md->fstab = NULL; 521 } 522 } else { 523 if (md->fstab == NULL) { 524 md->fstab = malloc(sizeof(struct fstab)); 525 } else { 526 free(md->fstab->fs_spec); 527 free(md->fstab->fs_file); 528 free(md->fstab->fs_vfstype); 529 free(md->fstab->fs_mntops); 530 free(md->fstab->fs_type); 531 } 532 md->fstab->fs_spec = malloc(strlen(name) + 6); 533 sprintf(md->fstab->fs_spec, "/dev/%s", name); 534 md->fstab->fs_file = strdup(mountpoint); 535 /* Get VFS from text after freebsd-, if possible */ 536 if (strncmp("freebsd-", type, 8)) 537 md->fstab->fs_vfstype = strdup(&type[8]); 538 else 539 md->fstab->fs_vfstype = strdup(type); /* Guess */ 540 md->fstab->fs_vfstype = strdup(&type[8]); 541 if (strcmp(type, "freebsd-swap") == 0) { 542 md->fstab->fs_type = strdup(FSTAB_SW); 543 md->fstab->fs_freq = 0; 544 md->fstab->fs_passno = 0; 545 } else { 546 md->fstab->fs_type = strdup(FSTAB_RW); 547 if (strcmp(mountpoint, "/") == 0) { 548 md->fstab->fs_freq = 1; 549 md->fstab->fs_passno = 1; 550 } else { 551 md->fstab->fs_freq = 2; 552 md->fstab->fs_passno = 2; 553 } 554 } 555 md->fstab->fs_mntops = strdup(md->fstab->fs_type); 556 } 557 } 558 559 static 560 int part_compare(const void *xa, const void *xb) 561 { 562 struct gprovider **a = (struct gprovider **)xa; 563 struct gprovider **b = (struct gprovider **)xb; 564 intmax_t astart, bstart; 565 struct gconfig *gc; 566 567 astart = bstart = 0; 568 LIST_FOREACH(gc, &(*a)->lg_config, lg_config) 569 if (strcmp(gc->lg_name, "start") == 0) { 570 astart = strtoimax(gc->lg_val, NULL, 0); 571 break; 572 } 573 LIST_FOREACH(gc, &(*b)->lg_config, lg_config) 574 if (strcmp(gc->lg_name, "start") == 0) { 575 bstart = strtoimax(gc->lg_val, NULL, 0); 576 break; 577 } 578 579 if (astart < bstart) 580 return -1; 581 else if (astart > bstart) 582 return 1; 583 else 584 return 0; 585 } 586 587 intmax_t 588 gpart_max_free(struct ggeom *geom, intmax_t *npartstart) 589 { 590 struct gconfig *gc; 591 struct gprovider *pp, **providers; 592 intmax_t lastend; 593 intmax_t start, end; 594 intmax_t maxsize, maxstart; 595 intmax_t partstart, partend; 596 int i, nparts; 597 598 /* Now get the maximum free size and free start */ 599 start = end = 0; 600 LIST_FOREACH(gc, &geom->lg_config, lg_config) { 601 if (strcmp(gc->lg_name, "first") == 0) 602 start = strtoimax(gc->lg_val, NULL, 0); 603 if (strcmp(gc->lg_name, "last") == 0) 604 end = strtoimax(gc->lg_val, NULL, 0); 605 } 606 607 i = nparts = 0; 608 LIST_FOREACH(pp, &geom->lg_provider, lg_provider) 609 nparts++; 610 providers = calloc(nparts, sizeof(providers[0])); 611 LIST_FOREACH(pp, &geom->lg_provider, lg_provider) 612 providers[i++] = pp; 613 qsort(providers, nparts, sizeof(providers[0]), part_compare); 614 615 lastend = start - 1; 616 maxsize = 0; 617 for (i = 0; i < nparts; i++) { 618 pp = providers[i]; 619 620 LIST_FOREACH(gc, &pp->lg_config, lg_config) { 621 if (strcmp(gc->lg_name, "start") == 0) 622 partstart = strtoimax(gc->lg_val, NULL, 0); 623 if (strcmp(gc->lg_name, "end") == 0) 624 partend = strtoimax(gc->lg_val, NULL, 0); 625 } 626 627 if (partstart - lastend > maxsize) { 628 maxsize = partstart - lastend - 1; 629 maxstart = lastend + 1; 630 } 631 632 lastend = partend; 633 } 634 635 if (end - lastend > maxsize) { 636 maxsize = end - lastend - 1; 637 maxstart = lastend + 1; 638 } 639 640 pp = LIST_FIRST(&geom->lg_consumer)->lg_provider; 641 642 /* Compute beginning of new partition and maximum available space */ 643 if (pp->lg_stripesize > 0 && 644 (maxstart*pp->lg_sectorsize % pp->lg_stripesize) != 0) { 645 intmax_t offset = (pp->lg_stripesize - 646 ((maxstart*pp->lg_sectorsize) % pp->lg_stripesize)) / 647 pp->lg_sectorsize; 648 maxstart += offset; 649 maxsize -= offset; 650 } 651 652 if (npartstart != NULL) 653 *npartstart = maxstart; 654 655 return (maxsize); 656 } 657 658 void 659 gpart_create(struct gprovider *pp, char *default_type, char *default_size, 660 char *default_mountpoint, char **partname, int interactive) 661 { 662 struct gctl_req *r; 663 struct gconfig *gc; 664 struct gconsumer *cp; 665 struct ggeom *geom; 666 const char *errstr, *scheme; 667 char sizestr[32], startstr[32], output[64]; 668 intmax_t maxsize, size, sector, firstfree, stripe; 669 uint64_t bytes; 670 int nitems, choice, junk; 671 unsigned i; 672 673 DIALOG_FORMITEM items[] = { 674 {0, "Type:", 5, 0, 0, FALSE, "freebsd-ufs", 11, 0, 12, 15, 0, 675 FALSE, "Filesystem type (e.g. freebsd-ufs, freebsd-swap)", 676 FALSE}, 677 {0, "Size:", 5, 1, 0, FALSE, "", 11, 1, 12, 15, 0, 678 FALSE, "Partition size. Append K, M, G for kilobytes, " 679 "megabytes or gigabytes.", FALSE}, 680 {0, "Mountpoint:", 11, 2, 0, FALSE, "", 11, 2, 12, 15, 0, 681 FALSE, "Path at which to mount partition (blank for " 682 "swap, / for root filesystem)", FALSE}, 683 {0, "Label:", 7, 3, 0, FALSE, "", 11, 3, 12, 15, 0, FALSE, 684 "Partition name. Not all partition schemes support this.", 685 FALSE}, 686 }; 687 688 if (partname != NULL) 689 *partname = NULL; 690 691 /* Record sector and stripe sizes */ 692 sector = pp->lg_sectorsize; 693 stripe = pp->lg_stripesize; 694 695 /* 696 * Find the PART geom we are manipulating. This may be a consumer of 697 * this provider, or its parent. Check the consumer case first. 698 */ 699 geom = NULL; 700 LIST_FOREACH(cp, &pp->lg_consumers, lg_consumers) 701 if (strcmp(cp->lg_geom->lg_class->lg_name, "PART") == 0) { 702 geom = cp->lg_geom; 703 break; 704 } 705 706 if (geom == NULL && strcmp(pp->lg_geom->lg_class->lg_name, "PART") == 0) 707 geom = pp->lg_geom; 708 709 /* Now get the partition scheme */ 710 scheme = NULL; 711 if (geom != NULL) { 712 LIST_FOREACH(gc, &geom->lg_config, lg_config) 713 if (strcmp(gc->lg_name, "scheme") == 0) 714 scheme = gc->lg_val; 715 } 716 717 if (geom == NULL || scheme == NULL || strcmp(scheme, "(none)") == 0) { 718 if (gpart_partition(pp->lg_geom->lg_name, NULL) == 0) 719 dialog_msgbox("", 720 "The partition table has been successfully created." 721 " Please press Create again to create partitions.", 722 0, 0, TRUE); 723 724 return; 725 } 726 727 /* 728 * If we still don't have a geom, either the user has 729 * canceled partitioning or there has been an error which has already 730 * been displayed, so bail. 731 */ 732 if (geom == NULL) 733 return; 734 735 maxsize = size = gpart_max_free(geom, &firstfree); 736 if (size <= 0) { 737 dialog_msgbox("Error", "No free space left on device.", 0, 0, 738 TRUE); 739 return; 740 } 741 742 humanize_number(sizestr, 7, size*sector, "B", HN_AUTOSCALE, 743 HN_NOSPACE | HN_DECIMAL); 744 items[1].text = sizestr; 745 746 /* Special-case the MBR default type for nested partitions */ 747 if (strcmp(scheme, "MBR") == 0 || strcmp(scheme, "PC98") == 0) 748 items[0].text = "freebsd"; 749 750 nitems = scheme_supports_labels(scheme) ? 4 : 3; 751 752 if (default_type != NULL) 753 items[0].text = default_type; 754 if (default_size != NULL) 755 items[1].text = default_size; 756 if (default_mountpoint != NULL) 757 items[2].text = default_mountpoint; 758 759 addpartform: 760 if (interactive) { 761 choice = dlg_form("Add Partition", "", 0, 0, 0, nitems, 762 items, &junk); 763 if (choice) /* Cancel pressed */ 764 return; 765 } 766 767 size = maxsize; 768 if (strlen(items[1].text) > 0) { 769 if (expand_number(items[1].text, &bytes) != 0) { 770 char error[512]; 771 772 sprintf(error, "Invalid size: %s\n", strerror(errno)); 773 dialog_msgbox("Error", error, 0, 0, TRUE); 774 goto addpartform; 775 } 776 size = MIN((intmax_t)(bytes/sector), maxsize); 777 } 778 779 /* Check if the label has a / in it */ 780 if (strchr(items[3].text, '/') != NULL) { 781 dialog_msgbox("Error", "Label contains a /, which is not an " 782 "allowed character.", 0, 0, TRUE); 783 goto addpartform; 784 } 785 786 /* Warn if no mountpoint set */ 787 if (strcmp(items[0].text, "freebsd-ufs") == 0 && 788 items[2].text[0] != '/') { 789 dialog_vars.defaultno = TRUE; 790 choice = dialog_yesno("Warning", 791 "This partition does not have a valid mountpoint " 792 "(for the partition from which you intend to boot the " 793 "operating system, the mountpoint should be /). Are you " 794 "sure you want to continue?" 795 , 0, 0); 796 dialog_vars.defaultno = FALSE; 797 if (choice == 1) /* cancel */ 798 goto addpartform; 799 } 800 801 /* If this is the root partition, check that this scheme is bootable */ 802 if (strcmp(items[2].text, "/") == 0 && !is_scheme_bootable(scheme)) { 803 char message[512]; 804 sprintf(message, "This partition scheme (%s) is not bootable " 805 "on this platform. Are you sure you want to proceed?", 806 scheme); 807 dialog_vars.defaultno = TRUE; 808 choice = dialog_yesno("Warning", message, 0, 0); 809 dialog_vars.defaultno = FALSE; 810 if (choice == 1) /* cancel */ 811 goto addpartform; 812 } 813 814 /* 815 * If this is the root partition, and we need a boot partition, ask 816 * the user to add one. 817 */ 818 if (strcmp(items[2].text, "/") == 0 && bootpart_size(scheme) > 0) { 819 if (interactive) 820 choice = dialog_yesno("Boot Partition", 821 "This partition scheme requires a boot partition " 822 "for the disk to be bootable. Would you like to " 823 "make one now?", 0, 0); 824 else 825 choice = 0; 826 827 if (choice == 0) { /* yes */ 828 r = gctl_get_handle(); 829 gctl_ro_param(r, "class", -1, "PART"); 830 gctl_ro_param(r, "arg0", -1, geom->lg_name); 831 gctl_ro_param(r, "flags", -1, GPART_FLAGS); 832 gctl_ro_param(r, "verb", -1, "add"); 833 gctl_ro_param(r, "type", -1, "freebsd-boot"); 834 snprintf(sizestr, sizeof(sizestr), "%jd", 835 bootpart_size(scheme) / sector); 836 gctl_ro_param(r, "size", -1, sizestr); 837 snprintf(startstr, sizeof(startstr), "%jd", firstfree); 838 gctl_ro_param(r, "start", -1, startstr); 839 gctl_rw_param(r, "output", sizeof(output), output); 840 errstr = gctl_issue(r); 841 if (errstr != NULL && errstr[0] != '\0') 842 gpart_show_error("Error", NULL, errstr); 843 gctl_free(r); 844 845 get_part_metadata(strtok(output, " "), 1)->bootcode = 1; 846 847 /* Now adjust the part we are really adding forward */ 848 firstfree += bootpart_size(scheme) / sector; 849 size -= (bootpart_size(scheme) + stripe)/sector; 850 if (stripe > 0 && (firstfree*sector % stripe) != 0) 851 firstfree += (stripe - ((firstfree*sector) % 852 stripe)) / sector; 853 } 854 } 855 856 r = gctl_get_handle(); 857 gctl_ro_param(r, "class", -1, "PART"); 858 gctl_ro_param(r, "arg0", -1, geom->lg_name); 859 gctl_ro_param(r, "flags", -1, GPART_FLAGS); 860 gctl_ro_param(r, "verb", -1, "add"); 861 862 gctl_ro_param(r, "type", -1, items[0].text); 863 snprintf(sizestr, sizeof(sizestr), "%jd", size); 864 gctl_ro_param(r, "size", -1, sizestr); 865 snprintf(startstr, sizeof(startstr), "%jd", firstfree); 866 gctl_ro_param(r, "start", -1, startstr); 867 if (items[3].text[0] != '\0') 868 gctl_ro_param(r, "label", -1, items[3].text); 869 gctl_rw_param(r, "output", sizeof(output), output); 870 871 errstr = gctl_issue(r); 872 if (errstr != NULL && errstr[0] != '\0') { 873 gpart_show_error("Error", NULL, errstr); 874 gctl_free(r); 875 goto addpartform; 876 } 877 878 if (strcmp(items[0].text, "freebsd-boot") == 0) 879 get_part_metadata(strtok(output, " "), 1)->bootcode = 1; 880 else if (strcmp(items[0].text, "freebsd") == 0) 881 gpart_partition(strtok(output, " "), "BSD"); 882 else 883 set_default_part_metadata(strtok(output, " "), scheme, 884 items[0].text, items[2].text, 1); 885 886 for (i = 0; i < (sizeof(items) / sizeof(items[0])); i++) 887 if (items[i].text_free) 888 free(items[i].text); 889 gctl_free(r); 890 891 if (partname != NULL) 892 *partname = strdup(strtok(output, " ")); 893 } 894 895 void 896 gpart_delete(struct gprovider *pp) 897 { 898 struct gconfig *gc; 899 struct ggeom *geom; 900 struct gconsumer *cp; 901 struct gctl_req *r; 902 const char *errstr; 903 intmax_t idx; 904 int choice, is_partition; 905 906 /* Is it a partition? */ 907 is_partition = (strcmp(pp->lg_geom->lg_class->lg_name, "PART") == 0); 908 909 /* Find out if this is the root of a gpart geom */ 910 geom = NULL; 911 LIST_FOREACH(cp, &pp->lg_consumers, lg_consumers) 912 if (strcmp(cp->lg_geom->lg_class->lg_name, "PART") == 0) { 913 geom = cp->lg_geom; 914 break; 915 } 916 917 /* Destroy all consumers */ 918 if (geom != NULL) { 919 if (is_partition) { 920 char message[512]; 921 /* 922 * We have to actually really delete the sub-partition 923 * tree so that the consumers will go away and the 924 * partition can be deleted. Warn the user. 925 */ 926 927 sprintf(message, "Deleting this partition (%s) " 928 "requires deleting all existing sub-partitions. " 929 "This will PERMANENTLY ERASE any data stored here " 930 "and CANNOT BE REVERTED. Are you sure you want to " 931 "proceed?", cp->lg_geom->lg_name); 932 dialog_vars.defaultno = TRUE; 933 choice = dialog_yesno("Warning", message, 0, 0); 934 dialog_vars.defaultno = FALSE; 935 936 if (choice == 1) /* cancel */ 937 return; 938 } 939 940 gpart_destroy(geom, is_partition); 941 } 942 943 /* 944 * If this is not a partition, see if that is a problem, complain if 945 * necessary, and return always, since we need not do anything further, 946 * error or no. 947 */ 948 if (!is_partition) { 949 if (geom == NULL) 950 dialog_msgbox("Error", 951 "Only partitions can be deleted.", 0, 0, TRUE); 952 return; 953 } 954 955 r = gctl_get_handle(); 956 gctl_ro_param(r, "class", -1, pp->lg_geom->lg_class->lg_name); 957 gctl_ro_param(r, "arg0", -1, pp->lg_geom->lg_name); 958 gctl_ro_param(r, "flags", -1, GPART_FLAGS); 959 gctl_ro_param(r, "verb", -1, "delete"); 960 961 LIST_FOREACH(gc, &pp->lg_config, lg_config) { 962 if (strcmp(gc->lg_name, "index") == 0) { 963 idx = atoi(gc->lg_val); 964 gctl_ro_param(r, "index", sizeof(idx), &idx); 965 break; 966 } 967 } 968 969 errstr = gctl_issue(r); 970 if (errstr != NULL && errstr[0] != '\0') { 971 gpart_show_error("Error", NULL, errstr); 972 gctl_free(r); 973 return; 974 } 975 976 gctl_free(r); 977 978 delete_part_metadata(pp->lg_name); 979 } 980 981 void 982 gpart_revert_all(struct gmesh *mesh) 983 { 984 struct gclass *classp; 985 struct gconfig *gc; 986 struct ggeom *gp; 987 struct gctl_req *r; 988 const char *errstr; 989 const char *modified; 990 991 LIST_FOREACH(classp, &mesh->lg_class, lg_class) { 992 if (strcmp(classp->lg_name, "PART") == 0) 993 break; 994 } 995 996 if (strcmp(classp->lg_name, "PART") != 0) { 997 dialog_msgbox("Error", "gpart not found!", 0, 0, TRUE); 998 return; 999 } 1000 1001 LIST_FOREACH(gp, &classp->lg_geom, lg_geom) { 1002 modified = "true"; /* XXX: If we don't know (kernel too old), 1003 * assume there are modifications. */ 1004 LIST_FOREACH(gc, &gp->lg_config, lg_config) { 1005 if (strcmp(gc->lg_name, "modified") == 0) { 1006 modified = gc->lg_val; 1007 break; 1008 } 1009 } 1010 1011 if (strcmp(modified, "false") == 0) 1012 continue; 1013 1014 r = gctl_get_handle(); 1015 gctl_ro_param(r, "class", -1, "PART"); 1016 gctl_ro_param(r, "arg0", -1, gp->lg_name); 1017 gctl_ro_param(r, "verb", -1, "undo"); 1018 1019 errstr = gctl_issue(r); 1020 if (errstr != NULL && errstr[0] != '\0') 1021 gpart_show_error("Error", NULL, errstr); 1022 gctl_free(r); 1023 } 1024 } 1025 1026 void 1027 gpart_commit(struct gmesh *mesh) 1028 { 1029 struct partition_metadata *md; 1030 struct gclass *classp; 1031 struct ggeom *gp; 1032 struct gconfig *gc; 1033 struct gconsumer *cp; 1034 struct gprovider *pp; 1035 struct gctl_req *r; 1036 const char *errstr; 1037 const char *modified; 1038 1039 LIST_FOREACH(classp, &mesh->lg_class, lg_class) { 1040 if (strcmp(classp->lg_name, "PART") == 0) 1041 break; 1042 } 1043 1044 if (strcmp(classp->lg_name, "PART") != 0) { 1045 dialog_msgbox("Error", "gpart not found!", 0, 0, TRUE); 1046 return; 1047 } 1048 1049 LIST_FOREACH(gp, &classp->lg_geom, lg_geom) { 1050 modified = "true"; /* XXX: If we don't know (kernel too old), 1051 * assume there are modifications. */ 1052 LIST_FOREACH(gc, &gp->lg_config, lg_config) { 1053 if (strcmp(gc->lg_name, "modified") == 0) { 1054 modified = gc->lg_val; 1055 break; 1056 } 1057 } 1058 1059 if (strcmp(modified, "false") == 0) 1060 continue; 1061 1062 /* Add bootcode if necessary, before the commit */ 1063 md = get_part_metadata(gp->lg_name, 0); 1064 if (md != NULL && md->bootcode) 1065 gpart_bootcode(gp); 1066 1067 /* Now install partcode on its partitions, if necessary */ 1068 LIST_FOREACH(pp, &gp->lg_provider, lg_provider) { 1069 md = get_part_metadata(pp->lg_name, 0); 1070 if (md == NULL || !md->bootcode) 1071 continue; 1072 1073 /* Mark this partition active if that's required */ 1074 gpart_activate(pp); 1075 1076 /* Check if the partition has sub-partitions */ 1077 LIST_FOREACH(cp, &pp->lg_consumers, lg_consumers) 1078 if (strcmp(cp->lg_geom->lg_class->lg_name, 1079 "PART") == 0) 1080 break; 1081 1082 if (cp == NULL) /* No sub-partitions */ 1083 gpart_partcode(pp); 1084 } 1085 1086 r = gctl_get_handle(); 1087 gctl_ro_param(r, "class", -1, "PART"); 1088 gctl_ro_param(r, "arg0", -1, gp->lg_name); 1089 gctl_ro_param(r, "verb", -1, "commit"); 1090 1091 errstr = gctl_issue(r); 1092 if (errstr != NULL && errstr[0] != '\0') 1093 gpart_show_error("Error", NULL, errstr); 1094 gctl_free(r); 1095 } 1096 } 1097 1098