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 fn_mapper_name(subpartition_get_device_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 fn_mapper_name(subpartition_get_device_name(sp), 0) : subpartition_get_device_name(sp)); 266 free(ham_name); 267 } 268 } 269 270 result = commands_execute(a, cmds); 271 commands_free(cmds); 272 return(result); 273 } 274 275 /* 276 * Return default capacity field filler. Return 0 for /build if drive 277 * space minus swap is < 40GB (causes installer to use PFS's on the root 278 * partition instead). 279 */ 280 static long 281 default_capacity(struct storage *s, const char *mtpt) 282 { 283 unsigned long boot, root, swap, build; 284 unsigned long capacity; 285 unsigned long mem; 286 287 capacity = slice_get_capacity(storage_get_selected_slice(s)); /* MB */ 288 mem = storage_get_memsize(s); 289 290 /* 291 * Slice capacity is at least 10G at this point. Calculate basic 292 * defaults. 293 */ 294 swap = 2 * mem; 295 if (swap > capacity / 10) /* max 1/10 capacity */ 296 swap = capacity / 10; 297 if (swap < SWAP_MIN) /* having a little is nice */ 298 swap = SWAP_MIN; 299 if (swap > SWAP_MAX) /* installer cap */ 300 swap = SWAP_MAX; 301 302 boot = 1024; 303 304 build = (capacity - swap - boot) / 3; 305 if (build > BUILD_MAX) 306 build = BUILD_MAX; 307 308 for (;;) { 309 root = (capacity - swap - boot - build); 310 311 /* 312 * Adjust until the defaults look sane 313 * 314 * root should be at least twice as large as build 315 */ 316 if (build && root < build * 2) { 317 --build; 318 continue; 319 } 320 321 /* 322 * root should be at least 1/2 capacity 323 */ 324 if (build && root < capacity / 2) { 325 --build; 326 continue; 327 } 328 break; 329 } 330 331 /* 332 * Finalize. If build is too small do not supply a /build, 333 * and if swap is too small do not supply swap. 334 */ 335 if (build < BUILD_MIN) 336 build = 0; 337 if (swap < SWAP_MIN) 338 swap = 0; 339 if (build == 0) 340 root = -1; /* root is the last part */ 341 else 342 build = -1; /* last partition just use remaining space */ 343 344 if (strcmp(mtpt, "/boot") == 0) 345 return(boot); 346 else if (strcmp(mtpt, "/build") == 0) 347 return(build); 348 else if (strcmp(mtpt, "swap") == 0) 349 return(swap); 350 else if (strcmp(mtpt, "/") == 0) 351 return(root); 352 353 /* shouldn't ever happen */ 354 return(-1); 355 } 356 357 static int 358 check_capacity(struct i_fn_args *a) 359 { 360 struct subpartition *sp; 361 unsigned long total_capacity = 0; 362 unsigned long remaining_capacity; 363 int mtpt, warn_smallpart = 0; 364 int good; 365 366 remaining_capacity = slice_get_capacity( 367 storage_get_selected_slice(a->s)); 368 for (sp = slice_subpartition_first(storage_get_selected_slice(a->s)); 369 sp != NULL; sp = subpartition_next(sp)) { 370 if (subpartition_get_capacity(sp) != -1) 371 remaining_capacity -= subpartition_get_capacity(sp); 372 } 373 374 for (sp = slice_subpartition_first(storage_get_selected_slice(a->s)); 375 sp != NULL; sp = subpartition_next(sp)) { 376 long subpart_capacity = subpartition_get_capacity(sp); 377 const char *mountpt = subpartition_get_mountpoint(sp); 378 379 if (subpart_capacity == -1) 380 total_capacity++; 381 else 382 total_capacity += subpart_capacity; 383 for (mtpt = 0; def_mountpt[mtpt] != NULL; mtpt++) { 384 if (strcmp(mountpt, def_mountpt[mtpt]) == 0 && 385 subpart_capacity < min_capacity[mtpt] && 386 subpart_capacity != -1) { 387 inform(a->c, 388 _("WARNING: The size (%ldM) specified for " 389 "the %s subpartition is too small. It " 390 "should be at least %ldM or you will " 391 "risk running out of space during " 392 "installation or operation."), 393 subpart_capacity, mountpt, 394 min_capacity[mtpt]); 395 } 396 } 397 if (strcmp(mountpt, "/boot") != 0 && 398 strcmp(mountpt, "swap") != 0) { 399 if ((subpart_capacity == -1 && 400 remaining_capacity < HAMMER_WARN) || 401 (subpart_capacity != -1 && 402 subpart_capacity < HAMMER_WARN)) { 403 warn_smallpart++; 404 } 405 } 406 } 407 408 if (total_capacity > slice_get_capacity(storage_get_selected_slice(a->s))) { 409 inform(a->c, _("The space allocated to all of your selected " 410 "subpartitions (%luM) exceeds the total " 411 "capacity of the selected primary partition " 412 "(%luM). Remove some subpartitions or choose " 413 "a smaller size for them and try again."), 414 total_capacity, 415 slice_get_capacity(storage_get_selected_slice(a->s))); 416 return(0); 417 } 418 419 if (warn_smallpart) { 420 good = confirm_dangerous_action(a->c, 421 _("WARNING: Small HAMMER filesystems can fill up " 422 "very quickly!\n" 423 "You may have to run 'hammer prune-everything' and " 424 "'hammer reblock'\n" 425 "manually or often via a cron job, even if using a " 426 "nohistory mount.")); 427 } else { 428 good = 1; 429 } 430 431 return (good); 432 } 433 434 static int 435 check_subpartition_selections(struct dfui_response *r, struct i_fn_args *a) 436 { 437 struct dfui_dataset *ds; 438 struct dfui_dataset *star_ds = NULL; 439 struct aura_dict *d; 440 const char *mountpoint, *capstring; 441 long capacity = 0; 442 int found_root = 0; 443 int valid = 1; 444 445 d = aura_dict_new(1, AURA_DICT_LIST); 446 447 if ((ds = dfui_response_dataset_get_first(r)) == NULL) { 448 inform(a->c, _("Please set up at least one subpartition.")); 449 valid = 0; 450 } 451 452 for (ds = dfui_response_dataset_get_first(r); valid && ds != NULL; 453 ds = dfui_dataset_get_next(ds)) { 454 #ifdef DEBUG 455 dfui_dataset_dump(ds); 456 #endif 457 mountpoint = dfui_dataset_get_value(ds, "mountpoint"); 458 capstring = dfui_dataset_get_value(ds, "capacity"); 459 460 if (aura_dict_exists(d, mountpoint, strlen(mountpoint) + 1)) { 461 inform(a->c, _("The same mount point cannot be specified " 462 "for two different subpartitions.")); 463 valid = 0; 464 } 465 466 if (strcmp(mountpoint, "/") == 0) 467 found_root = 1; 468 469 if (strcmp(capstring, "*") == 0) { 470 if (star_ds != NULL) { 471 inform(a->c, _("You cannot have more than one subpartition " 472 "with a '*' capacity (meaning 'use the remainder " 473 "of the primary partition'.)")); 474 valid = 0; 475 } else { 476 star_ds = ds; 477 } 478 } 479 480 if (!(!strcasecmp(mountpoint, "swap") || mountpoint[0] == '/')) { 481 inform(a->c, _("Mount point must be either 'swap', or it must " 482 "start with a '/'.")); 483 valid = 0; 484 } 485 486 if (strpbrk(mountpoint, " \\\"'`") != NULL) { 487 inform(a->c, _("Mount point may not contain the following " 488 "characters: blank space, backslash, or " 489 "single, double, or back quotes.")); 490 valid = 0; 491 } 492 493 if (strlen(capstring) == 0) { 494 inform(a->c, _("A capacity must be specified.")); 495 valid = 0; 496 } 497 498 if (!string_to_capacity(capstring, &capacity)) { 499 inform(a->c, _("Capacity must be either a '*' symbol " 500 "to indicate 'use the rest of the primary " 501 "partition', or it must be a series of decimal " 502 "digits ending with an 'M' (indicating " 503 "megabytes), a 'G' (indicating gigabytes) and " 504 "so on (up to 'E'.)")); 505 valid = 0; 506 } 507 508 /* 509 * Maybe remove this limit entirely? 510 */ 511 if ((strcasecmp(mountpoint, "swap") == 0) && 512 (capacity > SWAP_MAX)) { 513 inform(a->c, _("Swap capacity is limited to %dG."), 514 SWAP_MAX / 1024); 515 valid = 0; 516 } 517 518 /* 519 * If we made it through that obstacle course, all is well. 520 */ 521 522 if (valid) 523 aura_dict_store(d, mountpoint, strlen(mountpoint) + 1, "", 1); 524 } 525 526 if (!found_root) { 527 inform(a->c, _("You must include a / (root) subpartition.")); 528 valid = 0; 529 } 530 531 if (aura_dict_size(d) > 16) { 532 inform(a->c, _("You cannot have more than 16 subpartitions " 533 "on a single primary partition. Remove some " 534 "and try again.")); 535 valid = 0; 536 } 537 538 aura_dict_free(d); 539 540 return(valid); 541 } 542 543 static void 544 save_subpartition_selections(struct dfui_response *r, struct i_fn_args *a) 545 { 546 struct dfui_dataset *ds; 547 const char *mountpoint, *capstring; 548 long capacity; 549 int valid = 1; 550 551 subpartitions_free(storage_get_selected_slice(a->s)); 552 553 for (ds = dfui_response_dataset_get_first(r); valid && ds != NULL; 554 ds = dfui_dataset_get_next(ds)) { 555 mountpoint = dfui_dataset_get_value(ds, "mountpoint"); 556 capstring = dfui_dataset_get_value(ds, "capacity"); 557 558 if (string_to_capacity(capstring, &capacity)) { 559 subpartition_new_hammer(storage_get_selected_slice(a->s), 560 mountpoint, capacity, 561 strcasecmp(dfui_dataset_get_value(ds, "encrypted"), "Y") == 0); 562 } 563 } 564 } 565 566 static void 567 populate_create_subpartitions_form(struct dfui_form *f, struct i_fn_args *a) 568 { 569 struct subpartition *sp; 570 struct dfui_dataset *ds; 571 int i; 572 long capacity; 573 574 if (slice_subpartition_first(storage_get_selected_slice(a->s)) != NULL) { 575 /* 576 * The user has already given us their subpartition 577 * preferences, so use them here. 578 */ 579 for (sp = slice_subpartition_first(storage_get_selected_slice(a->s)); 580 sp != NULL; sp = subpartition_next(sp)) { 581 ds = dfui_dataset_new(); 582 dfui_dataset_celldata_add(ds, "mountpoint", 583 subpartition_get_mountpoint(sp)); 584 dfui_dataset_celldata_add(ds, "capacity", 585 capacity_to_string(subpartition_get_capacity(sp))); 586 dfui_dataset_celldata_add(ds, "encrypted", 587 subpartition_is_encrypted(sp) ? "Y" : "N"); 588 dfui_form_dataset_add(f, ds); 589 } 590 } else { 591 /* 592 * Otherwise, populate the form with datasets representing 593 * reasonably-calculated defaults. The defaults are chosen 594 * based on the slice's total capacity and the machine's 595 * total physical memory (for swap.) 596 */ 597 for (i = 0; def_mountpt[i] != NULL; i++) { 598 capacity = default_capacity(a->s, def_mountpt[i]); 599 if (capacity == 0) /* used to disable /build */ 600 continue; 601 ds = dfui_dataset_new(); 602 dfui_dataset_celldata_add(ds, "mountpoint", 603 def_mountpt[i]); 604 dfui_dataset_celldata_add(ds, "capacity", 605 capacity_to_string(capacity)); 606 dfui_dataset_celldata_add(ds, "encrypted", "N"); 607 dfui_form_dataset_add(f, ds); 608 } 609 } 610 } 611 612 static int 613 warn_subpartition_selections(struct i_fn_args *a) 614 { 615 int valid = 0; 616 617 if (subpartition_find(storage_get_selected_slice(a->s), "/boot") == NULL) { 618 inform(a->c, _("The /boot partition must not be omitted.")); 619 } else if (subpartition_find(storage_get_selected_slice(a->s), "/build") == NULL) { 620 inform(a->c, _("Without a /build, things like /usr/obj and " 621 "/var/crash will just be on the root mount.")); 622 valid = check_capacity(a); 623 } else { 624 valid = check_capacity(a); 625 } 626 627 return(!valid); 628 } 629 630 static int 631 warn_encrypted_boot(struct i_fn_args *a) 632 { 633 int valid = 1; 634 635 struct subpartition *sp; 636 637 sp = subpartition_find(storage_get_selected_slice(a->s), "/boot"); 638 if (sp == NULL) 639 return(!valid); 640 641 if (subpartition_is_encrypted(sp)) { 642 switch (dfui_be_present_dialog(a->c, _("/boot cannot be encrypted"), 643 _("Leave /boot unencrypted|Return to Create Subpartitions"), 644 _("You have selected encryption for the /boot partition which " 645 "is not supported."))) { 646 case 1: 647 subpartition_clr_encrypted(sp); 648 valid = 1; 649 break; 650 case 2: 651 valid = 0; 652 break; 653 default: 654 abort_backend(); 655 } 656 } 657 658 return(!valid); 659 } 660 661 static struct dfui_form * 662 make_create_subpartitions_form(struct i_fn_args *a) 663 { 664 struct dfui_form *f; 665 char msg_buf[1][1024]; 666 667 snprintf(msg_buf[0], sizeof(msg_buf[0]), 668 _("Subpartitions further divide a primary partition for " 669 "use with %s. Some reasons you may want " 670 "a set of subpartitions are:\n\n" 671 "- you want to restrict how much data can be written " 672 "to certain parts of the primary partition, to quell " 673 "denial-of-service attacks; and\n" 674 "- you want to speed up access to data on the disk." 675 ""), OPERATING_SYSTEM_NAME); 676 677 f = dfui_form_create( 678 "create_subpartitions", 679 _("Create Subpartitions"), 680 _("Set up the subpartitions you want to have on this primary " 681 "partition. In most cases you should be fine with " 682 "the default settings." 683 " Note that /build will hold /usr/obj, /var/crash, and other" 684 " elements of the topology that do not need to be backed up." 685 " If no /build is specified, these dirs will be on the root." 686 "\n\n" 687 "For Capacity, use 'M' to indicate megabytes, 'G' to " 688 "indicate gigabytes, and so on (up to 'E'.) A single '*' " 689 "indicates 'use the remaining space on the primary partition'."), 690 691 msg_buf[0], 692 693 "p", "special", "dfinstaller_create_subpartitions", 694 "p", "minimum_width","64", 695 696 "f", "mountpoint", _("Mountpoint"), "", "", 697 "f", "capacity", _("Capacity"), "", "", 698 699 "f", "encrypted", _("Encrypted"), "", "", 700 "p", "control", "checkbox", 701 702 "a", "ok", _("Accept and Create"), "", "", 703 "a", "cancel", 704 (disk_get_formatted(storage_get_selected_disk(a->s)) ? 705 _("Return to Select Disk") : 706 _("Return to Select Primary Partition")), "", "", 707 "p", "accelerator", "ESC", 708 709 NULL 710 ); 711 712 dfui_form_set_multiple(f, 1); 713 dfui_form_set_extensible(f, 1); 714 /* 715 * Remove ATM until HAMMER installer support is better 716 * dfui_form_set_extensible(f, 1); 717 */ 718 #if 0 719 if (expert) { 720 fi = dfui_form_field_add(f, "softupdates", 721 dfui_info_new(_("Softupdates"), "", "")); 722 dfui_field_property_set(fi, "control", "checkbox"); 723 724 fi = dfui_form_field_add(f, "tmpfsbacked", 725 dfui_info_new(_("TMPFS"), "", "")); 726 dfui_field_property_set(fi, "control", "checkbox"); 727 728 fi = dfui_form_field_add(f, "fsize", 729 dfui_info_new(_("Frag Sz"), "", "")); 730 731 fi = dfui_form_field_add(f, "bsize", 732 dfui_info_new(_("Block Sz"), "", "")); 733 734 dfui_form_action_add(f, "switch", 735 dfui_info_new(_("Switch to Normal Mode"), "", "")); 736 } else { 737 dfui_form_action_add(f, "switch", 738 dfui_info_new(_("Switch to Expert Mode"), "", "")); 739 } 740 #endif 741 return(f); 742 } 743 744 /* 745 * Returns: 746 * -1 = the form should be redisplayed 747 * 0 = failure, function is over 748 * 1 = success, function is over 749 */ 750 static int 751 show_create_subpartitions_form(struct dfui_form *f, struct i_fn_args *a) 752 { 753 struct dfui_dataset *ds; 754 struct dfui_response *r; 755 756 for (;;) { 757 if (dfui_form_dataset_get_first(f) == NULL) 758 populate_create_subpartitions_form(f, a); 759 760 if (!dfui_be_present(a->c, f, &r)) 761 abort_backend(); 762 763 if (strcmp(dfui_response_get_action_id(r), "cancel") == 0) { 764 dfui_response_free(r); 765 return(0); 766 } else if (strcmp(dfui_response_get_action_id(r), "switch") == 0) { 767 if (check_subpartition_selections(r, a)) { 768 save_subpartition_selections(r, a); 769 expert = expert ? 0 : 1; 770 dfui_response_free(r); 771 return(-1); 772 } 773 } else { 774 if (check_subpartition_selections(r, a)) { 775 save_subpartition_selections(r, a); 776 if (!warn_subpartition_selections(a) && 777 !warn_encrypted_boot(a)) { 778 if (!create_subpartitions(a)) { 779 inform(a->c, _("The subpartitions you chose were " 780 "not correctly created, and the " 781 "primary partition may " 782 "now be in an inconsistent state. " 783 "We recommend re-formatting it " 784 "before proceeding.")); 785 dfui_response_free(r); 786 return(0); 787 } else { 788 dfui_response_free(r); 789 return(1); 790 } 791 } 792 } 793 } 794 795 dfui_form_datasets_free(f); 796 /* dfui_form_datasets_add_from_response(f, r); */ 797 for (ds = dfui_response_dataset_get_first(r); ds != NULL; 798 ds = dfui_dataset_get_next(ds)) { 799 dfui_form_dataset_add(f, dfui_dataset_dup(ds)); 800 } 801 } 802 } 803 804 /* 805 * fn_create_subpartitions_hammer: let the user specify what subpartitions they 806 * want on the disk, how large each should be, and where it should be mounted. 807 */ 808 void 809 fn_create_subpartitions_hammer(struct i_fn_args *a) 810 { 811 struct dfui_form *f; 812 unsigned long capacity; 813 int done = 0; 814 815 a->result = 0; 816 capacity = slice_get_capacity(storage_get_selected_slice(a->s)); 817 if (capacity < HAMMER_MIN) { 818 inform(a->c, _("The selected disk is smaller than the " 819 "required %dM for the HAMMER filesystem."), HAMMER_MIN); 820 return; 821 } 822 while (!done) { 823 f = make_create_subpartitions_form(a); 824 switch (show_create_subpartitions_form(f, a)) { 825 case -1: 826 done = 0; 827 break; 828 case 0: 829 done = 1; 830 a->result = 0; 831 break; 832 case 1: 833 done = 1; 834 a->result = 1; 835 break; 836 } 837 dfui_form_free(f); 838 } 839 } 840 841 static 842 char * 843 construct_lname(const char *mtpt) 844 { 845 char *res; 846 int i; 847 848 if (strcmp(mtpt, "/") == 0) { 849 res = strdup("ROOT"); 850 } else { 851 if (strrchr(mtpt, '/')) 852 mtpt = strrchr(mtpt, '/') + 1; 853 if (*mtpt == 0) 854 mtpt = "unknown"; 855 res = malloc(strlen(mtpt) + 1); 856 for (i = 0; mtpt[i]; ++i) 857 res[i] = toupper(mtpt[i]); 858 res[i] = 0; 859 } 860 return res; 861 } 862