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