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