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, "FAT12") == 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) 369 { 370 struct gctl_req *r; 371 struct gprovider *pp; 372 const char *errstr; 373 int force = 1; 374 375 /* Delete all child metadata */ 376 LIST_FOREACH(pp, &lg_geom->lg_provider, lg_provider) 377 gpart_delete(pp); 378 379 /* Revert any local changes to get this geom into a pristine state */ 380 r = gctl_get_handle(); 381 gctl_ro_param(r, "class", -1, "PART"); 382 gctl_ro_param(r, "arg0", -1, lg_geom->lg_name); 383 gctl_ro_param(r, "verb", -1, "undo"); 384 gctl_issue(r); /* Ignore errors -- these are non-fatal */ 385 gctl_free(r); 386 387 /* Now destroy the geom itself */ 388 r = gctl_get_handle(); 389 gctl_ro_param(r, "class", -1, "PART"); 390 gctl_ro_param(r, "arg0", -1, lg_geom->lg_name); 391 gctl_ro_param(r, "flags", -1, GPART_FLAGS); 392 gctl_ro_param(r, "force", sizeof(force), &force); 393 gctl_ro_param(r, "verb", -1, "destroy"); 394 errstr = gctl_issue(r); 395 if (errstr != NULL && errstr[0] != '\0') { 396 /* 397 * Check if we reverted away the existence of the geom 398 * altogether. Show all other errors to the user. 399 */ 400 if (strtol(errstr, NULL, 0) != EINVAL) 401 gpart_show_error("Error", NULL, errstr); 402 } 403 gctl_free(r); 404 405 /* And any metadata associated with the partition scheme itself */ 406 delete_part_metadata(lg_geom->lg_name); 407 } 408 409 void 410 gpart_edit(struct gprovider *pp) 411 { 412 struct gctl_req *r; 413 struct gconfig *gc; 414 struct gconsumer *cp; 415 struct ggeom *geom; 416 const char *errstr, *oldtype, *scheme; 417 struct partition_metadata *md; 418 char sizestr[32]; 419 char newfs[64]; 420 intmax_t idx; 421 int hadlabel, choice, junk, nitems; 422 unsigned i; 423 424 DIALOG_FORMITEM items[] = { 425 {0, "Type:", 5, 0, 0, FALSE, "", 11, 0, 12, 15, 0, 426 FALSE, "Filesystem type (e.g. freebsd-ufs, freebsd-swap)", 427 FALSE}, 428 {0, "Size:", 5, 1, 0, FALSE, "", 11, 1, 12, 0, 0, 429 FALSE, "Partition size. Append K, M, G for kilobytes, " 430 "megabytes or gigabytes.", FALSE}, 431 {0, "Mountpoint:", 11, 2, 0, FALSE, "", 11, 2, 12, 15, 0, 432 FALSE, "Path at which to mount this partition (leave blank " 433 "for swap, set to / for root filesystem)", FALSE}, 434 {0, "Label:", 7, 3, 0, FALSE, "", 11, 3, 12, 15, 0, FALSE, 435 "Partition name. Not all partition schemes support this.", 436 FALSE}, 437 }; 438 439 /* 440 * Find the PART geom we are manipulating. This may be a consumer of 441 * this provider, or its parent. Check the consumer case first. 442 */ 443 geom = NULL; 444 LIST_FOREACH(cp, &pp->lg_consumers, lg_consumers) 445 if (strcmp(cp->lg_geom->lg_class->lg_name, "PART") == 0) { 446 /* Check for zombie geoms, treating them as blank */ 447 scheme = NULL; 448 LIST_FOREACH(gc, &cp->lg_geom->lg_config, lg_config) { 449 if (strcmp(gc->lg_name, "scheme") == 0) { 450 scheme = gc->lg_val; 451 break; 452 } 453 } 454 if (scheme == NULL || strcmp(scheme, "(none)") == 0) { 455 gpart_partition(cp->lg_geom->lg_name, NULL); 456 return; 457 } 458 459 /* If this is a nested partition, edit as usual */ 460 if (strcmp(pp->lg_geom->lg_class->lg_name, "PART") == 0) 461 break; 462 463 /* Destroy the geom and all sub-partitions */ 464 gpart_destroy(cp->lg_geom); 465 466 /* Now re-partition and return */ 467 gpart_partition(cp->lg_geom->lg_name, NULL); 468 return; 469 } 470 471 if (geom == NULL && strcmp(pp->lg_geom->lg_class->lg_name, "PART") == 0) 472 geom = pp->lg_geom; 473 474 if (geom == NULL) { 475 /* Disk not partitioned, so partition it */ 476 gpart_partition(pp->lg_name, NULL); 477 return; 478 } 479 480 LIST_FOREACH(gc, &geom->lg_config, lg_config) { 481 if (strcmp(gc->lg_name, "scheme") == 0) { 482 scheme = gc->lg_val; 483 break; 484 } 485 } 486 487 nitems = scheme_supports_labels(scheme) ? 4 : 3; 488 489 /* Edit editable parameters of a partition */ 490 hadlabel = 0; 491 LIST_FOREACH(gc, &pp->lg_config, lg_config) { 492 if (strcmp(gc->lg_name, "type") == 0) { 493 oldtype = gc->lg_val; 494 items[0].text = gc->lg_val; 495 } 496 if (strcmp(gc->lg_name, "label") == 0 && gc->lg_val != NULL) { 497 hadlabel = 1; 498 items[3].text = gc->lg_val; 499 } 500 if (strcmp(gc->lg_name, "index") == 0) 501 idx = atoi(gc->lg_val); 502 } 503 504 TAILQ_FOREACH(md, &part_metadata, metadata) { 505 if (md->name != NULL && strcmp(md->name, pp->lg_name) == 0) { 506 if (md->fstab != NULL) 507 items[2].text = md->fstab->fs_file; 508 break; 509 } 510 } 511 512 humanize_number(sizestr, 7, pp->lg_mediasize, "B", HN_AUTOSCALE, 513 HN_NOSPACE | HN_DECIMAL); 514 items[1].text = sizestr; 515 516 editpart: 517 choice = dlg_form("Edit Partition", "", 0, 0, 0, nitems, items, &junk); 518 519 if (choice) /* Cancel pressed */ 520 goto endedit; 521 522 /* Check if the label has a / in it */ 523 if (strchr(items[3].text, '/') != NULL) { 524 dialog_msgbox("Error", "Label contains a /, which is not an " 525 "allowed character.", 0, 0, TRUE); 526 goto editpart; 527 } 528 529 r = gctl_get_handle(); 530 gctl_ro_param(r, "class", -1, "PART"); 531 gctl_ro_param(r, "arg0", -1, geom->lg_name); 532 gctl_ro_param(r, "flags", -1, GPART_FLAGS); 533 gctl_ro_param(r, "verb", -1, "modify"); 534 gctl_ro_param(r, "index", sizeof(idx), &idx); 535 if (hadlabel || items[3].text[0] != '\0') 536 gctl_ro_param(r, "label", -1, items[3].text); 537 gctl_ro_param(r, "type", -1, items[0].text); 538 errstr = gctl_issue(r); 539 if (errstr != NULL && errstr[0] != '\0') { 540 gpart_show_error("Error", NULL, errstr); 541 gctl_free(r); 542 goto editpart; 543 } 544 gctl_free(r); 545 546 newfs_command(items[0].text, newfs, 1); 547 set_default_part_metadata(pp->lg_name, scheme, items[0].text, 548 items[2].text, (strcmp(oldtype, items[0].text) != 0) ? 549 newfs : NULL); 550 551 endedit: 552 if (strcmp(oldtype, items[0].text) != 0 && cp != NULL) 553 gpart_destroy(cp->lg_geom); 554 if (strcmp(oldtype, items[0].text) != 0 && strcmp(items[0].text, 555 "freebsd") == 0) 556 gpart_partition(pp->lg_name, "BSD"); 557 558 for (i = 0; i < (sizeof(items) / sizeof(items[0])); i++) 559 if (items[i].text_free) 560 free(items[i].text); 561 } 562 563 void 564 set_default_part_metadata(const char *name, const char *scheme, 565 const char *type, const char *mountpoint, const char *newfs) 566 { 567 struct partition_metadata *md; 568 569 /* Set part metadata */ 570 md = get_part_metadata(name, 1); 571 572 if (newfs) { 573 if (md->newfs != NULL) { 574 free(md->newfs); 575 md->newfs = NULL; 576 } 577 578 if (newfs != NULL && newfs[0] != '\0') { 579 md->newfs = malloc(strlen(newfs) + strlen(" /dev/") + 580 strlen(name) + 1); 581 sprintf(md->newfs, "%s /dev/%s", newfs, name); 582 } 583 } 584 585 if (strcmp(type, "freebsd-swap") == 0) 586 mountpoint = "none"; 587 if (strcmp(type, bootpart_type(scheme)) == 0) 588 md->bootcode = 1; 589 590 /* VTOC8 needs partcode in UFS partitions */ 591 if (strcmp(scheme, "VTOC8") == 0 && strcmp(type, "freebsd-ufs") == 0) 592 md->bootcode = 1; 593 594 if (mountpoint == NULL || mountpoint[0] == '\0') { 595 if (md->fstab != NULL) { 596 free(md->fstab->fs_spec); 597 free(md->fstab->fs_file); 598 free(md->fstab->fs_vfstype); 599 free(md->fstab->fs_mntops); 600 free(md->fstab->fs_type); 601 free(md->fstab); 602 md->fstab = NULL; 603 } 604 } else { 605 if (md->fstab == NULL) { 606 md->fstab = malloc(sizeof(struct fstab)); 607 } else { 608 free(md->fstab->fs_spec); 609 free(md->fstab->fs_file); 610 free(md->fstab->fs_vfstype); 611 free(md->fstab->fs_mntops); 612 free(md->fstab->fs_type); 613 } 614 md->fstab->fs_spec = malloc(strlen(name) + 6); 615 sprintf(md->fstab->fs_spec, "/dev/%s", name); 616 md->fstab->fs_file = strdup(mountpoint); 617 /* Get VFS from text after freebsd-, if possible */ 618 if (strncmp("freebsd-", type, 8) == 0) 619 md->fstab->fs_vfstype = strdup(&type[8]); 620 else if (strcmp("fat32", type) == 0 || strcmp("efi", type) == 0) 621 md->fstab->fs_vfstype = strdup("msdosfs"); 622 else 623 md->fstab->fs_vfstype = strdup(type); /* Guess */ 624 if (strcmp(type, "freebsd-swap") == 0) { 625 md->fstab->fs_type = strdup(FSTAB_SW); 626 md->fstab->fs_freq = 0; 627 md->fstab->fs_passno = 0; 628 } else { 629 md->fstab->fs_type = strdup(FSTAB_RW); 630 if (strcmp(mountpoint, "/") == 0) { 631 md->fstab->fs_freq = 1; 632 md->fstab->fs_passno = 1; 633 } else { 634 md->fstab->fs_freq = 2; 635 md->fstab->fs_passno = 2; 636 } 637 } 638 md->fstab->fs_mntops = strdup(md->fstab->fs_type); 639 } 640 } 641 642 static 643 int part_compare(const void *xa, const void *xb) 644 { 645 struct gprovider **a = (struct gprovider **)xa; 646 struct gprovider **b = (struct gprovider **)xb; 647 intmax_t astart, bstart; 648 struct gconfig *gc; 649 650 astart = bstart = 0; 651 LIST_FOREACH(gc, &(*a)->lg_config, lg_config) 652 if (strcmp(gc->lg_name, "start") == 0) { 653 astart = strtoimax(gc->lg_val, NULL, 0); 654 break; 655 } 656 LIST_FOREACH(gc, &(*b)->lg_config, lg_config) 657 if (strcmp(gc->lg_name, "start") == 0) { 658 bstart = strtoimax(gc->lg_val, NULL, 0); 659 break; 660 } 661 662 if (astart < bstart) 663 return -1; 664 else if (astart > bstart) 665 return 1; 666 else 667 return 0; 668 } 669 670 intmax_t 671 gpart_max_free(struct ggeom *geom, intmax_t *npartstart) 672 { 673 struct gconfig *gc; 674 struct gprovider *pp, **providers; 675 intmax_t lastend; 676 intmax_t start, end; 677 intmax_t maxsize, maxstart; 678 intmax_t partstart, partend; 679 int i, nparts; 680 681 /* Now get the maximum free size and free start */ 682 start = end = 0; 683 LIST_FOREACH(gc, &geom->lg_config, lg_config) { 684 if (strcmp(gc->lg_name, "first") == 0) 685 start = strtoimax(gc->lg_val, NULL, 0); 686 if (strcmp(gc->lg_name, "last") == 0) 687 end = strtoimax(gc->lg_val, NULL, 0); 688 } 689 690 i = nparts = 0; 691 LIST_FOREACH(pp, &geom->lg_provider, lg_provider) 692 nparts++; 693 providers = calloc(nparts, sizeof(providers[0])); 694 LIST_FOREACH(pp, &geom->lg_provider, lg_provider) 695 providers[i++] = pp; 696 qsort(providers, nparts, sizeof(providers[0]), part_compare); 697 698 lastend = start - 1; 699 maxsize = 0; 700 for (i = 0; i < nparts; i++) { 701 pp = providers[i]; 702 703 LIST_FOREACH(gc, &pp->lg_config, lg_config) { 704 if (strcmp(gc->lg_name, "start") == 0) 705 partstart = strtoimax(gc->lg_val, NULL, 0); 706 if (strcmp(gc->lg_name, "end") == 0) 707 partend = strtoimax(gc->lg_val, NULL, 0); 708 } 709 710 if (partstart - lastend > maxsize) { 711 maxsize = partstart - lastend - 1; 712 maxstart = lastend + 1; 713 } 714 715 lastend = partend; 716 } 717 718 if (end - lastend > maxsize) { 719 maxsize = end - lastend - 1; 720 maxstart = lastend + 1; 721 } 722 723 pp = LIST_FIRST(&geom->lg_consumer)->lg_provider; 724 725 /* Compute beginning of new partition and maximum available space */ 726 if (pp->lg_stripesize > 0 && 727 (maxstart*pp->lg_sectorsize % pp->lg_stripesize) != 0) { 728 intmax_t offset = (pp->lg_stripesize - 729 ((maxstart*pp->lg_sectorsize) % pp->lg_stripesize)) / 730 pp->lg_sectorsize; 731 maxstart += offset; 732 maxsize -= offset; 733 } 734 735 if (npartstart != NULL) 736 *npartstart = maxstart; 737 738 return (maxsize); 739 } 740 741 void 742 gpart_create(struct gprovider *pp, char *default_type, char *default_size, 743 char *default_mountpoint, char **partname, int interactive) 744 { 745 struct gctl_req *r; 746 struct gconfig *gc; 747 struct gconsumer *cp; 748 struct ggeom *geom; 749 const char *errstr, *scheme; 750 char sizestr[32], startstr[32], output[64], *newpartname; 751 char newfs[64], options_fstype[64]; 752 intmax_t maxsize, size, sector, firstfree, stripe; 753 uint64_t bytes; 754 int nitems, choice, junk; 755 unsigned i; 756 757 DIALOG_FORMITEM items[] = { 758 {0, "Type:", 5, 0, 0, FALSE, "freebsd-ufs", 11, 0, 12, 15, 0, 759 FALSE, "Filesystem type (e.g. freebsd-ufs, freebsd-swap)", 760 FALSE}, 761 {0, "Size:", 5, 1, 0, FALSE, "", 11, 1, 12, 15, 0, 762 FALSE, "Partition size. Append K, M, G for kilobytes, " 763 "megabytes or gigabytes.", FALSE}, 764 {0, "Mountpoint:", 11, 2, 0, FALSE, "", 11, 2, 12, 15, 0, 765 FALSE, "Path at which to mount partition (blank for " 766 "swap, / for root filesystem)", FALSE}, 767 {0, "Label:", 7, 3, 0, FALSE, "", 11, 3, 12, 15, 0, FALSE, 768 "Partition name. Not all partition schemes support this.", 769 FALSE}, 770 }; 771 772 if (partname != NULL) 773 *partname = NULL; 774 775 /* Record sector and stripe sizes */ 776 sector = pp->lg_sectorsize; 777 stripe = pp->lg_stripesize; 778 779 /* 780 * Find the PART geom we are manipulating. This may be a consumer of 781 * this provider, or its parent. Check the consumer case first. 782 */ 783 geom = NULL; 784 LIST_FOREACH(cp, &pp->lg_consumers, lg_consumers) 785 if (strcmp(cp->lg_geom->lg_class->lg_name, "PART") == 0) { 786 geom = cp->lg_geom; 787 break; 788 } 789 790 if (geom == NULL && strcmp(pp->lg_geom->lg_class->lg_name, "PART") == 0) 791 geom = pp->lg_geom; 792 793 /* Now get the partition scheme */ 794 scheme = NULL; 795 if (geom != NULL) { 796 LIST_FOREACH(gc, &geom->lg_config, lg_config) 797 if (strcmp(gc->lg_name, "scheme") == 0) 798 scheme = gc->lg_val; 799 } 800 801 if (geom == NULL || scheme == NULL || strcmp(scheme, "(none)") == 0) { 802 if (gpart_partition(pp->lg_name, NULL) == 0) 803 dialog_msgbox("", 804 "The partition table has been successfully created." 805 " Please press Create again to create partitions.", 806 0, 0, TRUE); 807 808 return; 809 } 810 811 /* 812 * If we still don't have a geom, either the user has 813 * canceled partitioning or there has been an error which has already 814 * been displayed, so bail. 815 */ 816 if (geom == NULL) 817 return; 818 819 maxsize = size = gpart_max_free(geom, &firstfree); 820 if (size <= 0) { 821 dialog_msgbox("Error", "No free space left on device.", 0, 0, 822 TRUE); 823 return; 824 } 825 826 humanize_number(sizestr, 7, size*sector, "B", HN_AUTOSCALE, 827 HN_NOSPACE | HN_DECIMAL); 828 items[1].text = sizestr; 829 830 /* Special-case the MBR default type for nested partitions */ 831 if (strcmp(scheme, "MBR") == 0 || strcmp(scheme, "PC98") == 0) { 832 items[0].text = "freebsd"; 833 items[0].help = "Filesystem type (e.g. freebsd, fat32)"; 834 } 835 836 nitems = scheme_supports_labels(scheme) ? 4 : 3; 837 838 if (default_type != NULL) 839 items[0].text = default_type; 840 if (default_size != NULL) 841 items[1].text = default_size; 842 if (default_mountpoint != NULL) 843 items[2].text = default_mountpoint; 844 845 /* Default options */ 846 strncpy(options_fstype, items[0].text, 847 sizeof(options_fstype)); 848 newfs_command(options_fstype, newfs, 1); 849 addpartform: 850 if (interactive) { 851 dialog_vars.extra_label = "Options"; 852 dialog_vars.extra_button = TRUE; 853 choice = dlg_form("Add Partition", "", 0, 0, 0, nitems, 854 items, &junk); 855 dialog_vars.extra_button = FALSE; 856 switch (choice) { 857 case 0: /* OK */ 858 break; 859 case 1: /* Cancel */ 860 return; 861 case 3: /* Options */ 862 strncpy(options_fstype, items[0].text, 863 sizeof(options_fstype)); 864 newfs_command(options_fstype, newfs, 0); 865 goto addpartform; 866 } 867 } 868 869 /* 870 * If the user changed the fs type after specifying options, undo 871 * their choices in favor of the new filesystem's defaults. 872 */ 873 if (strcmp(options_fstype, items[0].text) != 0) { 874 strncpy(options_fstype, items[0].text, sizeof(options_fstype)); 875 newfs_command(options_fstype, newfs, 1); 876 } 877 878 size = maxsize; 879 if (strlen(items[1].text) > 0) { 880 if (expand_number(items[1].text, &bytes) != 0) { 881 char error[512]; 882 883 sprintf(error, "Invalid size: %s\n", strerror(errno)); 884 dialog_msgbox("Error", error, 0, 0, TRUE); 885 goto addpartform; 886 } 887 size = MIN((intmax_t)(bytes/sector), maxsize); 888 } 889 890 /* Check if the label has a / in it */ 891 if (strchr(items[3].text, '/') != NULL) { 892 dialog_msgbox("Error", "Label contains a /, which is not an " 893 "allowed character.", 0, 0, TRUE); 894 goto addpartform; 895 } 896 897 /* Warn if no mountpoint set */ 898 if (strcmp(items[0].text, "freebsd-ufs") == 0 && 899 items[2].text[0] != '/') { 900 dialog_vars.defaultno = TRUE; 901 choice = dialog_yesno("Warning", 902 "This partition does not have a valid mountpoint " 903 "(for the partition from which you intend to boot the " 904 "operating system, the mountpoint should be /). Are you " 905 "sure you want to continue?" 906 , 0, 0); 907 dialog_vars.defaultno = FALSE; 908 if (choice == 1) /* cancel */ 909 goto addpartform; 910 } 911 912 /* 913 * Error if this scheme needs nested partitions, this is one, and 914 * a mountpoint was set. 915 */ 916 if (strcmp(items[0].text, "freebsd") == 0 && 917 strlen(items[2].text) > 0) { 918 dialog_msgbox("Error", "Partitions of type \"freebsd\" are " 919 "nested BSD-type partition schemes and cannot have " 920 "mountpoints. After creating one, select it and press " 921 "Create again to add the actual file systems.", 0, 0, TRUE); 922 goto addpartform; 923 } 924 925 /* If this is the root partition, check that this scheme is bootable */ 926 if (strcmp(items[2].text, "/") == 0 && !is_scheme_bootable(scheme)) { 927 char message[512]; 928 sprintf(message, "This partition scheme (%s) is not bootable " 929 "on this platform. Are you sure you want to proceed?", 930 scheme); 931 dialog_vars.defaultno = TRUE; 932 choice = dialog_yesno("Warning", message, 0, 0); 933 dialog_vars.defaultno = FALSE; 934 if (choice == 1) /* cancel */ 935 goto addpartform; 936 } 937 938 /* 939 * If this is the root partition, and we need a boot partition, ask 940 * the user to add one. 941 */ 942 943 /* Check for existing freebsd-boot partition */ 944 LIST_FOREACH(pp, &geom->lg_provider, lg_provider) { 945 struct partition_metadata *md; 946 md = get_part_metadata(pp->lg_name, 0); 947 if (md == NULL || !md->bootcode) 948 continue; 949 LIST_FOREACH(gc, &pp->lg_config, lg_config) 950 if (strcmp(gc->lg_name, "type") == 0) 951 break; 952 if (gc != NULL && strcmp(gc->lg_val, 953 bootpart_type(scheme)) == 0) 954 break; 955 } 956 957 /* If there isn't one, and we need one, ask */ 958 if ((strcmp(items[0].text, "freebsd") == 0 || 959 strcmp(items[2].text, "/") == 0) && bootpart_size(scheme) > 0 && 960 pp == NULL) { 961 if (interactive) 962 choice = dialog_yesno("Boot Partition", 963 "This partition scheme requires a boot partition " 964 "for the disk to be bootable. Would you like to " 965 "make one now?", 0, 0); 966 else 967 choice = 0; 968 969 if (choice == 0) { /* yes */ 970 r = gctl_get_handle(); 971 gctl_ro_param(r, "class", -1, "PART"); 972 gctl_ro_param(r, "arg0", -1, geom->lg_name); 973 gctl_ro_param(r, "flags", -1, GPART_FLAGS); 974 gctl_ro_param(r, "verb", -1, "add"); 975 gctl_ro_param(r, "type", -1, bootpart_type(scheme)); 976 snprintf(sizestr, sizeof(sizestr), "%jd", 977 bootpart_size(scheme) / sector); 978 gctl_ro_param(r, "size", -1, sizestr); 979 snprintf(startstr, sizeof(startstr), "%jd", firstfree); 980 gctl_ro_param(r, "start", -1, startstr); 981 gctl_rw_param(r, "output", sizeof(output), output); 982 errstr = gctl_issue(r); 983 if (errstr != NULL && errstr[0] != '\0') 984 gpart_show_error("Error", NULL, errstr); 985 gctl_free(r); 986 987 get_part_metadata(strtok(output, " "), 1)->bootcode = 1; 988 989 /* Now adjust the part we are really adding forward */ 990 firstfree += bootpart_size(scheme) / sector; 991 size -= (bootpart_size(scheme) + stripe)/sector; 992 if (stripe > 0 && (firstfree*sector % stripe) != 0) 993 firstfree += (stripe - ((firstfree*sector) % 994 stripe)) / sector; 995 } 996 } 997 998 r = gctl_get_handle(); 999 gctl_ro_param(r, "class", -1, "PART"); 1000 gctl_ro_param(r, "arg0", -1, geom->lg_name); 1001 gctl_ro_param(r, "flags", -1, GPART_FLAGS); 1002 gctl_ro_param(r, "verb", -1, "add"); 1003 1004 gctl_ro_param(r, "type", -1, items[0].text); 1005 snprintf(sizestr, sizeof(sizestr), "%jd", size); 1006 gctl_ro_param(r, "size", -1, sizestr); 1007 snprintf(startstr, sizeof(startstr), "%jd", firstfree); 1008 gctl_ro_param(r, "start", -1, startstr); 1009 if (items[3].text[0] != '\0') 1010 gctl_ro_param(r, "label", -1, items[3].text); 1011 gctl_rw_param(r, "output", sizeof(output), output); 1012 errstr = gctl_issue(r); 1013 if (errstr != NULL && errstr[0] != '\0') { 1014 gpart_show_error("Error", NULL, errstr); 1015 gctl_free(r); 1016 goto addpartform; 1017 } 1018 newpartname = strtok(output, " "); 1019 gctl_free(r); 1020 1021 /* 1022 * Try to destroy any geom that gpart picked up already here from 1023 * dirty blocks. 1024 */ 1025 r = gctl_get_handle(); 1026 gctl_ro_param(r, "class", -1, "PART"); 1027 gctl_ro_param(r, "arg0", -1, newpartname); 1028 gctl_ro_param(r, "flags", -1, GPART_FLAGS); 1029 junk = 1; 1030 gctl_ro_param(r, "force", sizeof(junk), &junk); 1031 gctl_ro_param(r, "verb", -1, "destroy"); 1032 gctl_issue(r); /* Error usually expected and non-fatal */ 1033 gctl_free(r); 1034 1035 if (strcmp(items[0].text, bootpart_type(scheme)) == 0) 1036 get_part_metadata(newpartname, 1)->bootcode = 1; 1037 else if (strcmp(items[0].text, "freebsd") == 0) 1038 gpart_partition(newpartname, "BSD"); 1039 else 1040 set_default_part_metadata(newpartname, scheme, 1041 items[0].text, items[2].text, newfs); 1042 1043 for (i = 0; i < (sizeof(items) / sizeof(items[0])); i++) 1044 if (items[i].text_free) 1045 free(items[i].text); 1046 1047 if (partname != NULL) 1048 *partname = strdup(newpartname); 1049 } 1050 1051 void 1052 gpart_delete(struct gprovider *pp) 1053 { 1054 struct gconfig *gc; 1055 struct ggeom *geom; 1056 struct gconsumer *cp; 1057 struct gctl_req *r; 1058 const char *errstr; 1059 intmax_t idx; 1060 int is_partition; 1061 1062 /* Is it a partition? */ 1063 is_partition = (strcmp(pp->lg_geom->lg_class->lg_name, "PART") == 0); 1064 1065 /* Find out if this is the root of a gpart geom */ 1066 geom = NULL; 1067 LIST_FOREACH(cp, &pp->lg_consumers, lg_consumers) 1068 if (strcmp(cp->lg_geom->lg_class->lg_name, "PART") == 0) { 1069 geom = cp->lg_geom; 1070 break; 1071 } 1072 1073 /* If so, destroy all children */ 1074 if (geom != NULL) { 1075 gpart_destroy(geom); 1076 1077 /* If this is a partition, revert it, so it can be deleted */ 1078 if (is_partition) { 1079 r = gctl_get_handle(); 1080 gctl_ro_param(r, "class", -1, "PART"); 1081 gctl_ro_param(r, "arg0", -1, geom->lg_name); 1082 gctl_ro_param(r, "verb", -1, "undo"); 1083 gctl_issue(r); /* Ignore non-fatal errors */ 1084 gctl_free(r); 1085 } 1086 } 1087 1088 /* 1089 * If this is not a partition, see if that is a problem, complain if 1090 * necessary, and return always, since we need not do anything further, 1091 * error or no. 1092 */ 1093 if (!is_partition) { 1094 if (geom == NULL) 1095 dialog_msgbox("Error", 1096 "Only partitions can be deleted.", 0, 0, TRUE); 1097 return; 1098 } 1099 1100 r = gctl_get_handle(); 1101 gctl_ro_param(r, "class", -1, pp->lg_geom->lg_class->lg_name); 1102 gctl_ro_param(r, "arg0", -1, pp->lg_geom->lg_name); 1103 gctl_ro_param(r, "flags", -1, GPART_FLAGS); 1104 gctl_ro_param(r, "verb", -1, "delete"); 1105 1106 LIST_FOREACH(gc, &pp->lg_config, lg_config) { 1107 if (strcmp(gc->lg_name, "index") == 0) { 1108 idx = atoi(gc->lg_val); 1109 gctl_ro_param(r, "index", sizeof(idx), &idx); 1110 break; 1111 } 1112 } 1113 1114 errstr = gctl_issue(r); 1115 if (errstr != NULL && errstr[0] != '\0') { 1116 gpart_show_error("Error", NULL, errstr); 1117 gctl_free(r); 1118 return; 1119 } 1120 1121 gctl_free(r); 1122 1123 delete_part_metadata(pp->lg_name); 1124 } 1125 1126 void 1127 gpart_revert_all(struct gmesh *mesh) 1128 { 1129 struct gclass *classp; 1130 struct gconfig *gc; 1131 struct ggeom *gp; 1132 struct gctl_req *r; 1133 const char *modified; 1134 1135 LIST_FOREACH(classp, &mesh->lg_class, lg_class) { 1136 if (strcmp(classp->lg_name, "PART") == 0) 1137 break; 1138 } 1139 1140 if (strcmp(classp->lg_name, "PART") != 0) { 1141 dialog_msgbox("Error", "gpart not found!", 0, 0, TRUE); 1142 return; 1143 } 1144 1145 LIST_FOREACH(gp, &classp->lg_geom, lg_geom) { 1146 modified = "true"; /* XXX: If we don't know (kernel too old), 1147 * assume there are modifications. */ 1148 LIST_FOREACH(gc, &gp->lg_config, lg_config) { 1149 if (strcmp(gc->lg_name, "modified") == 0) { 1150 modified = gc->lg_val; 1151 break; 1152 } 1153 } 1154 1155 if (strcmp(modified, "false") == 0) 1156 continue; 1157 1158 r = gctl_get_handle(); 1159 gctl_ro_param(r, "class", -1, "PART"); 1160 gctl_ro_param(r, "arg0", -1, gp->lg_name); 1161 gctl_ro_param(r, "verb", -1, "undo"); 1162 1163 gctl_issue(r); 1164 gctl_free(r); 1165 } 1166 } 1167 1168 void 1169 gpart_commit(struct gmesh *mesh) 1170 { 1171 struct partition_metadata *md; 1172 struct gclass *classp; 1173 struct ggeom *gp; 1174 struct gconfig *gc; 1175 struct gconsumer *cp; 1176 struct gprovider *pp; 1177 struct gctl_req *r; 1178 const char *errstr; 1179 const char *modified; 1180 1181 LIST_FOREACH(classp, &mesh->lg_class, lg_class) { 1182 if (strcmp(classp->lg_name, "PART") == 0) 1183 break; 1184 } 1185 1186 if (strcmp(classp->lg_name, "PART") != 0) { 1187 dialog_msgbox("Error", "gpart not found!", 0, 0, TRUE); 1188 return; 1189 } 1190 1191 LIST_FOREACH(gp, &classp->lg_geom, lg_geom) { 1192 modified = "true"; /* XXX: If we don't know (kernel too old), 1193 * assume there are modifications. */ 1194 LIST_FOREACH(gc, &gp->lg_config, lg_config) { 1195 if (strcmp(gc->lg_name, "modified") == 0) { 1196 modified = gc->lg_val; 1197 break; 1198 } 1199 } 1200 1201 if (strcmp(modified, "false") == 0) 1202 continue; 1203 1204 /* Add bootcode if necessary, before the commit */ 1205 md = get_part_metadata(gp->lg_name, 0); 1206 if (md != NULL && md->bootcode) 1207 gpart_bootcode(gp); 1208 1209 /* Now install partcode on its partitions, if necessary */ 1210 LIST_FOREACH(pp, &gp->lg_provider, lg_provider) { 1211 md = get_part_metadata(pp->lg_name, 0); 1212 if (md == NULL || !md->bootcode) 1213 continue; 1214 1215 /* Mark this partition active if that's required */ 1216 gpart_activate(pp); 1217 1218 /* Check if the partition has sub-partitions */ 1219 LIST_FOREACH(cp, &pp->lg_consumers, lg_consumers) 1220 if (strcmp(cp->lg_geom->lg_class->lg_name, 1221 "PART") == 0) 1222 break; 1223 1224 if (cp == NULL) /* No sub-partitions */ 1225 gpart_partcode(pp); 1226 } 1227 1228 r = gctl_get_handle(); 1229 gctl_ro_param(r, "class", -1, "PART"); 1230 gctl_ro_param(r, "arg0", -1, gp->lg_name); 1231 gctl_ro_param(r, "verb", -1, "commit"); 1232 1233 errstr = gctl_issue(r); 1234 if (errstr != NULL && errstr[0] != '\0') 1235 gpart_show_error("Error", NULL, errstr); 1236 gctl_free(r); 1237 } 1238 } 1239 1240