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