1 /* 2 * Copyright (c)2004 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 static int create_subpartitions(struct i_fn_args *); 70 static long default_capacity(struct storage *, int); 71 static int check_capacity(struct i_fn_args *); 72 static int check_subpartition_selections(struct dfui_response *, struct i_fn_args *); 73 static void save_subpartition_selections(struct dfui_response *, struct i_fn_args *); 74 static void populate_create_subpartitions_form(struct dfui_form *, struct i_fn_args *); 75 static int warn_subpartition_selections(struct i_fn_args *); 76 static struct dfui_form *make_create_subpartitions_form(struct i_fn_args *); 77 static int show_create_subpartitions_form(struct dfui_form *, struct i_fn_args *); 78 79 static const char *def_mountpt[] = {"/", "swap", "/var", "/tmp", "/usr", "/home", NULL}; 80 static int expert = 0; 81 82 /* 83 * Given a set of subpartitions-to-be in the selected slice, 84 * create them. 85 */ 86 static int 87 create_subpartitions(struct i_fn_args *a) 88 { 89 struct subpartition *sp; 90 struct commands *cmds; 91 int result = 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\t4.2BSD' >>%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%s -b %ld -f %ld %sdev/%s", 209 a->os_root, cmd_name(a, "NEWFS"), 210 subpartition_is_softupdated(sp) ? " -U" : "", 211 subpartition_get_bsize(sp), 212 subpartition_get_fsize(sp), 213 a->os_root, 214 subpartition_get_device_name(sp)); 215 } 216 217 result = commands_execute(a, cmds); 218 commands_free(cmds); 219 return(result); 220 } 221 222 static long 223 default_capacity(struct storage *s, int mtpt) 224 { 225 unsigned long swap; 226 unsigned long capacity; 227 unsigned long mem; 228 229 if (mtpt == MTPT_HOME) 230 return(-1); 231 232 capacity = slice_get_capacity(storage_get_selected_slice(s)); 233 mem = storage_get_memsize(s); 234 swap = 2 * mem; 235 if (mem > (capacity / 2) || capacity < 4096) 236 swap = mem; 237 if (mem > capacity) 238 swap = capacity / 2; 239 if (swap > 8192) 240 swap = 8192; 241 242 if (capacity < DISK_MIN) { 243 /* 244 * For the purposes of this installer: 245 * can't be done. Sorry. 246 */ 247 return(-1); 248 } else if (capacity < 4096) { 249 switch (mtpt) { 250 case MTPT_ROOT: return(256); 251 case MTPT_SWAP: return(swap); 252 case MTPT_VAR: return(128); 253 case MTPT_TMP: return(128); 254 case MTPT_USR: return(1536); 255 } 256 } else if (capacity < 10240) { 257 switch (mtpt) { 258 case MTPT_ROOT: return(256); 259 case MTPT_SWAP: return(swap); 260 case MTPT_VAR: return(256); 261 case MTPT_TMP: return(256); 262 case MTPT_USR: return(3072); 263 } 264 } else { 265 switch (mtpt) { 266 case MTPT_ROOT: return(256); 267 case MTPT_SWAP: return(swap); 268 case MTPT_VAR: return(256); 269 case MTPT_TMP: return(256); 270 case MTPT_USR: return(8192); 271 } 272 } 273 /* shouldn't ever happen */ 274 return(-1); 275 } 276 277 static int 278 check_capacity(struct i_fn_args *a) 279 { 280 struct subpartition *sp; 281 unsigned long min_capacity[] = {256, 0, 16, 0, 1536, 0, 0}; 282 unsigned long total_capacity = 0; 283 int mtpt; 284 285 if (subpartition_find(storage_get_selected_slice(a->s), "/usr") == NULL) 286 min_capacity[MTPT_ROOT] += min_capacity[MTPT_USR]; 287 288 for (sp = slice_subpartition_first(storage_get_selected_slice(a->s)); 289 sp != NULL; sp = subpartition_next(sp)) { 290 if (subpartition_get_capacity(sp) == -1) 291 total_capacity++; 292 else 293 total_capacity += subpartition_get_capacity(sp); 294 for (mtpt = 0; def_mountpt[mtpt] != NULL; mtpt++) { 295 if (strcmp(subpartition_get_mountpoint(sp), def_mountpt[mtpt]) == 0 && 296 min_capacity[mtpt] > 0 && 297 subpartition_get_capacity(sp) < min_capacity[mtpt]) { 298 inform(a->c, _("WARNING: the %s subpartition should " 299 "be at least %dM in size or you will " 300 "risk running out of space during " 301 "the installation."), 302 subpartition_get_mountpoint(sp), min_capacity[mtpt]); 303 } 304 } 305 } 306 307 if (total_capacity > slice_get_capacity(storage_get_selected_slice(a->s))) { 308 inform(a->c, _("The space allocated to all of your selected " 309 "subpartitions (%dM) exceeds the total " 310 "capacity of the selected primary partition " 311 "(%dM). Remove some subpartitions or choose " 312 "a smaller size for them and try again."), 313 total_capacity, slice_get_capacity(storage_get_selected_slice(a->s))); 314 return(0); 315 } 316 317 return(1); 318 } 319 320 static int 321 check_subpartition_selections(struct dfui_response *r, struct i_fn_args *a) 322 { 323 struct dfui_dataset *ds; 324 struct dfui_dataset *star_ds = NULL; 325 struct aura_dict *d; 326 const char *mountpoint, *capstring; 327 long capacity = 0; 328 long bsize, fsize; 329 int found_root = 0; 330 int softupdates, mfsbacked; 331 int valid = 1; 332 333 d = aura_dict_new(1, AURA_DICT_LIST); 334 335 if ((ds = dfui_response_dataset_get_first(r)) == NULL) { 336 inform(a->c, _("Please set up at least one subpartition.")); 337 valid = 0; 338 } 339 340 for (ds = dfui_response_dataset_get_first(r); valid && ds != NULL; 341 ds = dfui_dataset_get_next(ds)) { 342 #ifdef DEBUG 343 dfui_dataset_dump(ds); 344 #endif 345 mountpoint = dfui_dataset_get_value(ds, "mountpoint"); 346 capstring = dfui_dataset_get_value(ds, "capacity"); 347 348 if (expert) { 349 softupdates = 350 (strcmp(dfui_dataset_get_value(ds, "softupdates"), "Y") == 0); 351 fsize = atol(dfui_dataset_get_value(ds, "fsize")); 352 bsize = atol(dfui_dataset_get_value(ds, "bsize")); 353 mfsbacked = (strcmp(dfui_dataset_get_value(ds, "mfsbacked"), "Y") == 0); 354 } else { 355 softupdates = (strcmp(mountpoint, "/") == 0 ? 0 : 1); 356 mfsbacked = (strcmp(mountpoint, "/tmp") == 0 ? 0 : 1); 357 fsize = -1; 358 bsize = -1; 359 } 360 361 if (aura_dict_exists(d, mountpoint, strlen(mountpoint) + 1)) { 362 inform(a->c, _("The same mount point cannot be specified " 363 "for two different subpartitions.")); 364 valid = 0; 365 } 366 367 if (strcmp(mountpoint, "/") == 0) 368 found_root = 1; 369 370 if (strcmp(capstring, "*") == 0) { 371 if (star_ds != NULL) { 372 inform(a->c, _("You cannot have more than one subpartition " 373 "with a '*' capacity (meaning 'use the remainder " 374 "of the primary partition'.)")); 375 valid = 0; 376 } else { 377 star_ds = ds; 378 } 379 } 380 381 if (!(!strcasecmp(mountpoint, "swap") || mountpoint[0] == '/')) { 382 inform(a->c, _("Mount point must be either 'swap', or it must " 383 "start with a '/'.")); 384 valid = 0; 385 } 386 387 if (strpbrk(mountpoint, " \\\"'`") != NULL) { 388 inform(a->c, _("Mount point may not contain the following " 389 "characters: blank space, backslash, or " 390 "single, double, or back quotes.")); 391 valid = 0; 392 } 393 394 if (strlen(capstring) == 0) { 395 inform(a->c, _("A capacity must be specified.")); 396 valid = 0; 397 } 398 399 if (!string_to_capacity(capstring, &capacity)) { 400 inform(a->c, _("Capacity must be either a '*' symbol to indicate " 401 "'use the rest of the primary partition', or it " 402 "must be a series of decimal digits ending with a " 403 "'M' (indicating megabytes) or a 'G' (indicating " 404 "gigabytes.)")); 405 valid = 0; 406 } 407 408 if ((strcasecmp(mountpoint, "swap") == 0) && (capacity > 8192)) { 409 inform(a->c, _("Swap capacity is limited to 8G.")); 410 valid = 0; 411 } 412 413 /* 414 * If we made it through that obstacle course, all is well. 415 */ 416 417 if (valid) 418 aura_dict_store(d, mountpoint, strlen(mountpoint) + 1, "", 1); 419 } 420 421 if (!found_root) { 422 inform(a->c, _("You must include a / (root) subpartition.")); 423 valid = 0; 424 } 425 426 if (aura_dict_size(d) > 16) { 427 inform(a->c, _("You cannot have more than 16 subpartitions " 428 "on a single primary partition. Remove some " 429 "and try again.")); 430 valid = 0; 431 } 432 433 aura_dict_free(d); 434 435 return(valid); 436 } 437 438 static void 439 save_subpartition_selections(struct dfui_response *r, struct i_fn_args *a) 440 { 441 struct dfui_dataset *ds; 442 char mfsbacked; 443 const char *mountpoint, *capstring; 444 long capacity; 445 long bsize, fsize; 446 int softupdates; 447 int valid = 1; 448 449 subpartitions_free(storage_get_selected_slice(a->s)); 450 451 for (ds = dfui_response_dataset_get_first(r); valid && ds != NULL; 452 ds = dfui_dataset_get_next(ds)) { 453 mountpoint = dfui_dataset_get_value(ds, "mountpoint"); 454 capstring = dfui_dataset_get_value(ds, "capacity"); 455 456 if (expert) { 457 softupdates = 458 (strcmp(dfui_dataset_get_value(ds, "softupdates"), "Y") == 0); 459 fsize = atol(dfui_dataset_get_value(ds, "fsize")); 460 bsize = atol(dfui_dataset_get_value(ds, "bsize")); 461 mfsbacked = (strcmp(dfui_dataset_get_value(ds, "mfsbacked"), "Y") == 0); 462 } else { 463 softupdates = (strcmp(mountpoint, "/") == 0 ? 0 : 1); 464 mfsbacked = 0; 465 fsize = -1; 466 bsize = -1; 467 } 468 469 if (string_to_capacity(capstring, &capacity)) { 470 subpartition_new(storage_get_selected_slice(a->s), mountpoint, capacity, 471 softupdates, fsize, bsize, mfsbacked); 472 } 473 } 474 } 475 476 static void 477 populate_create_subpartitions_form(struct dfui_form *f, struct i_fn_args *a) 478 { 479 struct subpartition *sp; 480 struct dfui_dataset *ds; 481 char temp[32]; 482 int mtpt; 483 long capacity; 484 485 if (slice_subpartition_first(storage_get_selected_slice(a->s)) != NULL) { 486 /* 487 * The user has already given us their subpartition 488 * preferences, so use them here. 489 */ 490 for (sp = slice_subpartition_first(storage_get_selected_slice(a->s)); 491 sp != NULL; sp = subpartition_next(sp)) { 492 ds = dfui_dataset_new(); 493 dfui_dataset_celldata_add(ds, "mountpoint", 494 subpartition_get_mountpoint(sp)); 495 dfui_dataset_celldata_add(ds, "capacity", 496 capacity_to_string(subpartition_get_capacity(sp))); 497 if (expert) { 498 dfui_dataset_celldata_add(ds, "softupdates", 499 subpartition_is_softupdated(sp) ? "Y" : "N"); 500 dfui_dataset_celldata_add(ds, "mfsbacked", 501 subpartition_is_mfsbacked(sp) ? "Y" : "N"); 502 snprintf(temp, 32, "%ld", subpartition_get_fsize(sp)); 503 dfui_dataset_celldata_add(ds, "fsize", 504 temp); 505 snprintf(temp, 32, "%ld", subpartition_get_bsize(sp)); 506 dfui_dataset_celldata_add(ds, "bsize", 507 temp); 508 } 509 dfui_form_dataset_add(f, ds); 510 } 511 } else { 512 /* 513 * Otherwise, populate the form with datasets representing 514 * reasonably-calculated defaults. The defaults are chosen 515 * based on the slice's total capacity and the machine's 516 * total physical memory (for swap.) 517 */ 518 for (mtpt = 0; def_mountpt[mtpt] != NULL; mtpt++) { 519 capacity = default_capacity(a->s, mtpt); 520 ds = dfui_dataset_new(); 521 dfui_dataset_celldata_add(ds, "mountpoint", 522 def_mountpt[mtpt]); 523 dfui_dataset_celldata_add(ds, "capacity", 524 capacity_to_string(capacity)); 525 if (expert) { 526 dfui_dataset_celldata_add(ds, "softupdates", 527 strcmp(def_mountpt[mtpt], "/") != 0 ? "Y" : "N"); 528 dfui_dataset_celldata_add(ds, "mfsbacked", 529 "N"); 530 dfui_dataset_celldata_add(ds, "fsize", 531 capacity < 1024 ? "1024" : "2048"); 532 dfui_dataset_celldata_add(ds, "bsize", 533 capacity < 1024 ? "8192" : "16384"); 534 } 535 dfui_form_dataset_add(f, ds); 536 } 537 } 538 } 539 540 static int 541 warn_subpartition_selections(struct i_fn_args *a) 542 { 543 int valid = 0; 544 struct aura_buffer *omit, *consequences; 545 546 omit = aura_buffer_new(2048); 547 consequences = aura_buffer_new(2048); 548 549 valid = check_capacity(a); 550 if (subpartition_find(storage_get_selected_slice(a->s), "/var") == NULL) { 551 aura_buffer_cat(omit, "/var "); 552 aura_buffer_cat(consequences, _("/var will be a plain dir in /\n")); 553 } 554 if (subpartition_find(storage_get_selected_slice(a->s), "/usr") == NULL) { 555 aura_buffer_cat(omit, "/usr "); 556 aura_buffer_cat(consequences, _("/usr will be a plain dir in /\n")); 557 } 558 if (subpartition_find(storage_get_selected_slice(a->s), "/tmp") == NULL) { 559 aura_buffer_cat(omit, "/tmp "); 560 aura_buffer_cat(consequences, _("/tmp will be symlinked to /var/tmp\n")); 561 } 562 if (subpartition_find(storage_get_selected_slice(a->s), "/home") == NULL) { 563 aura_buffer_cat(omit, "/home "); 564 aura_buffer_cat(consequences, _("/home will be symlinked to /usr/home\n")); 565 } 566 567 if (valid && aura_buffer_len(omit) > 0) { 568 switch (dfui_be_present_dialog(a->c, _("Really omit?"), 569 _("Omit Subpartition(s)|Return to Create Subpartitions"), 570 _("You have elected to not have the following " 571 "subpartition(s):\n\n%s\n\n" 572 "The ramifications of these subpartition(s) being " 573 "missing will be:\n\n%s\n" 574 "Is this really what you want to do?"), 575 aura_buffer_buf(omit), aura_buffer_buf(consequences))) { 576 case 1: 577 valid = 1; 578 break; 579 case 2: 580 valid = 0; 581 break; 582 default: 583 abort_backend(); 584 } 585 } 586 587 aura_buffer_free(omit); 588 aura_buffer_free(consequences); 589 590 return(!valid); 591 } 592 593 static struct dfui_form * 594 make_create_subpartitions_form(struct i_fn_args *a) 595 { 596 struct dfui_field *fi; 597 struct dfui_form *f; 598 char msg_buf[1][1024]; 599 600 snprintf(msg_buf[0], sizeof(msg_buf[0]), 601 _("Subpartitions further divide a primary partition for " 602 "use with %s. Some reasons you may want " 603 "a set of subpartitions are:\n\n" 604 "- you want to restrict how much data can be written " 605 "to certain parts of the primary partition, to quell " 606 "denial-of-service attacks; and\n" 607 "- you want to speed up access to data on the disk." 608 ""), OPERATING_SYSTEM_NAME); 609 610 f = dfui_form_create( 611 "create_subpartitions", 612 _("Create Subpartitions"), 613 _("Set up the subpartitions (also known as just `partitions' " 614 "in BSD tradition) you want to have on this primary " 615 "partition.\n\n" 616 "For Capacity, use 'M' to indicate megabytes, 'G' to " 617 "indicate gigabytes, or a single '*' to indicate " 618 "'use the remaining space on the primary partition'."), 619 620 msg_buf[0], 621 622 "p", "special", "dfinstaller_create_subpartitions", 623 "p", "minimum_width","64", 624 625 "f", "mountpoint", _("Mountpoint"), "", "", 626 "f", "capacity", _("Capacity"), "", "", 627 628 "a", "ok", _("Accept and Create"), "", "", 629 "a", "cancel", 630 (disk_get_formatted(storage_get_selected_disk(a->s)) ? 631 _("Return to Select Disk") : 632 _("Return to Select Primary Partition")), "", "", 633 "p", "accelerator", "ESC", 634 635 NULL 636 ); 637 638 dfui_form_set_multiple(f, 1); 639 dfui_form_set_extensible(f, 1); 640 641 if (expert) { 642 fi = dfui_form_field_add(f, "softupdates", 643 dfui_info_new(_("Softupdates"), "", "")); 644 dfui_field_property_set(fi, "control", "checkbox"); 645 646 fi = dfui_form_field_add(f, "mfsbacked", 647 dfui_info_new(_("MFS"), "", "")); 648 dfui_field_property_set(fi, "control", "checkbox"); 649 650 fi = dfui_form_field_add(f, "fsize", 651 dfui_info_new(_("Frag Sz"), "", "")); 652 653 fi = dfui_form_field_add(f, "bsize", 654 dfui_info_new(_("Block Sz"), "", "")); 655 656 dfui_form_action_add(f, "switch", 657 dfui_info_new(_("Switch to Normal Mode"), "", "")); 658 } else { 659 dfui_form_action_add(f, "switch", 660 dfui_info_new(_("Switch to Expert Mode"), "", "")); 661 } 662 663 return(f); 664 } 665 666 /* 667 * Returns: 668 * -1 = the form should be redisplayed 669 * 0 = failure, function is over 670 * 1 = success, function is over 671 */ 672 static int 673 show_create_subpartitions_form(struct dfui_form *f, struct i_fn_args *a) 674 { 675 struct dfui_dataset *ds; 676 struct dfui_response *r; 677 678 for (;;) { 679 if (dfui_form_dataset_get_first(f) == NULL) 680 populate_create_subpartitions_form(f, a); 681 682 if (!dfui_be_present(a->c, f, &r)) 683 abort_backend(); 684 685 if (strcmp(dfui_response_get_action_id(r), "cancel") == 0) { 686 dfui_response_free(r); 687 return(0); 688 } else if (strcmp(dfui_response_get_action_id(r), "switch") == 0) { 689 if (check_subpartition_selections(r, a)) { 690 save_subpartition_selections(r, a); 691 expert = expert ? 0 : 1; 692 dfui_response_free(r); 693 return(-1); 694 } 695 } else { 696 if (check_subpartition_selections(r, a)) { 697 save_subpartition_selections(r, a); 698 if (!warn_subpartition_selections(a)) { 699 if (!create_subpartitions(a)) { 700 inform(a->c, _("The subpartitions you chose were " 701 "not correctly created, and the " 702 "primary partition may " 703 "now be in an inconsistent state. " 704 "We recommend re-formatting it " 705 "before proceeding.")); 706 dfui_response_free(r); 707 return(0); 708 } else { 709 dfui_response_free(r); 710 return(1); 711 } 712 } 713 } 714 } 715 716 dfui_form_datasets_free(f); 717 /* dfui_form_datasets_add_from_response(f, r); */ 718 for (ds = dfui_response_dataset_get_first(r); ds != NULL; 719 ds = dfui_dataset_get_next(ds)) { 720 dfui_form_dataset_add(f, dfui_dataset_dup(ds)); 721 } 722 } 723 } 724 725 /* 726 * fn_create_subpartitions: let the user specify what subpartitions they 727 * want on the disk, how large each should be, and where it should be mounted. 728 */ 729 void 730 fn_create_subpartitions(struct i_fn_args *a) 731 { 732 struct commands *cmds; 733 734 cmds = commands_new(); 735 736 /* 737 * Auto-disklabel the slice. 738 * NB: one cannot use "/dev/adXsY" here - 739 * it must be in the form "adXsY". 740 */ 741 command_add(cmds, "%s%s if=/dev/zero of=/dev/%s bs=32k count=16", 742 a->os_root, cmd_name(a, "DD"), 743 slice_get_raw_device_name(storage_get_selected_slice(a->s))); 744 command_add(cmds, "%s%s -B -r -w %s auto", 745 a->os_root, cmd_name(a, "DISKLABEL64"), 746 slice_get_raw_device_name(storage_get_selected_slice(a->s))); 747 commands_execute(a, cmds); 748 commands_free(cmds); 749 750 if (use_hammer) 751 fn_create_subpartitions_hammer(a); 752 else 753 fn_create_subpartitions_ufs(a); 754 } 755 756 void 757 fn_create_subpartitions_ufs(struct i_fn_args *a) 758 { 759 struct dfui_form *f; 760 int done = 0; 761 762 a->result = 0; 763 while (!done) { 764 f = make_create_subpartitions_form(a); 765 switch (show_create_subpartitions_form(f, a)) { 766 case -1: 767 done = 0; 768 break; 769 case 0: 770 done = 1; 771 a->result = 0; 772 break; 773 case 1: 774 done = 1; 775 a->result = 1; 776 break; 777 } 778 dfui_form_free(f); 779 } 780 } 781