1 /* $NetBSD: bsddisklabel.c,v 1.29 2019/10/25 12:24:34 martin Exp $ */ 2 3 /* 4 * Copyright 1997 Piermont Information Systems Inc. 5 * All rights reserved. 6 * 7 * Based on code written by Philip A. Nelson for Piermont Information 8 * Systems Inc. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 3. The name of Piermont Information Systems Inc. may not be used to endorse 19 * or promote products derived from this software without specific prior 20 * written permission. 21 * 22 * THIS SOFTWARE IS PROVIDED BY PIERMONT INFORMATION SYSTEMS INC. ``AS IS'' 23 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 25 * ARE DISCLAIMED. IN NO EVENT SHALL PIERMONT INFORMATION SYSTEMS INC. BE 26 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 27 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 28 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 29 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 30 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 31 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 32 * THE POSSIBILITY OF SUCH DAMAGE. 33 */ 34 35 /* bsddisklabel.c -- generate standard BSD disklabel */ 36 /* Included by appropriate arch/XXXX/md.c */ 37 38 #include <sys/param.h> 39 #include <sys/sysctl.h> 40 #include <sys/exec.h> 41 #include <sys/utsname.h> 42 #include <sys/types.h> 43 #include <sys/stat.h> 44 #include <machine/cpu.h> 45 #include <assert.h> 46 #include <stdio.h> 47 #include <stddef.h> 48 #include <util.h> 49 #include <dirent.h> 50 #include "defs.h" 51 #include "md.h" 52 #include "defsizes.h" 53 #include "endian.h" 54 #include "msg_defs.h" 55 #include "menu_defs.h" 56 57 static size_t fill_ptn_menu(struct partition_usage_set *pset); 58 59 /* 60 * The default partition layout. 61 */ 62 static const struct part_usage_info 63 default_parts_init[] = 64 { 65 /* 66 * Pretty complex setup for boot partitions. 67 * This is copy&pasted below, please keep in sync! 68 */ 69 #ifdef PART_BOOT 70 { .size = PART_BOOT/512, /* PART_BOOT is in BYTE, not MB! */ 71 #ifdef PART_BOOT_MOUNT 72 .mount = PART_BOOT_MOUNT, 73 .instflags = PUIINST_MOUNT|PUIINST_BOOT, 74 #else 75 .instflags = PUIINST_BOOT, 76 #endif 77 #ifdef PART_BOOT_TYPE 78 .fs_type = PART_BOOT_TYPE, 79 #if PART_BOOT_TYPE == FS_MSDOS 80 .flags = PUIFLAG_ADD_OUTER, 81 #endif 82 #endif 83 #ifdef PART_BOOT_SUBT 84 .fs_version = PART_BOOT_SUBT, 85 #endif 86 }, 87 #endif 88 89 /* 90 * Two more copies of above for _BOOT1 and _BOOT2, please 91 * keep in sync! 92 */ 93 #ifdef PART_BOOT1 94 { .size = PART_BOOT1/512, /* PART_BOOT1 is in BYTE, not MB! */ 95 #ifdef PART_BOOT1_MOUNT 96 .mount = PART_BOOT1_MOUNT, 97 .instflags = PUIINST_MOUNT|PUIINST_BOOT, 98 #else 99 .instflags = PUIINST_MOUNT|PUIINST_BOOT, 100 #endif 101 #ifdef PART_BOOT1_TYPE 102 .fs_type = PART_BOOT1_TYPE, 103 #if PART_BOOT1_TYPE == FS_MSDOS 104 .flags = PUIFLAG_ADD_OUTER, 105 #endif 106 #endif 107 #ifdef PART_BOOT1_SUBT 108 .fs_version = PART_BOOT1_SUBT, 109 #endif 110 }, 111 #endif 112 #ifdef PART_BOOT2 113 { .size = PART_BOOT2/512, /* PART_BOOT2 is in BYTE, not MB! */ 114 #ifdef PART_BOOT2_MOUNT 115 .mount = PART_BOOT2_MOUNT, 116 .instflags = PUIINST_MOUNT|PUIINST_BOOT, 117 #else 118 .instflags = PUIINST_MOUNT|PUIINST_BOOT, 119 #endif 120 #ifdef PART_BOOT2_TYPE 121 .fs_type = PART_BOOT2_TYPE, 122 #if PART_BOOT2_TYPE == FS_MSDOS 123 .flags = PUIFLAG_ADD_OUTER, 124 #endif 125 #endif 126 #ifdef PART_BOOT2_SUBT 127 .fs_version = PART_BOOT1_SUBT, 128 #endif 129 }, 130 #endif 131 132 { .size = DEFROOTSIZE*(MEG/512), .mount = "/", .type = PT_root, 133 .flags = PUIFLAG_EXTEND }, 134 { 135 #if DEFSWAPSIZE > 0 136 .size = DEFSWAPSIZE*(MEG/512), 137 #endif 138 .type = PT_swap, .fs_type = FS_SWAP }, 139 #ifdef HAVE_TMPFS 140 { .type = PT_root, .mount = "/tmp", .fs_type = FS_TMPFS, 141 .flags = PUIFLG_JUST_MOUNTPOINT }, 142 #else 143 { .type = PT_root, .mount = "/tmp", .fs_type = FS_MFS, 144 .flags = PUIFLG_JUST_MOUNTPOINT }, 145 #endif 146 { .def_size = DEFUSRSIZE*(MEG/512), .mount = "/usr", .type = PT_root }, 147 { .def_size = DEFVARSIZE*(MEG/512), .mount = "/var", .type = PT_root }, 148 }; 149 150 static const char size_separator[] = 151 "----------------------------------- - --------------------"; 152 static char size_menu_title[STRSIZE]; 153 static char size_menu_exit[MENUSTRSIZE]; 154 155 static void 156 set_pset_exit_str(struct partition_usage_set *pset) 157 { 158 char *str, num[25]; 159 const char *args[2]; 160 bool overrun; 161 daddr_t free_space = pset->cur_free_space; 162 163 /* format exit string */ 164 overrun = free_space < 0; 165 if (overrun) 166 free_space = -free_space; 167 168 snprintf(num, sizeof(num), "%" PRIu64, free_space / sizemult); 169 args[0] = num; 170 args[1] = multname; 171 str = str_arg_subst( 172 msg_string(overrun ? MSG_fssizesbad : MSG_fssizesok), 173 2, args); 174 strlcpy(size_menu_exit, str, sizeof(size_menu_exit)); 175 free(str); 176 } 177 178 static void 179 draw_size_menu_header(menudesc *m, void *arg) 180 { 181 struct partition_usage_set *pset = arg; 182 size_t i; 183 char col1[70], desc[MENUSTRSIZE]; 184 bool need_ext = false, need_existing = false; 185 186 msg_display(MSG_ptnsizes); 187 188 for (i = 0; i < pset->num; i++) { 189 if (pset->infos[i].flags & PUIFLG_IS_OUTER) 190 need_ext = true; 191 else if (pset->infos[i].cur_part_id != NO_PART) 192 need_existing = true; 193 } 194 if (need_ext && need_existing) 195 snprintf(desc, sizeof desc, "%s, %s", 196 msg_string(MSG_ptnsizes_mark_existing), 197 msg_string(MSG_ptnsizes_mark_external)); 198 else if (need_existing) 199 strlcpy(desc, msg_string(MSG_ptnsizes_mark_existing), 200 sizeof desc); 201 else if (need_ext) 202 strlcpy(desc, msg_string(MSG_ptnsizes_mark_external), 203 sizeof desc); 204 if (need_ext || need_existing) { 205 msg_printf("\n"); 206 msg_display_add_subst(msg_string(MSG_ptnsizes_markers), 207 1, &desc); 208 } 209 msg_printf("\n\n"); 210 211 /* update menu title */ 212 snprintf(col1, sizeof col1, "%s (%s)", msg_string(MSG_ptnheaders_size), 213 multname); 214 snprintf(size_menu_title, sizeof size_menu_title, 215 " %-37.37s %s\n %s", col1, 216 msg_string(MSG_ptnheaders_filesystem), size_separator); 217 } 218 219 static void 220 draw_size_menu_line(menudesc *m, int opt, void *arg) 221 { 222 struct partition_usage_set *pset = arg; 223 daddr_t size; 224 char psize[38], inc_free[16], flag, swap[40]; 225 const char *mount; 226 bool free_mount = false; 227 228 if (opt < 0 || (size_t)opt >= pset->num) 229 return; 230 231 inc_free[0] = 0; 232 if ((pset->infos[opt].flags & PUIFLAG_EXTEND) && 233 pset->cur_free_space > 0) { 234 size = pset->infos[opt].size + pset->cur_free_space; 235 snprintf(inc_free, sizeof inc_free, " (%" PRIu64 ")", 236 size / sizemult); 237 } 238 size = pset->infos[opt].size; 239 snprintf(psize, sizeof psize, "%" PRIu64 "%s", 240 size / sizemult, inc_free); 241 242 if (pset->infos[opt].type == PT_swap) { 243 snprintf(swap, sizeof swap, "<%s>", 244 msg_string(MSG_swap_display)); 245 mount = swap; 246 } else if (pset->infos[opt].flags & PUIFLG_JUST_MOUNTPOINT) { 247 snprintf(swap, sizeof swap, "%s (%s)", 248 pset->infos[opt].mount, 249 getfslabelname(pset->infos[opt].fs_type, 250 pset->infos[opt].fs_version)); 251 mount = swap; 252 } else if (pset->infos[opt].mount[0]) { 253 mount = pset->infos[opt].mount; 254 } else { 255 mount = NULL; 256 if (pset->infos[opt].parts->pscheme->other_partition_identifier 257 && pset->infos[opt].cur_part_id != NO_PART) 258 mount = pset->infos[opt].parts->pscheme-> 259 other_partition_identifier(pset->infos[opt].parts, 260 pset->infos[opt].cur_part_id); 261 if (mount == NULL) 262 mount = getfslabelname(pset->infos[opt].fs_type, 263 pset->infos[opt].fs_version); 264 mount = str_arg_subst(msg_string(MSG_size_ptn_not_mounted), 265 1, &mount); 266 free_mount = true; 267 } 268 flag = ' '; 269 if (pset->infos[opt].flags & PUIFLAG_EXTEND) 270 flag = '+'; 271 else if (pset->infos[opt].flags & PUIFLG_IS_OUTER) 272 flag = '@'; 273 else if (pset->infos[opt].cur_part_id != NO_PART) 274 flag = '='; 275 wprintw(m->mw, "%-35.35s %c %s", psize, flag, mount); 276 if (free_mount) 277 free(__UNCONST(mount)); 278 279 if (opt == 0) 280 set_pset_exit_str(pset); 281 } 282 283 static int 284 add_other_ptn_size(menudesc *menu, void *arg) 285 { 286 struct partition_usage_set *pset = arg; 287 struct part_usage_info *p; 288 struct menu_ent *m; 289 char new_mp[MOUNTLEN], *err; 290 const char *args; 291 292 for (;;) { 293 msg_prompt_win(partman_go?MSG_askfsmountadv:MSG_askfsmount, 294 -1, 18, 0, 0, NULL, new_mp, sizeof(new_mp)); 295 if (new_mp[0] == 0) 296 return 0; 297 if (new_mp[0] != '/') { 298 /* we need absolute mount paths */ 299 memmove(new_mp+1, new_mp, sizeof(new_mp)-1); 300 new_mp[0] = '/'; 301 } 302 303 /* duplicates? */ 304 bool duplicate = false; 305 for (size_t i = 0; i < pset->num; i++) { 306 if (strcmp(pset->infos[i].mount, 307 new_mp) == 0) { 308 args = new_mp; 309 err = str_arg_subst( 310 msg_string(MSG_mp_already_exists), 311 1, &args); 312 err_msg_win(err); 313 free(err); 314 duplicate = true; 315 break; 316 } 317 } 318 if (!duplicate) 319 break; 320 } 321 322 m = realloc(pset->menu_opts, (pset->num+4)*sizeof(*pset->menu_opts)); 323 if (m == NULL) 324 return 0; 325 p = realloc(pset->infos, (pset->num+1)*sizeof(*pset->infos)); 326 if (p == NULL) 327 return 0; 328 329 pset->infos = p; 330 pset->menu_opts = m; 331 menu->opts = m; 332 menu->numopts = pset->num+4; 333 m += pset->num; 334 p += pset->num; 335 memset(m, 0, sizeof(*m)); 336 memset(p, 0, sizeof(*p)); 337 p->parts = pset->parts; 338 p->cur_part_id = NO_PART; 339 p->type = PT_root; 340 p->fs_type = FS_BSDFFS; 341 p->fs_version = 2; 342 strncpy(p->mount, new_mp, sizeof(p->mount)); 343 344 menu->cursel = pset->num; 345 pset->num++; 346 fill_ptn_menu(pset); 347 348 return -1; 349 } 350 351 static size_t 352 fill_ptn_menu(struct partition_usage_set *pset) 353 { 354 struct part_usage_info *p; 355 struct disk_part_info info; 356 menu_ent *m; 357 size_t i; 358 daddr_t free_space; 359 360 memset(pset->menu_opts, 0, (pset->num+3)*sizeof(*pset->menu_opts)); 361 for (m = pset->menu_opts, p = pset->infos, i = 0; i < pset->num; 362 m++, p++, i++) { 363 m->opt_action = set_ptn_size; 364 } 365 366 m->opt_name = size_separator; 367 m->opt_flags = OPT_IGNORE|OPT_NOSHORT; 368 m++; 369 370 m->opt_name = MSG_add_another_ptn; 371 m->opt_action = add_other_ptn_size; 372 m++; 373 374 m->opt_name = MSG_askunits; 375 m->opt_menu = MENU_sizechoice; 376 m->opt_flags = OPT_SUB; 377 m++; 378 379 /* calculate free space */ 380 free_space = pset->parts->free_space; 381 for (i = 0; i < pset->parts->num_part; i++) { 382 if (!pset->parts->pscheme->get_part_info(pset->parts, i, 383 &info)) 384 continue; 385 if (info.flags & (PTI_SEC_CONTAINER|PTI_WHOLE_DISK| 386 PTI_PSCHEME_INTERNAL|PTI_RAW_PART)) 387 continue; 388 free_space += info.size; 389 } 390 for (i = 0; i < pset->num; i++) { 391 if (pset->infos[i].flags & 392 (PUIFLG_IS_OUTER|PUIFLG_JUST_MOUNTPOINT)) 393 continue; 394 free_space -= pset->infos[i].size; 395 } 396 pset->cur_free_space = free_space; 397 set_pset_exit_str(pset); 398 399 if (pset->menu >= 0) 400 set_menu_numopts(pset->menu, m - pset->menu_opts); 401 402 return m - pset->menu_opts; 403 } 404 405 static part_id 406 find_part_at(struct disk_partitions *parts, daddr_t start) 407 { 408 size_t i; 409 struct disk_part_info info; 410 411 for (i = 0; i < parts->num_part; i++) { 412 if (!parts->pscheme->get_part_info(parts, i, &info)) 413 continue; 414 if (info.start == start) 415 return i; 416 } 417 418 return NO_PART; 419 } 420 421 int 422 set_ptn_size(menudesc *m, void *arg) 423 { 424 struct partition_usage_set *pset = arg; 425 struct part_usage_info *p = &pset->infos[m->cursel]; 426 char answer[16], dflt[16]; 427 const char *err_msg; 428 size_t i, root = ~0U; 429 daddr_t size, old_size, new_size_val, mult; 430 int rv; 431 bool non_zero, extend; 432 433 if (pset->cur_free_space == 0 && p->size == 0 && 434 !(p->flags & PUIFLG_JUST_MOUNTPOINT)) 435 /* Don't allow 'free_parts' to go negative */ 436 return 0; 437 438 if (p->cur_part_id != NO_PART) { 439 rv = 0; 440 process_menu(MENU_ptnsize_replace_existing_partition, &rv); 441 if (rv == 0) 442 return 0; 443 if (!pset->parts->pscheme->delete_partition(pset->parts, 444 p->cur_part_id, &err_msg)) { 445 if (err_msg) 446 err_msg_win(err_msg); 447 return 0; 448 } 449 p->cur_part_id = NO_PART; 450 /* 451 * All other part ids are invalid now too - update them! 452 */ 453 for (i = 0; i < pset->num; i++) { 454 if (pset->infos[i].cur_part_id == NO_PART) 455 continue; 456 pset->infos[i].cur_part_id = 457 find_part_at(pset->parts, pset->infos[i].cur_start); 458 } 459 } 460 461 size = p->size; 462 old_size = size; 463 if (size == 0) 464 size = p->def_size; 465 size /= sizemult; 466 snprintf(dflt, sizeof dflt, "%" PRIu64 "%s", 467 size, p->flags & PUIFLAG_EXTEND ? "+" : ""); 468 469 for (;;) { 470 msg_fmt_prompt_win(MSG_askfssize, -1, 18, 0, 0, 471 dflt, answer, sizeof answer, "%s%s", p->mount, multname); 472 473 /* cp will be checked below */ 474 mult = sizemult; 475 new_size_val = parse_disk_pos(answer, &mult, pm->dlcylsize, 476 &extend); 477 478 if (strcmp(answer, dflt) == 0) 479 non_zero = p->def_size > 0; 480 else 481 non_zero = new_size_val > 0; 482 483 /* Some special cases when /usr is first given a size */ 484 if (old_size == 0 && non_zero && 485 strcmp(p->mount, "/usr") == 0) { 486 for (i = 0; i < pset->num; i++) { 487 if (strcmp(pset->infos[i].mount, "/") == 0) { 488 root = i; 489 break; 490 } 491 } 492 /* Remove space for /usr from / */ 493 if (root < pset->num && pset->infos[i].cur_part_id == 494 NO_PART) { 495 pset->infos[root].size -= p->def_size; 496 pset->cur_free_space += p->def_size; 497 } 498 /* hack to add free space to default sized /usr */ 499 if (strcmp(answer, dflt) == 0) { 500 size = p->def_size; 501 pset->infos[root].flags &= ~PUIFLAG_EXTEND; 502 p->flags |= PUIFLAG_EXTEND; 503 goto adjust_free; 504 } 505 } 506 if (new_size_val < 0) 507 continue; 508 size = new_size_val; 509 break; 510 } 511 512 daddr_t align = pset->parts->pscheme->get_part_alignment(pset->parts); 513 size = NUMSEC(size, mult, align); 514 if (p->flags & PUIFLAG_EXTEND) 515 p->flags &= ~PUIFLAG_EXTEND; 516 if (extend && (p->limit == 0 || p->limit > p->size)) { 517 p->flags |= PUIFLAG_EXTEND; 518 if (size == 0) 519 size = align; 520 } 521 if (p->limit != 0 && size > p->limit) 522 size = p->limit; 523 adjust_free: 524 if ((p->flags & (PUIFLG_IS_OUTER|PUIFLG_JUST_MOUNTPOINT)) == 0) 525 pset->cur_free_space += p->size - size; 526 p->size = size; 527 set_pset_exit_str(pset); 528 529 return 0; 530 } 531 532 /* 533 * User interface to edit a "wanted" partition layout "pset" as first 534 * abstract phase (not concrete partitions). 535 * Make sure to have everything (at least theoretically) fit the 536 * available space. 537 * During editing we keep the part_usage_info and the menu_opts 538 * in pset in sync, that is: we always allocate just enough entries 539 * in pset->infos as we have usage infos in the list (pset->num), 540 * and two additional menu entries ("add a partition" and "select units"). 541 * The menu exit string changes depending on content, and implies 542 * abort while the partition set is not valid (does not fit). 543 * Return true when the user wants to continue (by editing the concrete 544 * partitions), return false to abort. 545 */ 546 bool 547 get_ptn_sizes(struct partition_usage_set *pset) 548 { 549 size_t num; 550 551 wclear(stdscr); 552 wrefresh(stdscr); 553 554 if (pset->menu_opts == NULL) 555 pset->menu_opts = calloc(pset->num+3, sizeof(*pset->menu_opts)); 556 557 pset->menu = -1; 558 num = fill_ptn_menu(pset); 559 560 pset->menu = new_menu(size_menu_title, pset->menu_opts, num, 561 3, -1, 12, 70, 562 MC_ALWAYS_SCROLL|MC_NOBOX|MC_NOCLEAR|MC_CONTINUOUS, 563 draw_size_menu_header, draw_size_menu_line, NULL, 564 NULL, size_menu_exit); 565 566 if (pset->menu < 0) { 567 free(pset->menu_opts); 568 pset->menu_opts = NULL; 569 return false; 570 } 571 572 pset->ok = true; 573 process_menu(pset->menu, pset); 574 575 free_menu(pset->menu); 576 free(pset->menu_opts); 577 pset->menu = -1; 578 pset->menu_opts = NULL; 579 580 return pset->ok; 581 } 582 583 static int 584 set_keep_existing(menudesc *m, void *arg) 585 { 586 ((arg_rep_int*)arg)->rv = LY_KEEPEXISTING; 587 return 0; 588 } 589 590 static int 591 set_edit_part_sizes(menudesc *m, void *arg) 592 { 593 ((arg_rep_int*)arg)->rv = LY_SETSIZES; 594 return 0; 595 } 596 597 static int 598 set_use_default_sizes(menudesc *m, void *arg) 599 { 600 ((arg_rep_int*)arg)->rv = LY_USEDEFAULT; 601 return 0; 602 } 603 604 /* 605 * Check if there is a reasonable pre-existing partition for 606 * NetBSD. 607 */ 608 static bool 609 check_existing_netbsd(struct disk_partitions *parts) 610 { 611 size_t nbsd_parts; 612 struct disk_part_info info; 613 614 nbsd_parts = 0; 615 for (part_id p = 0; p < parts->num_part; p++) { 616 if (!parts->pscheme->get_part_info(parts, p, &info)) 617 continue; 618 if (info.flags & (PTI_PSCHEME_INTERNAL|PTI_RAW_PART)) 619 continue; 620 if (info.nat_type && info.nat_type->generic_ptype == PT_root) 621 nbsd_parts++; 622 } 623 624 return nbsd_parts > 0; 625 } 626 627 /* 628 * Query a partition layout type (with available options depending on 629 * pre-existing partitions). 630 */ 631 static enum layout_type 632 ask_layout(struct disk_partitions *parts, bool have_existing) 633 { 634 arg_rep_int ai; 635 const char *args[2]; 636 int menu; 637 size_t num_opts; 638 menu_ent options[3], *opt; 639 640 args[0] = msg_string(parts->pscheme->name); 641 args[1] = msg_string(parts->pscheme->short_name); 642 ai.args.argv = args; 643 ai.args.argc = 2; 644 ai.rv = LY_SETSIZES; 645 646 memset(options, 0, sizeof(options)); 647 num_opts = 0; 648 opt = &options[0]; 649 650 if (have_existing) { 651 opt->opt_name = MSG_Keep_existing_partitions; 652 opt->opt_flags = OPT_EXIT; 653 opt->opt_action = set_keep_existing; 654 opt++; 655 num_opts++; 656 } 657 opt->opt_name = MSG_Set_Sizes; 658 opt->opt_flags = OPT_EXIT; 659 opt->opt_action = set_edit_part_sizes; 660 opt++; 661 num_opts++; 662 663 opt->opt_name = MSG_Use_Default_Parts; 664 opt->opt_flags = OPT_EXIT; 665 opt->opt_action = set_use_default_sizes; 666 opt++; 667 num_opts++; 668 669 menu = new_menu(MSG_Select_your_choice, options, num_opts, 670 -1, -10, 0, 0, MC_NOEXITOPT, NULL, NULL, NULL, NULL, NULL); 671 if (menu != -1) { 672 get_menudesc(menu)->expand_act = expand_all_option_texts; 673 process_menu(menu, &ai); 674 free_menu(menu); 675 } 676 677 return ai.rv; 678 } 679 680 static void 681 merge_part_with_wanted(struct disk_partitions *parts, part_id pno, 682 const struct disk_part_info *info, struct partition_usage_set *wanted, 683 size_t wanted_num, bool is_outer) 684 { 685 struct part_usage_info *infos; 686 687 /* 688 * does this partition match something in the wanted set? 689 */ 690 for (size_t i = 0; i < wanted_num; i++) { 691 if (wanted->infos[i].type != info->nat_type->generic_ptype) 692 continue; 693 if (wanted->infos[i].type == PT_root && 694 info->last_mounted != NULL && info->last_mounted[0] != 0 && 695 strcmp(info->last_mounted, wanted->infos[i].mount) != 0) 696 continue; 697 if (wanted->infos[i].cur_part_id != NO_PART) 698 continue; 699 wanted->infos[i].cur_part_id = pno; 700 wanted->infos[i].parts = parts; 701 wanted->infos[i].size = info->size; 702 wanted->infos[i].cur_start = info->start; 703 wanted->infos[i].flags &= ~PUIFLAG_EXTEND; 704 if (wanted->infos[i].fs_type != FS_UNUSED && 705 wanted->infos[i].type != PT_swap) 706 wanted->infos[i].instflags |= PUIINST_MOUNT; 707 if (is_outer) 708 wanted->infos[i].flags |= PUIFLG_IS_OUTER; 709 else 710 wanted->infos[i].flags &= ~PUIFLG_IS_OUTER; 711 return; 712 } 713 714 /* 715 * no match - if this is fromt the outer scheme, we are done. 716 * otherwise it must be inserted into the wanted set. 717 */ 718 if (is_outer) 719 return; 720 721 /* 722 * create a new entry for this 723 */ 724 infos = realloc(wanted->infos, sizeof(*infos)*(wanted->num+1)); 725 if (infos == NULL) 726 return; 727 wanted->infos = infos; 728 infos += wanted->num; 729 wanted->num++; 730 memset(infos, 0, sizeof(*infos)); 731 if (info->last_mounted != NULL && info->last_mounted[0] != 0) 732 strlcpy(infos->mount, info->last_mounted, 733 sizeof(infos->mount)); 734 infos->type = info->nat_type->generic_ptype; 735 infos->cur_part_id = pno; 736 infos->parts = parts; 737 infos->size = info->size; 738 infos->cur_start = info->start; 739 infos->fs_type = info->fs_type; 740 infos->fs_version = info->fs_sub_type; 741 if (is_outer) 742 infos->flags |= PUIFLG_IS_OUTER; 743 } 744 745 static bool 746 have_x11_by_default(void) 747 { 748 static const uint8_t def_sets[] = { MD_SETS_SELECTED }; 749 750 for (size_t i = 0; i < __arraycount(def_sets); i++) 751 if (def_sets[i] >= SET_X11_FIRST && 752 def_sets[i] <= SET_X11_LAST) 753 return true; 754 755 return false; 756 } 757 758 static void 759 fill_defaults(struct partition_usage_set *wanted, struct disk_partitions *parts, 760 daddr_t ptstart, daddr_t ptsize) 761 { 762 size_t i, root = ~0U, usr = ~0U, swap = ~0U, def_usr = ~0U; 763 daddr_t free_space, dump_space, required; 764 #if defined(DEFAULT_UFS2) && !defined(HAVE_UFS2_BOOT) 765 size_t boot = ~0U; 766 #endif 767 768 memset(wanted, 0, sizeof(*wanted)); 769 wanted->parts = parts; 770 wanted->num = __arraycount(default_parts_init); 771 wanted->infos = calloc(wanted->num, sizeof(*wanted->infos)); 772 if (wanted->infos == NULL) { 773 err_msg_win(err_outofmem); 774 return; 775 } 776 777 memcpy(wanted->infos, default_parts_init, sizeof(default_parts_init)); 778 779 #ifdef MD_PART_DEFAULTS 780 MD_PART_DEFAULTS(pm, wanted->infos, wanted->num); 781 #endif 782 783 for (i = 0; i < wanted->num; i++) { 784 wanted->infos[i].parts = parts; 785 wanted->infos[i].cur_part_id = NO_PART; 786 787 #if DEFSWAPSIZE == -1 788 if (wanted->infos[i].type == PT_swap) 789 wanted->infos[i].size = get_ramsize() * (MEG / 512); 790 #endif 791 if (wanted->infos[i].type == PT_swap && swap > wanted->num) 792 swap = i; 793 #if defined(DEFAULT_UFS2) && !defined(HAVE_UFS2_BOOT) 794 if (wanted->infos[i].instflags & PUIINST_BOOT) 795 boot = i; 796 #endif 797 if (wanted->infos[i].type == PT_root) { 798 if (strcmp(wanted->infos[i].mount, "/") == 0) { 799 root = i; 800 } else if ( 801 strcmp(wanted->infos[i].mount, "/usr") == 0) { 802 if (wanted->infos[i].size > 0) 803 usr = i; 804 else 805 def_usr = i; 806 } 807 if (wanted->infos[i].fs_type == FS_UNUSED) 808 wanted->infos[i].fs_type = FS_BSDFFS; 809 if (wanted->infos[i].fs_type == FS_BSDFFS) { 810 #ifdef DEFAULT_UFS2 811 #ifndef HAVE_UFS2_BOOT 812 if (boot < wanted->num || i != root) 813 #endif 814 wanted->infos[i].fs_version = 2; 815 #endif 816 } 817 } 818 if ((wanted->infos[i].flags & PUIFLG_JUST_MOUNTPOINT) && 819 wanted->infos[i].size == 0) 820 /* default tmpfs to 1/4 RAM */ 821 wanted->infos[i].def_size = 822 get_ramsize() * (MEG/512/4); 823 } 824 825 /* 826 * Now we have the defaults as if we were installing to an 827 * empty disk. Merge the partitions in target range that are already 828 * there (match with wanted) or are there additionaly. 829 * The only thing outside of target range that we care for 830 * is a potential swap partition - we assume one is enough. 831 */ 832 size_t num = wanted->num; 833 if (parts->parent) { 834 for (part_id pno = 0; pno < parts->parent->num_part; pno++) { 835 struct disk_part_info info; 836 837 if (!parts->parent->pscheme->get_part_info( 838 parts->parent, pno, &info)) 839 continue; 840 if (info.nat_type->generic_ptype != PT_swap) 841 continue; 842 merge_part_with_wanted(parts->parent, pno, &info, 843 wanted, num, true); 844 break; 845 } 846 } 847 for (part_id pno = 0; pno < parts->num_part; pno++) { 848 struct disk_part_info info; 849 850 if (!parts->pscheme->get_part_info(parts, pno, &info)) 851 continue; 852 853 if (info.flags & PTI_PSCHEME_INTERNAL) 854 continue; 855 856 if (info.nat_type->generic_ptype != PT_swap && 857 (info.start < ptstart || 858 (info.start + info.size) > (ptstart+ptsize))) 859 continue; 860 861 merge_part_with_wanted(parts, pno, &info, 862 wanted, num, false); 863 } 864 865 daddr_t align = parts->pscheme->get_part_alignment(parts); 866 867 if (root < wanted->num && wanted->infos[root].cur_part_id == NO_PART) { 868 daddr_t max_root_size = parts->disk_start + parts->disk_size; 869 if (root_limit > 0) { 870 /* Bah - bios can not read all the disk, limit root */ 871 max_root_size = root_limit - parts->disk_start; 872 } 873 wanted->infos[root].limit = max_root_size; 874 } 875 876 if (have_x11_by_default()) { 877 daddr_t xsize = XNEEDMB * (MEG / 512); 878 if (usr < wanted->num) { 879 if (wanted->infos[usr].cur_part_id == NO_PART) { 880 wanted->infos[usr].size += xsize; 881 wanted->infos[usr].def_size += xsize; 882 } 883 } else if (root < wanted->num && 884 wanted->infos[root].cur_part_id == NO_PART && 885 (wanted->infos[root].limit == 0 || 886 (wanted->infos[root].size + xsize) <= 887 wanted->infos[root].limit)) { 888 wanted->infos[root].size += xsize; 889 } 890 } 891 if (wanted->infos[root].limit > 0 && 892 wanted->infos[root].size > wanted->infos[root].limit) { 893 if (usr < wanted->num) { 894 /* move space from root to usr */ 895 daddr_t spill = wanted->infos[root].size - 896 wanted->infos[root].limit; 897 spill = roundup(spill, align); 898 wanted->infos[root].size = 899 wanted->infos[root].limit; 900 wanted->infos[usr].size = spill; 901 } else { 902 wanted->infos[root].size = 903 wanted->infos[root].limit; 904 } 905 } 906 907 /* 908 * Preliminary calc additional space to allocate and how much 909 * we likely will have left over. Use that to do further 910 * adjustments, so we don't present the user inherently 911 * impossible defaults. 912 */ 913 free_space = parts->free_space; 914 required = 0; 915 if (root < wanted->num) 916 required += wanted->infos[root].size; 917 if (usr < wanted->num) 918 required += wanted->infos[usr].size; 919 else if (def_usr < wanted->num) 920 required += wanted->infos[def_usr].def_size; 921 free_space -= required; 922 for (i = 0; i < wanted->num; i++) { 923 if (i == root || i == usr) 924 continue; /* already accounted above */ 925 if (wanted->infos[i].cur_part_id != NO_PART) 926 continue; 927 if (wanted->infos[i].size == 0) 928 continue; 929 if (wanted->infos[i].flags 930 & (PUIFLG_IS_OUTER|PUIFLG_JUST_MOUNTPOINT)) 931 continue; 932 free_space -= wanted->infos[i].size; 933 } 934 if (free_space < 0 && swap < wanted->num) { 935 /* steel from swap partition */ 936 daddr_t d = wanted->infos[swap].size; 937 daddr_t inc = roundup(-free_space, align); 938 if (inc > d) 939 inc = d; 940 free_space += inc; 941 wanted->infos[swap].size -= inc; 942 } 943 if (root < wanted->num) { 944 /* Add space for 2 system dumps to / (traditional) */ 945 dump_space = get_ramsize() * (MEG/512); 946 dump_space = roundup(dump_space, align); 947 if (free_space > dump_space*2) 948 dump_space *= 2; 949 if (free_space > dump_space) 950 wanted->infos[root].size += dump_space; 951 } 952 } 953 954 /* 955 * We sort pset->infos to sync with pset->parts and 956 * the cur_part_id, to allow using the same index into both 957 * "array" in later phases. This may include inserting 958 * dummy entries (when we do not actually want the 959 * partition, but it is forced upon us, like RAW_PART in 960 * disklabel). 961 */ 962 static void 963 sort_and_sync_parts(struct partition_usage_set *pset) 964 { 965 struct part_usage_info *infos; 966 size_t i, j, no; 967 part_id pno; 968 969 pset->cur_free_space = pset->parts->free_space; 970 971 /* count non-empty entries that are not in pset->parts */ 972 no = pset->parts->num_part; 973 for (i = 0; i < pset->num; i++) { 974 if (pset->infos[i].size == 0) 975 continue; 976 if (pset->infos[i].cur_part_id != NO_PART) 977 continue; 978 no++; 979 } 980 981 /* allocate new infos */ 982 infos = calloc(no, sizeof *infos); 983 if (infos == NULL) 984 return; 985 986 /* pre-initialize the first entires as dummy entries */ 987 for (i = 0; i < pset->parts->num_part; i++) { 988 infos[i].cur_part_id = NO_PART; 989 infos[i].cur_flags = PTI_PSCHEME_INTERNAL; 990 } 991 /* 992 * Now copy over eveything from our old entries that points to 993 * a real partition. 994 */ 995 for (i = 0; i < pset->num; i++) { 996 pno = pset->infos[i].cur_part_id; 997 if (pno == NO_PART) 998 continue; 999 if (pset->parts != pset->infos[i].parts) 1000 continue; 1001 if (pset->infos[i].flags & PUIFLG_JUST_MOUNTPOINT) 1002 continue; 1003 if ((pset->infos[i].flags & (PUIFLG_IS_OUTER|PUIFLAG_ADD_INNER)) 1004 == PUIFLG_IS_OUTER) 1005 continue; 1006 if (pno >= pset->parts->num_part) 1007 continue; 1008 memcpy(infos+pno, pset->infos+i, sizeof(*infos)); 1009 } 1010 /* Fill in the infos for real partitions where we had no data */ 1011 for (pno = 0; pno < pset->parts->num_part; pno++) { 1012 struct disk_part_info info; 1013 1014 if (infos[pno].cur_part_id != NO_PART) 1015 continue; 1016 1017 if (!pset->parts->pscheme->get_part_info(pset->parts, pno, 1018 &info)) 1019 continue; 1020 1021 infos[pno].parts = pset->parts; 1022 infos[pno].cur_part_id = pno; 1023 infos[pno].cur_flags = info.flags; 1024 infos[pno].size = info.size; 1025 infos[pno].type = info.nat_type->generic_ptype; 1026 infos[pno].cur_start = info.start; 1027 infos[pno].fs_type = info.fs_type; 1028 infos[pno].fs_version = info.fs_sub_type; 1029 } 1030 /* Add the non-partition entires after that */ 1031 j = pset->num; 1032 for (i = 0; i < pset->num; i++) { 1033 if (j >= no) 1034 break; 1035 if (pset->infos[i].size == 0) 1036 continue; 1037 if (pset->infos[i].cur_part_id != NO_PART) 1038 continue; 1039 memcpy(infos+j, pset->infos+i, sizeof(*infos)); 1040 j++; 1041 } 1042 1043 /* done, replace infos */ 1044 free(pset->infos); 1045 pset->num = no; 1046 pset->infos = infos; 1047 } 1048 1049 static void 1050 apply_settings_to_partitions(struct pm_devs *p, struct disk_partitions *parts, 1051 struct partition_usage_set *wanted, daddr_t start, daddr_t size) 1052 { 1053 size_t i, exp_ndx = ~0U; 1054 daddr_t planned_space = 0, nsp, from, align; 1055 struct disk_part_info *infos; 1056 struct disk_part_free_space space; 1057 struct disk_partitions *ps = NULL; 1058 part_id pno, new_part_id; 1059 1060 infos = calloc(wanted->num, sizeof(*infos)); 1061 if (infos == NULL) { 1062 err_msg_win(err_outofmem); 1063 return; 1064 } 1065 1066 align = wanted->parts->pscheme->get_part_alignment(wanted->parts); 1067 1068 /* 1069 * Pass one: calculate space available for expanding 1070 * the marked partition. 1071 */ 1072 for (i = 0; i < wanted->num; i++) { 1073 if ((wanted->infos[i].flags & PUIFLAG_EXTEND) && 1074 exp_ndx == ~0U) 1075 exp_ndx = i; 1076 if (wanted->infos[i].flags & 1077 (PUIFLG_JUST_MOUNTPOINT|PUIFLG_IS_OUTER)) 1078 continue; 1079 nsp = wanted->infos[i].size; 1080 if (wanted->infos[i].cur_part_id != NO_PART) { 1081 ps = wanted->infos[i].flags & PUIFLG_IS_OUTER ? 1082 parts->parent : parts; 1083 1084 if (ps->pscheme->get_part_info(ps, 1085 wanted->infos[i].cur_part_id, &infos[i])) 1086 nsp -= infos[i].size; 1087 } 1088 if (nsp > 0) 1089 planned_space += roundup(nsp, align); 1090 } 1091 1092 /* 1093 * Expand the pool partition (or shrink, if we overran), 1094 */ 1095 if (exp_ndx < wanted->num) 1096 wanted->infos[exp_ndx].size += 1097 parts->free_space - planned_space; 1098 1099 /* 1100 * Now it gets tricky: we want the wanted partitions in order 1101 * as defined, but any already existing partitions should not 1102 * be moved. We allow them to change size though. 1103 * To keep it simple, we just assign in order and skip blocked 1104 * spaces. This may shuffle the order of the resulting partitions 1105 * compared to the wanted list. 1106 */ 1107 1108 /* Adjust sizes of existing partitions */ 1109 for (i = 0; i < wanted->num; i++) { 1110 ps = wanted->infos[i].flags & PUIFLG_IS_OUTER ? 1111 parts->parent : parts; 1112 const struct part_usage_info *want = &wanted->infos[i]; 1113 1114 if (want->cur_part_id == NO_PART) 1115 continue; 1116 if (i == exp_ndx) /* the exp. part. can not exist yet */ 1117 continue; 1118 daddr_t free_size = ps->pscheme->max_free_space_at(ps, 1119 infos[i].start); 1120 if (free_size < wanted->infos[i].size) 1121 continue; 1122 infos[i].size = wanted->infos[i].size; 1123 ps->pscheme->set_part_info(ps, want->cur_part_id, 1124 &infos[i], NULL); 1125 } 1126 1127 from = -1; 1128 /* 1129 * First add all outer partitions - we need to align those exactly 1130 * with the inner counterpart later. 1131 */ 1132 if (parts->parent) { 1133 ps = parts->parent; 1134 daddr_t outer_align = ps->pscheme->get_part_alignment(ps); 1135 1136 for (i = 0; i < wanted->num; i++) { 1137 struct part_usage_info *want = &wanted->infos[i]; 1138 1139 if (want->cur_part_id != NO_PART) 1140 continue; 1141 if (!(want->flags & PUIFLAG_ADD_OUTER)) 1142 continue; 1143 if (want->size <= 0) 1144 continue; 1145 1146 size_t cnt = ps->pscheme->get_free_spaces(ps, 1147 &space, 1, want->size-2*outer_align, 1148 outer_align, from, -1); 1149 1150 if (cnt == 0) /* no free space for this partition */ 1151 continue; 1152 1153 infos[i].start = space.start; 1154 infos[i].size = min(want->size, space.size); 1155 infos[i].nat_type = 1156 ps->pscheme->get_fs_part_type( 1157 want->fs_type, want->fs_version); 1158 infos[i].last_mounted = want->mount; 1159 infos[i].fs_type = want->fs_type; 1160 infos[i].fs_sub_type = want->fs_version; 1161 new_part_id = ps->pscheme->add_partition(ps, 1162 &infos[i], NULL); 1163 if (new_part_id == NO_PART) 1164 continue; /* failed to add, skip */ 1165 1166 ps->pscheme->get_part_info(ps, 1167 new_part_id, &infos[i]); 1168 want->cur_part_id = new_part_id; 1169 1170 want->flags |= PUIFLAG_ADD_INNER|PUIFLG_IS_OUTER; 1171 from = rounddown(infos[i].start + 1172 infos[i].size+outer_align, outer_align); 1173 } 1174 } 1175 1176 /* 1177 * Now add new inner partitions 1178 */ 1179 for (i = 0; i < wanted->num && from < wanted->parts->disk_size; i++) { 1180 struct part_usage_info *want = &wanted->infos[i]; 1181 1182 if (want->cur_part_id != NO_PART) 1183 continue; 1184 if (want->flags & (PUIFLG_JUST_MOUNTPOINT|PUIFLG_IS_OUTER)) 1185 continue; 1186 if (want->size <= 0) 1187 continue; 1188 1189 size_t cnt = wanted->parts->pscheme->get_free_spaces( 1190 wanted->parts, &space, 1, want->size-align, align, from, 1191 -1); 1192 if (cnt == 0) 1193 cnt = wanted->parts->pscheme->get_free_spaces( 1194 wanted->parts, &space, 1, 1195 want->size-5*align, align, from, -1); 1196 1197 if (cnt == 0) 1198 continue; /* no free space for this partition */ 1199 1200 infos[i].start = space.start; 1201 infos[i].size = min(want->size, space.size); 1202 infos[i].nat_type = 1203 wanted->parts->pscheme->get_fs_part_type(want->fs_type, 1204 want->fs_version); 1205 infos[i].last_mounted = want->mount; 1206 infos[i].fs_type = want->fs_type; 1207 infos[i].fs_sub_type = want->fs_version; 1208 if (want->fs_type != FS_UNUSED && want->type != PT_swap) { 1209 want->instflags |= PUIINST_NEWFS; 1210 if (want->mount[0] != 0) 1211 want->instflags |= PUIINST_MOUNT; 1212 } 1213 new_part_id = wanted->parts->pscheme->add_partition( 1214 wanted->parts, &infos[i], NULL); 1215 if (new_part_id == NO_PART) 1216 continue; /* failed to add, skip */ 1217 1218 wanted->parts->pscheme->get_part_info( 1219 wanted->parts, new_part_id, &infos[i]); 1220 from = rounddown(infos[i].start+infos[i].size+align, align); 1221 } 1222 1223 1224 /* 1225 * If there are any outer partitions that we need as inner ones 1226 * too, add them to the inner partitioning scheme. 1227 */ 1228 for (i = 0; i < wanted->num; i++) { 1229 struct part_usage_info *want = &wanted->infos[i]; 1230 1231 if (want->cur_part_id != NO_PART) 1232 continue; 1233 if (want->flags & PUIFLG_JUST_MOUNTPOINT) 1234 continue; 1235 if (want->size <= 0) 1236 continue; 1237 1238 if ((want->flags & (PUIFLAG_ADD_INNER|PUIFLG_IS_OUTER)) != 1239 (PUIFLAG_ADD_INNER|PUIFLG_IS_OUTER)) 1240 continue; 1241 1242 infos[i].start = want->cur_start; 1243 infos[i].size = want->size; 1244 infos[i].nat_type = wanted->parts->pscheme->get_fs_part_type( 1245 want->fs_type, want->fs_version); 1246 infos[i].last_mounted = want->mount; 1247 infos[i].fs_type = want->fs_type; 1248 infos[i].fs_sub_type = want->fs_version; 1249 1250 if (wanted->parts->pscheme->add_outer_partition 1251 != NULL) 1252 new_part_id = wanted->parts->pscheme-> 1253 add_outer_partition( 1254 wanted->parts, &infos[i], NULL); 1255 else 1256 new_part_id = wanted->parts->pscheme-> 1257 add_partition( 1258 wanted->parts, &infos[i], NULL); 1259 1260 if (new_part_id == NO_PART) 1261 continue; /* failed to add, skip */ 1262 1263 wanted->parts->pscheme->get_part_info( 1264 wanted->parts, new_part_id, &infos[i]); 1265 } 1266 1267 /* 1268 * Note: all part_ids are invalid now, as we have added things! 1269 */ 1270 for (i = 0; i < wanted->num; i++) 1271 wanted->infos[i].cur_part_id = NO_PART; 1272 for (pno = 0; pno < parts->num_part; pno++) { 1273 struct disk_part_info t; 1274 1275 if (!parts->pscheme->get_part_info(parts, pno, &t)) 1276 continue; 1277 1278 for (i = 0; i < wanted->num; i++) { 1279 if (wanted->infos[i].cur_part_id != NO_PART) 1280 continue; 1281 if (wanted->infos[i].size <= 0) 1282 continue; 1283 if (t.start == infos[i].start) { 1284 wanted->infos[i].cur_part_id = pno; 1285 wanted->infos[i].cur_start = infos[i].start; 1286 wanted->infos[i].cur_flags = infos[i].flags; 1287 break; 1288 } 1289 } 1290 } 1291 free(infos); 1292 1293 /* sort, and sync part ids and wanted->infos[] indices */ 1294 sort_and_sync_parts(wanted); 1295 } 1296 1297 static void 1298 replace_by_default(struct pm_devs *p, struct disk_partitions *parts, 1299 daddr_t start, daddr_t size, struct partition_usage_set *wanted) 1300 { 1301 1302 if (start == 0 && size == parts->disk_size) 1303 parts->pscheme->delete_all_partitions(parts); 1304 else if (parts->pscheme->delete_partitions_in_range != NULL) 1305 parts->pscheme->delete_partitions_in_range(parts, start, size); 1306 else 1307 assert(parts->num_part == 0); 1308 1309 fill_defaults(wanted, parts, start, size); 1310 apply_settings_to_partitions(p, parts, wanted, start, size); 1311 } 1312 1313 static bool 1314 edit_with_defaults(struct pm_devs *p, struct disk_partitions *parts, 1315 daddr_t start, daddr_t size, struct partition_usage_set *wanted) 1316 { 1317 bool ok; 1318 1319 fill_defaults(wanted, parts, start, size); 1320 ok = get_ptn_sizes(wanted); 1321 if (ok) 1322 apply_settings_to_partitions(p, parts, wanted, start, size); 1323 return ok; 1324 } 1325 1326 /* 1327 * md back-end code for menu-driven BSD disklabel editor. 1328 * returns 0 on failure, 1 on success. 1329 * fills the install target with a list for newfs/fstab. 1330 */ 1331 bool 1332 make_bsd_partitions(struct install_partition_desc *install) 1333 { 1334 struct disk_partitions *parts = pm->parts; 1335 const struct disk_partitioning_scheme *pscheme; 1336 struct partition_usage_set wanted; 1337 enum layout_type layoutkind = LY_SETSIZES; 1338 bool have_existing; 1339 1340 if (pm && pm->no_part && parts == NULL) 1341 return true; 1342 1343 if (parts == NULL) { 1344 pscheme = select_part_scheme(pm, NULL, !pm->no_mbr, NULL); 1345 if (pscheme == NULL) 1346 return false; 1347 parts = pscheme->create_new_for_disk(pm->diskdev, 1348 0, pm->dlsize, pm->dlsize, true); 1349 if (parts == NULL) 1350 return false; 1351 pm->parts = parts; 1352 } else { 1353 pscheme = parts->pscheme; 1354 } 1355 1356 if (pscheme->secondary_partitions) { 1357 struct disk_partitions *p; 1358 1359 p = pscheme->secondary_partitions(parts, pm->ptstart, false); 1360 if (p) { 1361 parts = p; 1362 pscheme = parts->pscheme; 1363 } 1364 } 1365 1366 have_existing = check_existing_netbsd(parts); 1367 1368 /* 1369 * Initialize global variables that track space used on this disk. 1370 */ 1371 if (pm->ptsize == 0) 1372 pm->ptsize = pm->dlsize - pm->ptstart; 1373 if (pm->dlsize == 0) 1374 pm->dlsize = pm->ptstart + pm->ptsize; 1375 1376 if (logfp) fprintf(logfp, "dlsize=%" PRId64 " ptsize=%" PRId64 1377 " ptstart=%" PRId64 "\n", 1378 pm->dlsize, pm->ptsize, pm->ptstart); 1379 1380 if (pm->current_cylsize == 0) 1381 pm->current_cylsize = pm->dlcylsize; 1382 1383 /* Ask for layout type -- standard or special */ 1384 if (partman_go == 0) { 1385 char bsd_size[6], min_size[6], x_size[6]; 1386 1387 humanize_number(bsd_size, sizeof(bsd_size), 1388 (uint64_t)pm->ptsize*pm->sectorsize, 1389 "", HN_AUTOSCALE, HN_B | HN_NOSPACE | HN_DECIMAL); 1390 humanize_number(min_size, sizeof(min_size), 1391 (uint64_t)(DEFROOTSIZE + DEFSWAPSIZE + DEFUSRSIZE)*MEG, 1392 "", HN_AUTOSCALE, HN_B | HN_NOSPACE | HN_DECIMAL); 1393 humanize_number(x_size, sizeof(x_size), 1394 (uint64_t)(DEFROOTSIZE + DEFSWAPSIZE + DEFUSRSIZE 1395 + XNEEDMB)*MEG, 1396 "", HN_AUTOSCALE, HN_B | HN_NOSPACE | HN_DECIMAL); 1397 1398 msg_display_subst( 1399 have_existing ? MSG_layout_prologue_existing 1400 : MSG_layout_prologue_none, 6, pm->diskdev, 1401 msg_string(parts->pscheme->name), 1402 msg_string(parts->pscheme->short_name), 1403 bsd_size, min_size, x_size); 1404 msg_display_add_subst(MSG_layout_main, 6, 1405 pm->diskdev, 1406 msg_string(parts->pscheme->name), 1407 msg_string(parts->pscheme->short_name), 1408 bsd_size, min_size, x_size); 1409 msg_display_add("\n\n"); 1410 layoutkind = ask_layout(parts, have_existing); 1411 } 1412 1413 if (layoutkind == LY_USEDEFAULT) { 1414 replace_by_default(pm, parts, pm->ptstart, pm->ptsize, 1415 &wanted); 1416 } else if (layoutkind == LY_SETSIZES) { 1417 if (!edit_with_defaults(pm, parts, pm->ptstart, pm->ptsize, 1418 &wanted)) { 1419 free_usage_set(&wanted); 1420 return false; 1421 } 1422 } else { 1423 usage_set_from_parts(&wanted, parts); 1424 } 1425 1426 /* 1427 * OK, we have a partition table. Give the user the chance to 1428 * edit it and verify it's OK, or abort altogether. 1429 */ 1430 for (;;) { 1431 int rv = edit_and_check_label(pm, &wanted); 1432 if (rv == 0) { 1433 msg_display(MSG_abort_part); 1434 free_usage_set(&wanted); 1435 return false; 1436 } 1437 /* update install infos */ 1438 install->num = wanted.num; 1439 install->infos = wanted.infos; 1440 /* and check them */ 1441 if (check_partitions(install)) 1442 break; 1443 } 1444 1445 /* we moved infos from wanted to install target */ 1446 wanted.infos = NULL; 1447 free_usage_set(&wanted); 1448 1449 /* Everything looks OK. */ 1450 return true; 1451 } 1452 1453 #ifndef MD_NEED_BOOTBLOCK 1454 #define MD_NEED_BOOTBLOCK(A) true 1455 #endif 1456 1457 /* 1458 * check that there is at least a / somewhere. 1459 */ 1460 bool 1461 check_partitions(struct install_partition_desc *install) 1462 { 1463 #ifdef HAVE_BOOTXX_xFS 1464 int rv = 1; 1465 char *bootxx; 1466 #endif 1467 #ifndef HAVE_UFS2_BOOT 1468 size_t i; 1469 #endif 1470 1471 #ifdef HAVE_BOOTXX_xFS 1472 if (MD_NEED_BOOTBLOCK(install)) { 1473 /* check if we have boot code for the root partition type */ 1474 bootxx = bootxx_name(install); 1475 if (bootxx != NULL) { 1476 rv = access(bootxx, R_OK); 1477 free(bootxx); 1478 } else 1479 rv = -1; 1480 if (rv != 0) { 1481 hit_enter_to_continue(NULL, MSG_No_Bootcode); 1482 return false; 1483 } 1484 } 1485 #endif 1486 #ifndef HAVE_UFS2_BOOT 1487 if (MD_NEED_BOOTBLOCK(install)) { 1488 for (i = 0; i < install->num; i++) { 1489 if (install->infos[i].type != PT_root) 1490 continue; 1491 if (strcmp(install->infos[i].mount, "/") != 0) 1492 continue; 1493 if (install->infos[i].fs_type != FS_BSDFFS) 1494 continue; 1495 if (install->infos[i].fs_version != 2) 1496 continue; 1497 hit_enter_to_continue(NULL, MSG_cannot_ufs2_root); 1498 return false; 1499 } 1500 } 1501 #endif 1502 1503 return md_check_partitions(install); 1504 } 1505