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