1 /* 2 * Copyright (c) 2008 The DragonFly Project. All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 8 * Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 11 * Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in 13 * the documentation and/or other materials provided with the 14 * distribution. 15 * 16 * Neither the name of the DragonFly Project nor the names of its 17 * contributors may be used to endorse or promote products derived 18 * from this software without specific prior written permission. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 23 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 24 * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 25 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 26 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 27 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 29 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 30 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 31 * OF THE POSSIBILITY OF SUCH DAMAGE. 32 */ 33 34 /* 35 * fn_subpart_hammer.c 36 * Installer Function : Create HAMMER Subpartitions. 37 */ 38 39 #include <stdio.h> 40 #include <stdlib.h> 41 #include <string.h> 42 #include <ctype.h> 43 44 #ifdef ENABLE_NLS 45 #include <libintl.h> 46 #define _(String) gettext (String) 47 #else 48 #define _(String) (String) 49 #endif 50 51 #include "libaura/mem.h" 52 #include "libaura/buffer.h" 53 #include "libaura/dict.h" 54 #include "libaura/fspred.h" 55 56 #include "libdfui/dfui.h" 57 #include "libdfui/dump.h" 58 #include "libdfui/system.h" 59 60 #include "libinstaller/commands.h" 61 #include "libinstaller/diskutil.h" 62 #include "libinstaller/functions.h" 63 #include "libinstaller/uiutil.h" 64 65 #include "fn.h" 66 #include "flow.h" 67 #include "pathnames.h" 68 69 static int create_subpartitions(struct i_fn_args *); 70 static long default_capacity(struct storage *, const char *); 71 static int check_capacity(struct i_fn_args *); 72 static int check_subpartition_selections(struct dfui_response *, 73 struct i_fn_args *); 74 static void save_subpartition_selections(struct dfui_response *, 75 struct i_fn_args *); 76 static void populate_create_subpartitions_form(struct dfui_form *, 77 struct i_fn_args *); 78 static int warn_subpartition_selections(struct i_fn_args *); 79 static int warn_encrypted_boot(struct i_fn_args *); 80 static struct dfui_form *make_create_subpartitions_form(struct i_fn_args *); 81 static int show_create_subpartitions_form(struct dfui_form *, 82 struct i_fn_args *); 83 static char *construct_lname(const char *mtpt); 84 85 static const char *def_mountpt[] = {"/boot", "swap", "/", "/build", NULL}; 86 static long min_capacity[] = { 128, 0, DISK_MIN - 128, BUILD_MIN }; 87 static int expert = 0; 88 89 /* 90 * Given a set of subpartitions-to-be in the selected slice, 91 * create them. 92 */ 93 static int 94 create_subpartitions(struct i_fn_args *a) 95 { 96 struct subpartition *sp; 97 struct commands *cmds; 98 int result = 0; 99 int num_partitions; 100 101 cmds = commands_new(); 102 if (!is_file("%sinstall.disklabel.%s", 103 a->tmp, 104 slice_get_device_name(storage_get_selected_slice(a->s)))) { 105 /* 106 * Get a copy of the 'virgin' disklabel. 107 * XXX It might make more sense for this to 108 * happen right after format_slice() instead. 109 */ 110 command_add(cmds, "%s%s -r %s >%sinstall.disklabel.%s", 111 a->os_root, cmd_name(a, "DISKLABEL64"), 112 slice_get_device_name(storage_get_selected_slice(a->s)), 113 a->tmp, 114 slice_get_device_name(storage_get_selected_slice(a->s))); 115 } 116 117 /* 118 * Weave together a new disklabel out the of the 'virgin' 119 * disklabel, and the user's subpartition choices. 120 */ 121 122 /* 123 * Take everything from the 'virgin' disklabel up until the 124 * '16 partitions' line. 125 */ 126 num_partitions = 16; 127 command_add(cmds, 128 "%s%s '$2==\"partitions:\" || " 129 "cut { cut = 1 } !cut { print $0 }' " 130 "<%sinstall.disklabel.%s >%sinstall.disklabel", 131 a->os_root, cmd_name(a, "AWK"), 132 a->tmp, 133 slice_get_device_name(storage_get_selected_slice(a->s)), 134 a->tmp); 135 136 /* 137 * 16 partitions: 138 * # size offset fstype 139 * c: 16383969 0 unused # 7999.985MB 140 */ 141 142 command_add(cmds, "%s%s '%d partitions:' >>%sinstall.disklabel", 143 a->os_root, cmd_name(a, "ECHO"), num_partitions ,a->tmp); 144 command_add(cmds, "%s%s '%s' >>%sinstall.disklabel", 145 a->os_root, cmd_name(a, "ECHO"), 146 "# size offset fstype", 147 a->tmp); 148 149 #ifdef DEBUG 150 for (sp = slice_subpartition_first(storage_get_selected_slice(a->s)); 151 sp != NULL; sp = subpartition_next(sp)) { 152 command_add(cmds, "%s%s 'mountpoint: %s device: %s'", 153 a->os_root, cmd_name(a, "ECHO"), 154 subpartition_get_mountpoint(sp), 155 subpartition_get_device_name(sp)); 156 } 157 #endif 158 159 /* 160 * Write a line for each subpartition the user wants. 161 */ 162 for (sp = slice_subpartition_first(storage_get_selected_slice(a->s)); 163 sp != NULL; sp = subpartition_next(sp)) { 164 if (subpartition_is_tmpfsbacked(sp)) { 165 continue; 166 } 167 if (subpartition_is_swap(sp)) { 168 command_add(cmds, 169 "%s%s ' %c:\t%s\t*\tswap' " 170 ">>%sinstall.disklabel", 171 a->os_root, cmd_name(a, "ECHO"), 172 subpartition_get_letter(sp), 173 capacity_to_string(subpartition_get_capacity(sp)), 174 a->tmp); 175 } else if (strcmp(subpartition_get_mountpoint(sp), "/boot") == 0) { 176 command_add(cmds, 177 "%s%s ' %c:\t%s\t0\t4.2BSD' " 178 ">>%sinstall.disklabel", 179 a->os_root, cmd_name(a, "ECHO"), 180 subpartition_get_letter(sp), 181 capacity_to_string(subpartition_get_capacity(sp)), 182 a->tmp); 183 } else { 184 command_add(cmds, 185 "%s%s ' %c:\t%s\t*\tHAMMER' " 186 ">>%sinstall.disklabel", 187 a->os_root, cmd_name(a, "ECHO"), 188 subpartition_get_letter(sp), 189 capacity_to_string(subpartition_get_capacity(sp)), 190 a->tmp); 191 } 192 } 193 temp_file_add(a, "install.disklabel"); 194 195 /* 196 * Label the slice from the disklabel we just wove together. 197 */ 198 command_add(cmds, "%s%s -R -B -r %s %sinstall.disklabel", 199 a->os_root, cmd_name(a, "DISKLABEL64"), 200 slice_get_device_name(storage_get_selected_slice(a->s)), 201 a->tmp); 202 203 /* 204 * Create a snapshot of the disklabel we just created 205 * for debugging inspection in the log. 206 */ 207 command_add(cmds, "%s%s %s", 208 a->os_root, cmd_name(a, "DISKLABEL64"), 209 slice_get_device_name(storage_get_selected_slice(a->s))); 210 211 /* 212 * If encryption was specified, load dm(4). 213 */ 214 for (sp = slice_subpartition_first(storage_get_selected_slice(a->s)); 215 sp != NULL; sp = subpartition_next(sp)) { 216 if (subpartition_is_encrypted(sp)) { 217 fn_get_passphrase(a); 218 break; 219 } 220 } 221 222 /* 223 * Create filesystems on the newly-created subpartitions. 224 */ 225 for (sp = slice_subpartition_first(storage_get_selected_slice(a->s)); 226 sp != NULL; sp = subpartition_next(sp)) { 227 if (subpartition_is_swap(sp) || 228 subpartition_is_tmpfsbacked(sp)) { 229 if (subpartition_is_swap(sp) && 230 subpartition_is_encrypted(sp)) { 231 command_add(cmds, 232 "%s%s -d /tmp/t1 luksFormat /dev/%s", 233 a->os_root, cmd_name(a, "CRYPTSETUP"), 234 subpartition_get_device_name(sp)); 235 command_add(cmds, 236 "%s%s -d /tmp/t1 luksOpen /dev/%s swap", 237 a->os_root, cmd_name(a, "CRYPTSETUP"), 238 subpartition_get_device_name(sp)); 239 } 240 continue; 241 } 242 243 if (strcmp(subpartition_get_mountpoint(sp), "/boot") == 0) { 244 command_add(cmds, "%s%s -i 65536 /dev/%s", 245 a->os_root, cmd_name(a, "NEWFS"), 246 subpartition_get_device_name(sp)); 247 } else { 248 char *ham_name; 249 if (subpartition_is_encrypted(sp)) { 250 command_add(cmds, 251 "%s%s -d /tmp/t1 luksFormat /dev/%s", 252 a->os_root, cmd_name(a, "CRYPTSETUP"), 253 subpartition_get_device_name(sp)); 254 command_add(cmds, 255 "%s%s -d /tmp/t1 luksOpen /dev/%s %s", 256 a->os_root, cmd_name(a, "CRYPTSETUP"), 257 subpartition_get_device_name(sp), 258 subpartition_get_mapper_name(sp, -1)); 259 } 260 ham_name = construct_lname(subpartition_get_mountpoint(sp)); 261 command_add(cmds, "%s%s -f -L %s /dev/%s", 262 a->os_root, cmd_name(a, "NEWFS_HAMMER"), 263 ham_name, 264 (subpartition_is_encrypted(sp) ? 265 subpartition_get_mapper_name(sp, 0) : 266 subpartition_get_device_name(sp))); 267 free(ham_name); 268 } 269 } 270 271 result = commands_execute(a, cmds); 272 commands_free(cmds); 273 return(result); 274 } 275 276 /* 277 * Return default capacity field filler. Return 0 for /build if drive 278 * space minus swap is < 40GB (causes installer to use PFS's on the root 279 * partition instead). 280 */ 281 static long 282 default_capacity(struct storage *s, const char *mtpt) 283 { 284 unsigned long boot, root, swap, build; 285 unsigned long capacity; 286 unsigned long mem; 287 288 capacity = slice_get_capacity(storage_get_selected_slice(s)); /* MB */ 289 mem = storage_get_memsize(s); 290 291 /* 292 * Slice capacity is at least 10G at this point. Calculate basic 293 * defaults. 294 */ 295 swap = 2 * mem; 296 if (swap > capacity / 10) /* max 1/10 capacity */ 297 swap = capacity / 10; 298 if (swap < SWAP_MIN) /* having a little is nice */ 299 swap = SWAP_MIN; 300 if (swap > SWAP_MAX) /* installer cap */ 301 swap = SWAP_MAX; 302 303 boot = 1024; 304 305 build = (capacity - swap - boot) / 3; 306 if (build > BUILD_MAX) 307 build = BUILD_MAX; 308 309 for (;;) { 310 root = (capacity - swap - boot - build); 311 312 /* 313 * Adjust until the defaults look sane 314 * 315 * root should be at least twice as large as build 316 */ 317 if (build && root < build * 2) { 318 --build; 319 continue; 320 } 321 322 /* 323 * root should be at least 1/2 capacity 324 */ 325 if (build && root < capacity / 2) { 326 --build; 327 continue; 328 } 329 break; 330 } 331 332 /* 333 * Finalize. If build is too small do not supply a /build, 334 * and if swap is too small do not supply swap. 335 */ 336 if (build < BUILD_MIN) 337 build = 0; 338 if (swap < SWAP_MIN) 339 swap = 0; 340 if (build == 0) 341 root = -1; /* root is the last part */ 342 else 343 build = -1; /* last partition just use remaining space */ 344 345 if (strcmp(mtpt, "/boot") == 0) 346 return(boot); 347 else if (strcmp(mtpt, "/build") == 0) 348 return(build); 349 else if (strcmp(mtpt, "swap") == 0) 350 return(swap); 351 else if (strcmp(mtpt, "/") == 0) 352 return(root); 353 354 /* shouldn't ever happen */ 355 return(-1); 356 } 357 358 static int 359 check_capacity(struct i_fn_args *a) 360 { 361 struct subpartition *sp; 362 unsigned long total_capacity = 0; 363 unsigned long remaining_capacity; 364 int mtpt, warn_smallpart = 0; 365 int good; 366 367 remaining_capacity = slice_get_capacity( 368 storage_get_selected_slice(a->s)); 369 for (sp = slice_subpartition_first(storage_get_selected_slice(a->s)); 370 sp != NULL; sp = subpartition_next(sp)) { 371 if (subpartition_get_capacity(sp) != -1) 372 remaining_capacity -= subpartition_get_capacity(sp); 373 } 374 375 for (sp = slice_subpartition_first(storage_get_selected_slice(a->s)); 376 sp != NULL; sp = subpartition_next(sp)) { 377 long subpart_capacity = subpartition_get_capacity(sp); 378 const char *mountpt = subpartition_get_mountpoint(sp); 379 380 if (subpart_capacity == -1) 381 total_capacity++; 382 else 383 total_capacity += subpart_capacity; 384 for (mtpt = 0; def_mountpt[mtpt] != NULL; mtpt++) { 385 if (strcmp(mountpt, def_mountpt[mtpt]) == 0 && 386 subpart_capacity < min_capacity[mtpt] && 387 subpart_capacity != -1) { 388 inform(a->c, 389 _("WARNING: The size (%ldM) specified for " 390 "the %s subpartition is too small. It " 391 "should be at least %ldM or you will " 392 "risk running out of space during " 393 "installation or operation."), 394 subpart_capacity, mountpt, 395 min_capacity[mtpt]); 396 } 397 } 398 if (strcmp(mountpt, "/boot") != 0 && 399 strcmp(mountpt, "swap") != 0) { 400 if ((subpart_capacity == -1 && 401 remaining_capacity < HAMMER_WARN) || 402 (subpart_capacity != -1 && 403 subpart_capacity < HAMMER_WARN)) { 404 warn_smallpart++; 405 } 406 } 407 } 408 409 if (total_capacity > slice_get_capacity(storage_get_selected_slice(a->s))) { 410 inform(a->c, _("The space allocated to all of your selected " 411 "subpartitions (%luM) exceeds the total " 412 "capacity of the selected primary partition " 413 "(%luM). Remove some subpartitions or choose " 414 "a smaller size for them and try again."), 415 total_capacity, 416 slice_get_capacity(storage_get_selected_slice(a->s))); 417 return(0); 418 } 419 420 if (warn_smallpart) { 421 good = confirm_dangerous_action(a->c, 422 _("WARNING: Small HAMMER filesystems can fill up " 423 "very quickly!\n" 424 "You may have to run 'hammer prune-everything' and " 425 "'hammer reblock'\n" 426 "manually or often via a cron job, even if using a " 427 "nohistory mount.")); 428 } else { 429 good = 1; 430 } 431 432 return (good); 433 } 434 435 static int 436 check_subpartition_selections(struct dfui_response *r, struct i_fn_args *a) 437 { 438 struct dfui_dataset *ds; 439 struct dfui_dataset *star_ds = NULL; 440 struct aura_dict *d; 441 const char *mountpoint, *capstring; 442 long capacity = 0; 443 int found_root = 0; 444 int valid = 1; 445 446 d = aura_dict_new(1, AURA_DICT_LIST); 447 448 if ((ds = dfui_response_dataset_get_first(r)) == NULL) { 449 inform(a->c, _("Please set up at least one subpartition.")); 450 valid = 0; 451 } 452 453 for (ds = dfui_response_dataset_get_first(r); valid && ds != NULL; 454 ds = dfui_dataset_get_next(ds)) { 455 #ifdef DEBUG 456 dfui_dataset_dump(ds); 457 #endif 458 mountpoint = dfui_dataset_get_value(ds, "mountpoint"); 459 capstring = dfui_dataset_get_value(ds, "capacity"); 460 461 if (aura_dict_exists(d, mountpoint, strlen(mountpoint) + 1)) { 462 inform(a->c, _("The same mount point cannot be specified " 463 "for two different subpartitions.")); 464 valid = 0; 465 } 466 467 if (strcmp(mountpoint, "/") == 0) 468 found_root = 1; 469 470 if (strcmp(capstring, "*") == 0) { 471 if (star_ds != NULL) { 472 inform(a->c, _("You cannot have more than one subpartition " 473 "with a '*' capacity (meaning 'use the remainder " 474 "of the primary partition'.)")); 475 valid = 0; 476 } else { 477 star_ds = ds; 478 } 479 } 480 481 if (!(!strcasecmp(mountpoint, "swap") || mountpoint[0] == '/')) { 482 inform(a->c, _("Mount point must be either 'swap', or it must " 483 "start with a '/'.")); 484 valid = 0; 485 } 486 487 if (strpbrk(mountpoint, " \\\"'`") != NULL) { 488 inform(a->c, _("Mount point may not contain the following " 489 "characters: blank space, backslash, or " 490 "single, double, or back quotes.")); 491 valid = 0; 492 } 493 494 if (strlen(capstring) == 0) { 495 inform(a->c, _("A capacity must be specified.")); 496 valid = 0; 497 } 498 499 if (!string_to_capacity(capstring, &capacity)) { 500 inform(a->c, _("Capacity must be either a '*' symbol " 501 "to indicate 'use the rest of the primary " 502 "partition', or it must be a series of decimal " 503 "digits ending with an 'M' (indicating " 504 "megabytes), a 'G' (indicating gigabytes) and " 505 "so on (up to 'E'.)")); 506 valid = 0; 507 } 508 509 /* 510 * Maybe remove this limit entirely? 511 */ 512 if ((strcasecmp(mountpoint, "swap") == 0) && 513 (capacity > SWAP_MAX)) { 514 inform(a->c, _("Swap capacity is limited to %dG."), 515 SWAP_MAX / 1024); 516 valid = 0; 517 } 518 519 /* 520 * If we made it through that obstacle course, all is well. 521 */ 522 523 if (valid) 524 aura_dict_store(d, mountpoint, strlen(mountpoint) + 1, "", 1); 525 } 526 527 if (!found_root) { 528 inform(a->c, _("You must include a / (root) subpartition.")); 529 valid = 0; 530 } 531 532 if (aura_dict_size(d) > 16) { 533 inform(a->c, _("You cannot have more than 16 subpartitions " 534 "on a single primary partition. Remove some " 535 "and try again.")); 536 valid = 0; 537 } 538 539 aura_dict_free(d); 540 541 return(valid); 542 } 543 544 static void 545 save_subpartition_selections(struct dfui_response *r, struct i_fn_args *a) 546 { 547 struct dfui_dataset *ds; 548 const char *mountpoint, *capstring; 549 long capacity; 550 int valid = 1; 551 552 subpartitions_free(storage_get_selected_slice(a->s)); 553 554 for (ds = dfui_response_dataset_get_first(r); valid && ds != NULL; 555 ds = dfui_dataset_get_next(ds)) { 556 mountpoint = dfui_dataset_get_value(ds, "mountpoint"); 557 capstring = dfui_dataset_get_value(ds, "capacity"); 558 559 if (string_to_capacity(capstring, &capacity)) { 560 subpartition_new_hammer(storage_get_selected_slice(a->s), 561 mountpoint, capacity, 562 strcasecmp(dfui_dataset_get_value(ds, "encrypted"), "Y") == 0); 563 } 564 } 565 } 566 567 static void 568 populate_create_subpartitions_form(struct dfui_form *f, struct i_fn_args *a) 569 { 570 struct subpartition *sp; 571 struct dfui_dataset *ds; 572 int i; 573 long capacity; 574 575 if (slice_subpartition_first(storage_get_selected_slice(a->s)) != NULL) { 576 /* 577 * The user has already given us their subpartition 578 * preferences, so use them here. 579 */ 580 for (sp = slice_subpartition_first(storage_get_selected_slice(a->s)); 581 sp != NULL; sp = subpartition_next(sp)) { 582 ds = dfui_dataset_new(); 583 dfui_dataset_celldata_add(ds, "mountpoint", 584 subpartition_get_mountpoint(sp)); 585 dfui_dataset_celldata_add(ds, "capacity", 586 capacity_to_string(subpartition_get_capacity(sp))); 587 dfui_dataset_celldata_add(ds, "encrypted", 588 subpartition_is_encrypted(sp) ? "Y" : "N"); 589 dfui_form_dataset_add(f, ds); 590 } 591 } else { 592 /* 593 * Otherwise, populate the form with datasets representing 594 * reasonably-calculated defaults. The defaults are chosen 595 * based on the slice's total capacity and the machine's 596 * total physical memory (for swap.) 597 */ 598 for (i = 0; def_mountpt[i] != NULL; i++) { 599 capacity = default_capacity(a->s, def_mountpt[i]); 600 if (capacity == 0) /* used to disable /build */ 601 continue; 602 ds = dfui_dataset_new(); 603 dfui_dataset_celldata_add(ds, "mountpoint", 604 def_mountpt[i]); 605 dfui_dataset_celldata_add(ds, "capacity", 606 capacity_to_string(capacity)); 607 dfui_dataset_celldata_add(ds, "encrypted", "N"); 608 dfui_form_dataset_add(f, ds); 609 } 610 } 611 } 612 613 static int 614 warn_subpartition_selections(struct i_fn_args *a) 615 { 616 int valid = 0; 617 618 if (subpartition_find(storage_get_selected_slice(a->s), "/boot") == NULL) { 619 inform(a->c, _("The /boot partition must not be omitted.")); 620 } else if (subpartition_find(storage_get_selected_slice(a->s), "/build") == NULL) { 621 inform(a->c, _("Without a /build, things like /usr/obj and " 622 "/var/crash will just be on the root mount.")); 623 valid = check_capacity(a); 624 } else { 625 valid = check_capacity(a); 626 } 627 628 return(!valid); 629 } 630 631 static int 632 warn_encrypted_boot(struct i_fn_args *a) 633 { 634 int valid = 1; 635 636 struct subpartition *sp; 637 638 sp = subpartition_find(storage_get_selected_slice(a->s), "/boot"); 639 if (sp == NULL) 640 return(!valid); 641 642 if (subpartition_is_encrypted(sp)) { 643 switch (dfui_be_present_dialog(a->c, _("/boot cannot be encrypted"), 644 _("Leave /boot unencrypted|Return to Create Subpartitions"), 645 _("You have selected encryption for the /boot partition which " 646 "is not supported."))) { 647 case 1: 648 subpartition_clr_encrypted(sp); 649 valid = 1; 650 break; 651 case 2: 652 valid = 0; 653 break; 654 default: 655 abort_backend(); 656 } 657 } 658 659 return(!valid); 660 } 661 662 static struct dfui_form * 663 make_create_subpartitions_form(struct i_fn_args *a) 664 { 665 struct dfui_form *f; 666 char msg_buf[1][1024]; 667 668 snprintf(msg_buf[0], sizeof(msg_buf[0]), 669 _("Subpartitions further divide a primary partition for " 670 "use with %s. Some reasons you may want " 671 "a set of subpartitions are:\n\n" 672 "- you want to restrict how much data can be written " 673 "to certain parts of the primary partition, to quell " 674 "denial-of-service attacks; and\n" 675 "- you want to speed up access to data on the disk." 676 ""), OPERATING_SYSTEM_NAME); 677 678 f = dfui_form_create( 679 "create_subpartitions", 680 _("Create Subpartitions"), 681 _("Set up the subpartitions you want to have on this primary " 682 "partition. In most cases you should be fine with " 683 "the default settings." 684 " Note that /build will hold /usr/obj, /var/crash, and other" 685 " elements of the topology that do not need to be backed up." 686 " If no /build is specified, these dirs will be on the root." 687 "\n\n" 688 "For Capacity, use 'M' to indicate megabytes, 'G' to " 689 "indicate gigabytes, and so on (up to 'E'.) A single '*' " 690 "indicates 'use the remaining space on the primary partition'."), 691 692 msg_buf[0], 693 694 "p", "special", "dfinstaller_create_subpartitions", 695 "p", "minimum_width","64", 696 697 "f", "mountpoint", _("Mountpoint"), "", "", 698 "f", "capacity", _("Capacity"), "", "", 699 700 "f", "encrypted", _("Encrypted"), "", "", 701 "p", "control", "checkbox", 702 703 "a", "ok", _("Accept and Create"), "", "", 704 "a", "cancel", 705 (disk_get_formatted(storage_get_selected_disk(a->s)) ? 706 _("Return to Select Disk") : 707 _("Return to Select Primary Partition")), "", "", 708 "p", "accelerator", "ESC", 709 710 NULL 711 ); 712 713 dfui_form_set_multiple(f, 1); 714 dfui_form_set_extensible(f, 1); 715 /* 716 * Remove ATM until HAMMER installer support is better 717 * dfui_form_set_extensible(f, 1); 718 */ 719 #if 0 720 if (expert) { 721 fi = dfui_form_field_add(f, "softupdates", 722 dfui_info_new(_("Softupdates"), "", "")); 723 dfui_field_property_set(fi, "control", "checkbox"); 724 725 fi = dfui_form_field_add(f, "tmpfsbacked", 726 dfui_info_new(_("TMPFS"), "", "")); 727 dfui_field_property_set(fi, "control", "checkbox"); 728 729 fi = dfui_form_field_add(f, "fsize", 730 dfui_info_new(_("Frag Sz"), "", "")); 731 732 fi = dfui_form_field_add(f, "bsize", 733 dfui_info_new(_("Block Sz"), "", "")); 734 735 dfui_form_action_add(f, "switch", 736 dfui_info_new(_("Switch to Normal Mode"), "", "")); 737 } else { 738 dfui_form_action_add(f, "switch", 739 dfui_info_new(_("Switch to Expert Mode"), "", "")); 740 } 741 #endif 742 return(f); 743 } 744 745 /* 746 * Returns: 747 * -1 = the form should be redisplayed 748 * 0 = failure, function is over 749 * 1 = success, function is over 750 */ 751 static int 752 show_create_subpartitions_form(struct dfui_form *f, struct i_fn_args *a) 753 { 754 struct dfui_dataset *ds; 755 struct dfui_response *r; 756 757 for (;;) { 758 if (dfui_form_dataset_get_first(f) == NULL) 759 populate_create_subpartitions_form(f, a); 760 761 if (!dfui_be_present(a->c, f, &r)) 762 abort_backend(); 763 764 if (strcmp(dfui_response_get_action_id(r), "cancel") == 0) { 765 dfui_response_free(r); 766 return(0); 767 } else if (strcmp(dfui_response_get_action_id(r), "switch") == 0) { 768 if (check_subpartition_selections(r, a)) { 769 save_subpartition_selections(r, a); 770 expert = expert ? 0 : 1; 771 dfui_response_free(r); 772 return(-1); 773 } 774 } else { 775 if (check_subpartition_selections(r, a)) { 776 save_subpartition_selections(r, a); 777 if (!warn_subpartition_selections(a) && 778 !warn_encrypted_boot(a)) { 779 if (!create_subpartitions(a)) { 780 inform(a->c, _("The subpartitions you chose were " 781 "not correctly created, and the " 782 "primary partition may " 783 "now be in an inconsistent state. " 784 "We recommend re-formatting it " 785 "before proceeding.")); 786 dfui_response_free(r); 787 return(0); 788 } else { 789 dfui_response_free(r); 790 return(1); 791 } 792 } 793 } 794 } 795 796 dfui_form_datasets_free(f); 797 /* dfui_form_datasets_add_from_response(f, r); */ 798 for (ds = dfui_response_dataset_get_first(r); ds != NULL; 799 ds = dfui_dataset_get_next(ds)) { 800 dfui_form_dataset_add(f, dfui_dataset_dup(ds)); 801 } 802 } 803 } 804 805 /* 806 * fn_create_subpartitions_hammer: let the user specify what subpartitions they 807 * want on the disk, how large each should be, and where it should be mounted. 808 */ 809 void 810 fn_create_subpartitions_hammer(struct i_fn_args *a) 811 { 812 struct dfui_form *f; 813 unsigned long capacity; 814 int done = 0; 815 816 a->result = 0; 817 capacity = disk_get_capacity(storage_get_selected_disk(a->s)); 818 if (capacity < HAMMER_MIN) { 819 inform(a->c, _("The selected %dM disk is smaller than the " 820 "required %dM for the HAMMER filesystem."), 821 (int)capacity, 822 (int)HAMMER_MIN); 823 return; 824 } 825 while (!done) { 826 f = make_create_subpartitions_form(a); 827 switch (show_create_subpartitions_form(f, a)) { 828 case -1: 829 done = 0; 830 break; 831 case 0: 832 done = 1; 833 a->result = 0; 834 break; 835 case 1: 836 done = 1; 837 a->result = 1; 838 break; 839 } 840 dfui_form_free(f); 841 } 842 } 843 844 static 845 char * 846 construct_lname(const char *mtpt) 847 { 848 char *res; 849 int i; 850 851 if (strcmp(mtpt, "/") == 0) { 852 res = strdup("ROOT"); 853 } else { 854 if (strrchr(mtpt, '/')) 855 mtpt = strrchr(mtpt, '/') + 1; 856 if (*mtpt == 0) 857 mtpt = "unknown"; 858 res = malloc(strlen(mtpt) + 1); 859 for (i = 0; mtpt[i]; ++i) 860 res[i] = toupper(mtpt[i]); 861 res[i] = 0; 862 } 863 return res; 864 } 865