1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 3 * 4 * Copyright (c) 2011 Nathan Whitehorn 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 * SUCH DAMAGE. 27 * 28 * $FreeBSD$ 29 */ 30 31 #include <sys/param.h> 32 #include <sys/stat.h> 33 34 #include <bsddialog.h> 35 #include <ctype.h> 36 #include <errno.h> 37 #include <fcntl.h> 38 #include <libutil.h> 39 #include <inttypes.h> 40 #include <stdio.h> 41 #include <stdlib.h> 42 #include <string.h> 43 #include <unistd.h> 44 45 #include <libgeom.h> 46 47 #include "partedit.h" 48 49 #define GPART_FLAGS "x" /* Do not commit changes by default */ 50 51 static void 52 gpart_show_error(const char *title, const char *explanation, const char *errstr) 53 { 54 char *errmsg; 55 char message[512]; 56 int error; 57 struct bsddialog_conf conf; 58 59 if (explanation == NULL) 60 explanation = ""; 61 62 error = strtol(errstr, &errmsg, 0); 63 if (errmsg != errstr) { 64 while (errmsg[0] == ' ') 65 errmsg++; 66 if (errmsg[0] != '\0') 67 sprintf(message, "%s%s. %s", explanation, 68 strerror(error), errmsg); 69 else 70 sprintf(message, "%s%s", explanation, strerror(error)); 71 } else { 72 sprintf(message, "%s%s", explanation, errmsg); 73 } 74 75 bsddialog_initconf(&conf); 76 conf.title = title; 77 bsddialog_msgbox(&conf, message, 0, 0); 78 } 79 80 static int 81 scheme_supports_labels(const char *scheme) 82 { 83 if (strcmp(scheme, "APM") == 0) 84 return (1); 85 if (strcmp(scheme, "GPT") == 0) 86 return (1); 87 88 return (0); 89 } 90 91 static void 92 newfs_command(const char *fstype, char *command, int use_default) 93 { 94 struct bsddialog_conf conf; 95 96 bsddialog_initconf(&conf); 97 98 if (strcmp(fstype, "freebsd-ufs") == 0) { 99 int i; 100 struct bsddialog_menuitem items[] = { 101 {"", false, 0, "UFS1", "UFS Version 1", 102 "Use version 1 of the UFS file system instead " 103 "of version 2 (not recommended)"}, 104 {"", true, 0, "SU", "Softupdates", 105 "Enable softupdates (default)"}, 106 {"", true, 0, "SUJ", "Softupdates journaling", 107 "Enable file system journaling (default - " 108 "turn off for SSDs)"}, 109 {"", false, 0, "TRIM", "Enable SSD TRIM support", 110 "Enable TRIM support, useful on solid-state " 111 "drives" }, 112 }; 113 114 if (!use_default) { 115 int choice; 116 conf.title = "UFS Options"; 117 choice = bsddialog_checklist(&conf, "", 0, 0, 0, 118 nitems(items), items, NULL); 119 if (choice == BSDDIALOG_CANCEL) 120 return; 121 } 122 123 strcpy(command, "newfs "); 124 for (i = 0; i < (int)nitems(items); i++) { 125 if (items[i].on == false) 126 continue; 127 if (strcmp(items[i].name, "UFS1") == 0) 128 strcat(command, "-O1 "); 129 else if (strcmp(items[i].name, "SU") == 0) 130 strcat(command, "-U "); 131 else if (strcmp(items[i].name, "SUJ") == 0) 132 strcat(command, "-j "); 133 else if (strcmp(items[i].name, "TRIM") == 0) 134 strcat(command, "-t "); 135 } 136 } else if (strcmp(fstype, "freebsd-zfs") == 0) { 137 int i; 138 struct bsddialog_menuitem items[] = { 139 {"", 0, true, "fletcher4", "checksum algorithm: fletcher4", 140 "Use fletcher4 for data integrity checking. " 141 "(default)"}, 142 {"", 0, false, "fletcher2", "checksum algorithm: fletcher2", 143 "Use fletcher2 for data integrity checking. " 144 "(not recommended)"}, 145 {"", 0, false, "sha256", "checksum algorithm: sha256", 146 "Use sha256 for data integrity checking. " 147 "(not recommended)"}, 148 {"", 0, false, "atime", "Update atimes for files", 149 "Disable atime update"}, 150 }; 151 152 if (!use_default) { 153 int choice; 154 conf.title = "ZFS Options"; 155 choice = bsddialog_checklist(&conf, "", 0, 0, 0, 156 nitems(items), items, NULL); 157 if (choice == BSDDIALOG_CANCEL) 158 return; 159 } 160 161 strcpy(command, "zpool create -f -m none "); 162 if (getenv("BSDINSTALL_TMPBOOT") != NULL) { 163 char zfsboot_path[MAXPATHLEN]; 164 snprintf(zfsboot_path, sizeof(zfsboot_path), "%s/zfs", 165 getenv("BSDINSTALL_TMPBOOT")); 166 mkdir(zfsboot_path, S_IRWXU | S_IRGRP | S_IXGRP | 167 S_IROTH | S_IXOTH); 168 sprintf(command, "%s -o cachefile=%s/zpool.cache ", 169 command, zfsboot_path); 170 } 171 for (i = 0; i < (int)nitems(items); i++) { 172 if (items[i].on == false) 173 continue; 174 if (strcmp(items[i].name, "fletcher4") == 0) 175 strcat(command, "-O checksum=fletcher4 "); 176 else if (strcmp(items[i].name, "fletcher2") == 0) 177 strcat(command, "-O checksum=fletcher2 "); 178 else if (strcmp(items[i].name, "sha256") == 0) 179 strcat(command, "-O checksum=sha256 "); 180 else if (strcmp(items[i].name, "atime") == 0) 181 strcat(command, "-O atime=off "); 182 } 183 } else if (strcmp(fstype, "fat32") == 0 || strcmp(fstype, "efi") == 0 || 184 strcmp(fstype, "ms-basic-data") == 0) { 185 int i; 186 struct bsddialog_menuitem items[] = { 187 {"", 0, true, "FAT32", "FAT Type 32", 188 "Create a FAT32 filesystem (default)"}, 189 {"", 0, false, "FAT16", "FAT Type 16", 190 "Create a FAT16 filesystem"}, 191 {"", 0, false, "FAT12", "FAT Type 12", 192 "Create a FAT12 filesystem"}, 193 }; 194 195 if (!use_default) { 196 int choice; 197 conf.title = "FAT Options"; 198 choice = bsddialog_radiolist(&conf, "", 0, 0, 0, 199 nitems(items), items, NULL); 200 if (choice == BSDDIALOG_CANCEL) 201 return; 202 } 203 204 strcpy(command, "newfs_msdos "); 205 for (i = 0; i < (int)nitems(items); i++) { 206 if (items[i].on == false) 207 continue; 208 if (strcmp(items[i].name, "FAT32") == 0) 209 strcat(command, "-F 32 -c 1"); 210 else if (strcmp(items[i].name, "FAT16") == 0) 211 strcat(command, "-F 16 "); 212 else if (strcmp(items[i].name, "FAT12") == 0) 213 strcat(command, "-F 12 "); 214 } 215 } else { 216 if (!use_default) { 217 conf.title = "Error"; 218 bsddialog_msgbox(&conf, "No configurable options exist " 219 "for this filesystem.", 0, 0); 220 } 221 command[0] = '\0'; 222 } 223 } 224 225 const char * 226 choose_part_type(const char *def_scheme) 227 { 228 int button, choice, i; 229 const char *scheme = NULL; 230 struct bsddialog_conf conf; 231 232 struct bsddialog_menuitem items[] = { 233 {"", false, 0, "APM", "Apple Partition Map", 234 "Bootable on PowerPC Apple Hardware" }, 235 {"", false, 0, "BSD", "BSD Labels", 236 "Bootable on most x86 systems" }, 237 {"", false, 0, "GPT", "GUID Partition Table", 238 "Bootable on most x86 systems and EFI aware ARM64" }, 239 {"", false, 0, "MBR", "DOS Partitions", 240 "Bootable on most x86 systems" }, 241 }; 242 243 for (i = 0; i < (int)nitems(items); i++) 244 if (strcmp(items[i].name, def_scheme) == 0) 245 choice = i; 246 247 bsddialog_initconf(&conf); 248 249 parttypemenu: 250 conf.title = "Partition Scheme"; 251 button = bsddialog_menu(&conf, 252 "Select a partition scheme for this volume:", 0, 0, 0, 253 nitems(items), items, &choice); 254 255 if (button == BSDDIALOG_CANCEL) 256 return NULL; 257 258 if (!is_scheme_bootable(items[choice].name)) { 259 char message[512]; 260 sprintf(message, "This partition scheme (%s) is not " 261 "bootable on this platform. Are you sure you want " 262 "to proceed?", items[choice].name); 263 conf.button.default_cancel = true; 264 conf.title = "Warning"; 265 button = bsddialog_yesno(&conf, message, 0, 0); 266 conf.button.default_cancel = false; 267 if (button == BSDDIALOG_NO) 268 goto parttypemenu; 269 } 270 271 scheme = items[choice].name; 272 273 return scheme; 274 } 275 276 int 277 gpart_partition(const char *lg_name, const char *scheme) 278 { 279 int button; 280 struct gctl_req *r; 281 const char *errstr; 282 struct bsddialog_conf conf; 283 284 bsddialog_initconf(&conf); 285 286 schememenu: 287 if (scheme == NULL) { 288 scheme = choose_part_type(default_scheme()); 289 290 if (scheme == NULL) 291 return (-1); 292 293 if (!is_scheme_bootable(scheme)) { 294 char message[512]; 295 sprintf(message, "This partition scheme (%s) is not " 296 "bootable on this platform. Are you sure you want " 297 "to proceed?", scheme); 298 conf.button.default_cancel = true; 299 conf.title = "Warning"; 300 button = bsddialog_yesno(&conf, message, 0, 0); 301 conf.button.default_cancel = false; 302 if (button == BSDDIALOG_NO) { 303 /* Reset scheme so user can choose another */ 304 scheme = NULL; 305 goto schememenu; 306 } 307 } 308 } 309 310 r = gctl_get_handle(); 311 gctl_ro_param(r, "class", -1, "PART"); 312 gctl_ro_param(r, "arg0", -1, lg_name); 313 gctl_ro_param(r, "flags", -1, GPART_FLAGS); 314 gctl_ro_param(r, "scheme", -1, scheme); 315 gctl_ro_param(r, "verb", -1, "create"); 316 317 errstr = gctl_issue(r); 318 if (errstr != NULL && errstr[0] != '\0') { 319 gpart_show_error("Error", NULL, errstr); 320 gctl_free(r); 321 scheme = NULL; 322 goto schememenu; 323 } 324 gctl_free(r); 325 326 if (bootcode_path(scheme) != NULL) 327 get_part_metadata(lg_name, 1)->bootcode = 1; 328 return (0); 329 } 330 331 static void 332 gpart_activate(struct gprovider *pp) 333 { 334 struct gconfig *gc; 335 struct gctl_req *r; 336 const char *errstr, *scheme; 337 const char *attribute = NULL; 338 intmax_t idx; 339 340 /* 341 * Some partition schemes need this partition to be marked 'active' 342 * for it to be bootable. 343 */ 344 LIST_FOREACH(gc, &pp->lg_geom->lg_config, lg_config) { 345 if (strcmp(gc->lg_name, "scheme") == 0) { 346 scheme = gc->lg_val; 347 break; 348 } 349 } 350 351 if (strcmp(scheme, "MBR") == 0 || strcmp(scheme, "EBR") == 0) 352 attribute = "active"; 353 else 354 return; 355 356 LIST_FOREACH(gc, &pp->lg_config, lg_config) { 357 if (strcmp(gc->lg_name, "index") == 0) { 358 idx = atoi(gc->lg_val); 359 break; 360 } 361 } 362 363 r = gctl_get_handle(); 364 gctl_ro_param(r, "class", -1, "PART"); 365 gctl_ro_param(r, "arg0", -1, pp->lg_geom->lg_name); 366 gctl_ro_param(r, "verb", -1, "set"); 367 gctl_ro_param(r, "attrib", -1, attribute); 368 gctl_ro_param(r, "index", sizeof(idx), &idx); 369 370 errstr = gctl_issue(r); 371 if (errstr != NULL && errstr[0] != '\0') 372 gpart_show_error("Error", "Error marking partition active:", 373 errstr); 374 gctl_free(r); 375 } 376 377 void 378 gpart_set_root(const char *lg_name, const char *attribute) 379 { 380 struct gctl_req *r; 381 const char *errstr; 382 383 r = gctl_get_handle(); 384 gctl_ro_param(r, "class", -1, "PART"); 385 gctl_ro_param(r, "arg0", -1, lg_name); 386 gctl_ro_param(r, "flags", -1, "C"); 387 gctl_ro_param(r, "verb", -1, "set"); 388 gctl_ro_param(r, "attrib", -1, attribute); 389 390 errstr = gctl_issue(r); 391 if (errstr != NULL && errstr[0] != '\0') 392 gpart_show_error("Error", "Error setting parameter on disk:", 393 errstr); 394 gctl_free(r); 395 } 396 397 static void 398 gpart_bootcode(struct ggeom *gp) 399 { 400 const char *bootcode; 401 struct gconfig *gc; 402 struct gctl_req *r; 403 const char *errstr, *scheme; 404 uint8_t *boot; 405 size_t bootsize, bytes; 406 int bootfd; 407 struct bsddialog_conf conf; 408 409 /* 410 * Write default bootcode to the newly partitioned disk, if that 411 * applies on this platform. 412 */ 413 LIST_FOREACH(gc, &gp->lg_config, lg_config) { 414 if (strcmp(gc->lg_name, "scheme") == 0) { 415 scheme = gc->lg_val; 416 break; 417 } 418 } 419 420 bootcode = bootcode_path(scheme); 421 if (bootcode == NULL) 422 return; 423 424 bootfd = open(bootcode, O_RDONLY); 425 if (bootfd < 0) { 426 bsddialog_initconf(&conf); 427 conf.title = "Bootcode Error"; 428 bsddialog_msgbox(&conf, strerror(errno), 0, 0); 429 return; 430 } 431 432 bootsize = lseek(bootfd, 0, SEEK_END); 433 boot = malloc(bootsize); 434 lseek(bootfd, 0, SEEK_SET); 435 bytes = 0; 436 while (bytes < bootsize) 437 bytes += read(bootfd, boot + bytes, bootsize - bytes); 438 close(bootfd); 439 440 r = gctl_get_handle(); 441 gctl_ro_param(r, "class", -1, "PART"); 442 gctl_ro_param(r, "arg0", -1, gp->lg_name); 443 gctl_ro_param(r, "verb", -1, "bootcode"); 444 gctl_ro_param(r, "bootcode", bootsize, boot); 445 446 errstr = gctl_issue(r); 447 if (errstr != NULL && errstr[0] != '\0') 448 gpart_show_error("Bootcode Error", NULL, errstr); 449 gctl_free(r); 450 free(boot); 451 } 452 453 static void 454 gpart_partcode(struct gprovider *pp, const char *fstype) 455 { 456 struct gconfig *gc; 457 const char *scheme; 458 const char *indexstr; 459 char message[255], command[255]; 460 struct bsddialog_conf conf; 461 462 LIST_FOREACH(gc, &pp->lg_geom->lg_config, lg_config) { 463 if (strcmp(gc->lg_name, "scheme") == 0) { 464 scheme = gc->lg_val; 465 break; 466 } 467 } 468 469 /* Make sure this partition scheme needs partcode on this platform */ 470 if (partcode_path(scheme, fstype) == NULL) 471 return; 472 473 LIST_FOREACH(gc, &pp->lg_config, lg_config) { 474 if (strcmp(gc->lg_name, "index") == 0) { 475 indexstr = gc->lg_val; 476 break; 477 } 478 } 479 480 /* Shell out to gpart for partcode for now */ 481 sprintf(command, "gpart bootcode -p %s -i %s %s", 482 partcode_path(scheme, fstype), indexstr, pp->lg_geom->lg_name); 483 if (system(command) != 0) { 484 sprintf(message, "Error installing partcode on partition %s", 485 pp->lg_name); 486 bsddialog_initconf(&conf); 487 conf.title = "Error"; 488 bsddialog_msgbox(&conf, message, 0, 0); 489 } 490 } 491 492 void 493 gpart_destroy(struct ggeom *lg_geom) 494 { 495 struct gctl_req *r; 496 struct gprovider *pp; 497 const char *errstr; 498 int force = 1; 499 500 /* Delete all child metadata */ 501 LIST_FOREACH(pp, &lg_geom->lg_provider, lg_provider) 502 gpart_delete(pp); 503 504 /* Revert any local changes to get this geom into a pristine state */ 505 r = gctl_get_handle(); 506 gctl_ro_param(r, "class", -1, "PART"); 507 gctl_ro_param(r, "arg0", -1, lg_geom->lg_name); 508 gctl_ro_param(r, "verb", -1, "undo"); 509 gctl_issue(r); /* Ignore errors -- these are non-fatal */ 510 gctl_free(r); 511 512 /* Now destroy the geom itself */ 513 r = gctl_get_handle(); 514 gctl_ro_param(r, "class", -1, "PART"); 515 gctl_ro_param(r, "arg0", -1, lg_geom->lg_name); 516 gctl_ro_param(r, "flags", -1, GPART_FLAGS); 517 gctl_ro_param(r, "force", sizeof(force), &force); 518 gctl_ro_param(r, "verb", -1, "destroy"); 519 errstr = gctl_issue(r); 520 if (errstr != NULL && errstr[0] != '\0') { 521 /* 522 * Check if we reverted away the existence of the geom 523 * altogether. Show all other errors to the user. 524 */ 525 if (strtol(errstr, NULL, 0) != EINVAL) 526 gpart_show_error("Error", NULL, errstr); 527 } 528 gctl_free(r); 529 530 /* And any metadata associated with the partition scheme itself */ 531 delete_part_metadata(lg_geom->lg_name); 532 } 533 534 void 535 gpart_edit(struct gprovider *pp) 536 { 537 struct gctl_req *r; 538 struct gconfig *gc; 539 struct gconsumer *cp; 540 struct ggeom *geom; 541 const char *errstr, *oldtype, *scheme; 542 struct partition_metadata *md; 543 char sizestr[32]; 544 char newfs[255]; 545 intmax_t idx; 546 int hadlabel, choice, nitems; 547 unsigned i; 548 struct bsddialog_conf conf; 549 550 struct bsddialog_formitem items[] = { 551 { "Type:", 1, 1, "", 1, 12, 12, 15, NULL, 0, 552 "Filesystem type (e.g. freebsd-ufs, freebsd-zfs, " 553 "freebsd-swap)"}, 554 { "Size:", 2, 1, "", 2, 12, 12, 15, NULL, 0, 555 "Partition size. Append K, M, G for kilobytes, " 556 "megabytes or gigabytes."}, 557 { "Mountpoint:", 3, 1, "", 3, 12, 12, 15, NULL, 0, 558 "Path at which to mount this partition (leave blank " 559 "for swap, set to / for root filesystem)"}, 560 { "Label:", 4, 1, "", 4, 12, 12, 15, NULL, 0, 561 "Partition name. Not all partition schemes support this."}, 562 }; 563 564 bsddialog_initconf(&conf); 565 566 /* 567 * Find the PART geom we are manipulating. This may be a consumer of 568 * this provider, or its parent. Check the consumer case first. 569 */ 570 geom = NULL; 571 LIST_FOREACH(cp, &pp->lg_consumers, lg_consumers) 572 if (strcmp(cp->lg_geom->lg_class->lg_name, "PART") == 0) { 573 /* Check for zombie geoms, treating them as blank */ 574 scheme = NULL; 575 LIST_FOREACH(gc, &cp->lg_geom->lg_config, lg_config) { 576 if (strcmp(gc->lg_name, "scheme") == 0) { 577 scheme = gc->lg_val; 578 break; 579 } 580 } 581 if (scheme == NULL || strcmp(scheme, "(none)") == 0) { 582 gpart_partition(cp->lg_geom->lg_name, NULL); 583 return; 584 } 585 586 /* If this is a nested partition, edit as usual */ 587 if (strcmp(pp->lg_geom->lg_class->lg_name, "PART") == 0) 588 break; 589 590 /* Destroy the geom and all sub-partitions */ 591 gpart_destroy(cp->lg_geom); 592 593 /* Now re-partition and return */ 594 gpart_partition(cp->lg_geom->lg_name, NULL); 595 return; 596 } 597 598 if (geom == NULL && strcmp(pp->lg_geom->lg_class->lg_name, "PART") == 0) 599 geom = pp->lg_geom; 600 601 if (geom == NULL) { 602 /* Disk not partitioned, so partition it */ 603 gpart_partition(pp->lg_name, NULL); 604 return; 605 } 606 607 LIST_FOREACH(gc, &geom->lg_config, lg_config) { 608 if (strcmp(gc->lg_name, "scheme") == 0) { 609 scheme = gc->lg_val; 610 break; 611 } 612 } 613 614 nitems = scheme_supports_labels(scheme) ? 4 : 3; 615 616 /* Edit editable parameters of a partition */ 617 hadlabel = 0; 618 LIST_FOREACH(gc, &pp->lg_config, lg_config) { 619 if (strcmp(gc->lg_name, "type") == 0) { 620 oldtype = gc->lg_val; 621 items[0].init = gc->lg_val; 622 } 623 if (strcmp(gc->lg_name, "label") == 0 && gc->lg_val != NULL) { 624 hadlabel = 1; 625 items[3].init = gc->lg_val; 626 } 627 if (strcmp(gc->lg_name, "index") == 0) 628 idx = atoi(gc->lg_val); 629 } 630 631 TAILQ_FOREACH(md, &part_metadata, metadata) { 632 if (md->name != NULL && strcmp(md->name, pp->lg_name) == 0) { 633 if (md->fstab != NULL) 634 items[2].init = md->fstab->fs_file; 635 break; 636 } 637 } 638 639 humanize_number(sizestr, 7, pp->lg_mediasize, "B", HN_AUTOSCALE, 640 HN_NOSPACE | HN_DECIMAL); 641 items[1].init = sizestr; 642 643 editpart: 644 conf.form.value_without_ok = true; 645 conf.title = "Edit Partition"; 646 choice = bsddialog_form(&conf, "", 0, 0, 0, nitems, items); 647 648 if (choice == BSDDIALOG_CANCEL) 649 goto endedit; 650 651 /* If this is the root partition, check that this fs is bootable */ 652 if (strcmp(items[2].value, "/") == 0 && !is_fs_bootable(scheme, 653 items[0].value)) { 654 char message[512]; 655 sprintf(message, "This file system (%s) is not bootable " 656 "on this system. Are you sure you want to proceed?", 657 items[0].value); 658 conf.button.default_cancel = true; 659 conf.title = "Warning"; 660 choice = bsddialog_yesno(&conf, message, 0, 0); 661 conf.button.default_cancel = false; 662 if (choice == BSDDIALOG_CANCEL) 663 goto editpart; 664 } 665 666 /* Check if the label has a / in it */ 667 if (items[3].value != NULL && strchr(items[3].value, '/') != NULL) { 668 conf.title = "Error"; 669 bsddialog_msgbox(&conf, "Label contains a /, which is not an " 670 "allowed character.", 0, 0); 671 goto editpart; 672 } 673 674 r = gctl_get_handle(); 675 gctl_ro_param(r, "class", -1, "PART"); 676 gctl_ro_param(r, "arg0", -1, geom->lg_name); 677 gctl_ro_param(r, "flags", -1, GPART_FLAGS); 678 gctl_ro_param(r, "verb", -1, "modify"); 679 gctl_ro_param(r, "index", sizeof(idx), &idx); 680 if (items[3].value != NULL && (hadlabel || items[3].value[0] != '\0')) 681 gctl_ro_param(r, "label", -1, items[3].value); 682 gctl_ro_param(r, "type", -1, items[0].value); 683 errstr = gctl_issue(r); 684 if (errstr != NULL && errstr[0] != '\0') { 685 gpart_show_error("Error", NULL, errstr); 686 gctl_free(r); 687 goto editpart; 688 } 689 gctl_free(r); 690 691 newfs_command(items[0].value, newfs, 1); 692 set_default_part_metadata(pp->lg_name, scheme, items[0].value, 693 items[2].value, (strcmp(oldtype, items[0].value) != 0) ? 694 newfs : NULL); 695 696 endedit: 697 if (strcmp(oldtype, items[0].value) != 0 && cp != NULL) 698 gpart_destroy(cp->lg_geom); 699 if (strcmp(oldtype, items[0].value) != 0 && strcmp(items[0].value, 700 "freebsd") == 0) 701 gpart_partition(pp->lg_name, "BSD"); 702 703 for (i = 0; i < nitems(items); i++) 704 if (items[i].value != NULL) 705 free(items[i].value); 706 } 707 708 void 709 set_default_part_metadata(const char *name, const char *scheme, 710 const char *type, const char *mountpoint, const char *newfs) 711 { 712 struct partition_metadata *md; 713 char *zpool_name = NULL; 714 const char *default_bootmount = NULL; 715 int i; 716 717 /* Set part metadata */ 718 md = get_part_metadata(name, 1); 719 720 if (newfs) { 721 if (md->newfs != NULL) { 722 free(md->newfs); 723 md->newfs = NULL; 724 } 725 726 if (newfs != NULL && newfs[0] != '\0') { 727 md->newfs = malloc(strlen(newfs) + strlen(" /dev/") + 728 strlen(mountpoint) + 5 + strlen(name) + 1); 729 if (strcmp("freebsd-zfs", type) == 0) { 730 zpool_name = strdup((strlen(mountpoint) == 1) ? 731 "root" : &mountpoint[1]); 732 for (i = 0; zpool_name[i] != 0; i++) 733 if (!isalnum(zpool_name[i])) 734 zpool_name[i] = '_'; 735 sprintf(md->newfs, "%s %s /dev/%s", newfs, 736 zpool_name, name); 737 } else { 738 sprintf(md->newfs, "%s /dev/%s", newfs, name); 739 } 740 } 741 } 742 743 if (strcmp(type, "freebsd-swap") == 0) 744 mountpoint = "none"; 745 if (strcmp(type, bootpart_type(scheme, &default_bootmount)) == 0) { 746 if (default_bootmount == NULL) 747 md->bootcode = 1; 748 else if (mountpoint == NULL || strlen(mountpoint) == 0) 749 mountpoint = default_bootmount; 750 } 751 752 if (mountpoint == NULL || mountpoint[0] == '\0') { 753 if (md->fstab != NULL) { 754 free(md->fstab->fs_spec); 755 free(md->fstab->fs_file); 756 free(md->fstab->fs_vfstype); 757 free(md->fstab->fs_mntops); 758 free(md->fstab->fs_type); 759 free(md->fstab); 760 md->fstab = NULL; 761 } 762 } else { 763 if (md->fstab == NULL) { 764 md->fstab = malloc(sizeof(struct fstab)); 765 } else { 766 free(md->fstab->fs_spec); 767 free(md->fstab->fs_file); 768 free(md->fstab->fs_vfstype); 769 free(md->fstab->fs_mntops); 770 free(md->fstab->fs_type); 771 } 772 if (strcmp("freebsd-zfs", type) == 0) { 773 md->fstab->fs_spec = strdup(zpool_name); 774 } else { 775 md->fstab->fs_spec = malloc(strlen(name) + 776 strlen("/dev/") + 1); 777 sprintf(md->fstab->fs_spec, "/dev/%s", name); 778 } 779 md->fstab->fs_file = strdup(mountpoint); 780 /* Get VFS from text after freebsd-, if possible */ 781 if (strncmp("freebsd-", type, 8) == 0) 782 md->fstab->fs_vfstype = strdup(&type[8]); 783 else if (strcmp("fat32", type) == 0 || strcmp("efi", type) == 0 784 || strcmp("ms-basic-data", type) == 0) 785 md->fstab->fs_vfstype = strdup("msdosfs"); 786 else 787 md->fstab->fs_vfstype = strdup(type); /* Guess */ 788 if (strcmp(type, "freebsd-swap") == 0) { 789 md->fstab->fs_type = strdup(FSTAB_SW); 790 md->fstab->fs_freq = 0; 791 md->fstab->fs_passno = 0; 792 } else if (strcmp(type, "freebsd-zfs") == 0) { 793 md->fstab->fs_type = strdup(FSTAB_RW); 794 md->fstab->fs_freq = 0; 795 md->fstab->fs_passno = 0; 796 } else { 797 md->fstab->fs_type = strdup(FSTAB_RW); 798 if (strcmp(mountpoint, "/") == 0) { 799 md->fstab->fs_freq = 1; 800 md->fstab->fs_passno = 1; 801 } else { 802 md->fstab->fs_freq = 2; 803 md->fstab->fs_passno = 2; 804 } 805 } 806 md->fstab->fs_mntops = strdup(md->fstab->fs_type); 807 } 808 809 if (zpool_name != NULL) 810 free(zpool_name); 811 } 812 813 static 814 int part_compare(const void *xa, const void *xb) 815 { 816 struct gprovider **a = (struct gprovider **)xa; 817 struct gprovider **b = (struct gprovider **)xb; 818 intmax_t astart, bstart; 819 struct gconfig *gc; 820 821 astart = bstart = 0; 822 LIST_FOREACH(gc, &(*a)->lg_config, lg_config) 823 if (strcmp(gc->lg_name, "start") == 0) { 824 astart = strtoimax(gc->lg_val, NULL, 0); 825 break; 826 } 827 LIST_FOREACH(gc, &(*b)->lg_config, lg_config) 828 if (strcmp(gc->lg_name, "start") == 0) { 829 bstart = strtoimax(gc->lg_val, NULL, 0); 830 break; 831 } 832 833 if (astart < bstart) 834 return -1; 835 else if (astart > bstart) 836 return 1; 837 else 838 return 0; 839 } 840 841 intmax_t 842 gpart_max_free(struct ggeom *geom, intmax_t *npartstart) 843 { 844 struct gconfig *gc; 845 struct gprovider *pp, **providers; 846 intmax_t sectorsize, stripesize, offset; 847 intmax_t lastend; 848 intmax_t start, end; 849 intmax_t maxsize, maxstart; 850 intmax_t partstart, partend; 851 int i, nparts; 852 853 /* Now get the maximum free size and free start */ 854 start = end = 0; 855 LIST_FOREACH(gc, &geom->lg_config, lg_config) { 856 if (strcmp(gc->lg_name, "first") == 0) 857 start = strtoimax(gc->lg_val, NULL, 0); 858 if (strcmp(gc->lg_name, "last") == 0) 859 end = strtoimax(gc->lg_val, NULL, 0); 860 } 861 862 i = nparts = 0; 863 LIST_FOREACH(pp, &geom->lg_provider, lg_provider) 864 nparts++; 865 providers = calloc(nparts, sizeof(providers[0])); 866 LIST_FOREACH(pp, &geom->lg_provider, lg_provider) 867 providers[i++] = pp; 868 qsort(providers, nparts, sizeof(providers[0]), part_compare); 869 870 lastend = start - 1; 871 maxsize = 0; 872 for (i = 0; i < nparts; i++) { 873 pp = providers[i]; 874 875 LIST_FOREACH(gc, &pp->lg_config, lg_config) { 876 if (strcmp(gc->lg_name, "start") == 0) 877 partstart = strtoimax(gc->lg_val, NULL, 0); 878 if (strcmp(gc->lg_name, "end") == 0) 879 partend = strtoimax(gc->lg_val, NULL, 0); 880 } 881 882 if (partstart - lastend > maxsize) { 883 maxsize = partstart - lastend - 1; 884 maxstart = lastend + 1; 885 } 886 887 lastend = partend; 888 } 889 890 if (end - lastend > maxsize) { 891 maxsize = end - lastend; 892 maxstart = lastend + 1; 893 } 894 895 pp = LIST_FIRST(&geom->lg_consumer)->lg_provider; 896 897 /* 898 * Round the start and size of the largest available space up to 899 * the nearest multiple of the adjusted stripe size. 900 * 901 * The adjusted stripe size is the least common multiple of the 902 * actual stripe size, or the sector size if no stripe size was 903 * reported, and 4096. The reason for this is that contemporary 904 * disks often have 4096-byte physical sectors but report 512 905 * bytes instead for compatibility with older / broken operating 906 * systems and BIOSes. For the same reasons, virtualized storage 907 * may also report a 512-byte stripe size, or none at all. 908 */ 909 sectorsize = pp->lg_sectorsize; 910 if ((stripesize = pp->lg_stripesize) == 0) 911 stripesize = sectorsize; 912 while (stripesize % 4096 != 0) 913 stripesize *= 2; 914 if ((offset = maxstart * sectorsize % stripesize) != 0) { 915 offset = (stripesize - offset) / sectorsize; 916 maxstart += offset; 917 maxsize -= offset; 918 } 919 920 if (npartstart != NULL) 921 *npartstart = maxstart; 922 923 return (maxsize); 924 } 925 926 static size_t 927 add_boot_partition(struct ggeom *geom, struct gprovider *pp, 928 const char *scheme, int interactive) 929 { 930 struct gconfig *gc; 931 struct gprovider *ppi; 932 int choice; 933 struct bsddialog_conf conf; 934 935 /* Check for existing freebsd-boot partition */ 936 LIST_FOREACH(ppi, &geom->lg_provider, lg_provider) { 937 struct partition_metadata *md; 938 const char *bootmount = NULL; 939 940 LIST_FOREACH(gc, &ppi->lg_config, lg_config) 941 if (strcmp(gc->lg_name, "type") == 0) 942 break; 943 if (gc == NULL) 944 continue; 945 if (strcmp(gc->lg_val, bootpart_type(scheme, &bootmount)) != 0) 946 continue; 947 948 /* 949 * If the boot partition is not mountable and needs partcode, 950 * but doesn't have it, it doesn't satisfy our requirements. 951 */ 952 md = get_part_metadata(ppi->lg_name, 0); 953 if (bootmount == NULL && (md == NULL || !md->bootcode)) 954 continue; 955 956 /* If it is mountable, but mounted somewhere else, remount */ 957 if (bootmount != NULL && md != NULL && md->fstab != NULL 958 && strlen(md->fstab->fs_file) > 0 959 && strcmp(md->fstab->fs_file, bootmount) != 0) 960 continue; 961 962 /* If it is mountable, but mountpoint is not set, mount it */ 963 if (bootmount != NULL && md == NULL) 964 set_default_part_metadata(ppi->lg_name, scheme, 965 gc->lg_val, bootmount, NULL); 966 967 /* Looks good at this point, no added data needed */ 968 return (0); 969 } 970 971 if (interactive) { 972 bsddialog_initconf(&conf); 973 conf.title = "Boot Partition"; 974 choice = bsddialog_yesno(&conf, 975 "This partition scheme requires a boot partition " 976 "for the disk to be bootable. Would you like to " 977 "make one now?", 0, 0); 978 } else { 979 choice = BSDDIALOG_YES; 980 } 981 982 if (choice == BSDDIALOG_YES) { 983 struct partition_metadata *md; 984 const char *bootmount = NULL; 985 char *bootpartname = NULL; 986 char sizestr[7]; 987 988 humanize_number(sizestr, 7, 989 bootpart_size(scheme), "B", HN_AUTOSCALE, 990 HN_NOSPACE | HN_DECIMAL); 991 992 gpart_create(pp, bootpart_type(scheme, &bootmount), 993 sizestr, bootmount, &bootpartname, 0); 994 995 if (bootpartname == NULL) /* Error reported to user already */ 996 return 0; 997 998 /* If the part is not mountable, make sure newfs isn't set */ 999 if (bootmount == NULL) { 1000 md = get_part_metadata(bootpartname, 0); 1001 if (md != NULL && md->newfs != NULL) { 1002 free(md->newfs); 1003 md->newfs = NULL; 1004 } 1005 } 1006 1007 free(bootpartname); 1008 1009 return (bootpart_size(scheme)); 1010 } 1011 1012 return (0); 1013 } 1014 1015 void 1016 gpart_create(struct gprovider *pp, const char *default_type, 1017 const char *default_size, const char *default_mountpoint, 1018 char **partname, int interactive) 1019 { 1020 struct gctl_req *r; 1021 struct gconfig *gc; 1022 struct gconsumer *cp; 1023 struct ggeom *geom; 1024 const char *errstr, *scheme; 1025 char sizestr[32], startstr[32], output[64], *newpartname; 1026 char newfs[255], options_fstype[64]; 1027 intmax_t maxsize, size, sector, firstfree, stripe; 1028 uint64_t bytes; 1029 int nitems, choice, junk; 1030 unsigned i; 1031 bool init_allocated; 1032 struct bsddialog_conf conf; 1033 1034 struct bsddialog_formitem items[] = { 1035 {"Type:", 1, 1, "freebsd-ufs", 1, 12, 12, 15, NULL, 0, 1036 "Filesystem type (e.g. freebsd-ufs, freebsd-zfs, " 1037 "freebsd-swap)"}, 1038 {"Size:", 2, 1, "", 2, 12, 12, 15, NULL, 0, 1039 "Partition size. Append K, M, G for kilobytes, " 1040 "megabytes or gigabytes."}, 1041 {"Mountpoint:", 3, 1, "", 3, 12, 12, 15, NULL, 0, 1042 "Path at which to mount partition (blank for " 1043 "swap, / for root filesystem)"}, 1044 {"Label:", 4, 1, "", 4, 12, 12, 15, NULL, 0, 1045 "Partition name. Not all partition schemes support this."}, 1046 }; 1047 1048 bsddialog_initconf(&conf); 1049 1050 if (partname != NULL) 1051 *partname = NULL; 1052 1053 /* Record sector and stripe sizes */ 1054 sector = pp->lg_sectorsize; 1055 stripe = pp->lg_stripesize; 1056 1057 /* 1058 * Find the PART geom we are manipulating. This may be a consumer of 1059 * this provider, or its parent. Check the consumer case first. 1060 */ 1061 geom = NULL; 1062 LIST_FOREACH(cp, &pp->lg_consumers, lg_consumers) 1063 if (strcmp(cp->lg_geom->lg_class->lg_name, "PART") == 0) { 1064 geom = cp->lg_geom; 1065 break; 1066 } 1067 1068 if (geom == NULL && strcmp(pp->lg_geom->lg_class->lg_name, "PART") == 0) 1069 geom = pp->lg_geom; 1070 1071 /* Now get the partition scheme */ 1072 scheme = NULL; 1073 if (geom != NULL) { 1074 LIST_FOREACH(gc, &geom->lg_config, lg_config) 1075 if (strcmp(gc->lg_name, "scheme") == 0) 1076 scheme = gc->lg_val; 1077 } 1078 1079 if (geom == NULL || scheme == NULL || strcmp(scheme, "(none)") == 0) { 1080 if (gpart_partition(pp->lg_name, NULL) == 0) { 1081 bsddialog_msgbox(&conf, 1082 "The partition table has been successfully created." 1083 " Please press Create again to create partitions.", 1084 0, 0); 1085 } 1086 1087 return; 1088 } 1089 1090 /* 1091 * If we still don't have a geom, either the user has 1092 * canceled partitioning or there has been an error which has already 1093 * been displayed, so bail. 1094 */ 1095 if (geom == NULL) 1096 return; 1097 1098 maxsize = size = gpart_max_free(geom, &firstfree); 1099 if (size <= 0) { 1100 conf .title = "Error"; 1101 bsddialog_msgbox(&conf, "No free space left on device.", 0, 0); 1102 return; 1103 } 1104 1105 humanize_number(sizestr, 7, size*sector, "B", HN_AUTOSCALE, 1106 HN_NOSPACE | HN_DECIMAL); 1107 items[1].init = sizestr; 1108 1109 /* Special-case the MBR default type for nested partitions */ 1110 if (strcmp(scheme, "MBR") == 0) { 1111 items[0].init = "freebsd"; 1112 items[0].bottomdesc = "Filesystem type (e.g. freebsd, fat32)"; 1113 } 1114 1115 nitems = scheme_supports_labels(scheme) ? 4 : 3; 1116 1117 if (default_type != NULL) 1118 items[0].init = (char *)default_type; 1119 if (default_size != NULL) 1120 items[1].init = (char *)default_size; 1121 if (default_mountpoint != NULL) 1122 items[2].init = (char *)default_mountpoint; 1123 1124 /* Default options */ 1125 strncpy(options_fstype, items[0].init, 1126 sizeof(options_fstype)); 1127 newfs_command(options_fstype, newfs, 1); 1128 1129 init_allocated = false; 1130 addpartform: 1131 if (interactive) { 1132 conf.button.with_extra = true; 1133 conf.button.extra_label = "Options"; 1134 conf.form.value_without_ok = true; 1135 conf.title = "Add Partition"; 1136 choice = bsddialog_form(&conf, "", 0, 0, 0, nitems, items); 1137 conf.button.with_extra = false; 1138 conf.button.extra_label = NULL; 1139 conf.form.value_without_ok = false; 1140 switch (choice) { 1141 case BSDDIALOG_OK: 1142 break; 1143 case BSDDIALOG_CANCEL: 1144 return; 1145 case BSDDIALOG_EXTRA: /* Options */ 1146 strncpy(options_fstype, items[0].value, 1147 sizeof(options_fstype)); 1148 newfs_command(options_fstype, newfs, 0); 1149 for (i = 0; i < nitems(items); i++) { 1150 if (init_allocated) 1151 free((char*)items[i].init); 1152 items[i].init = items[i].value; 1153 } 1154 init_allocated = true; 1155 goto addpartform; 1156 } 1157 } else { /* auto partitioning */ 1158 items[0].value = strdup(items[0].init); 1159 items[1].value = strdup(items[1].init); 1160 items[2].value = strdup(items[2].init); 1161 if (nitems > 3) 1162 items[3].value = strdup(items[3].init); 1163 } 1164 1165 /* 1166 * If the user changed the fs type after specifying options, undo 1167 * their choices in favor of the new filesystem's defaults. 1168 */ 1169 if (strcmp(options_fstype, items[0].value) != 0) { 1170 strncpy(options_fstype, items[0].value, sizeof(options_fstype)); 1171 newfs_command(options_fstype, newfs, 1); 1172 } 1173 1174 size = maxsize; 1175 if (strlen(items[1].value) > 0) { 1176 if (expand_number(items[1].value, &bytes) != 0) { 1177 char error[512]; 1178 1179 sprintf(error, "Invalid size: %s\n", strerror(errno)); 1180 conf.title = "Error"; 1181 bsddialog_msgbox(&conf, error, 0, 0); 1182 goto addpartform; 1183 } 1184 size = MIN((intmax_t)(bytes/sector), maxsize); 1185 } 1186 1187 /* Check if the label has a / in it */ 1188 if (items[3].value != NULL && strchr(items[3].value, '/') != NULL) { 1189 conf.title = "Error"; 1190 bsddialog_msgbox(&conf, "Label contains a /, which is not an " 1191 "allowed character.", 0, 0); 1192 goto addpartform; 1193 } 1194 1195 /* Warn if no mountpoint set */ 1196 if (strcmp(items[0].value, "freebsd-ufs") == 0 && 1197 items[2].value[0] != '/') { 1198 choice = 0; 1199 if (interactive) { 1200 conf.button.default_cancel = true; 1201 conf.title = "Warning"; 1202 choice = bsddialog_yesno(&conf, 1203 "This partition does not have a valid mountpoint " 1204 "(for the partition from which you intend to boot the " 1205 "operating system, the mountpoint should be /). Are you " 1206 "sure you want to continue?" 1207 , 0, 0); 1208 conf.button.default_cancel = false; 1209 } 1210 if (choice == BSDDIALOG_CANCEL) 1211 goto addpartform; 1212 } 1213 1214 /* 1215 * Error if this scheme needs nested partitions, this is one, and 1216 * a mountpoint was set. 1217 */ 1218 if (strcmp(items[0].value, "freebsd") == 0 && 1219 strlen(items[2].value) > 0) { 1220 conf.title = "Error"; 1221 bsddialog_msgbox(&conf, "Partitions of type \"freebsd\" are " 1222 "nested BSD-type partition schemes and cannot have " 1223 "mountpoints. After creating one, select it and press " 1224 "Create again to add the actual file systems.", 0, 0); 1225 goto addpartform; 1226 } 1227 1228 /* If this is the root partition, check that this scheme is bootable */ 1229 if (strcmp(items[2].value, "/") == 0 && !is_scheme_bootable(scheme)) { 1230 char message[512]; 1231 sprintf(message, "This partition scheme (%s) is not bootable " 1232 "on this platform. Are you sure you want to proceed?", 1233 scheme); 1234 conf.button.default_cancel = true; 1235 conf.title = "Warning"; 1236 choice = bsddialog_yesno(&conf, message, 0, 0); 1237 conf.button.default_cancel = false; 1238 if (choice == BSDDIALOG_CANCEL) 1239 goto addpartform; 1240 } 1241 1242 /* If this is the root partition, check that this fs is bootable */ 1243 if (strcmp(items[2].value, "/") == 0 && !is_fs_bootable(scheme, 1244 items[0].value)) { 1245 char message[512]; 1246 sprintf(message, "This file system (%s) is not bootable " 1247 "on this system. Are you sure you want to proceed?", 1248 items[0].value); 1249 conf.button.default_cancel = true; 1250 conf.title = "Warning"; 1251 choice = bsddialog_yesno(&conf, message, 0, 0); 1252 conf.button.default_cancel = false; 1253 if (choice == BSDDIALOG_CANCEL) 1254 goto addpartform; 1255 } 1256 1257 /* 1258 * If this is the root partition, and we need a boot partition, ask 1259 * the user to add one. 1260 */ 1261 1262 if ((strcmp(items[0].value, "freebsd") == 0 || 1263 strcmp(items[2].value, "/") == 0) && bootpart_size(scheme) > 0) { 1264 size_t bytes = add_boot_partition(geom, pp, scheme, 1265 interactive); 1266 1267 /* Now adjust the part we are really adding forward */ 1268 if (bytes > 0) { 1269 firstfree += bytes / sector; 1270 size -= (bytes + stripe)/sector; 1271 if (stripe > 0 && (firstfree*sector % stripe) != 0) 1272 firstfree += (stripe - ((firstfree*sector) % 1273 stripe)) / sector; 1274 } 1275 } 1276 1277 output[0] = '\0'; 1278 1279 r = gctl_get_handle(); 1280 gctl_ro_param(r, "class", -1, "PART"); 1281 gctl_ro_param(r, "arg0", -1, geom->lg_name); 1282 gctl_ro_param(r, "flags", -1, GPART_FLAGS); 1283 gctl_ro_param(r, "verb", -1, "add"); 1284 1285 gctl_ro_param(r, "type", -1, items[0].value); 1286 snprintf(sizestr, sizeof(sizestr), "%jd", size); 1287 gctl_ro_param(r, "size", -1, sizestr); 1288 snprintf(startstr, sizeof(startstr), "%jd", firstfree); 1289 gctl_ro_param(r, "start", -1, startstr); 1290 if (items[3].value != NULL && items[3].value[0] != '\0') 1291 gctl_ro_param(r, "label", -1, items[3].value); 1292 gctl_add_param(r, "output", sizeof(output), output, 1293 GCTL_PARAM_WR | GCTL_PARAM_ASCII); 1294 errstr = gctl_issue(r); 1295 if (errstr != NULL && errstr[0] != '\0') { 1296 gpart_show_error("Error", NULL, errstr); 1297 gctl_free(r); 1298 goto addpartform; 1299 } 1300 newpartname = strtok(output, " "); 1301 gctl_free(r); 1302 1303 /* 1304 * Try to destroy any geom that gpart picked up already here from 1305 * dirty blocks. 1306 */ 1307 r = gctl_get_handle(); 1308 gctl_ro_param(r, "class", -1, "PART"); 1309 gctl_ro_param(r, "arg0", -1, newpartname); 1310 gctl_ro_param(r, "flags", -1, GPART_FLAGS); 1311 junk = 1; 1312 gctl_ro_param(r, "force", sizeof(junk), &junk); 1313 gctl_ro_param(r, "verb", -1, "destroy"); 1314 gctl_issue(r); /* Error usually expected and non-fatal */ 1315 gctl_free(r); 1316 1317 1318 if (strcmp(items[0].value, "freebsd") == 0) 1319 gpart_partition(newpartname, "BSD"); 1320 else 1321 set_default_part_metadata(newpartname, scheme, 1322 items[0].value, items[2].value, newfs); 1323 1324 for (i = 0; i < nitems(items); i++) { 1325 if (items[i].value != NULL) { 1326 free(items[i].value); 1327 if (init_allocated && items[i].init != NULL) 1328 free((char*)items[i].init); 1329 } 1330 } 1331 1332 if (partname != NULL) 1333 *partname = strdup(newpartname); 1334 } 1335 1336 void 1337 gpart_delete(struct gprovider *pp) 1338 { 1339 struct gconfig *gc; 1340 struct ggeom *geom; 1341 struct gconsumer *cp; 1342 struct gctl_req *r; 1343 const char *errstr; 1344 intmax_t idx; 1345 int is_partition; 1346 struct bsddialog_conf conf; 1347 1348 /* Is it a partition? */ 1349 is_partition = (strcmp(pp->lg_geom->lg_class->lg_name, "PART") == 0); 1350 1351 /* Find out if this is the root of a gpart geom */ 1352 geom = NULL; 1353 LIST_FOREACH(cp, &pp->lg_consumers, lg_consumers) 1354 if (strcmp(cp->lg_geom->lg_class->lg_name, "PART") == 0) { 1355 geom = cp->lg_geom; 1356 break; 1357 } 1358 1359 /* If so, destroy all children */ 1360 if (geom != NULL) { 1361 gpart_destroy(geom); 1362 1363 /* If this is a partition, revert it, so it can be deleted */ 1364 if (is_partition) { 1365 r = gctl_get_handle(); 1366 gctl_ro_param(r, "class", -1, "PART"); 1367 gctl_ro_param(r, "arg0", -1, geom->lg_name); 1368 gctl_ro_param(r, "verb", -1, "undo"); 1369 gctl_issue(r); /* Ignore non-fatal errors */ 1370 gctl_free(r); 1371 } 1372 } 1373 1374 /* 1375 * If this is not a partition, see if that is a problem, complain if 1376 * necessary, and return always, since we need not do anything further, 1377 * error or no. 1378 */ 1379 if (!is_partition) { 1380 if (geom == NULL) { 1381 bsddialog_initconf(&conf); 1382 conf.title = "Error"; 1383 bsddialog_msgbox(&conf, 1384 "Only partitions can be deleted.", 0, 0); 1385 } 1386 return; 1387 } 1388 1389 r = gctl_get_handle(); 1390 gctl_ro_param(r, "class", -1, pp->lg_geom->lg_class->lg_name); 1391 gctl_ro_param(r, "arg0", -1, pp->lg_geom->lg_name); 1392 gctl_ro_param(r, "flags", -1, GPART_FLAGS); 1393 gctl_ro_param(r, "verb", -1, "delete"); 1394 1395 LIST_FOREACH(gc, &pp->lg_config, lg_config) { 1396 if (strcmp(gc->lg_name, "index") == 0) { 1397 idx = atoi(gc->lg_val); 1398 gctl_ro_param(r, "index", sizeof(idx), &idx); 1399 break; 1400 } 1401 } 1402 1403 errstr = gctl_issue(r); 1404 if (errstr != NULL && errstr[0] != '\0') { 1405 gpart_show_error("Error", NULL, errstr); 1406 gctl_free(r); 1407 return; 1408 } 1409 1410 gctl_free(r); 1411 1412 delete_part_metadata(pp->lg_name); 1413 } 1414 1415 void 1416 gpart_revert_all(struct gmesh *mesh) 1417 { 1418 struct gclass *classp; 1419 struct gconfig *gc; 1420 struct ggeom *gp; 1421 struct gctl_req *r; 1422 const char *modified; 1423 struct bsddialog_conf conf; 1424 1425 LIST_FOREACH(classp, &mesh->lg_class, lg_class) { 1426 if (strcmp(classp->lg_name, "PART") == 0) 1427 break; 1428 } 1429 1430 if (strcmp(classp->lg_name, "PART") != 0) { 1431 bsddialog_initconf(&conf); 1432 conf.title = "Error"; 1433 bsddialog_msgbox(&conf, "gpart not found!", 0, 0); 1434 return; 1435 } 1436 1437 LIST_FOREACH(gp, &classp->lg_geom, lg_geom) { 1438 modified = "true"; /* XXX: If we don't know (kernel too old), 1439 * assume there are modifications. */ 1440 LIST_FOREACH(gc, &gp->lg_config, lg_config) { 1441 if (strcmp(gc->lg_name, "modified") == 0) { 1442 modified = gc->lg_val; 1443 break; 1444 } 1445 } 1446 1447 if (strcmp(modified, "false") == 0) 1448 continue; 1449 1450 r = gctl_get_handle(); 1451 gctl_ro_param(r, "class", -1, "PART"); 1452 gctl_ro_param(r, "arg0", -1, gp->lg_name); 1453 gctl_ro_param(r, "verb", -1, "undo"); 1454 1455 gctl_issue(r); 1456 gctl_free(r); 1457 } 1458 } 1459 1460 void 1461 gpart_commit(struct gmesh *mesh) 1462 { 1463 struct partition_metadata *md; 1464 struct gclass *classp; 1465 struct ggeom *gp; 1466 struct gconfig *gc; 1467 struct gconsumer *cp; 1468 struct gprovider *pp; 1469 struct gctl_req *r; 1470 const char *errstr; 1471 const char *modified; 1472 const char *rootfs; 1473 struct bsddialog_conf conf; 1474 1475 LIST_FOREACH(classp, &mesh->lg_class, lg_class) { 1476 if (strcmp(classp->lg_name, "PART") == 0) 1477 break; 1478 } 1479 1480 /* Figure out what filesystem / uses */ 1481 rootfs = "ufs"; /* Assume ufs if nothing else present */ 1482 TAILQ_FOREACH(md, &part_metadata, metadata) { 1483 if (md->fstab != NULL && strcmp(md->fstab->fs_file, "/") == 0) { 1484 rootfs = md->fstab->fs_vfstype; 1485 break; 1486 } 1487 } 1488 1489 if (strcmp(classp->lg_name, "PART") != 0) { 1490 bsddialog_initconf(&conf); 1491 conf.title = "Error"; 1492 bsddialog_msgbox(&conf, "gpart not found!", 0, 0); 1493 return; 1494 } 1495 1496 LIST_FOREACH(gp, &classp->lg_geom, lg_geom) { 1497 modified = "true"; /* XXX: If we don't know (kernel too old), 1498 * assume there are modifications. */ 1499 LIST_FOREACH(gc, &gp->lg_config, lg_config) { 1500 if (strcmp(gc->lg_name, "modified") == 0) { 1501 modified = gc->lg_val; 1502 break; 1503 } 1504 } 1505 1506 if (strcmp(modified, "false") == 0) 1507 continue; 1508 1509 /* Add bootcode if necessary, before the commit */ 1510 md = get_part_metadata(gp->lg_name, 0); 1511 if (md != NULL && md->bootcode) 1512 gpart_bootcode(gp); 1513 1514 /* Now install partcode on its partitions, if necessary */ 1515 LIST_FOREACH(pp, &gp->lg_provider, lg_provider) { 1516 md = get_part_metadata(pp->lg_name, 0); 1517 if (md == NULL || !md->bootcode) 1518 continue; 1519 1520 /* Mark this partition active if that's required */ 1521 gpart_activate(pp); 1522 1523 /* Check if the partition has sub-partitions */ 1524 LIST_FOREACH(cp, &pp->lg_consumers, lg_consumers) 1525 if (strcmp(cp->lg_geom->lg_class->lg_name, 1526 "PART") == 0) 1527 break; 1528 1529 if (cp == NULL) /* No sub-partitions */ 1530 gpart_partcode(pp, rootfs); 1531 } 1532 1533 r = gctl_get_handle(); 1534 gctl_ro_param(r, "class", -1, "PART"); 1535 gctl_ro_param(r, "arg0", -1, gp->lg_name); 1536 gctl_ro_param(r, "verb", -1, "commit"); 1537 1538 errstr = gctl_issue(r); 1539 if (errstr != NULL && errstr[0] != '\0') 1540 gpart_show_error("Error", NULL, errstr); 1541 gctl_free(r); 1542 } 1543 } 1544 1545