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