1 /* 2 * Copyright (c)2004,2015 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.c 36 * Installer Function : Create Subpartitions. 37 * $Id: fn_subpart.c,v 1.50 2005/04/07 20:22:40 cpressey Exp $ 38 */ 39 40 #include <stdio.h> 41 #include <stdlib.h> 42 #include <string.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 #define MTPT_BOOT 0 70 #define MTPT_SWAP 1 71 #define MTPT_ROOT 2 72 #define MTPT_BUILD 3 73 74 static int create_subpartitions(struct i_fn_args *); 75 static long default_capacity(struct storage *, const char *); 76 static int check_capacity(struct i_fn_args *); 77 static int check_subpartition_selections(struct dfui_response *, struct i_fn_args *); 78 static void save_subpartition_selections(struct dfui_response *, struct i_fn_args *); 79 static void populate_create_subpartitions_form(struct dfui_form *, struct i_fn_args *); 80 static int warn_subpartition_selections(struct i_fn_args *); 81 static struct dfui_form *make_create_subpartitions_form(struct i_fn_args *); 82 static int show_create_subpartitions_form(struct dfui_form *, struct i_fn_args *); 83 84 static const char *def_mountpt[] = {"/boot", "swap", "/", "/build", NULL}; 85 static long min_capacity[] = { 128, 0, DISK_MIN - 128, BUILD_MIN }; 86 static int expert = 0; 87 88 /* 89 * Given a set of subpartitions-to-be in the selected slice, 90 * create them. 91 */ 92 static int 93 create_subpartitions(struct i_fn_args *a) 94 { 95 struct subpartition *sp; 96 struct commands *cmds; 97 int result = 0; 98 int num_partitions; 99 100 cmds = commands_new(); 101 if (!is_file("%sinstall.disklabel.%s", 102 a->tmp, 103 slice_get_device_name(storage_get_selected_slice(a->s)))) { 104 /* 105 * Get a copy of the 'virgin' disklabel. 106 * XXX It might make more sense for this to 107 * happen right after format_slice() instead. 108 */ 109 command_add(cmds, "%s%s -r %s >%sinstall.disklabel.%s", 110 a->os_root, cmd_name(a, "DISKLABEL64"), 111 slice_get_device_name(storage_get_selected_slice(a->s)), 112 a->tmp, 113 slice_get_device_name(storage_get_selected_slice(a->s))); 114 } 115 116 /* 117 * Weave together a new disklabel out the of the 'virgin' 118 * disklabel, and the user's subpartition choices. 119 */ 120 121 /* 122 * Take everything from the 'virgin' disklabel up until the 123 * '16 partitions' line. 124 */ 125 num_partitions = 16; 126 command_add(cmds, "%s%s '$2==\"partitions:\" || cut { cut = 1 } !cut { print $0 }' <%sinstall.disklabel.%s >%sinstall.disklabel", 127 a->os_root, cmd_name(a, "AWK"), 128 a->tmp, 129 slice_get_device_name(storage_get_selected_slice(a->s)), 130 a->tmp); 131 132 /* 133 * 16 partitions: 134 * # size offset fstype 135 * c: 16383969 0 unused # 7999.985MB 136 */ 137 138 command_add(cmds, "%s%s '%d partitions:' >>%sinstall.disklabel", 139 a->os_root, cmd_name(a, "ECHO"), num_partitions ,a->tmp); 140 command_add(cmds, "%s%s '%s' >>%sinstall.disklabel", 141 a->os_root, cmd_name(a, "ECHO"), 142 "# size offset fstype", 143 a->tmp); 144 145 #ifdef DEBUG 146 for (sp = slice_subpartition_first(storage_get_selected_slice(a->s)); 147 sp != NULL; sp = subpartition_next(sp)) { 148 command_add(cmds, "%s%s 'mountpoint: %s device: %s'", 149 a->os_root, cmd_name(a, "ECHO"), 150 subpartition_get_mountpoint(sp), 151 subpartition_get_device_name(sp)); 152 } 153 #endif 154 155 /* 156 * Write a line for each subpartition the user wants. 157 */ 158 for (sp = slice_subpartition_first(storage_get_selected_slice(a->s)); 159 sp != NULL; sp = subpartition_next(sp)) { 160 if (subpartition_is_tmpfsbacked(sp)) { 161 continue; 162 } 163 if (subpartition_is_swap(sp)) { 164 command_add(cmds, "%s%s ' %c:\t%s\t*\tswap' >>%sinstall.disklabel", 165 a->os_root, cmd_name(a, "ECHO"), 166 subpartition_get_letter(sp), 167 capacity_to_string(subpartition_get_capacity(sp)), 168 a->tmp); 169 } else { 170 command_add(cmds, "%s%s ' %c:\t%s\t%s\t4.2BSD' >>%sinstall.disklabel", 171 a->os_root, cmd_name(a, "ECHO"), 172 subpartition_get_letter(sp), 173 capacity_to_string(subpartition_get_capacity(sp)), 174 subpartition_get_letter(sp) == 'a' ? "0" : "*", 175 a->tmp); 176 } 177 } 178 temp_file_add(a, "install.disklabel"); 179 180 /* 181 * Label the slice from the disklabel we just wove together. 182 */ 183 command_add(cmds, "%s%s -R -B -r %s %sinstall.disklabel", 184 a->os_root, cmd_name(a, "DISKLABEL64"), 185 slice_get_device_name(storage_get_selected_slice(a->s)), 186 a->tmp); 187 188 /* 189 * Create a snapshot of the disklabel we just created 190 * for debugging inspection in the log. 191 */ 192 command_add(cmds, "%s%s %s", 193 a->os_root, cmd_name(a, "DISKLABEL64"), 194 slice_get_device_name(storage_get_selected_slice(a->s))); 195 196 /* 197 * If encryption was specified, load dm(4). 198 */ 199 for (sp = slice_subpartition_first(storage_get_selected_slice(a->s)); 200 sp != NULL; sp = subpartition_next(sp)) { 201 if (subpartition_is_encrypted(sp)) { 202 fn_get_passphrase(a); 203 break; 204 } 205 } 206 207 /* 208 * Create filesystems on the newly-created subpartitions. 209 */ 210 for (sp = slice_subpartition_first(storage_get_selected_slice(a->s)); 211 sp != NULL; sp = subpartition_next(sp)) { 212 if (subpartition_is_swap(sp) || subpartition_is_tmpfsbacked(sp)) { 213 if (subpartition_is_swap(sp) && 214 subpartition_is_encrypted(sp)) { 215 command_add(cmds, 216 "%s%s -d /tmp/t1 luksFormat /dev/%s", 217 a->os_root, cmd_name(a, "CRYPTSETUP"), 218 subpartition_get_device_name(sp)); 219 command_add(cmds, 220 "%s%s -d /tmp/t1 luksOpen /dev/%s swap", 221 a->os_root, cmd_name(a, "CRYPTSETUP"), 222 subpartition_get_device_name(sp)); 223 } 224 continue; 225 } 226 227 if (subpartition_is_encrypted(sp)) { 228 command_add(cmds, 229 "%s%s -d /tmp/t1 luksFormat /dev/%s", 230 a->os_root, cmd_name(a, "CRYPTSETUP"), 231 subpartition_get_device_name(sp)); 232 command_add(cmds, 233 "%s%s -d /tmp/t1 luksOpen /dev/%s %s", 234 a->os_root, cmd_name(a, "CRYPTSETUP"), 235 subpartition_get_device_name(sp), 236 subpartition_get_mapper_name(sp, -1)); 237 command_add(cmds, "%s%s%s -b %ld -f %ld /dev/mapper/%s", 238 a->os_root, cmd_name(a, "NEWFS"), 239 subpartition_is_softupdated(sp) ? " -U" : "", 240 subpartition_get_bsize(sp), 241 subpartition_get_fsize(sp), 242 subpartition_get_mapper_name(sp, -1)); 243 } else { 244 command_add(cmds, "%s%s%s -b %ld -f %ld /dev/%s", 245 a->os_root, cmd_name(a, "NEWFS"), 246 subpartition_is_softupdated(sp) ? " -U" : "", 247 subpartition_get_bsize(sp), 248 subpartition_get_fsize(sp), 249 subpartition_get_device_name(sp)); 250 } 251 } 252 253 result = commands_execute(a, cmds); 254 commands_free(cmds); 255 return(result); 256 } 257 258 /* 259 * Return default capacity field filler. Return 0 for /build if drive 260 * space minus swap is < 40GB (causes installer to use PFS's on the root 261 * partition instead). 262 */ 263 static long 264 default_capacity(struct storage *s, const char *mtpt) 265 { 266 unsigned long boot, root, swap, build; 267 unsigned long capacity; 268 unsigned long mem; 269 270 capacity = slice_get_capacity(storage_get_selected_slice(s)); /* MB */ 271 mem = storage_get_memsize(s); 272 273 /* 274 * Slice capacity is at least 10G at this point. Calculate basic 275 * defaults. 276 */ 277 swap = 2 * mem; 278 if (swap > capacity / 10) /* max 1/10 capacity */ 279 swap = capacity / 10; 280 if (swap < SWAP_MIN) /* having a little is nice */ 281 swap = SWAP_MIN; 282 if (swap > SWAP_MAX) /* installer cap */ 283 swap = SWAP_MAX; 284 285 boot = 1024; 286 287 build = (capacity - swap - boot) / 3; 288 if (build > BUILD_MAX) 289 build = BUILD_MAX; 290 291 for (;;) { 292 root = (capacity - swap - boot - build); 293 294 /* 295 * Adjust until the defaults look sane 296 * 297 * root should be at least twice as large as build 298 */ 299 if (build && root < build * 2) { 300 --build; 301 continue; 302 } 303 304 /* 305 * root should be at least 1/2 capacity 306 */ 307 if (build && root < capacity / 2) { 308 --build; 309 continue; 310 } 311 break; 312 } 313 314 /* 315 * Finalize. If build is too small do not supply a /build, 316 * and if swap is too small do not supply swap. 317 */ 318 if (build < BUILD_MIN) 319 build = 0; 320 if (swap < SWAP_MIN) 321 swap = 0; 322 if (build == 0) 323 root = -1; /* no /build, root is the last part */ 324 else 325 build = -1; /* last partition just use remaining space */ 326 327 if (strcmp(mtpt, "/boot") == 0) 328 return(boot); 329 else if (strcmp(mtpt, "/build") == 0) 330 return(build); 331 else if (strcmp(mtpt, "swap") == 0) 332 return(swap); 333 else if (strcmp(mtpt, "/") == 0) 334 return(root); 335 336 /* shouldn't ever happen */ 337 return(-1); 338 } 339 340 static int 341 check_capacity(struct i_fn_args *a) 342 { 343 struct subpartition *sp; 344 unsigned long total_capacity = 0; 345 int mtpt; 346 347 for (sp = slice_subpartition_first(storage_get_selected_slice(a->s)); 348 sp != NULL; sp = subpartition_next(sp)) { 349 long subpart_capacity = subpartition_get_capacity(sp); 350 const char *mountpt = subpartition_get_mountpoint(sp); 351 352 if (subpart_capacity == -1) 353 total_capacity++; 354 else 355 total_capacity += subpart_capacity; 356 for (mtpt = 0; def_mountpt[mtpt] != NULL; mtpt++) { 357 if (strcmp(mountpt, def_mountpt[mtpt]) == 0 && 358 subpart_capacity < min_capacity[mtpt] && 359 subpart_capacity != -1) { 360 inform(a->c, _("WARNING: The size (%ldM) specified for " 361 "the %s subpartition is too small. It " 362 "should be at least %ldM or you will " 363 "risk running out of space during " 364 "the installation."), 365 subpart_capacity, mountpt, 366 min_capacity[mtpt]); 367 } 368 } 369 } 370 371 if (total_capacity > slice_get_capacity(storage_get_selected_slice(a->s))) { 372 inform(a->c, _("The space allocated to all of your selected " 373 "subpartitions (%luM) exceeds the total " 374 "capacity of the selected primary partition " 375 "(%luM). Remove some subpartitions or choose " 376 "a smaller size for them and try again."), 377 total_capacity, slice_get_capacity(storage_get_selected_slice(a->s))); 378 return(0); 379 } 380 381 return(1); 382 } 383 384 static int 385 check_subpartition_selections(struct dfui_response *r, struct i_fn_args *a) 386 { 387 struct dfui_dataset *ds; 388 struct dfui_dataset *star_ds = NULL; 389 struct aura_dict *d; 390 const char *mountpoint, *capstring; 391 long capacity = 0; 392 int found_root = 0; 393 int valid = 1; 394 395 d = aura_dict_new(1, AURA_DICT_LIST); 396 397 if ((ds = dfui_response_dataset_get_first(r)) == NULL) { 398 inform(a->c, _("Please set up at least one subpartition.")); 399 valid = 0; 400 } 401 402 for (ds = dfui_response_dataset_get_first(r); valid && ds != NULL; 403 ds = dfui_dataset_get_next(ds)) { 404 #ifdef DEBUG 405 dfui_dataset_dump(ds); 406 #endif 407 mountpoint = dfui_dataset_get_value(ds, "mountpoint"); 408 capstring = dfui_dataset_get_value(ds, "capacity"); 409 410 if (expert) { 411 int tmpfsbacked; 412 413 tmpfsbacked = (strcmp(dfui_dataset_get_value(ds, "tmpfsbacked"), "Y") == 0); 414 if (tmpfsbacked && strcmp(mountpoint, "/") == 0) { 415 inform(a->c, _("/ cannot be TMPFS backed.")); 416 valid = 0; 417 } 418 } 419 420 if (aura_dict_exists(d, mountpoint, strlen(mountpoint) + 1)) { 421 inform(a->c, _("The same mount point cannot be specified " 422 "for two different subpartitions.")); 423 valid = 0; 424 } 425 426 if (strcmp(mountpoint, "/") == 0) 427 found_root = 1; 428 429 if (strcmp(capstring, "*") == 0) { 430 if (star_ds != NULL) { 431 inform(a->c, _("You cannot have more than one subpartition " 432 "with a '*' capacity (meaning 'use the remainder " 433 "of the primary partition'.)")); 434 valid = 0; 435 } else { 436 star_ds = ds; 437 } 438 } 439 440 if (!(!strcasecmp(mountpoint, "swap") || mountpoint[0] == '/')) { 441 inform(a->c, _("Mount point must be either 'swap', or it must " 442 "start with a '/'.")); 443 valid = 0; 444 } 445 446 if (strpbrk(mountpoint, " \\\"'`") != NULL) { 447 inform(a->c, _("Mount point may not contain the following " 448 "characters: blank space, backslash, or " 449 "single, double, or back quotes.")); 450 valid = 0; 451 } 452 453 if (strlen(capstring) == 0) { 454 inform(a->c, _("A capacity must be specified.")); 455 valid = 0; 456 } 457 458 if (!string_to_capacity(capstring, &capacity)) { 459 inform(a->c, _("Capacity must be either a '*' symbol " 460 "to indicate 'use the rest of the primary " 461 "partition', or it must be a series of decimal " 462 "digits ending with an 'M' (indicating " 463 "megabytes), a 'G' (indicating gigabytes) and " 464 "so on (up to 'E'.)")); 465 valid = 0; 466 } 467 468 /* 469 * Maybe remove this limit entirely? 470 */ 471 if ((strcasecmp(mountpoint, "swap") == 0) && 472 (capacity > SWAP_MAX)) { 473 inform(a->c, _("Swap capacity is limited to %dG."), 474 SWAP_MAX / 1024); 475 valid = 0; 476 } 477 478 /* 479 * If we made it through that obstacle course, all is well. 480 */ 481 482 if (valid) 483 aura_dict_store(d, mountpoint, strlen(mountpoint) + 1, "", 1); 484 } 485 486 if (!found_root) { 487 inform(a->c, _("You must include a / (root) subpartition.")); 488 valid = 0; 489 } 490 491 if (aura_dict_size(d) > 16) { 492 inform(a->c, _("You cannot have more than 16 subpartitions " 493 "on a single primary partition. Remove some " 494 "and try again.")); 495 valid = 0; 496 } 497 498 aura_dict_free(d); 499 500 return(valid); 501 } 502 503 static void 504 save_subpartition_selections(struct dfui_response *r, struct i_fn_args *a) 505 { 506 struct dfui_dataset *ds; 507 char tmpfsbacked; 508 const char *mountpoint, *capstring; 509 long capacity; 510 long bsize, fsize; 511 int softupdates; 512 int valid = 1; 513 514 subpartitions_free(storage_get_selected_slice(a->s)); 515 516 for (ds = dfui_response_dataset_get_first(r); valid && ds != NULL; 517 ds = dfui_dataset_get_next(ds)) { 518 mountpoint = dfui_dataset_get_value(ds, "mountpoint"); 519 capstring = dfui_dataset_get_value(ds, "capacity"); 520 521 if (expert) { 522 softupdates = 523 (strcmp(dfui_dataset_get_value(ds, "softupdates"), "Y") == 0); 524 fsize = atol(dfui_dataset_get_value(ds, "fsize")); 525 bsize = atol(dfui_dataset_get_value(ds, "bsize")); 526 tmpfsbacked = (strcmp(dfui_dataset_get_value(ds, "tmpfsbacked"), "Y") == 0); 527 } else { 528 softupdates = (strcmp(mountpoint, "/boot") == 0 ? 0 : 1); 529 tmpfsbacked = 0; 530 fsize = -1; 531 bsize = -1; 532 } 533 534 if (string_to_capacity(capstring, &capacity)) { 535 subpartition_new_ufs(storage_get_selected_slice(a->s), 536 mountpoint, capacity, 537 strcasecmp(dfui_dataset_get_value(ds, "encrypted"), "Y") == 0, 538 softupdates, fsize, bsize, tmpfsbacked); 539 } 540 } 541 } 542 543 static void 544 populate_create_subpartitions_form(struct dfui_form *f, struct i_fn_args *a) 545 { 546 struct subpartition *sp; 547 struct dfui_dataset *ds; 548 char temp[32]; 549 int mtpt; 550 long capacity; 551 552 if (slice_subpartition_first(storage_get_selected_slice(a->s)) != NULL) { 553 /* 554 * The user has already given us their subpartition 555 * preferences, so use them here. 556 */ 557 for (sp = slice_subpartition_first(storage_get_selected_slice(a->s)); 558 sp != NULL; sp = subpartition_next(sp)) { 559 ds = dfui_dataset_new(); 560 dfui_dataset_celldata_add(ds, "mountpoint", 561 subpartition_get_mountpoint(sp)); 562 dfui_dataset_celldata_add(ds, "capacity", 563 capacity_to_string(subpartition_get_capacity(sp))); 564 dfui_dataset_celldata_add(ds, "encrypted", 565 subpartition_is_encrypted(sp) ? "Y" : "N"); 566 if (expert) { 567 dfui_dataset_celldata_add(ds, "softupdates", 568 subpartition_is_softupdated(sp) ? "Y" : "N"); 569 dfui_dataset_celldata_add(ds, "tmpfsbacked", 570 subpartition_is_tmpfsbacked(sp) ? "Y" : "N"); 571 snprintf(temp, 32, "%ld", subpartition_get_fsize(sp)); 572 dfui_dataset_celldata_add(ds, "fsize", 573 temp); 574 snprintf(temp, 32, "%ld", subpartition_get_bsize(sp)); 575 dfui_dataset_celldata_add(ds, "bsize", 576 temp); 577 } 578 dfui_form_dataset_add(f, ds); 579 } 580 } else { 581 /* 582 * Otherwise, populate the form with datasets representing 583 * reasonably-calculated defaults. The defaults are chosen 584 * based on the slice's total capacity and the machine's 585 * total physical memory (for swap.) 586 */ 587 for (mtpt = 0; def_mountpt[mtpt] != NULL; mtpt++) { 588 capacity = default_capacity(a->s, def_mountpt[mtpt]); 589 if (capacity == 0) 590 continue; 591 ds = dfui_dataset_new(); 592 dfui_dataset_celldata_add(ds, "mountpoint", 593 def_mountpt[mtpt]); 594 dfui_dataset_celldata_add(ds, "capacity", 595 capacity_to_string(capacity)); 596 dfui_dataset_celldata_add(ds, "encrypted", "N"); 597 if (expert) { 598 dfui_dataset_celldata_add(ds, "softupdates", 599 strcmp(def_mountpt[mtpt], "/boot") != 0 ? "Y" : "N"); 600 dfui_dataset_celldata_add(ds, "tmpfsbacked", 601 "N"); 602 dfui_dataset_celldata_add(ds, "fsize", 603 capacity < 1024 ? "1024" : "2048"); 604 dfui_dataset_celldata_add(ds, "bsize", 605 capacity < 1024 ? "8192" : "16384"); 606 } 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 /* Skip this check for disks <= 8GB */ 618 if (slice_get_capacity(storage_get_selected_slice(a->s)) <= 8192) 619 return 0; 620 621 valid = check_capacity(a); 622 623 return(!valid); 624 } 625 626 static struct dfui_form * 627 make_create_subpartitions_form(struct i_fn_args *a) 628 { 629 struct dfui_field *fi; 630 struct dfui_form *f; 631 char msg_buf[1][1024]; 632 633 snprintf(msg_buf[0], sizeof(msg_buf[0]), 634 _("Subpartitions further divide a primary partition for " 635 "use with %s. Some reasons you may want " 636 "a set of subpartitions are:\n\n" 637 "- you want to restrict how much data can be written " 638 "to certain parts of the primary partition, to quell " 639 "denial-of-service attacks; and\n" 640 "- you want to speed up access to data on the disk." 641 ""), OPERATING_SYSTEM_NAME); 642 643 f = dfui_form_create( 644 "create_subpartitions", 645 _("Create Subpartitions"), 646 _("Set up the subpartitions (also known as just `partitions' " 647 "in BSD tradition) you want to have on this primary " 648 "partition.\n\n" 649 "For Capacity, use 'M' to indicate megabytes, 'G' to " 650 "indicate gigabytes, and so on (up to 'E'.) A single '*' " 651 "indicates 'use the remaining space on the primary partition'."), 652 653 msg_buf[0], 654 655 "p", "special", "dfinstaller_create_subpartitions", 656 "p", "minimum_width","64", 657 658 "f", "mountpoint", _("Mountpoint"), "", "", 659 "f", "capacity", _("Capacity"), "", "", 660 661 "f", "encrypted", _("Encrypted"), "", "", 662 "p", "control", "checkbox", 663 664 "a", "ok", _("Accept and Create"), "", "", 665 "a", "cancel", 666 (disk_get_formatted(storage_get_selected_disk(a->s)) ? 667 _("Return to Select Disk") : 668 _("Return to Select Primary Partition")), "", "", 669 "p", "accelerator", "ESC", 670 671 NULL 672 ); 673 674 dfui_form_set_multiple(f, 1); 675 dfui_form_set_extensible(f, 1); 676 677 if (expert) { 678 fi = dfui_form_field_add(f, "softupdates", 679 dfui_info_new(_("Softupdates"), "", "")); 680 dfui_field_property_set(fi, "control", "checkbox"); 681 682 fi = dfui_form_field_add(f, "tmpfsbacked", 683 dfui_info_new(_("TMPFS"), "", "")); 684 dfui_field_property_set(fi, "control", "checkbox"); 685 686 fi = dfui_form_field_add(f, "fsize", 687 dfui_info_new(_("Frag Sz"), "", "")); 688 689 fi = dfui_form_field_add(f, "bsize", 690 dfui_info_new(_("Block Sz"), "", "")); 691 692 dfui_form_action_add(f, "switch", 693 dfui_info_new(_("Switch to Normal Mode"), "", "")); 694 } else { 695 dfui_form_action_add(f, "switch", 696 dfui_info_new(_("Switch to Expert Mode"), "", "")); 697 } 698 699 return(f); 700 } 701 702 /* 703 * Returns: 704 * -1 = the form should be redisplayed 705 * 0 = failure, function is over 706 * 1 = success, function is over 707 */ 708 static int 709 show_create_subpartitions_form(struct dfui_form *f, struct i_fn_args *a) 710 { 711 struct dfui_dataset *ds; 712 struct dfui_response *r; 713 714 for (;;) { 715 if (dfui_form_dataset_get_first(f) == NULL) 716 populate_create_subpartitions_form(f, a); 717 718 if (!dfui_be_present(a->c, f, &r)) 719 abort_backend(); 720 721 if (strcmp(dfui_response_get_action_id(r), "cancel") == 0) { 722 dfui_response_free(r); 723 return(0); 724 } else if (strcmp(dfui_response_get_action_id(r), "switch") == 0) { 725 if (check_subpartition_selections(r, a)) { 726 save_subpartition_selections(r, a); 727 expert = expert ? 0 : 1; 728 dfui_response_free(r); 729 return(-1); 730 } 731 } else { 732 if (check_subpartition_selections(r, a)) { 733 save_subpartition_selections(r, a); 734 if (!warn_subpartition_selections(a)) { 735 if (!create_subpartitions(a)) { 736 inform(a->c, _("The subpartitions you chose were " 737 "not correctly created, and the " 738 "primary partition may " 739 "now be in an inconsistent state. " 740 "We recommend re-formatting it " 741 "before proceeding.")); 742 dfui_response_free(r); 743 return(0); 744 } else { 745 dfui_response_free(r); 746 return(1); 747 } 748 } 749 } 750 } 751 752 dfui_form_datasets_free(f); 753 /* dfui_form_datasets_add_from_response(f, r); */ 754 for (ds = dfui_response_dataset_get_first(r); ds != NULL; 755 ds = dfui_dataset_get_next(ds)) { 756 dfui_form_dataset_add(f, dfui_dataset_dup(ds)); 757 } 758 } 759 } 760 761 void 762 fn_create_subpartitions_ufs(struct i_fn_args *a) 763 { 764 struct dfui_form *f; 765 unsigned long capacity; 766 int done = 0; 767 768 a->result = 0; 769 capacity = slice_get_capacity(storage_get_selected_slice(a->s)); 770 if (capacity < DISK_MIN) { 771 inform(a->c, _("The selected disk is smaller than the " 772 "required %dM for the UFS filesystem."), DISK_MIN); 773 return; 774 } 775 #if 0 776 if (capacity <= 8192) 777 def_mountpt[2] = NULL; /* XXX adjust each time in a session */ 778 #endif 779 while (!done) { 780 f = make_create_subpartitions_form(a); 781 switch (show_create_subpartitions_form(f, a)) { 782 case -1: 783 done = 0; 784 break; 785 case 0: 786 done = 1; 787 a->result = 0; 788 break; 789 case 1: 790 done = 1; 791 a->result = 1; 792 break; 793 } 794 dfui_form_free(f); 795 } 796 } 797