1 /* $NetBSD: bsddisklabel.c,v 1.39 2020/02/06 20:17:04 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 if (pset->infos[opt].fs_type == FS_TMPFS) { 240 if (pset->infos[opt].size < 0) 241 snprintf(psize, sizeof psize, "%" PRIu64 "%%", -size); 242 else 243 snprintf(psize, sizeof psize, "%" PRIu64 " %s", size, 244 msg_string(MSG_megname)); 245 } else { 246 snprintf(psize, sizeof psize, "%" PRIu64 "%s", 247 size / sizemult, inc_free); 248 } 249 250 if (pset->infos[opt].type == PT_swap) { 251 snprintf(swap, sizeof swap, "<%s>", 252 msg_string(MSG_swap_display)); 253 mount = swap; 254 } else if (pset->infos[opt].flags & PUIFLG_JUST_MOUNTPOINT) { 255 snprintf(swap, sizeof swap, "%s (%s)", 256 pset->infos[opt].mount, 257 getfslabelname(pset->infos[opt].fs_type, 258 pset->infos[opt].fs_version)); 259 mount = swap; 260 } else if (pset->infos[opt].mount[0]) { 261 mount = pset->infos[opt].mount; 262 #ifndef NO_CLONES 263 } else if (pset->infos[opt].flags & PUIFLG_CLONE_PARTS) { 264 snprintf(swap, sizeof swap, "%zu %s", 265 pset->infos[opt].clone_src->num_sel, 266 msg_string(MSG_clone_target_disp)); 267 mount = swap; 268 #endif 269 } else { 270 mount = NULL; 271 if (pset->infos[opt].parts->pscheme->other_partition_identifier 272 && pset->infos[opt].cur_part_id != NO_PART) 273 mount = pset->infos[opt].parts->pscheme-> 274 other_partition_identifier(pset->infos[opt].parts, 275 pset->infos[opt].cur_part_id); 276 if (mount == NULL) 277 mount = getfslabelname(pset->infos[opt].fs_type, 278 pset->infos[opt].fs_version); 279 mount = str_arg_subst(msg_string(MSG_size_ptn_not_mounted), 280 1, &mount); 281 free_mount = true; 282 } 283 flag = ' '; 284 if (pset->infos[opt].flags & PUIFLAG_EXTEND) 285 flag = '+'; 286 else if (pset->infos[opt].flags & PUIFLG_IS_OUTER) 287 flag = '@'; 288 else if (pset->infos[opt].cur_part_id != NO_PART) 289 flag = '='; 290 wprintw(m->mw, "%-35.35s %c %s", psize, flag, mount); 291 if (free_mount) 292 free(__UNCONST(mount)); 293 294 if (opt == 0) 295 set_pset_exit_str(pset); 296 } 297 298 static int 299 add_other_ptn_size(menudesc *menu, void *arg) 300 { 301 struct partition_usage_set *pset = arg; 302 struct part_usage_info *p; 303 struct menu_ent *m; 304 char new_mp[MOUNTLEN], *err; 305 const char *args; 306 307 for (;;) { 308 msg_prompt_win(partman_go?MSG_askfsmountadv:MSG_askfsmount, 309 -1, 18, 0, 0, NULL, new_mp, sizeof(new_mp)); 310 if (new_mp[0] == 0) 311 return 0; 312 if (new_mp[0] != '/') { 313 /* we need absolute mount paths */ 314 memmove(new_mp+1, new_mp, sizeof(new_mp)-1); 315 new_mp[0] = '/'; 316 } 317 318 /* duplicates? */ 319 bool duplicate = false; 320 for (size_t i = 0; i < pset->num; i++) { 321 if (strcmp(pset->infos[i].mount, 322 new_mp) == 0) { 323 args = new_mp; 324 err = str_arg_subst( 325 msg_string(MSG_mp_already_exists), 326 1, &args); 327 err_msg_win(err); 328 free(err); 329 duplicate = true; 330 break; 331 } 332 } 333 if (!duplicate) 334 break; 335 } 336 337 m = realloc(pset->menu_opts, (pset->num+5)*sizeof(*pset->menu_opts)); 338 if (m == NULL) 339 return 0; 340 p = realloc(pset->infos, (pset->num+1)*sizeof(*pset->infos)); 341 if (p == NULL) 342 return 0; 343 344 pset->infos = p; 345 pset->menu_opts = m; 346 menu->opts = m; 347 menu->numopts = pset->num+4; 348 m += pset->num; 349 p += pset->num; 350 memset(m, 0, sizeof(*m)); 351 memset(p, 0, sizeof(*p)); 352 p->parts = pset->parts; 353 p->cur_part_id = NO_PART; 354 p->type = PT_root; 355 p->fs_type = FS_BSDFFS; 356 p->fs_version = 2; 357 strncpy(p->mount, new_mp, sizeof(p->mount)); 358 359 menu->cursel = pset->num; 360 pset->num++; 361 fill_ptn_menu(pset); 362 363 return -1; 364 } 365 366 #ifndef NO_CLONES 367 static int 368 inst_ext_clone(menudesc *menu, void *arg) 369 { 370 struct selected_partitions selected; 371 struct clone_target_menu_data data; 372 struct partition_usage_set *pset = arg; 373 struct part_usage_info *p; 374 menu_ent *men; 375 int num_men, i; 376 377 if (!select_partitions(&selected, pm->parts)) 378 return 0; 379 380 num_men = pset->num+1; 381 men = calloc(num_men, sizeof *men); 382 if (men == NULL) 383 return 0; 384 for (i = 0; i < num_men; i++) 385 men[i].opt_action = clone_target_select; 386 men[num_men-1].opt_name = MSG_clone_target_end; 387 388 memset(&data, 0, sizeof data); 389 data.usage = *pset; 390 data.res = -1; 391 392 data.usage.menu = new_menu(MSG_clone_target_hdr, 393 men, num_men, 3, 2, 0, 65, MC_SCROLL, 394 NULL, draw_size_menu_line, NULL, NULL, MSG_cancel); 395 process_menu(data.usage.menu, &data); 396 free_menu(data.usage.menu); 397 free(men); 398 399 if (data.res < 0) 400 goto err; 401 402 /* insert clone record */ 403 men = realloc(pset->menu_opts, (pset->num+5)*sizeof(*pset->menu_opts)); 404 if (men == NULL) 405 goto err; 406 pset->menu_opts = men; 407 menu->opts = men; 408 menu->numopts = pset->num+4; 409 410 p = realloc(pset->infos, (pset->num+1)*sizeof(*pset->infos)); 411 if (p == NULL) 412 goto err; 413 pset->infos = p; 414 415 men += data.res; 416 p += data.res; 417 memmove(men+1, men, sizeof(*men)*((pset->num+4)-data.res)); 418 memmove(p+1, p, sizeof(*p)*((pset->num)-data.res)); 419 memset(men, 0, sizeof(*men)); 420 memset(p, 0, sizeof(*p)); 421 p->flags = PUIFLG_CLONE_PARTS; 422 p->cur_part_id = NO_PART; 423 p->clone_src = malloc(sizeof(selected)); 424 if (p->clone_src != NULL) { 425 *p->clone_src = selected; 426 p->clone_ndx = ~0U; 427 p->size = selected_parts_size(&selected); 428 p->parts = pset->parts; 429 } else { 430 p->clone_ndx = 0; 431 free_selected_partitions(&selected); 432 } 433 434 menu->cursel = data.res == 0 ? 1 : 0; 435 pset->num++; 436 fill_ptn_menu(pset); 437 438 return -1; 439 440 err: 441 free_selected_partitions(&selected); 442 return 0; 443 } 444 #endif 445 446 static size_t 447 fill_ptn_menu(struct partition_usage_set *pset) 448 { 449 struct part_usage_info *p; 450 struct disk_part_info info; 451 menu_ent *m; 452 size_t i; 453 daddr_t free_space; 454 455 #ifdef NO_CLONES 456 #define ADD_ITEMS 3 457 #else 458 #define ADD_ITEMS 4 459 #endif 460 461 memset(pset->menu_opts, 0, (pset->num+ADD_ITEMS) 462 *sizeof(*pset->menu_opts)); 463 for (m = pset->menu_opts, p = pset->infos, i = 0; i < pset->num; 464 m++, p++, i++) { 465 if (p->flags & PUIFLG_CLONE_PARTS) 466 m->opt_flags = OPT_IGNORE|OPT_NOSHORT; 467 else 468 m->opt_action = set_ptn_size; 469 } 470 471 m->opt_name = size_separator; 472 m->opt_flags = OPT_IGNORE|OPT_NOSHORT; 473 m++; 474 475 m->opt_name = MSG_add_another_ptn; 476 m->opt_action = add_other_ptn_size; 477 m++; 478 479 #ifndef NO_CLONES 480 m->opt_name = MSG_clone_from_elsewhere; 481 m->opt_action = inst_ext_clone; 482 m++; 483 #endif 484 485 m->opt_name = MSG_askunits; 486 m->opt_menu = MENU_sizechoice; 487 m->opt_flags = OPT_SUB; 488 m++; 489 490 /* calculate free space */ 491 free_space = pset->parts->free_space; 492 for (i = 0; i < pset->parts->num_part; i++) { 493 if (!pset->parts->pscheme->get_part_info(pset->parts, i, 494 &info)) 495 continue; 496 if (info.flags & (PTI_SEC_CONTAINER|PTI_WHOLE_DISK| 497 PTI_PSCHEME_INTERNAL|PTI_RAW_PART)) 498 continue; 499 free_space += info.size; 500 } 501 for (i = 0; i < pset->num; i++) { 502 if (pset->infos[i].flags & 503 (PUIFLG_IS_OUTER|PUIFLG_JUST_MOUNTPOINT)) 504 continue; 505 free_space -= pset->infos[i].size; 506 } 507 pset->cur_free_space = free_space; 508 set_pset_exit_str(pset); 509 510 if (pset->menu >= 0) 511 set_menu_numopts(pset->menu, m - pset->menu_opts); 512 513 return m - pset->menu_opts; 514 } 515 516 static part_id 517 find_part_at(struct disk_partitions *parts, daddr_t start) 518 { 519 size_t i; 520 struct disk_part_info info; 521 522 for (i = 0; i < parts->num_part; i++) { 523 if (!parts->pscheme->get_part_info(parts, i, &info)) 524 continue; 525 if (info.start == start) 526 return i; 527 } 528 529 return NO_PART; 530 } 531 532 static daddr_t 533 parse_ram_size(const char *str, bool *is_percent) 534 { 535 daddr_t val; 536 char *cp; 537 538 val = strtoull(str, &cp, 10); 539 while (*cp && isspace((unsigned char)*cp)) 540 cp++; 541 542 *is_percent = *cp == '%'; 543 return val; 544 } 545 546 int 547 set_ptn_size(menudesc *m, void *arg) 548 { 549 struct partition_usage_set *pset = arg; 550 struct part_usage_info *p = &pset->infos[m->cursel]; 551 char answer[16], dflt[16]; 552 const char *err_msg; 553 size_t i, root = ~0U; 554 daddr_t size, old_size, new_size_val, mult; 555 int rv; 556 bool non_zero, extend, is_ram_size, is_percent = false; 557 558 if (pset->cur_free_space == 0 && p->size == 0 && 559 !(p->flags & PUIFLG_JUST_MOUNTPOINT)) 560 /* Don't allow 'free_parts' to go negative */ 561 return 0; 562 563 if (p->cur_part_id != NO_PART) { 564 rv = 0; 565 process_menu(MENU_ptnsize_replace_existing_partition, &rv); 566 if (rv == 0) 567 return 0; 568 if (!pset->parts->pscheme->delete_partition(pset->parts, 569 p->cur_part_id, &err_msg)) { 570 if (err_msg) 571 err_msg_win(err_msg); 572 return 0; 573 } 574 p->cur_part_id = NO_PART; 575 /* 576 * All other part ids are invalid now too - update them! 577 */ 578 for (i = 0; i < pset->num; i++) { 579 if (pset->infos[i].cur_part_id == NO_PART) 580 continue; 581 pset->infos[i].cur_part_id = 582 find_part_at(pset->parts, pset->infos[i].cur_start); 583 } 584 } 585 586 is_ram_size = (p->flags & PUIFLG_JUST_MOUNTPOINT) 587 && p->fs_type == FS_TMPFS; 588 589 size = p->size; 590 if (is_ram_size && size < 0) { 591 is_percent = true; 592 size = -size; 593 } 594 old_size = size; 595 if (size == 0) 596 size = p->def_size; 597 if (!is_ram_size) 598 size /= sizemult; 599 600 if (is_ram_size) { 601 snprintf(dflt, sizeof dflt, "%" PRIu64 "%s", 602 size, is_percent ? "%" : ""); 603 } else { 604 snprintf(dflt, sizeof dflt, "%" PRIu64 "%s", 605 size, p->flags & PUIFLAG_EXTEND ? "+" : ""); 606 } 607 608 for (;;) { 609 msg_fmt_prompt_win(MSG_askfssize, -1, 18, 0, 0, 610 dflt, answer, sizeof answer, "%s%s", p->mount, 611 is_ram_size ? msg_string(MSG_megname) : multname); 612 613 if (is_ram_size) { 614 new_size_val = parse_ram_size(answer, &is_percent); 615 if (is_percent && 616 (new_size_val < 0 || new_size_val > 100)) 617 continue; 618 if (!is_percent && new_size_val < 0) 619 continue; 620 size = new_size_val; 621 extend = false; 622 break; 623 } 624 mult = sizemult; 625 new_size_val = parse_disk_pos(answer, &mult, pm->sectorsize, 626 pm->dlcylsize, &extend); 627 628 if (strcmp(answer, dflt) == 0) 629 non_zero = p->def_size > 0; 630 else 631 non_zero = new_size_val > 0; 632 633 /* Some special cases when /usr is first given a size */ 634 if (old_size == 0 && non_zero && 635 strcmp(p->mount, "/usr") == 0) { 636 for (i = 0; i < pset->num; i++) { 637 if (strcmp(pset->infos[i].mount, "/") == 0) { 638 root = i; 639 break; 640 } 641 } 642 /* Remove space for /usr from / */ 643 if (root < pset->num && pset->infos[i].cur_part_id == 644 NO_PART) { 645 pset->infos[root].size -= p->def_size; 646 pset->cur_free_space += p->def_size; 647 } 648 /* hack to add free space to default sized /usr */ 649 if (strcmp(answer, dflt) == 0) { 650 size = p->def_size; 651 pset->infos[root].flags &= ~PUIFLAG_EXTEND; 652 p->flags |= PUIFLAG_EXTEND; 653 goto adjust_free; 654 } 655 } 656 if (new_size_val < 0) 657 continue; 658 size = new_size_val; 659 break; 660 } 661 662 daddr_t align = pset->parts->pscheme->get_part_alignment(pset->parts); 663 if (!is_ram_size) { 664 size = NUMSEC(size, mult, align); 665 } 666 if (p->flags & PUIFLAG_EXTEND) 667 p->flags &= ~PUIFLAG_EXTEND; 668 if (extend && (p->limit == 0 || p->limit > p->size)) { 669 for (size_t k = 0; k < pset->num; k++) 670 pset->infos[k].flags &= ~PUIFLAG_EXTEND; 671 p->flags |= PUIFLAG_EXTEND; 672 if (size == 0) 673 size = align; 674 } 675 if (p->limit != 0 && size > p->limit) 676 size = p->limit; 677 adjust_free: 678 if ((p->flags & (PUIFLG_IS_OUTER|PUIFLG_JUST_MOUNTPOINT)) == 0) 679 pset->cur_free_space += p->size - size; 680 p->size = is_percent ? -size : size; 681 set_pset_exit_str(pset); 682 683 return 0; 684 } 685 686 /* 687 * User interface to edit a "wanted" partition layout "pset" as first 688 * abstract phase (not concrete partitions). 689 * Make sure to have everything (at least theoretically) fit the 690 * available space. 691 * During editing we keep the part_usage_info and the menu_opts 692 * in pset in sync, that is: we always allocate just enough entries 693 * in pset->infos as we have usage infos in the list (pset->num), 694 * and two additional menu entries ("add a partition" and "select units"). 695 * The menu exit string changes depending on content, and implies 696 * abort while the partition set is not valid (does not fit). 697 * Return true when the user wants to continue (by editing the concrete 698 * partitions), return false to abort. 699 */ 700 bool 701 get_ptn_sizes(struct partition_usage_set *pset) 702 { 703 size_t num; 704 705 wclear(stdscr); 706 wrefresh(stdscr); 707 708 if (pset->menu_opts == NULL) 709 pset->menu_opts = calloc(pset->num+4, sizeof(*pset->menu_opts)); 710 711 pset->menu = -1; 712 num = fill_ptn_menu(pset); 713 714 pset->menu = new_menu(size_menu_title, pset->menu_opts, num, 715 3, -1, 12, 70, 716 MC_ALWAYS_SCROLL|MC_NOBOX|MC_NOCLEAR|MC_CONTINUOUS, 717 draw_size_menu_header, draw_size_menu_line, NULL, 718 NULL, size_menu_exit); 719 720 if (pset->menu < 0) { 721 free(pset->menu_opts); 722 pset->menu_opts = NULL; 723 return false; 724 } 725 726 pset->ok = true; 727 process_menu(pset->menu, pset); 728 729 free_menu(pset->menu); 730 free(pset->menu_opts); 731 pset->menu = -1; 732 pset->menu_opts = NULL; 733 734 return pset->ok; 735 } 736 737 static int 738 set_keep_existing(menudesc *m, void *arg) 739 { 740 ((arg_rep_int*)arg)->rv = LY_KEEPEXISTING; 741 return 0; 742 } 743 744 static int 745 set_edit_part_sizes(menudesc *m, void *arg) 746 { 747 ((arg_rep_int*)arg)->rv = LY_SETSIZES; 748 return 0; 749 } 750 751 static int 752 set_use_default_sizes(menudesc *m, void *arg) 753 { 754 ((arg_rep_int*)arg)->rv = LY_USEDEFAULT; 755 return 0; 756 } 757 758 /* 759 * Check if there is a reasonable pre-existing partition for 760 * NetBSD. 761 */ 762 static bool 763 check_existing_netbsd(struct disk_partitions *parts) 764 { 765 size_t nbsd_parts; 766 struct disk_part_info info; 767 768 nbsd_parts = 0; 769 for (part_id p = 0; p < parts->num_part; p++) { 770 if (!parts->pscheme->get_part_info(parts, p, &info)) 771 continue; 772 if (info.flags & (PTI_PSCHEME_INTERNAL|PTI_RAW_PART)) 773 continue; 774 if (info.nat_type && info.nat_type->generic_ptype == PT_root) 775 nbsd_parts++; 776 } 777 778 return nbsd_parts > 0; 779 } 780 781 /* 782 * Query a partition layout type (with available options depending on 783 * pre-existing partitions). 784 */ 785 static enum layout_type 786 ask_layout(struct disk_partitions *parts, bool have_existing) 787 { 788 arg_rep_int ai; 789 const char *args[2]; 790 int menu; 791 size_t num_opts; 792 menu_ent options[3], *opt; 793 794 args[0] = msg_string(parts->pscheme->name); 795 args[1] = msg_string(parts->pscheme->short_name); 796 ai.args.argv = args; 797 ai.args.argc = 2; 798 ai.rv = LY_SETSIZES; 799 800 memset(options, 0, sizeof(options)); 801 num_opts = 0; 802 opt = &options[0]; 803 804 if (have_existing) { 805 opt->opt_name = MSG_Keep_existing_partitions; 806 opt->opt_flags = OPT_EXIT; 807 opt->opt_action = set_keep_existing; 808 opt++; 809 num_opts++; 810 } 811 opt->opt_name = MSG_Set_Sizes; 812 opt->opt_flags = OPT_EXIT; 813 opt->opt_action = set_edit_part_sizes; 814 opt++; 815 num_opts++; 816 817 opt->opt_name = MSG_Use_Default_Parts; 818 opt->opt_flags = OPT_EXIT; 819 opt->opt_action = set_use_default_sizes; 820 opt++; 821 num_opts++; 822 823 menu = new_menu(MSG_Select_your_choice, options, num_opts, 824 -1, -10, 0, 0, MC_NOEXITOPT, NULL, NULL, NULL, NULL, NULL); 825 if (menu != -1) { 826 get_menudesc(menu)->expand_act = expand_all_option_texts; 827 process_menu(menu, &ai); 828 free_menu(menu); 829 } 830 831 return ai.rv; 832 } 833 834 static void 835 merge_part_with_wanted(struct disk_partitions *parts, part_id pno, 836 const struct disk_part_info *info, struct partition_usage_set *wanted, 837 size_t wanted_num, bool is_outer) 838 { 839 struct part_usage_info *infos; 840 841 /* 842 * does this partition match something in the wanted set? 843 */ 844 for (size_t i = 0; i < wanted_num; i++) { 845 if (wanted->infos[i].type != info->nat_type->generic_ptype) 846 continue; 847 if (wanted->infos[i].type == PT_root && 848 info->last_mounted != NULL && info->last_mounted[0] != 0 && 849 strcmp(info->last_mounted, wanted->infos[i].mount) != 0) 850 continue; 851 if (wanted->infos[i].cur_part_id != NO_PART) 852 continue; 853 wanted->infos[i].cur_part_id = pno; 854 wanted->infos[i].parts = parts; 855 wanted->infos[i].size = info->size; 856 wanted->infos[i].cur_start = info->start; 857 wanted->infos[i].flags &= ~PUIFLAG_EXTEND; 858 if (wanted->infos[i].fs_type != FS_UNUSED && 859 wanted->infos[i].type != PT_swap) 860 wanted->infos[i].instflags |= PUIINST_MOUNT; 861 if (is_outer) 862 wanted->infos[i].flags |= PUIFLG_IS_OUTER; 863 else 864 wanted->infos[i].flags &= ~PUIFLG_IS_OUTER; 865 return; 866 } 867 868 /* 869 * no match - if this is fromt the outer scheme, we are done. 870 * otherwise it must be inserted into the wanted set. 871 */ 872 if (is_outer) 873 return; 874 875 /* 876 * create a new entry for this 877 */ 878 infos = realloc(wanted->infos, sizeof(*infos)*(wanted->num+1)); 879 if (infos == NULL) 880 return; 881 wanted->infos = infos; 882 infos += wanted->num; 883 wanted->num++; 884 memset(infos, 0, sizeof(*infos)); 885 if (info->last_mounted != NULL && info->last_mounted[0] != 0) 886 strlcpy(infos->mount, info->last_mounted, 887 sizeof(infos->mount)); 888 infos->type = info->nat_type->generic_ptype; 889 infos->cur_part_id = pno; 890 infos->parts = parts; 891 infos->size = info->size; 892 infos->cur_start = info->start; 893 infos->fs_type = info->fs_type; 894 infos->fs_version = info->fs_sub_type; 895 if (is_outer) 896 infos->flags |= PUIFLG_IS_OUTER; 897 } 898 899 static bool 900 have_x11_by_default(void) 901 { 902 static const uint8_t def_sets[] = { MD_SETS_SELECTED }; 903 904 for (size_t i = 0; i < __arraycount(def_sets); i++) 905 if (def_sets[i] >= SET_X11_FIRST && 906 def_sets[i] <= SET_X11_LAST) 907 return true; 908 909 return false; 910 } 911 912 static void 913 fill_defaults(struct partition_usage_set *wanted, struct disk_partitions *parts, 914 daddr_t ptstart, daddr_t ptsize) 915 { 916 size_t i, root = ~0U, usr = ~0U, swap = ~0U, def_usr = ~0U; 917 daddr_t free_space, dump_space, required; 918 #if defined(DEFAULT_UFS2) && !defined(HAVE_UFS2_BOOT) 919 size_t boot = ~0U; 920 #endif 921 922 memset(wanted, 0, sizeof(*wanted)); 923 wanted->parts = parts; 924 wanted->num = __arraycount(default_parts_init); 925 wanted->infos = calloc(wanted->num, sizeof(*wanted->infos)); 926 if (wanted->infos == NULL) { 927 err_msg_win(err_outofmem); 928 return; 929 } 930 931 memcpy(wanted->infos, default_parts_init, sizeof(default_parts_init)); 932 933 #ifdef HAVE_TMPFS 934 if (get_ramsize() >= SMALL_RAM_SIZE) { 935 for (i = 0; i < wanted->num; i++) { 936 if (wanted->infos[i].type != PT_root || 937 wanted->infos[i].fs_type != FS_TMPFS) 938 continue; 939 /* default tmpfs to 1/4 RAM */ 940 wanted->infos[i].size = -25; 941 wanted->infos[i].def_size = -25; 942 break; 943 } 944 } 945 #endif 946 947 #ifdef MD_PART_DEFAULTS 948 MD_PART_DEFAULTS(pm, wanted->infos, wanted->num); 949 #endif 950 951 for (i = 0; i < wanted->num; i++) { 952 wanted->infos[i].parts = parts; 953 wanted->infos[i].cur_part_id = NO_PART; 954 if (wanted->infos[i].type == PT_undef && 955 wanted->infos[i].fs_type != FS_UNUSED) { 956 const struct part_type_desc *pt = 957 parts->pscheme->get_fs_part_type(PT_undef, 958 wanted->infos[i].fs_type, 959 wanted->infos[i].fs_version); 960 if (pt != NULL) 961 wanted->infos[i].type = pt->generic_ptype; 962 } 963 if (wanted->parts->parent != NULL && 964 wanted->infos[i].fs_type == FS_MSDOS) 965 wanted->infos[i].flags |= 966 PUIFLG_ADD_INNER|PUIFLAG_ADD_OUTER; 967 968 #if DEFSWAPSIZE == -1 969 if (wanted->infos[i].type == PT_swap) { 970 #ifdef MD_MAY_SWAP_TO 971 if (MD_MAY_SWAP_TO(wanted->parts->disk)) 972 #endif 973 wanted->infos[i].size = 974 get_ramsize() * (MEG / 512); 975 } 976 #endif 977 if (wanted->infos[i].type == PT_swap && swap > wanted->num) 978 swap = i; 979 #if defined(DEFAULT_UFS2) && !defined(HAVE_UFS2_BOOT) 980 if (wanted->infos[i].instflags & PUIINST_BOOT) 981 boot = i; 982 #endif 983 if (wanted->infos[i].type == PT_root) { 984 if (strcmp(wanted->infos[i].mount, "/") == 0) { 985 root = i; 986 } else if ( 987 strcmp(wanted->infos[i].mount, "/usr") == 0) { 988 if (wanted->infos[i].size > 0) 989 usr = i; 990 else 991 def_usr = i; 992 } 993 if (wanted->infos[i].fs_type == FS_UNUSED) 994 wanted->infos[i].fs_type = FS_BSDFFS; 995 if (wanted->infos[i].fs_type == FS_BSDFFS) { 996 #ifdef DEFAULT_UFS2 997 #ifndef HAVE_UFS2_BOOT 998 if (boot < wanted->num || i != root) 999 #endif 1000 wanted->infos[i].fs_version = 2; 1001 #endif 1002 } 1003 } 1004 } 1005 1006 /* 1007 * Now we have the defaults as if we were installing to an 1008 * empty disk. Merge the partitions in target range that are already 1009 * there (match with wanted) or are there additionaly. 1010 * The only thing outside of target range that we care for 1011 * are FAT partitions and a potential swap partition - we assume one 1012 * is enough. 1013 */ 1014 size_t num = wanted->num; 1015 if (parts->parent) { 1016 for (part_id pno = 0; pno < parts->parent->num_part; pno++) { 1017 struct disk_part_info info; 1018 1019 if (!parts->parent->pscheme->get_part_info( 1020 parts->parent, pno, &info)) 1021 continue; 1022 if (info.nat_type->generic_ptype != PT_swap && 1023 info.fs_type != FS_MSDOS) 1024 continue; 1025 merge_part_with_wanted(parts->parent, pno, &info, 1026 wanted, num, true); 1027 break; 1028 } 1029 } 1030 for (part_id pno = 0; pno < parts->num_part; pno++) { 1031 struct disk_part_info info; 1032 1033 if (!parts->pscheme->get_part_info(parts, pno, &info)) 1034 continue; 1035 1036 if (info.flags & PTI_PSCHEME_INTERNAL) 1037 continue; 1038 1039 if (info.nat_type->generic_ptype != PT_swap && 1040 (info.start < ptstart || 1041 (info.start + info.size) > (ptstart+ptsize))) 1042 continue; 1043 1044 merge_part_with_wanted(parts, pno, &info, 1045 wanted, num, false); 1046 } 1047 1048 daddr_t align = parts->pscheme->get_part_alignment(parts); 1049 1050 if (root < wanted->num && wanted->infos[root].cur_part_id == NO_PART) { 1051 daddr_t max_root_size = parts->disk_start + parts->disk_size; 1052 if (root_limit > 0) { 1053 /* Bah - bios can not read all the disk, limit root */ 1054 max_root_size = root_limit - parts->disk_start; 1055 } 1056 wanted->infos[root].limit = max_root_size; 1057 } 1058 1059 if (have_x11_by_default()) { 1060 daddr_t xsize = XNEEDMB * (MEG / 512); 1061 if (usr < wanted->num) { 1062 if (wanted->infos[usr].cur_part_id == NO_PART) { 1063 wanted->infos[usr].size += xsize; 1064 wanted->infos[usr].def_size += xsize; 1065 } 1066 } else if (root < wanted->num && 1067 wanted->infos[root].cur_part_id == NO_PART && 1068 (wanted->infos[root].limit == 0 || 1069 (wanted->infos[root].size + xsize) <= 1070 wanted->infos[root].limit)) { 1071 wanted->infos[root].size += xsize; 1072 } 1073 } 1074 if (wanted->infos[root].limit > 0 && 1075 wanted->infos[root].size > wanted->infos[root].limit) { 1076 if (usr < wanted->num) { 1077 /* move space from root to usr */ 1078 daddr_t spill = wanted->infos[root].size - 1079 wanted->infos[root].limit; 1080 spill = roundup(spill, align); 1081 wanted->infos[root].size = 1082 wanted->infos[root].limit; 1083 wanted->infos[usr].size = spill; 1084 } else { 1085 wanted->infos[root].size = 1086 wanted->infos[root].limit; 1087 } 1088 } 1089 1090 /* 1091 * Preliminary calc additional space to allocate and how much 1092 * we likely will have left over. Use that to do further 1093 * adjustments, so we don't present the user inherently 1094 * impossible defaults. 1095 */ 1096 free_space = parts->free_space; 1097 required = 0; 1098 if (root < wanted->num) 1099 required += wanted->infos[root].size; 1100 if (usr < wanted->num) 1101 required += wanted->infos[usr].size; 1102 else if (def_usr < wanted->num) 1103 required += wanted->infos[def_usr].def_size; 1104 free_space -= required; 1105 for (i = 0; i < wanted->num; i++) { 1106 if (i == root || i == usr) 1107 continue; /* already accounted above */ 1108 if (wanted->infos[i].cur_part_id != NO_PART) 1109 continue; 1110 if (wanted->infos[i].size == 0) 1111 continue; 1112 if (wanted->infos[i].flags 1113 & (PUIFLG_IS_OUTER|PUIFLG_JUST_MOUNTPOINT)) 1114 continue; 1115 free_space -= wanted->infos[i].size; 1116 } 1117 if (free_space < 0 && swap < wanted->num) { 1118 /* steel from swap partition */ 1119 daddr_t d = wanted->infos[swap].size; 1120 daddr_t inc = roundup(-free_space, align); 1121 if (inc > d) 1122 inc = d; 1123 free_space += inc; 1124 wanted->infos[swap].size -= inc; 1125 } 1126 if (root < wanted->num) { 1127 /* Add space for 2 system dumps to / (traditional) */ 1128 dump_space = get_ramsize() * (MEG/512); 1129 dump_space = roundup(dump_space, align); 1130 if (free_space > dump_space*2) 1131 dump_space *= 2; 1132 if (free_space > dump_space) 1133 wanted->infos[root].size += dump_space; 1134 } 1135 } 1136 1137 /* 1138 * We sort pset->infos to sync with pset->parts and 1139 * the cur_part_id, to allow using the same index into both 1140 * "array" in later phases. This may include inserting 1141 * dummy entries (when we do not actually want the 1142 * partition, but it is forced upon us, like RAW_PART in 1143 * disklabel). 1144 */ 1145 static void 1146 sort_and_sync_parts(struct partition_usage_set *pset) 1147 { 1148 struct part_usage_info *infos; 1149 size_t i, j, no; 1150 part_id pno; 1151 1152 pset->cur_free_space = pset->parts->free_space; 1153 1154 /* count non-empty entries that are not in pset->parts */ 1155 no = pset->parts->num_part; 1156 for (i = 0; i < pset->num; i++) { 1157 if (pset->infos[i].size == 0) 1158 continue; 1159 if (pset->infos[i].cur_part_id != NO_PART) 1160 continue; 1161 no++; 1162 } 1163 1164 /* allocate new infos */ 1165 infos = calloc(no, sizeof *infos); 1166 if (infos == NULL) 1167 return; 1168 1169 /* pre-initialize the first entires as dummy entries */ 1170 for (i = 0; i < pset->parts->num_part; i++) { 1171 infos[i].cur_part_id = NO_PART; 1172 infos[i].cur_flags = PTI_PSCHEME_INTERNAL; 1173 } 1174 /* 1175 * Now copy over eveything from our old entries that points to 1176 * a real partition. 1177 */ 1178 for (i = 0; i < pset->num; i++) { 1179 pno = pset->infos[i].cur_part_id; 1180 if (pno == NO_PART) 1181 continue; 1182 if (pset->parts != pset->infos[i].parts) 1183 continue; 1184 if (pset->infos[i].flags & PUIFLG_JUST_MOUNTPOINT) 1185 continue; 1186 if ((pset->infos[i].flags & (PUIFLG_IS_OUTER|PUIFLG_ADD_INNER)) 1187 == PUIFLG_IS_OUTER) 1188 continue; 1189 if (pno >= pset->parts->num_part) 1190 continue; 1191 memcpy(infos+pno, pset->infos+i, sizeof(*infos)); 1192 } 1193 /* Fill in the infos for real partitions where we had no data */ 1194 for (pno = 0; pno < pset->parts->num_part; pno++) { 1195 struct disk_part_info info; 1196 1197 if (infos[pno].cur_part_id != NO_PART) 1198 continue; 1199 1200 if (!pset->parts->pscheme->get_part_info(pset->parts, pno, 1201 &info)) 1202 continue; 1203 1204 infos[pno].parts = pset->parts; 1205 infos[pno].cur_part_id = pno; 1206 infos[pno].cur_flags = info.flags; 1207 infos[pno].size = info.size; 1208 infos[pno].type = info.nat_type->generic_ptype; 1209 infos[pno].cur_start = info.start; 1210 infos[pno].fs_type = info.fs_type; 1211 infos[pno].fs_version = info.fs_sub_type; 1212 } 1213 /* Add the non-partition entires after that */ 1214 j = pset->parts->num_part; 1215 for (i = 0; i < pset->num; i++) { 1216 if (j >= no) 1217 break; 1218 if (pset->infos[i].size == 0) 1219 continue; 1220 if (pset->infos[i].cur_part_id != NO_PART) 1221 continue; 1222 memcpy(infos+j, pset->infos+i, sizeof(*infos)); 1223 j++; 1224 } 1225 1226 /* done, replace infos */ 1227 free(pset->infos); 1228 pset->num = no; 1229 pset->infos = infos; 1230 } 1231 1232 #ifndef NO_CLONES 1233 /* 1234 * Convert clone entries with more than one source into 1235 * several entries with a single source each. 1236 */ 1237 static void 1238 normalize_clones(struct part_usage_info **infos, size_t *num) 1239 { 1240 size_t i, j, add_clones; 1241 struct part_usage_info *ui, *src, *target; 1242 struct disk_part_info info; 1243 struct selected_partition *clone; 1244 1245 for (add_clones = 0, i = 0; i < *num; i++) { 1246 if ((*infos)[i].clone_src != NULL && 1247 (*infos)[i].flags & PUIFLG_CLONE_PARTS && 1248 (*infos)[i].cur_part_id == NO_PART) 1249 add_clones += (*infos)[i].clone_src->num_sel-1; 1250 } 1251 if (add_clones == 0) 1252 return; 1253 1254 ui = calloc(*num+add_clones, sizeof(**infos)); 1255 if (ui == NULL) 1256 return; /* can not handle this well here, drop some clones */ 1257 1258 /* walk the list and dedup clones */ 1259 for (src = *infos, target = ui, i = 0; i < *num; i++) { 1260 if (src != target) 1261 *target = *src; 1262 if (target->clone_src != NULL && 1263 (target->flags & PUIFLG_CLONE_PARTS) && 1264 target->cur_part_id == NO_PART) { 1265 for (j = 0; j < src->clone_src->num_sel; j++) { 1266 if (j > 0) { 1267 target++; 1268 *target = *src; 1269 } 1270 target->clone_ndx = j; 1271 clone = &target->clone_src->selection[j]; 1272 clone->parts->pscheme->get_part_info( 1273 clone->parts, clone->id, &info); 1274 target->size = info.size; 1275 } 1276 } 1277 target++; 1278 src++; 1279 } 1280 *num += add_clones; 1281 assert((target-ui) >= 0 && (size_t)(target-ui) == *num); 1282 free(*infos); 1283 *infos = ui; 1284 } 1285 #endif 1286 1287 static void 1288 apply_settings_to_partitions(struct pm_devs *p, struct disk_partitions *parts, 1289 struct partition_usage_set *wanted, daddr_t start, daddr_t size) 1290 { 1291 size_t i, exp_ndx = ~0U; 1292 daddr_t planned_space = 0, nsp, from, align; 1293 struct disk_part_info *infos; 1294 #ifndef NO_CLONES 1295 struct disk_part_info cinfo, srcinfo; 1296 struct selected_partition *sp; 1297 #endif 1298 struct disk_part_free_space space; 1299 struct disk_partitions *ps = NULL; 1300 part_id pno, new_part_id; 1301 1302 #ifndef NO_CLONES 1303 normalize_clones(&wanted->infos, &wanted->num); 1304 #endif 1305 1306 infos = calloc(wanted->num, sizeof(*infos)); 1307 if (infos == NULL) { 1308 err_msg_win(err_outofmem); 1309 return; 1310 } 1311 1312 align = wanted->parts->pscheme->get_part_alignment(wanted->parts); 1313 1314 /* 1315 * Pass one: calculate space available for expanding 1316 * the marked partition. 1317 */ 1318 for (i = 0; i < wanted->num; i++) { 1319 if ((wanted->infos[i].flags & PUIFLAG_EXTEND) && 1320 exp_ndx == ~0U) 1321 exp_ndx = i; 1322 if (wanted->infos[i].flags & PUIFLG_JUST_MOUNTPOINT) 1323 continue; 1324 nsp = wanted->infos[i].size; 1325 if (wanted->infos[i].cur_part_id != NO_PART) { 1326 ps = wanted->infos[i].flags & PUIFLG_IS_OUTER ? 1327 parts->parent : parts; 1328 1329 ps->pscheme->get_part_info(ps, 1330 wanted->infos[i].cur_part_id, &infos[i]); 1331 if (!(wanted->infos[i].flags & PUIFLG_IS_OUTER)) 1332 nsp -= infos[i].size; 1333 } 1334 if (nsp > 0) 1335 planned_space += roundup(nsp, align); 1336 } 1337 1338 /* 1339 * Expand the pool partition (or shrink, if we overran), 1340 */ 1341 if (exp_ndx < wanted->num) 1342 wanted->infos[exp_ndx].size += 1343 parts->free_space - planned_space; 1344 1345 /* 1346 * Now it gets tricky: we want the wanted partitions in order 1347 * as defined, but any already existing partitions should not 1348 * be moved. We allow them to change size though. 1349 * To keep it simple, we just assign in order and skip blocked 1350 * spaces. This may shuffle the order of the resulting partitions 1351 * compared to the wanted list. 1352 */ 1353 1354 /* Adjust sizes of existing partitions */ 1355 for (i = 0; i < wanted->num; i++) { 1356 ps = wanted->infos[i].flags & PUIFLG_IS_OUTER ? 1357 parts->parent : parts; 1358 const struct part_usage_info *want = &wanted->infos[i]; 1359 1360 if (want->cur_part_id == NO_PART) 1361 continue; 1362 if (i == exp_ndx) /* the exp. part. can not exist yet */ 1363 continue; 1364 daddr_t free_size = ps->pscheme->max_free_space_at(ps, 1365 infos[i].start); 1366 if (free_size < wanted->infos[i].size) 1367 continue; 1368 if (infos[i].size != wanted->infos[i].size) { 1369 infos[i].size = wanted->infos[i].size; 1370 ps->pscheme->set_part_info(ps, want->cur_part_id, 1371 &infos[i], NULL); 1372 } 1373 } 1374 1375 from = -1; 1376 /* 1377 * First add all outer partitions - we need to align those exactly 1378 * with the inner counterpart later. 1379 */ 1380 if (parts->parent) { 1381 ps = parts->parent; 1382 daddr_t outer_align = ps->pscheme->get_part_alignment(ps); 1383 1384 for (i = 0; i < wanted->num; i++) { 1385 struct part_usage_info *want = &wanted->infos[i]; 1386 1387 if (want->cur_part_id != NO_PART) 1388 continue; 1389 if (!(want->flags & PUIFLAG_ADD_OUTER)) 1390 continue; 1391 if (want->size <= 0) 1392 continue; 1393 1394 size_t cnt = ps->pscheme->get_free_spaces(ps, 1395 &space, 1, want->size-2*outer_align, 1396 outer_align, from, -1); 1397 1398 if (cnt == 0) /* no free space for this partition */ 1399 continue; 1400 1401 infos[i].start = space.start; 1402 infos[i].size = min(want->size, space.size); 1403 infos[i].nat_type = 1404 ps->pscheme->get_fs_part_type( 1405 want->type, want->fs_type, want->fs_version); 1406 infos[i].last_mounted = want->mount; 1407 infos[i].fs_type = want->fs_type; 1408 infos[i].fs_sub_type = want->fs_version; 1409 new_part_id = ps->pscheme->add_partition(ps, 1410 &infos[i], NULL); 1411 if (new_part_id == NO_PART) 1412 continue; /* failed to add, skip */ 1413 1414 ps->pscheme->get_part_info(ps, 1415 new_part_id, &infos[i]); 1416 want->cur_part_id = new_part_id; 1417 1418 want->flags |= PUIFLG_ADD_INNER|PUIFLG_IS_OUTER; 1419 from = rounddown(infos[i].start + 1420 infos[i].size+outer_align, outer_align); 1421 } 1422 } 1423 1424 /* 1425 * Now add new inner partitions (and cloned partitions) 1426 */ 1427 for (i = 0; i < wanted->num && from < 1428 (wanted->parts->disk_size + wanted->parts->disk_start); i++) { 1429 struct part_usage_info *want = &wanted->infos[i]; 1430 1431 if (want->cur_part_id != NO_PART) 1432 continue; 1433 if (want->flags & (PUIFLG_JUST_MOUNTPOINT|PUIFLG_IS_OUTER)) 1434 continue; 1435 #ifndef NO_CLONES 1436 if ((want->flags & PUIFLG_CLONE_PARTS) && 1437 want->clone_src != NULL && 1438 want->clone_ndx < want->clone_src->num_sel) { 1439 sp = &want->clone_src->selection[want->clone_ndx]; 1440 if (!sp->parts->pscheme->get_part_info( 1441 sp->parts, sp->id, &srcinfo)) 1442 continue; 1443 if (!wanted->parts->pscheme-> 1444 adapt_foreign_part_info(wanted->parts, 1445 &cinfo, sp->parts->pscheme, &srcinfo)) 1446 continue; 1447 1448 /* find space for cinfo and add a partition */ 1449 size_t cnt = wanted->parts->pscheme->get_free_spaces( 1450 wanted->parts, &space, 1, want->size-align, align, 1451 from, -1); 1452 if (cnt == 0) 1453 cnt = wanted->parts->pscheme->get_free_spaces( 1454 wanted->parts, &space, 1, 1455 want->size-5*align, align, from, -1); 1456 1457 if (cnt == 0) 1458 continue; /* no free space for this clone */ 1459 1460 infos[i] = cinfo; 1461 infos[i].start = space.start; 1462 new_part_id = wanted->parts->pscheme->add_partition( 1463 wanted->parts, &infos[i], NULL); 1464 } else { 1465 #else 1466 { 1467 #endif 1468 if (want->size <= 0) 1469 continue; 1470 size_t cnt = wanted->parts->pscheme->get_free_spaces( 1471 wanted->parts, &space, 1, want->size-align, align, 1472 from, -1); 1473 if (cnt == 0) 1474 cnt = wanted->parts->pscheme->get_free_spaces( 1475 wanted->parts, &space, 1, 1476 want->size-5*align, align, from, -1); 1477 1478 if (cnt == 0) 1479 continue; /* no free space for this partition */ 1480 1481 infos[i].start = space.start; 1482 infos[i].size = min(want->size, space.size); 1483 infos[i].nat_type = 1484 wanted->parts->pscheme->get_fs_part_type( 1485 want->type, want->fs_type, want->fs_version); 1486 infos[i].last_mounted = want->mount; 1487 infos[i].fs_type = want->fs_type; 1488 infos[i].fs_sub_type = want->fs_version; 1489 if (want->fs_type != FS_UNUSED && 1490 want->type != PT_swap) { 1491 want->instflags |= PUIINST_NEWFS; 1492 if (want->mount[0] != 0) 1493 want->instflags |= PUIINST_MOUNT; 1494 } 1495 new_part_id = wanted->parts->pscheme->add_partition( 1496 wanted->parts, &infos[i], NULL); 1497 } 1498 1499 if (new_part_id == NO_PART) 1500 continue; /* failed to add, skip */ 1501 1502 wanted->parts->pscheme->get_part_info( 1503 wanted->parts, new_part_id, &infos[i]); 1504 from = rounddown(infos[i].start+infos[i].size+align, align); 1505 } 1506 1507 1508 /* 1509 * If there are any outer partitions that we need as inner ones 1510 * too, add them to the inner partitioning scheme. 1511 */ 1512 for (i = 0; i < wanted->num; i++) { 1513 struct part_usage_info *want = &wanted->infos[i]; 1514 1515 if (want->cur_part_id == NO_PART) 1516 continue; 1517 if (want->flags & PUIFLG_JUST_MOUNTPOINT) 1518 continue; 1519 if (want->size <= 0) 1520 continue; 1521 1522 if ((want->flags & (PUIFLG_ADD_INNER|PUIFLG_IS_OUTER)) != 1523 (PUIFLG_ADD_INNER|PUIFLG_IS_OUTER)) 1524 continue; 1525 1526 infos[i].start = want->cur_start; 1527 infos[i].size = want->size; 1528 infos[i].nat_type = wanted->parts->pscheme->get_fs_part_type( 1529 want->type, want->fs_type, want->fs_version); 1530 infos[i].last_mounted = want->mount; 1531 infos[i].fs_type = want->fs_type; 1532 infos[i].fs_sub_type = want->fs_version; 1533 1534 if (wanted->parts->pscheme->add_outer_partition 1535 != NULL) 1536 new_part_id = wanted->parts->pscheme-> 1537 add_outer_partition( 1538 wanted->parts, &infos[i], NULL); 1539 else 1540 new_part_id = wanted->parts->pscheme-> 1541 add_partition( 1542 wanted->parts, &infos[i], NULL); 1543 1544 if (new_part_id == NO_PART) 1545 continue; /* failed to add, skip */ 1546 1547 wanted->parts->pscheme->get_part_info( 1548 wanted->parts, new_part_id, &infos[i]); 1549 want->parts = wanted->parts; 1550 if (want->fs_type != FS_UNUSED && 1551 want->type != PT_swap) { 1552 want->instflags |= PUIINST_NEWFS; 1553 if (want->mount[0] != 0) 1554 want->instflags |= PUIINST_MOUNT; 1555 } 1556 } 1557 1558 /* 1559 * Note: all part_ids are invalid now, as we have added things! 1560 */ 1561 for (i = 0; i < wanted->num; i++) 1562 wanted->infos[i].cur_part_id = NO_PART; 1563 for (pno = 0; pno < parts->num_part; pno++) { 1564 struct disk_part_info t; 1565 1566 if (!parts->pscheme->get_part_info(parts, pno, &t)) 1567 continue; 1568 1569 for (i = 0; i < wanted->num; i++) { 1570 if (wanted->infos[i].cur_part_id != NO_PART) 1571 continue; 1572 if (wanted->infos[i].size <= 0) 1573 continue; 1574 if (t.start == infos[i].start) { 1575 wanted->infos[i].cur_part_id = pno; 1576 wanted->infos[i].cur_start = infos[i].start; 1577 wanted->infos[i].cur_flags = infos[i].flags; 1578 break; 1579 } 1580 } 1581 } 1582 free(infos); 1583 1584 /* sort, and sync part ids and wanted->infos[] indices */ 1585 sort_and_sync_parts(wanted); 1586 } 1587 1588 static void 1589 replace_by_default(struct pm_devs *p, struct disk_partitions *parts, 1590 daddr_t start, daddr_t size, struct partition_usage_set *wanted) 1591 { 1592 1593 if (start == 0 && size == parts->disk_size) 1594 parts->pscheme->delete_all_partitions(parts); 1595 else if (parts->pscheme->delete_partitions_in_range != NULL) 1596 parts->pscheme->delete_partitions_in_range(parts, start, size); 1597 else 1598 assert(parts->num_part == 0); 1599 1600 fill_defaults(wanted, parts, start, size); 1601 apply_settings_to_partitions(p, parts, wanted, start, size); 1602 } 1603 1604 static bool 1605 edit_with_defaults(struct pm_devs *p, struct disk_partitions *parts, 1606 daddr_t start, daddr_t size, struct partition_usage_set *wanted) 1607 { 1608 bool ok; 1609 1610 fill_defaults(wanted, parts, start, size); 1611 ok = get_ptn_sizes(wanted); 1612 if (ok) 1613 apply_settings_to_partitions(p, parts, wanted, start, size); 1614 return ok; 1615 } 1616 1617 /* 1618 * md back-end code for menu-driven BSD disklabel editor. 1619 * returns 0 on failure, 1 on success. 1620 * fills the install target with a list for newfs/fstab. 1621 */ 1622 bool 1623 make_bsd_partitions(struct install_partition_desc *install) 1624 { 1625 struct disk_partitions *parts = pm->parts; 1626 const struct disk_partitioning_scheme *pscheme; 1627 struct partition_usage_set wanted; 1628 enum layout_type layoutkind = LY_SETSIZES; 1629 bool have_existing; 1630 1631 if (pm && pm->no_part && parts == NULL) 1632 return true; 1633 1634 if (parts == NULL) { 1635 pscheme = select_part_scheme(pm, NULL, !pm->no_mbr, NULL); 1636 if (pscheme == NULL) 1637 return false; 1638 parts = pscheme->create_new_for_disk(pm->diskdev, 1639 0, pm->dlsize, true, NULL); 1640 if (parts == NULL) 1641 return false; 1642 pm->parts = parts; 1643 } else { 1644 pscheme = parts->pscheme; 1645 } 1646 1647 if (pscheme->secondary_partitions) { 1648 struct disk_partitions *p; 1649 1650 p = pscheme->secondary_partitions(parts, pm->ptstart, false); 1651 if (p) { 1652 parts = p; 1653 pscheme = parts->pscheme; 1654 } 1655 } 1656 1657 have_existing = check_existing_netbsd(parts); 1658 1659 /* 1660 * Initialize global variables that track space used on this disk. 1661 */ 1662 if (pm->ptsize == 0) 1663 pm->ptsize = pm->dlsize - pm->ptstart; 1664 if (pm->dlsize == 0) 1665 pm->dlsize = pm->ptstart + pm->ptsize; 1666 1667 if (logfp) fprintf(logfp, "dlsize=%" PRId64 " ptsize=%" PRId64 1668 " ptstart=%" PRId64 "\n", 1669 pm->dlsize, pm->ptsize, pm->ptstart); 1670 1671 if (pm->current_cylsize == 0) 1672 pm->current_cylsize = pm->dlcylsize; 1673 1674 /* Ask for layout type -- standard or special */ 1675 if (partman_go == 0) { 1676 char bsd_size[6], min_size[6], x_size[6]; 1677 1678 humanize_number(bsd_size, sizeof(bsd_size), 1679 (uint64_t)pm->ptsize*pm->sectorsize, 1680 "", HN_AUTOSCALE, HN_B | HN_NOSPACE | HN_DECIMAL); 1681 humanize_number(min_size, sizeof(min_size), 1682 (uint64_t)(DEFROOTSIZE + DEFSWAPSIZE + DEFUSRSIZE)*MEG, 1683 "", HN_AUTOSCALE, HN_B | HN_NOSPACE | HN_DECIMAL); 1684 humanize_number(x_size, sizeof(x_size), 1685 (uint64_t)(DEFROOTSIZE + DEFSWAPSIZE + DEFUSRSIZE 1686 + XNEEDMB)*MEG, 1687 "", HN_AUTOSCALE, HN_B | HN_NOSPACE | HN_DECIMAL); 1688 1689 msg_display_subst( 1690 have_existing ? MSG_layout_prologue_existing 1691 : MSG_layout_prologue_none, 6, pm->diskdev, 1692 msg_string(parts->pscheme->name), 1693 msg_string(parts->pscheme->short_name), 1694 bsd_size, min_size, x_size); 1695 msg_display_add_subst(MSG_layout_main, 6, 1696 pm->diskdev, 1697 msg_string(parts->pscheme->name), 1698 msg_string(parts->pscheme->short_name), 1699 bsd_size, min_size, x_size); 1700 msg_display_add("\n\n"); 1701 layoutkind = ask_layout(parts, have_existing); 1702 } 1703 1704 if (layoutkind == LY_USEDEFAULT) { 1705 replace_by_default(pm, parts, pm->ptstart, pm->ptsize, 1706 &wanted); 1707 } else if (layoutkind == LY_SETSIZES) { 1708 if (!edit_with_defaults(pm, parts, pm->ptstart, pm->ptsize, 1709 &wanted)) { 1710 free_usage_set(&wanted); 1711 return false; 1712 } 1713 } else { 1714 usage_set_from_parts(&wanted, parts); 1715 } 1716 1717 /* 1718 * OK, we have a partition table. Give the user the chance to 1719 * edit it and verify it's OK, or abort altogether. 1720 */ 1721 for (;;) { 1722 int rv = edit_and_check_label(pm, &wanted, true); 1723 if (rv == 0) { 1724 msg_display(MSG_abort_part); 1725 free_usage_set(&wanted); 1726 return false; 1727 } 1728 /* update install infos */ 1729 install->num = wanted.num; 1730 install->infos = wanted.infos; 1731 /* and check them */ 1732 if (check_partitions(install)) 1733 break; 1734 } 1735 1736 /* we moved infos from wanted to install target */ 1737 wanted.infos = NULL; 1738 free_usage_set(&wanted); 1739 1740 /* Everything looks OK. */ 1741 return true; 1742 } 1743 1744 #ifndef MD_NEED_BOOTBLOCK 1745 #define MD_NEED_BOOTBLOCK(A) true 1746 #endif 1747 1748 /* 1749 * check that there is at least a / somewhere. 1750 */ 1751 bool 1752 check_partitions(struct install_partition_desc *install) 1753 { 1754 #ifdef HAVE_BOOTXX_xFS 1755 int rv = 1; 1756 char *bootxx; 1757 #endif 1758 #ifndef HAVE_UFS2_BOOT 1759 size_t i; 1760 #endif 1761 1762 #ifdef HAVE_BOOTXX_xFS 1763 if (MD_NEED_BOOTBLOCK(install)) { 1764 /* check if we have boot code for the root partition type */ 1765 bootxx = bootxx_name(install); 1766 if (bootxx != NULL) { 1767 rv = access(bootxx, R_OK); 1768 free(bootxx); 1769 } else 1770 rv = -1; 1771 if (rv != 0) { 1772 hit_enter_to_continue(NULL, MSG_No_Bootcode); 1773 return false; 1774 } 1775 } 1776 #endif 1777 #ifndef HAVE_UFS2_BOOT 1778 if (MD_NEED_BOOTBLOCK(install)) { 1779 for (i = 0; i < install->num; i++) { 1780 if (install->infos[i].type != PT_root) 1781 continue; 1782 if (strcmp(install->infos[i].mount, "/") != 0) 1783 continue; 1784 if (install->infos[i].fs_type != FS_BSDFFS) 1785 continue; 1786 if (install->infos[i].fs_version != 2) 1787 continue; 1788 hit_enter_to_continue(NULL, MSG_cannot_ufs2_root); 1789 return false; 1790 } 1791 } 1792 #endif 1793 1794 return md_check_partitions(install); 1795 } 1796