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