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