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 43 #ifdef ENABLE_NLS 44 #include <libintl.h> 45 #define _(String) gettext (String) 46 #else 47 #define _(String) (String) 48 #endif 49 50 #include "libaura/mem.h" 51 #include "libaura/buffer.h" 52 #include "libaura/dict.h" 53 #include "libaura/fspred.h" 54 55 #include "libdfui/dfui.h" 56 #include "libdfui/dump.h" 57 #include "libdfui/system.h" 58 59 #include "libinstaller/commands.h" 60 #include "libinstaller/diskutil.h" 61 #include "libinstaller/functions.h" 62 #include "libinstaller/uiutil.h" 63 64 #include "fn.h" 65 #include "flow.h" 66 #include "pathnames.h" 67 68 static int create_subpartitions(struct i_fn_args *); 69 static long default_capacity(struct storage *, int); 70 static int check_capacity(struct i_fn_args *); 71 static int check_subpartition_selections(struct dfui_response *, struct i_fn_args *); 72 static void save_subpartition_selections(struct dfui_response *, struct i_fn_args *); 73 static void populate_create_subpartitions_form(struct dfui_form *, struct i_fn_args *); 74 static int warn_subpartition_selections(struct i_fn_args *); 75 static struct dfui_form *make_create_subpartitions_form(struct i_fn_args *); 76 static int show_create_subpartitions_form(struct dfui_form *, struct i_fn_args *); 77 78 static const char *def_mountpt[7] = {"/", "swap", "/var", "/tmp", "/usr", "/home", NULL}; 79 static int expert = 0; 80 81 /* 82 * Given a set of subpartitions-to-be in the selected slice, 83 * create them. 84 */ 85 static int 86 create_subpartitions(struct i_fn_args *a) 87 { 88 struct subpartition *sp; 89 struct commands *cmds; 90 int result = 0; 91 int num_partitions; 92 93 cmds = commands_new(); 94 if (!is_file("%sinstall.disklabel.%s", 95 a->tmp, 96 slice_get_device_name(storage_get_selected_slice(a->s)))) { 97 /* 98 * Get a copy of the 'virgin' disklabel. 99 * XXX It might make more sense for this to 100 * happen right after format_slice() instead. 101 */ 102 command_add(cmds, "%s%s -r %s >%sinstall.disklabel.%s", 103 a->os_root, cmd_name(a, "DISKLABEL64"), 104 slice_get_device_name(storage_get_selected_slice(a->s)), 105 a->tmp, 106 slice_get_device_name(storage_get_selected_slice(a->s))); 107 } 108 109 /* 110 * Weave together a new disklabel out the of the 'virgin' 111 * disklabel, and the user's subpartition choices. 112 */ 113 114 /* 115 * Take everything from the 'virgin' disklabel up until the 116 * '16 partitions' line. 117 */ 118 num_partitions = 16; 119 command_add(cmds, "%s%s '$2==\"partitions:\" || cut { cut = 1 } !cut { print $0 }' <%sinstall.disklabel.%s >%sinstall.disklabel", 120 a->os_root, cmd_name(a, "AWK"), 121 a->tmp, 122 slice_get_device_name(storage_get_selected_slice(a->s)), 123 a->tmp); 124 125 /* 126 * 16 partitions: 127 * # size offset fstype 128 * c: 16383969 0 unused # 7999.985MB 129 */ 130 131 command_add(cmds, "%s%s '%d partitions:' >>%sinstall.disklabel", 132 a->os_root, cmd_name(a, "ECHO"), num_partitions ,a->tmp); 133 command_add(cmds, "%s%s '%s' >>%sinstall.disklabel", 134 a->os_root, cmd_name(a, "ECHO"), 135 "# size offset fstype", 136 a->tmp); 137 138 #ifdef DEBUG 139 for (sp = slice_subpartition_first(storage_get_selected_slice(a->s)); 140 sp != NULL; sp = subpartition_next(sp)) { 141 command_add(cmds, "%s%s 'mountpoint: %s device: %s'", 142 a->os_root, cmd_name(a, "ECHO"), 143 subpartition_get_mountpoint(sp), 144 subpartition_get_device_name(sp)); 145 } 146 #endif 147 148 /* 149 * Write a line for each subpartition the user wants. 150 */ 151 for (sp = slice_subpartition_first(storage_get_selected_slice(a->s)); 152 sp != NULL; sp = subpartition_next(sp)) { 153 if (subpartition_is_mfsbacked(sp)) { 154 continue; 155 } 156 if (subpartition_is_swap(sp)) { 157 command_add(cmds, "%s%s ' %c:\t%s\t*\tswap' >>%sinstall.disklabel", 158 a->os_root, cmd_name(a, "ECHO"), 159 subpartition_get_letter(sp), 160 capacity_to_string(subpartition_get_capacity(sp)), 161 a->tmp); 162 } else { 163 command_add(cmds, "%s%s ' %c:\t%s\t%s\tHAMMER' >>%sinstall.disklabel", 164 a->os_root, cmd_name(a, "ECHO"), 165 subpartition_get_letter(sp), 166 capacity_to_string(subpartition_get_capacity(sp)), 167 subpartition_get_letter(sp) == 'a' ? "0" : "*", 168 a->tmp); 169 } 170 } 171 temp_file_add(a, "install.disklabel"); 172 173 /* 174 * Label the slice from the disklabel we just wove together. 175 */ 176 command_add(cmds, "%s%s -R -B -r %s %sinstall.disklabel", 177 a->os_root, cmd_name(a, "DISKLABEL64"), 178 slice_get_device_name(storage_get_selected_slice(a->s)), 179 a->tmp); 180 181 /* 182 * Create a snapshot of the disklabel we just created 183 * for debugging inspection in the log. 184 */ 185 command_add(cmds, "%s%s %s", 186 a->os_root, cmd_name(a, "DISKLABEL64"), 187 slice_get_device_name(storage_get_selected_slice(a->s))); 188 189 /* 190 * Create filesystems on the newly-created subpartitions. 191 */ 192 for (sp = slice_subpartition_first(storage_get_selected_slice(a->s)); 193 sp != NULL; sp = subpartition_next(sp)) { 194 if (subpartition_is_swap(sp) || subpartition_is_mfsbacked(sp)) 195 continue; 196 197 /* 198 * Ensure that all the needed device nodes exist. 199 */ 200 command_add_ensure_dev(a, cmds, 201 disk_get_device_name(storage_get_selected_disk(a->s))); 202 command_add_ensure_dev(a, cmds, 203 slice_get_device_name(storage_get_selected_slice(a->s))); 204 command_add_ensure_dev(a, cmds, 205 subpartition_get_device_name(sp)); 206 207 command_add(cmds, "%s%s -f -L ROOT %sdev/%s", 208 a->os_root, cmd_name(a, "NEWFS_HAMMER"), 209 a->os_root, 210 subpartition_get_device_name(sp)); 211 } 212 213 result = commands_execute(a, cmds); 214 commands_free(cmds); 215 return(result); 216 } 217 218 static long 219 default_capacity(struct storage *s, int mtpt) 220 { 221 unsigned long swap; 222 unsigned long capacity; 223 #if 0 224 if (mtpt == MTPT_HOME) 225 return(-1); 226 #endif 227 capacity = slice_get_capacity(storage_get_selected_slice(s)); 228 swap = 2 * storage_get_memsize(s); 229 if (storage_get_memsize(s) > (capacity / 2) || capacity < 4096) 230 swap = storage_get_memsize(s); 231 if (storage_get_memsize(s) > capacity) 232 swap = capacity / 2; 233 if (swap > 8192) 234 swap = 8192; 235 236 if (capacity < DISK_MIN) { 237 /* 238 * For the purposes of this installer: 239 * can't be done. Sorry. 240 */ 241 return(-1); 242 } else if (capacity < 1024) { 243 switch (mtpt) { 244 case MTPT_ROOT: return(-1); 245 case MTPT_SWAP: return(swap); 246 case MTPT_VAR: return(256); 247 case MTPT_TMP: return(256); 248 case MTPT_USR: return(3072); 249 } 250 } else { 251 switch (mtpt) { 252 case MTPT_ROOT: return(-1); 253 case MTPT_SWAP: return(swap); 254 case MTPT_VAR: return(256); 255 case MTPT_TMP: return(256); 256 case MTPT_USR: return(8192); 257 } 258 } 259 /* shouldn't ever happen */ 260 return(-1); 261 } 262 263 static int 264 check_capacity(struct i_fn_args *a) 265 { 266 struct subpartition *sp; 267 unsigned long min_capacity[7] = {128, 128,0, 8, 0, 174, 0}; 268 unsigned long total_capacity = 0; 269 int mtpt; 270 271 if (subpartition_find(storage_get_selected_slice(a->s), "/usr") == NULL) 272 min_capacity[MTPT_ROOT] += min_capacity[MTPT_USR]; 273 274 for (sp = slice_subpartition_first(storage_get_selected_slice(a->s)); 275 sp != NULL; sp = subpartition_next(sp)) { 276 if (subpartition_get_capacity(sp) == -1) 277 total_capacity++; 278 else 279 total_capacity += subpartition_get_capacity(sp); 280 for (mtpt = 0; def_mountpt[mtpt] != NULL; mtpt++) { 281 if (strcmp(subpartition_get_mountpoint(sp), def_mountpt[mtpt]) == 0 && 282 min_capacity[mtpt] > 0 && 283 subpartition_get_capacity(sp) < min_capacity[mtpt]) { 284 inform(a->c, _("WARNING: the %s subpartition should " 285 "be at least %dM in size or you will " 286 "risk running out of space during " 287 "the installation."), 288 subpartition_get_mountpoint(sp), min_capacity[mtpt]); 289 } 290 } 291 if (strcmp(subpartition_get_mountpoint(sp), "/") == 0 && 292 subpartition_get_capacity(sp) < HAMMER_MIN) { 293 inform(a->c, _("WARNING: HAMMER file systems" 294 "less than 50G are not recommended! You may" 295 "have to run 'hammer prune-everything' and 'hammer reblock'" 296 "quite often, even if using a nohistory mount.")); 297 } 298 } 299 300 if (total_capacity > slice_get_capacity(storage_get_selected_slice(a->s))) { 301 inform(a->c, _("The space allocated to all of your selected " 302 "subpartitions (%dM) exceeds the total " 303 "capacity of the selected primary partition " 304 "(%dM). Remove some subpartitions or choose " 305 "a smaller size for them and try again."), 306 total_capacity, slice_get_capacity(storage_get_selected_slice(a->s))); 307 return(0); 308 } 309 310 return(1); 311 } 312 313 static int 314 check_subpartition_selections(struct dfui_response *r, struct i_fn_args *a) 315 { 316 struct dfui_dataset *ds; 317 struct dfui_dataset *star_ds = NULL; 318 struct aura_dict *d; 319 const char *mountpoint, *capstring; 320 long capacity = 0; 321 int found_root = 0; 322 int valid = 1; 323 324 d = aura_dict_new(1, AURA_DICT_LIST); 325 326 if ((ds = dfui_response_dataset_get_first(r)) == NULL) { 327 inform(a->c, _("Please set up at least one subpartition.")); 328 valid = 0; 329 } 330 331 for (ds = dfui_response_dataset_get_first(r); valid && ds != NULL; 332 ds = dfui_dataset_get_next(ds)) { 333 #ifdef DEBUG 334 dfui_dataset_dump(ds); 335 #endif 336 mountpoint = dfui_dataset_get_value(ds, "mountpoint"); 337 capstring = dfui_dataset_get_value(ds, "capacity"); 338 339 if (aura_dict_exists(d, mountpoint, strlen(mountpoint) + 1)) { 340 inform(a->c, _("The same mount point cannot be specified " 341 "for two different subpartitions.")); 342 valid = 0; 343 } 344 345 if (strcmp(mountpoint, "/") == 0) 346 found_root = 1; 347 348 if (strcmp(capstring, "*") == 0) { 349 if (star_ds != NULL) { 350 inform(a->c, _("You cannot have more than one subpartition " 351 "with a '*' capacity (meaning 'use the remainder " 352 "of the primary partition'.)")); 353 valid = 0; 354 } else { 355 star_ds = ds; 356 } 357 } 358 359 if (!(!strcasecmp(mountpoint, "swap") || mountpoint[0] == '/')) { 360 inform(a->c, _("Mount point must be either 'swap', or it must " 361 "start with a '/'.")); 362 valid = 0; 363 } 364 365 if (strpbrk(mountpoint, " \\\"'`") != NULL) { 366 inform(a->c, _("Mount point may not contain the following " 367 "characters: blank space, backslash, or " 368 "single, double, or back quotes.")); 369 valid = 0; 370 } 371 372 if (strlen(capstring) == 0) { 373 inform(a->c, _("A capacity must be specified.")); 374 valid = 0; 375 } 376 377 if (!string_to_capacity(capstring, &capacity)) { 378 inform(a->c, _("Capacity must be either a '*' symbol to indicate " 379 "'use the rest of the primary partition', or it " 380 "must be a series of decimal digits ending with a " 381 "'M' (indicating megabytes) or a 'G' (indicating " 382 "gigabytes.)")); 383 valid = 0; 384 } 385 386 if ((strcasecmp(mountpoint, "swap") == 0) && (capacity > 8192)) { 387 inform(a->c, _("Swap capacity is limited to 8G.")); 388 valid = 0; 389 } 390 391 /* 392 * If we made it through that obstacle course, all is well. 393 */ 394 395 if (valid) 396 aura_dict_store(d, mountpoint, strlen(mountpoint) + 1, "", 1); 397 } 398 399 if (!found_root) { 400 inform(a->c, _("You must include a / (root) subpartition.")); 401 valid = 0; 402 } 403 404 if (aura_dict_size(d) > 16) { 405 inform(a->c, _("You cannot have more than 16 subpartitions " 406 "on a single primary partition. Remove some " 407 "and try again.")); 408 valid = 0; 409 } 410 411 aura_dict_free(d); 412 413 return(valid); 414 } 415 416 static void 417 save_subpartition_selections(struct dfui_response *r, struct i_fn_args *a) 418 { 419 struct dfui_dataset *ds; 420 const char *mountpoint, *capstring; 421 long capacity; 422 int valid = 1; 423 424 subpartitions_free(storage_get_selected_slice(a->s)); 425 426 for (ds = dfui_response_dataset_get_first(r); valid && ds != NULL; 427 ds = dfui_dataset_get_next(ds)) { 428 mountpoint = dfui_dataset_get_value(ds, "mountpoint"); 429 capstring = dfui_dataset_get_value(ds, "capacity"); 430 431 if (string_to_capacity(capstring, &capacity)) { 432 subpartition_new_hammer(storage_get_selected_slice(a->s), 433 mountpoint, capacity); 434 } 435 } 436 } 437 438 static void 439 populate_create_subpartitions_form(struct dfui_form *f, struct i_fn_args *a) 440 { 441 struct subpartition *sp; 442 struct dfui_dataset *ds; 443 int mtpt; 444 long capacity; 445 446 if (slice_subpartition_first(storage_get_selected_slice(a->s)) != NULL) { 447 /* 448 * The user has already given us their subpartition 449 * preferences, so use them here. 450 */ 451 for (sp = slice_subpartition_first(storage_get_selected_slice(a->s)); 452 sp != NULL; sp = subpartition_next(sp)) { 453 ds = dfui_dataset_new(); 454 dfui_dataset_celldata_add(ds, "mountpoint", 455 subpartition_get_mountpoint(sp)); 456 dfui_dataset_celldata_add(ds, "capacity", 457 capacity_to_string(subpartition_get_capacity(sp))); 458 dfui_form_dataset_add(f, ds); 459 } 460 } else { 461 /* 462 * Otherwise, populate the form with datasets representing 463 * reasonably-calculated defaults. The defaults are chosen 464 * based on the slice's total capacity and the machine's 465 * total physical memory (for swap.) 466 */ 467 for (mtpt = 0; def_mountpt[mtpt] != NULL; mtpt++) { 468 /* XXX matthias. skip them for now */ 469 if (mtpt != MTPT_ROOT && mtpt != MTPT_SWAP) 470 continue; 471 capacity = default_capacity(a->s, mtpt); 472 ds = dfui_dataset_new(); 473 dfui_dataset_celldata_add(ds, "mountpoint", 474 def_mountpt[mtpt]); 475 dfui_dataset_celldata_add(ds, "capacity", 476 capacity_to_string(capacity)); 477 dfui_form_dataset_add(f, ds); 478 } 479 } 480 } 481 482 static int 483 warn_subpartition_selections(struct i_fn_args *a) 484 { 485 int valid = 0; 486 struct aura_buffer *omit, *consequences; 487 488 omit = aura_buffer_new(2048); 489 consequences = aura_buffer_new(2048); 490 491 valid = check_capacity(a); 492 /* XXX matthias 493 * 494 * Should we add an information here, that we created /usr, /var, /home 495 * and /tmp as PFS? 496 */ 497 #if 0 498 if (subpartition_find(storage_get_selected_slice(a->s), "/var") == NULL) { 499 aura_buffer_cat(omit, "/var "); 500 aura_buffer_cat(consequences, _("/var will be a plain dir in /\n")); 501 } 502 if (subpartition_find(storage_get_selected_slice(a->s), "/usr") == NULL) { 503 aura_buffer_cat(omit, "/usr "); 504 aura_buffer_cat(consequences, _("/usr will be a plain dir in /\n")); 505 } 506 if (subpartition_find(storage_get_selected_slice(a->s), "/tmp") == NULL) { 507 aura_buffer_cat(omit, "/tmp "); 508 aura_buffer_cat(consequences, _("/tmp will be symlinked to /var/tmp\n")); 509 } 510 if (subpartition_find(storage_get_selected_slice(a->s), "/home") == NULL) { 511 aura_buffer_cat(omit, "/home "); 512 aura_buffer_cat(consequences, _("/home will be symlinked to /usr/home\n")); 513 } 514 515 if (valid && aura_buffer_len(omit) > 0) { 516 switch (dfui_be_present_dialog(a->c, _("Really omit?"), 517 _("Omit Subpartition(s)|Return to Create Subpartitions"), 518 _("You have elected to not have the following " 519 "subpartition(s):\n\n%s\n\n" 520 "The ramifications of these subpartition(s) being " 521 "missing will be:\n\n%s\n" 522 "Is this really what you want to do?"), 523 aura_buffer_buf(omit), aura_buffer_buf(consequences))) { 524 case 1: 525 valid = 1; 526 break; 527 case 2: 528 valid = 0; 529 break; 530 default: 531 abort_backend(); 532 } 533 } 534 #endif 535 aura_buffer_free(omit); 536 aura_buffer_free(consequences); 537 538 return(!valid); 539 } 540 541 static struct dfui_form * 542 make_create_subpartitions_form(struct i_fn_args *a) 543 { 544 struct dfui_form *f; 545 char msg_buf[1][1024]; 546 547 snprintf(msg_buf[0], sizeof(msg_buf[0]), 548 _("Subpartitions further divide a primary partition for " 549 "use with %s. Some reasons you may want " 550 "a set of subpartitions are:\n\n" 551 "- you want to restrict how much data can be written " 552 "to certain parts of the primary partition, to quell " 553 "denial-of-service attacks; and\n" 554 "- you want to speed up access to data on the disk." 555 ""), OPERATING_SYSTEM_NAME); 556 557 f = dfui_form_create( 558 "create_subpartitions", 559 _("Create Subpartitions"), 560 _("Set up the subpartitions (also known as just `partitions' " 561 "in BSD tradition) you want to have on this primary " 562 "partition.\n\nIMPORTANT: " 563 "You have chosen HAMMER as your file system. This means you will " 564 "not need to create separate subpartitions for /home, /usr, /var and " 565 "/tmp. The installer will create them automatically as pseudo-" 566 "filesystems (PFS) for you. In most cases you should be fine with " 567 "the default settings.\n\n" 568 "For Capacity, use 'M' to indicate megabytes, 'G' to " 569 "indicate gigabytes, or a single '*' to indicate " 570 "'use the remaining space on the primary partition'."), 571 572 msg_buf[0], 573 574 "p", "special", "dfinstaller_create_subpartitions", 575 "p", "minimum_width","64", 576 577 "f", "mountpoint", _("Mountpoint"), "", "", 578 "f", "capacity", _("Capacity"), "", "", 579 580 "a", "ok", _("Accept and Create"), "", "", 581 "a", "cancel", 582 (disk_get_formatted(storage_get_selected_disk(a->s)) ? 583 _("Return to Select Disk") : 584 _("Return to Select Primary Partition")), "", "", 585 "p", "accelerator", "ESC", 586 587 NULL 588 ); 589 590 dfui_form_set_multiple(f, 1); 591 dfui_form_set_extensible(f, 1); 592 /* 593 * Remove ATM until HAMMER installer support is better 594 * dfui_form_set_extensible(f, 1); 595 */ 596 #if 0 597 if (expert) { 598 fi = dfui_form_field_add(f, "softupdates", 599 dfui_info_new(_("Softupdates"), "", "")); 600 dfui_field_property_set(fi, "control", "checkbox"); 601 602 fi = dfui_form_field_add(f, "mfsbacked", 603 dfui_info_new(_("MFS"), "", "")); 604 dfui_field_property_set(fi, "control", "checkbox"); 605 606 fi = dfui_form_field_add(f, "fsize", 607 dfui_info_new(_("Frag Sz"), "", "")); 608 609 fi = dfui_form_field_add(f, "bsize", 610 dfui_info_new(_("Block Sz"), "", "")); 611 612 dfui_form_action_add(f, "switch", 613 dfui_info_new(_("Switch to Normal Mode"), "", "")); 614 } else { 615 dfui_form_action_add(f, "switch", 616 dfui_info_new(_("Switch to Expert Mode"), "", "")); 617 } 618 #endif 619 return(f); 620 } 621 622 /* 623 * Returns: 624 * -1 = the form should be redisplayed 625 * 0 = failure, function is over 626 * 1 = success, function is over 627 */ 628 static int 629 show_create_subpartitions_form(struct dfui_form *f, struct i_fn_args *a) 630 { 631 struct dfui_dataset *ds; 632 struct dfui_response *r; 633 634 for (;;) { 635 if (dfui_form_dataset_get_first(f) == NULL) 636 populate_create_subpartitions_form(f, a); 637 638 if (!dfui_be_present(a->c, f, &r)) 639 abort_backend(); 640 641 if (strcmp(dfui_response_get_action_id(r), "cancel") == 0) { 642 dfui_response_free(r); 643 return(0); 644 } else if (strcmp(dfui_response_get_action_id(r), "switch") == 0) { 645 if (check_subpartition_selections(r, a)) { 646 save_subpartition_selections(r, a); 647 expert = expert ? 0 : 1; 648 dfui_response_free(r); 649 return(-1); 650 } 651 } else { 652 if (check_subpartition_selections(r, a)) { 653 save_subpartition_selections(r, a); 654 if (!warn_subpartition_selections(a)) { 655 if (!create_subpartitions(a)) { 656 inform(a->c, _("The subpartitions you chose were " 657 "not correctly created, and the " 658 "primary partition may " 659 "now be in an inconsistent state. " 660 "We recommend re-formatting it " 661 "before proceeding.")); 662 dfui_response_free(r); 663 return(0); 664 } else { 665 dfui_response_free(r); 666 return(1); 667 } 668 } 669 } 670 } 671 672 dfui_form_datasets_free(f); 673 /* dfui_form_datasets_add_from_response(f, r); */ 674 for (ds = dfui_response_dataset_get_first(r); ds != NULL; 675 ds = dfui_dataset_get_next(ds)) { 676 dfui_form_dataset_add(f, dfui_dataset_dup(ds)); 677 } 678 } 679 } 680 681 /* 682 * fn_create_subpartitions_hammer: let the user specify what subpartitions they 683 * want on the disk, how large each should be, and where it should be mounted. 684 */ 685 void 686 fn_create_subpartitions_hammer(struct i_fn_args *a) 687 { 688 struct dfui_form *f; 689 int done = 0; 690 691 a->result = 0; 692 while (!done) { 693 f = make_create_subpartitions_form(a); 694 switch (show_create_subpartitions_form(f, a)) { 695 case -1: 696 done = 0; 697 break; 698 case 0: 699 done = 1; 700 a->result = 0; 701 break; 702 case 1: 703 done = 1; 704 a->result = 1; 705 break; 706 } 707 dfui_form_free(f); 708 } 709 } 710