1/* $NetBSD: mbr.c,v 1.89.2.2.6.1 2013/10/20 13:43:23 bouyer Exp $ */ 2 3/* 4 * Copyright 1997 Piermont Information Systems Inc. 5 * All rights reserved. 6 * 7 * Written by Philip A. Nelson for Piermont Information Systems Inc. 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted provided that the following conditions 11 * are met: 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 2. Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in the 16 * documentation and/or other materials provided with the distribution. 17 * 3. The name of Piermont Information Systems Inc. may not be used to endorse 18 * or promote products derived from this software without specific prior 19 * written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY PIERMONT INFORMATION SYSTEMS INC. ``AS IS'' 22 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 * ARE DISCLAIMED. IN NO EVENT SHALL PIERMONT INFORMATION SYSTEMS INC. BE 25 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 26 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 27 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 28 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 29 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 30 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 31 * THE POSSIBILITY OF SUCH DAMAGE. 32 * 33 */ 34 35/* 36 * Following applies to the geometry guessing code 37 */ 38 39/* 40 * Mach Operating System 41 * Copyright (c) 1992 Carnegie Mellon University 42 * All Rights Reserved. 43 * 44 * Permission to use, copy, modify and distribute this software and its 45 * documentation is hereby granted, provided that both the copyright 46 * notice and this permission notice appear in all copies of the 47 * software, derivative works or modified versions, and any portions 48 * thereof, and that both notices appear in supporting documentation. 49 * 50 * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" 51 * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR 52 * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. 53 * 54 * Carnegie Mellon requests users of this software to return to 55 * 56 * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU 57 * School of Computer Science 58 * Carnegie Mellon University 59 * Pittsburgh PA 15213-3890 60 * 61 * any improvements or extensions that they make and grant Carnegie Mellon 62 * the rights to redistribute these changes. 63 */ 64 65/* mbr.c -- DOS Master Boot Record editing code */ 66 67#include <sys/param.h> 68#include <sys/types.h> 69#include <stdio.h> 70#include <unistd.h> 71#include <fcntl.h> 72#include <util.h> 73#include "defs.h" 74#include "mbr.h" 75#include "md.h" 76#include "msg_defs.h" 77#include "menu_defs.h" 78#include "endian.h" 79 80#define NO_BOOTMENU (-0x100) 81 82#define MAXCYL 1023 /* Possibly 1024 */ 83#define MAXHEAD 255 /* Possibly 256 */ 84#define MAXSECTOR 63 85 86struct part_id { 87 int id; 88 const char *name; 89} part_ids[] = { 90 {0, "unused"}, 91 {MBR_PTYPE_NETBSD, "NetBSD"}, 92 {MBR_PTYPE_EXT_LBA, "Extended partition, LBA"}, 93 {MBR_PTYPE_386BSD, "FreeBSD/386BSD"}, 94 {MBR_PTYPE_OPENBSD, "OpenBSD"}, 95 {MBR_PTYPE_LNXEXT2, "Linux native"}, 96 {MBR_PTYPE_LNXSWAP, "Linux swap"}, 97 {MBR_PTYPE_FAT12, "DOS FAT12"}, 98 {MBR_PTYPE_FAT16S, "DOS FAT16, <32M"}, 99 {MBR_PTYPE_FAT16B, "DOS FAT16, >32M"}, 100 {MBR_PTYPE_FAT16L, "Windows FAT16, LBA"}, 101 {MBR_PTYPE_FAT32, "Windows FAT32"}, 102 {MBR_PTYPE_FAT32L, "Windows FAT32, LBA"}, 103 {MBR_PTYPE_NTFSVOL, "NTFS volume set"}, 104 {MBR_PTYPE_NTFS, "NTFS"}, 105 {MBR_PTYPE_PREP, "PReP Boot"}, 106#ifdef MBR_PTYPE_SOLARIS 107 {MBR_PTYPE_SOLARIS, "Solaris"}, 108#endif 109 {-1, "Unknown"}, 110}; 111 112static int get_mapping(struct mbr_partition *, int, int *, int *, int *, 113 daddr_t *); 114static void convert_mbr_chs(int, int, int, uint8_t *, uint8_t *, 115 uint8_t *, uint32_t); 116static void get_ptn_alignment(struct mbr_partition *); 117 118static unsigned int ptn_alignment; 119static unsigned int ptn_0_offset; 120 121/* 122 * Notes on the extended partition editor. 123 * 124 * The extended partition structure is actually a singly linked list. 125 * Each of the 'mbr' sectors can only contain 2 items, the first describes 126 * a user partition (relative to that mbr sector), the second describes 127 * the following partition (relative to the start of the extended partition). 128 * 129 * The 'start' sector for the user partition is always the size of one 130 * track - very often 63. The extended partitions themselves should 131 * always start on a cylinder boundary using the BIOS geometry - often 132 * 16065 sectors per cylinder. 133 * 134 * The disk is also always described in increasing sector order. 135 * 136 * During editing we keep the mbr sectors accurate (it might have been 137 * easier to use absolute sector numbers though), and keep a copy of the 138 * entire sector - to preserve any information any other OS has tried 139 * to squirrel away in the (apparently) unused space. 140 * 141 * For simplicity we add entries for unused space. These should not 142 * get written to the disk. 143 * 144 * Typical disk (with some small numbers): 145 * 146 * 0 -> a 63 37 dos 147 * b 100 1000 extended LBA (type 15) 148 * 149 * 100 -> a 63 37 user 150 * b 100 200 extended partiton (type 5) 151 * 152 * 200 -> a 63 37 user 153 * b 200 300 extended partiton (type 5) 154 * 155 * 300 -> a 63 37 user 156 * b 0 0 0 (end of chain) 157 * 158 * If there is a gap, the 'b' partition will start beyond the area 159 * described by the 'a' partition. 160 * 161 * While writing this comment, I can't remember what happens is there 162 * is space at the start of the extended partition. 163 */ 164 165#ifndef debug_extended 166#define dump_mbr(mbr, msg) 167#else 168void 169dump_mbr(mbr_info_t *mbr, const char *msg) 170{ 171 int i; 172 173 fprintf(stderr, "%s: bsec %d\n", msg, bsec); 174 do { 175 fprintf(stderr, "%9p: %9d %9p %6.6s:", 176 mbr, mbr->sector, mbr->extended, 177 mbr->prev_ext, mbr->last_mounted); 178 for (i = 0; i < 4; i++) 179 fprintf(stderr, " %*d %9d %9d %9d,\n", 180 i ? 41 : 3, 181 mbr->mbr.mbr_parts[i].mbrp_type, 182 mbr->mbr.mbr_parts[i].mbrp_start, 183 mbr->mbr.mbr_parts[i].mbrp_size, 184 mbr->mbr.mbr_parts[i].mbrp_start + 185 mbr->mbr.mbr_parts[i].mbrp_size); 186 } while ((mbr = mbr->extended)); 187} 188#endif 189 190/* 191 * To be used only on ports which cannot provide any bios geometry 192 */ 193int 194set_bios_geom_with_mbr_guess(void) 195{ 196 int cyl, head; 197 daddr_t sec; 198 199 read_mbr(diskdev, &mbr); 200 msg_display(MSG_nobiosgeom, dlcyl, dlhead, dlsec); 201 if (guess_biosgeom_from_mbr(&mbr, &cyl, &head, &sec) >= 0) 202 msg_display_add(MSG_biosguess, cyl, head, sec); 203 set_bios_geom(cyl, head, sec); 204 return edit_mbr(&mbr); 205} 206 207/* 208 * get C/H/S geometry from user via menu interface and 209 * store in globals. 210 */ 211void 212set_bios_geom(int cyl, int head, int sec) 213{ 214 char res[80]; 215 216 msg_display_add(MSG_setbiosgeom); 217 218 do { 219 snprintf(res, 80, "%d", sec); 220 msg_prompt_add(MSG_sectors, res, res, 80); 221 bsec = atoi(res); 222 } while (bsec <= 0 || bsec > 63); 223 224 do { 225 snprintf(res, 80, "%d", head); 226 msg_prompt_add(MSG_heads, res, res, 80); 227 bhead = atoi(res); 228 } while (bhead <= 0 || bhead > 256); 229 230 bcyl = dlsize / bsec / bhead; 231 if (dlsize != bcyl * bsec * bhead) 232 bcyl++; 233} 234 235#ifdef notdef 236void 237disp_cur_geom(void) 238{ 239 240 msg_display_add(MSG_realgeom, dlcyl, dlhead, dlsec); 241 msg_display_add(MSG_biosgeom, bcyl, bhead, bsec); 242} 243#endif 244 245 246/* 247 * Then, the partition stuff... 248 */ 249 250/* 251 * If we change the mbr partitioning, the we must remove any references 252 * in the netbsd disklabel to the part we changed. 253 */ 254static void 255remove_old_partitions(uint start, int64_t size) 256{ 257 partinfo *p; 258 uint end; 259 260 if (size > 0) { 261 end = start + size; 262 } else { 263 end = start; 264 start = end - size; 265 } 266 267 if (end == 0) 268 return; 269 270 for (p = oldlabel; p < oldlabel + nelem(oldlabel); p++) { 271 if (p->pi_offset >= end || p->pi_offset + p->pi_size <= start) 272 continue; 273 memset(p, 0, sizeof *p); 274 } 275} 276 277static int 278find_mbr_space(struct mbr_sector *mbrs, uint *start, uint *size, uint from, int ignore) 279{ 280 uint sz; 281 int i; 282 uint s, e; 283 284 check_again: 285 sz = dlsize - from; 286 for (i = 0; i < MBR_PART_COUNT; i++) { 287 if (i == ignore) 288 continue; 289 s = mbrs->mbr_parts[i].mbrp_start; 290 e = s + mbrs->mbr_parts[i].mbrp_size; 291 if (s <= from && e > from) { 292 from = e; 293 goto check_again; 294 } 295 if (s > from && s - from < sz) 296 sz = s - from; 297 } 298 if (sz == 0) 299 return -1; 300 if (start != NULL) 301 *start = from; 302 if (size != NULL) 303 *size = sz; 304 return 0; 305} 306 307static struct mbr_partition * 308get_mbrp(mbr_info_t **mbrip, int opt) 309{ 310 mbr_info_t *mbri = *mbrip; 311 312 if (opt >= MBR_PART_COUNT) 313 for (opt -= MBR_PART_COUNT - 1; opt; opt--) 314 mbri = mbri->extended; 315 316 *mbrip = mbri; 317 return &mbri->mbr.mbr_parts[opt]; 318} 319 320static int 321err_msg_win(const char *errmsg) 322{ 323 const char *cont; 324 int l, l1; 325 326 errmsg = msg_string(errmsg); 327 cont = msg_string(MSG_Hit_enter_to_continue); 328 329 l = strlen(errmsg); 330 l1 = strlen(cont); 331 if (l < l1) 332 l = l1; 333 334 msg_prompt_win("%s.\n%s", -1, 18, l + 5, 4, 335 NULL, NULL, 1, errmsg, cont); 336 return 0; 337} 338 339static int 340set_mbr_type(menudesc *m, void *arg) 341{ 342 mbr_info_t *mbri = arg; 343 mbr_info_t *ombri = arg; 344 mbr_info_t *ext; 345 struct mbr_partition *mbrp; 346 char *cp; 347 int opt = mbri->opt; 348 int type; 349 u_int start, sz; 350 int i; 351 char numbuf[5]; 352 353 dump_mbr(ombri, "set type"); 354 355 mbrp = get_mbrp(&mbri, opt); 356 if (opt >= MBR_PART_COUNT) 357 opt = 0; 358 359 type = m->cursel; 360 if (type == 0) 361 return 1; 362 type = part_ids[type - 1].id; 363 while (type == -1) { 364 snprintf(numbuf, sizeof numbuf, "%u", mbrp->mbrp_type); 365 msg_prompt_win(MSG_get_ptn_id, -1, 18, 0, 0, 366 numbuf, numbuf, sizeof numbuf); 367 type = strtoul(numbuf, &cp, 0); 368 if (*cp != 0) 369 type = -1; 370 } 371 372 if (type == mbrp->mbrp_type) 373 /* type not changed... */ 374 return 1; 375 376 mbri->last_mounted[opt < MBR_PART_COUNT ? opt : 0] = NULL; 377 378 if (MBR_IS_EXTENDED(mbrp->mbrp_type)) { 379 /* deleting extended partition.... */ 380 if (mbri->sector || mbri->extended->extended) 381 /* We should have stopped this happening... */ 382 return err_msg_win("can't delete extended"); 383 free(mbri->extended); 384 mbri->extended = NULL; 385 } 386 387 if (type == 0) { 388 /* Deleting partition */ 389 mbrp->mbrp_type = 0; 390 /* Remove references to this space from the NetBSD label */ 391 remove_old_partitions(mbri->sector + mbrp->mbrp_start, 392 mbrp->mbrp_size); 393#ifdef BOOTSEL 394 if (ombri->bootsec == mbri->sector + mbrp->mbrp_start) 395 ombri->bootsec = 0; 396 397 memset(mbri->mbrb.mbrbs_nametab[opt], 0, 398 sizeof mbri->mbrb.mbrbs_nametab[opt]); 399#endif 400 if (mbri->sector == 0) { 401 /* A main partition */ 402 memset(mbrp, 0, sizeof *mbrp); 403 return 1; 404 } 405 406 /* Merge with previous and next free areas */ 407 ext = mbri->prev_ext; 408 if (ext != NULL && ext->mbr.mbr_parts[0].mbrp_type == 0) { 409 /* previous was free - back up one entry */ 410 mbri = ext; 411 ombri->opt--; 412 } 413 while ((ext = mbri->extended)) { 414 if (ext->mbr.mbr_parts[0].mbrp_type != 0) 415 break; 416 sz = ext->mbr.mbr_parts[0].mbrp_start + 417 ext->mbr.mbr_parts[0].mbrp_size; 418 /* Increase size of our (empty) partition */ 419 mbri->mbr.mbr_parts[0].mbrp_size += sz; 420 /* Make us describe the next partition */ 421 mbri->mbr.mbr_parts[1] = ext->mbr.mbr_parts[1]; 422 /* fix list of extended partitions */ 423 mbri->extended = ext->extended; 424 if (ext->extended != NULL) 425 ext->extended->prev_ext = mbri; 426 free(ext); 427 /* Make previous size cover all our ptn */ 428 ext = mbri->prev_ext; 429 if (ext != NULL) 430 ext->mbr.mbr_parts[1].mbrp_size += sz; 431 } 432 return 1; 433 } 434 435 if (mbrp->mbrp_start == 0) { 436 /* Find first chunk of space... */ 437 /* Must be in the main partition */ 438 if (mbri->sector != 0) 439 /* shouldn't be possible to have null start... */ 440 return err_msg_win("main-extended mixup"); 441 if (find_mbr_space(&mbri->mbr, &start, &sz, bsec, -1) != 0) 442 /* no space */ 443 return err_msg_win(MSG_No_free_space); 444 mbrp->mbrp_start = start; 445 mbrp->mbrp_size = sz; 446 /* If there isn't an active partition mark this one active */ 447 if (!MBR_IS_EXTENDED(type)) { 448 for (i = 0; i < MBR_PART_COUNT; i++) 449 if (mbri->mbr.mbr_parts[i].mbrp_flag != 0) 450 break; 451 if (i == MBR_PART_COUNT) 452 mbrp->mbrp_flag = MBR_PFLAG_ACTIVE; 453 } 454 } 455 456 if (MBR_IS_EXTENDED(type)) { 457 if (mbri->sector != 0) 458 /* Can't set extended partition in an extended one */ 459 return err_msg_win(MSG_Only_one_extended_ptn); 460 if (mbri->extended) 461 /* Can't have two extended partitions */ 462 return err_msg_win(MSG_Only_one_extended_ptn); 463 /* Create new extended partition */ 464 ext = calloc(1, sizeof *mbri->extended); 465 if (!ext) 466 return 0; 467 mbri->extended = ext; 468 ext->sector = mbrp->mbrp_start; 469 ext->mbr.mbr_parts[0].mbrp_start = ptn_0_offset; 470 ext->mbr.mbr_parts[0].mbrp_size = 471 mbrp->mbrp_size - ptn_0_offset; 472 } 473 mbrp->mbrp_type = type; 474 475 return 1; 476} 477 478static void 479set_type_label(menudesc *m, int opt, void *arg) 480{ 481 482 if (opt == 0) { 483 wprintw(m->mw, "%s", msg_string(MSG_Dont_change)); 484 return; 485 } 486 if (opt == 1) { 487 wprintw(m->mw, "%s", msg_string(MSG_Delete_partition)); 488 return; 489 } 490 if (part_ids[opt - 1].id == -1) { 491 wprintw(m->mw, "%s", msg_string(MSG_Other_kind)); 492 return; 493 } 494 wprintw(m->mw, "%s", part_ids[opt - 1].name); 495} 496 497static int 498edit_mbr_type(menudesc *m, void *arg) 499{ 500 static menu_ent type_opts[1 + nelem(part_ids)]; 501 static int type_menu = -1; 502 unsigned int i; 503 504 if (type_menu == -1) { 505 for (i = 0; i < nelem(type_opts); i++) { 506 type_opts[i].opt_menu = OPT_NOMENU; 507 type_opts[i].opt_action = set_mbr_type; 508 } 509 type_menu = new_menu(NULL, type_opts, nelem(type_opts), 510 13, 12, 0, 30, 511 MC_SUBMENU | MC_SCROLL | MC_NOEXITOPT | MC_NOCLEAR, 512 NULL, set_type_label, NULL, 513 NULL, NULL); 514 } 515 516 if (type_menu != -1) 517 process_menu(type_menu, arg); 518 519 return 0; 520} 521 522static int 523edit_mbr_start(menudesc *m, void *arg) 524{ 525 mbr_info_t *mbri = arg; 526 mbr_info_t *ext; 527 struct mbr_partition *mbrp; 528 int opt = mbri->opt; 529 uint start, sz; 530 uint new_r, new, limit, dflt_r; 531 int64_t delta; 532 const char *errmsg; 533 char *cp; 534 struct { 535 uint start; 536 uint start_r; 537 uint limit; 538 } freespace[MBR_PART_COUNT]; 539 unsigned int spaces; 540 unsigned int i; 541 char prompt[MBR_PART_COUNT * 60]; 542 unsigned int len; 543 char numbuf[12]; 544 545 if (opt >= MBR_PART_COUNT) 546 /* should not be able to get here... */ 547 return 1; 548 549 mbrp = mbri->mbr.mbr_parts + opt; 550 /* locate the start of all free areas */ 551 spaces = 0; 552 for (start = bsec, i = 0; i < MBR_PART_COUNT; start += sz, i++) { 553 if (find_mbr_space(&mbri->mbr, &start, &sz, start, opt)) 554 break; 555 if (MBR_IS_EXTENDED(mbrp->mbrp_type)) { 556 /* Only want the area that contains this partition */ 557 if (mbrp->mbrp_start < start || 558 mbrp->mbrp_start >= start + sz) 559 continue; 560 i = MBR_PART_COUNT - 1; 561 } 562 freespace[spaces].start = start; 563 freespace[spaces].start_r = start / sizemult; 564 freespace[spaces].limit = start + sz; 565 if (++spaces >= sizeof freespace) 566 /* shouldn't happen... */ 567 break; 568 } 569 570 /* Add description of start/size to user prompt */ 571 len = 0; 572 for (i = 0; i < spaces; i++) { 573 len += snprintf(prompt + len, sizeof prompt - len, 574 msg_string(MSG_ptn_starts), 575 freespace[i].start_r, 576 freespace[i].limit / sizemult, multname, 577 freespace[i].limit / sizemult - freespace[i].start_r, 578 multname); 579 if (len >= sizeof prompt) 580 break; 581 } 582 583 /* And loop until the user gives a sensible answer */ 584 dflt_r = mbrp->mbrp_start / sizemult; 585 errmsg = ""; 586 for (;;) { 587 snprintf(numbuf, sizeof numbuf, "%d", dflt_r); 588 msg_prompt_win(MSG_get_ptn_start, -1, 18, 60, spaces + 3, 589 numbuf, numbuf, sizeof numbuf, 590 prompt, msg_string(errmsg), multname); 591 new_r = strtoul(numbuf, &cp, 0); 592 if (*cp != 0) { 593 errmsg = MSG_Invalid_numeric; 594 continue; 595 } 596 if (new_r == dflt_r) 597 /* Unchanged */ 598 return 0; 599 /* 600 * Check that the start address from the user is inside one 601 * of the free areas. 602 */ 603 new = new_r * sizemult; 604 for (i = 0; i < spaces; i++) { 605 if (new_r == freespace[i].start_r) { 606 new = freespace[i].start; 607 break; 608 } 609 if (new >= freespace[i].start && 610 new < freespace[i].limit) 611 break; 612 } 613 if (i >= spaces) { 614 errmsg = MSG_Space_allocated; 615 continue; 616 } 617 limit = freespace[i].limit; 618 /* 619 * We can only increase the start of an extended partition 620 * if the corresponding space inside the partition isn't used. 621 */ 622 if (new > mbrp->mbrp_start && 623 MBR_IS_EXTENDED(mbrp->mbrp_type) && 624 (mbri->extended->mbr.mbr_parts[0].mbrp_type != 0 || 625 mbri->extended->mbr.mbr_parts[0].mbrp_size < 626 new - mbrp->mbrp_start)) { 627 errmsg = MSG_Space_allocated; 628 continue; 629 } 630 break; 631 } 632 633 if (new < mbrp->mbrp_start + mbrp->mbrp_size && 634 limit > mbrp->mbrp_start) 635 /* Keep end of partition in the same place */ 636 limit = mbrp->mbrp_start + mbrp->mbrp_size; 637 638 delta = new - mbrp->mbrp_start; 639 if (MBR_IS_EXTENDED(mbrp->mbrp_type)) { 640 ext = mbri->extended; 641 if (ext->mbr.mbr_parts[0].mbrp_type != 0) { 642 /* allocate an extended ptn for the free item */ 643 ext = calloc(1, sizeof *ext); 644 if (!ext) 645 return 0; 646 ext->sector = mbrp->mbrp_start; 647 ext->extended = mbri->extended; 648 mbri->extended->prev_ext = ext; 649 mbri->extended = ext; 650 ext->mbr.mbr_parts[0].mbrp_start = bsec; 651 ext->mbr.mbr_parts[0].mbrp_size = -bsec; 652 ext->mbr.mbr_parts[1].mbrp_type = MBR_PTYPE_EXT; 653 ext->mbr.mbr_parts[1].mbrp_start = 0; 654 ext->mbr.mbr_parts[1].mbrp_size = 655 ext->extended->mbr.mbr_parts[0].mbrp_start + 656 ext->extended->mbr.mbr_parts[0].mbrp_size; 657 } 658 /* adjust size of first free item */ 659 ext->mbr.mbr_parts[0].mbrp_size -= delta; 660 ext->sector += delta; 661 /* and the link of all extended partitions */ 662 do 663 if (ext->extended) 664 ext->mbr.mbr_parts[1].mbrp_start -= delta; 665 while ((ext = ext->extended)); 666 } 667 remove_old_partitions(mbri->sector + mbrp->mbrp_start, delta); 668 669 /* finally set partition base and size */ 670 mbrp->mbrp_start = new; 671 mbrp->mbrp_size = limit - new; 672 mbri->last_mounted[opt] = NULL; 673 674 return 0; 675} 676 677static int 678edit_mbr_size(menudesc *m, void *arg) 679{ 680 mbr_info_t *mbri = arg; 681 mbr_info_t *ombri = arg; 682 mbr_info_t *ext; 683 struct mbr_partition *mbrp; 684 int opt = mbri->opt; 685 uint start, max, max_r, dflt, dflt_r, new; 686 uint freespace; 687 int delta; 688 char numbuf[12]; 689 char *cp; 690 const char *errmsg; 691 692 mbrp = get_mbrp(&mbri, opt); 693 dflt = mbrp->mbrp_size; 694 if (opt < MBR_PART_COUNT) { 695 max = 0; 696 find_mbr_space(&mbri->mbr, &start, &max, mbrp->mbrp_start, opt); 697 if (start != mbrp->mbrp_start) 698 return 0; 699 if (dflt == 0) 700 dflt = max; 701 } else { 702 ext = mbri->extended; 703 max = dflt; 704 /* 705 * If the next extended partition describes a free area, 706 * then merge it onto this area. 707 */ 708 if (ext != NULL && ext->mbr.mbr_parts[0].mbrp_type == 0) { 709 if (ext->extended) 710 ext->extended->prev_ext = mbri; 711 mbri->extended = ext->extended; 712 if (mbri->prev_ext) 713 mbri->prev_ext->mbr.mbr_parts[1].mbrp_size 714 += mbri->mbr.mbr_parts[1].mbrp_size; 715 mbrp->mbrp_size += mbri->mbr.mbr_parts[1].mbrp_size; 716 max += mbri->mbr.mbr_parts[1].mbrp_size; 717 mbri->mbr.mbr_parts[1] = ext->mbr.mbr_parts[1]; 718 free(ext); 719 } 720 } 721 722 start = mbri->sector + mbrp->mbrp_start; 723 /* We need to keep both the unrounded and rounded (_r) max and dflt */ 724 dflt_r = (start + dflt) / sizemult - start / sizemult; 725 if (max == dflt) 726 max_r = dflt_r; 727 else 728 max_r = max / sizemult; 729 for (errmsg = "";;) { 730 snprintf(numbuf, sizeof numbuf, "%d", dflt_r); 731 msg_prompt_win(MSG_get_ptn_size, -1, 18, 0, 0, 732 numbuf, numbuf, sizeof numbuf, 733 msg_string(errmsg), max_r, multname); 734 new = strtoul(numbuf, &cp, 0); 735 if (*cp != 0) { 736 errmsg = MSG_Invalid_numeric; 737 continue; 738 } 739 if (new > max_r) { 740 errmsg = MSG_Too_large; 741 continue; 742 } 743 if (new == 0) 744 /* Treat zero as a request for the maximum */ 745 new = max_r; 746 if (new == dflt_r) 747 /* If unchanged, don't re-round size */ 748 new = dflt; 749 else { 750 /* Round end to the partition alignment */ 751 if (sizemult != 1) { 752 new *= sizemult; 753 new += rounddown(start, ptn_alignment); 754 new = roundup(new, ptn_alignment); 755 new -= start; 756 while (new <= 0) 757 new += ptn_alignment; 758 } 759 } 760 if (new > max) 761 /* We rounded the value to above the max */ 762 new = max; 763 764 if (new == dflt || opt >= MBR_PART_COUNT 765 || !MBR_IS_EXTENDED(mbrp->mbrp_type)) 766 break; 767 /* 768 * We've been asked to change the size of the main extended 769 * partition. If this reduces the size, then that space 770 * must be unallocated. If it increases the size then 771 * we must add a description ofthe new free space. 772 */ 773 /* Find last extended partition */ 774 for (ext = mbri->extended; ext->extended; ext = ext->extended) 775 continue; 776 if ((new < dflt && (ext->mbr.mbr_parts[0].mbrp_type != 0 777 || (mbrp->mbrp_start + new < ext->sector + bsec 778 && mbrp->mbrp_start + new != ext->sector))) 779 || (new > dflt && ext->mbr.mbr_parts[0].mbrp_type != 0 780 && new < dflt + bsec)) { 781 errmsg = MSG_Space_allocated; 782 continue; 783 } 784 delta = new - dflt; 785 if (ext->mbr.mbr_parts[0].mbrp_type == 0) { 786 /* adjust size of last item (free space) */ 787 if (mbrp->mbrp_start + new == ext->sector) { 788 /* kill last extended ptn */ 789 ext = ext->prev_ext; 790 free(ext->extended); 791 ext->extended = NULL; 792 memset(&ext->mbr.mbr_parts[1], 0, 793 sizeof ext->mbr.mbr_parts[1]); 794 break; 795 } 796 ext->mbr.mbr_parts[0].mbrp_size += delta; 797 ext = ext->prev_ext; 798 if (ext != NULL) 799 ext->mbr.mbr_parts[1].mbrp_size += delta; 800 break; 801 } 802 /* Joy of joys, we must allocate another extended ptn */ 803 mbri = ext; 804 ext = calloc(1, sizeof *ext); 805 if (!ext) 806 return 0; 807 mbri->extended = ext; 808 ext->prev_ext = mbri; 809 ext->mbr.mbr_parts[0].mbrp_start = bsec; 810 ext->mbr.mbr_parts[0].mbrp_size = delta - bsec; 811 ext->sector = mbri->sector + mbri->mbr.mbr_parts[0].mbrp_start 812 + mbri->mbr.mbr_parts[0].mbrp_size; 813 mbri->mbr.mbr_parts[1].mbrp_start = ext->sector - ombri->extended->sector; 814 mbri->mbr.mbr_parts[1].mbrp_type = MBR_PTYPE_EXT; 815 mbri->mbr.mbr_parts[1].mbrp_size = delta; 816 break; 817 } 818 819 if (opt >= MBR_PART_COUNT && max - new <= (uint32_t)bsec) 820 /* Round up if not enough space for a header for free area */ 821 new = max; 822 823 if (new != mbrp->mbrp_size) { 824 /* Kill information about old partition from label */ 825 mbri->last_mounted[opt < MBR_PART_COUNT ? opt : 0] = NULL; 826 remove_old_partitions(mbri->sector + mbrp->mbrp_start + 827 mbrp->mbrp_size, (int64_t)new - mbrp->mbrp_size); 828 } 829 830 mbrp->mbrp_size = new; 831 if (opt < MBR_PART_COUNT || new == max) 832 return 0; 833 834 /* Add extended partition for the free space */ 835 ext = calloc(1, sizeof *ext); 836 if (!ext) { 837 mbrp->mbrp_size = max; 838 return 0; 839 } 840 /* Link into our extended chain */ 841 ext->extended = mbri->extended; 842 mbri->extended = ext; 843 ext->prev_ext = mbri; 844 if (ext->extended != NULL) 845 ext->extended->prev_ext = ext; 846 ext->mbr.mbr_parts[1] = mbri->mbr.mbr_parts[1]; 847 freespace = max - new; 848 if (mbri->prev_ext != NULL) 849 mbri->prev_ext->mbr.mbr_parts[1].mbrp_size -= freespace; 850 851 ext->mbr.mbr_parts[0].mbrp_start = bsec; 852 ext->mbr.mbr_parts[0].mbrp_size = freespace - bsec; 853 854 ext->sector = mbri->sector + mbri->mbr.mbr_parts[0].mbrp_start 855 + mbri->mbr.mbr_parts[0].mbrp_size; 856 mbri->mbr.mbr_parts[1].mbrp_start = ext->sector - ombri->extended->sector; 857 mbri->mbr.mbr_parts[1].mbrp_type = MBR_PTYPE_EXT; 858 mbri->mbr.mbr_parts[1].mbrp_size = freespace; 859 860 return 0; 861} 862 863static int 864edit_mbr_active(menudesc *m, void *arg) 865{ 866 mbr_info_t *mbri = arg; 867 int i; 868 uint8_t *fl; 869 870 if (mbri->opt >= MBR_PART_COUNT) 871 /* sanity */ 872 return 0; 873 874 /* Invert active flag */ 875 fl = &mbri->mbr.mbr_parts[mbri->opt].mbrp_flag; 876 if (*fl == MBR_PFLAG_ACTIVE) { 877 *fl = 0; 878 return 0; 879 } 880 881 /* Ensure there is at most one active partition */ 882 for (i = 0; i < MBR_PART_COUNT; i++) 883 mbri->mbr.mbr_parts[i].mbrp_flag = 0; 884 *fl = MBR_PFLAG_ACTIVE; 885 886 return 0; 887} 888 889static int 890edit_mbr_install(menudesc *m, void *arg) 891{ 892 mbr_info_t *mbri = arg; 893 mbr_info_t *ombri = arg; 894 struct mbr_partition *mbrp; 895 int opt = mbri->opt; 896 uint start; 897 898 mbrp = get_mbrp(&mbri, opt); 899 if (opt >= MBR_PART_COUNT) 900 opt = 0; 901 902 start = mbri->sector + mbrp->mbrp_start; 903 /* We just remember the start address of the partition... */ 904 if (start == ombri->install) 905 ombri->install = 0; 906 else 907 ombri->install = start; 908 return 0; 909} 910 911#ifdef BOOTSEL 912static int 913edit_mbr_bootmenu(menudesc *m, void *arg) 914{ 915 mbr_info_t *mbri = arg; 916 mbr_info_t *ombri = arg; 917 struct mbr_partition *mbrp; 918 int opt = mbri->opt; 919 920 mbrp = get_mbrp(&mbri, opt); 921 if (opt >= MBR_PART_COUNT) 922 opt = 0; 923 924 msg_prompt_win(/* XXX translate? */ "bootmenu", -1, 18, 0, 0, 925 mbri->mbrb.mbrbs_nametab[opt], 926 mbri->mbrb.mbrbs_nametab[opt], 927 sizeof mbri->mbrb.mbrbs_nametab[opt]); 928 if (mbri->mbrb.mbrbs_nametab[opt][0] == ' ') 929 mbri->mbrb.mbrbs_nametab[opt][0] = 0; 930 if (mbri->mbrb.mbrbs_nametab[opt][0] == 0 931 && ombri->bootsec == mbri->sector + mbrp->mbrp_start) 932 ombri->bootsec = 0; 933 return 0; 934} 935 936static int 937edit_mbr_bootdefault(menudesc *m, void *arg) 938{ 939 mbr_info_t *mbri = arg; 940 mbr_info_t *ombri = arg; 941 struct mbr_partition *mbrp; 942 943 mbrp = get_mbrp(&mbri, mbri->opt); 944 945 ombri->bootsec = mbri->sector + mbrp->mbrp_start; 946 return 0; 947} 948#endif 949 950static void set_ptn_label(menudesc *m, int line, void *arg); 951static void set_ptn_header(menudesc *m, void *arg); 952 953static int 954edit_mbr_entry(menudesc *m, void *arg) 955{ 956 mbr_info_t *mbri = arg; 957 static int ptn_menu = -1; 958 959 static menu_ent ptn_opts[] = { 960#define PTN_OPT_TYPE 0 961 {NULL, OPT_NOMENU, 0, edit_mbr_type}, 962#define PTN_OPT_START 1 963 {NULL, OPT_NOMENU, 0, edit_mbr_start}, 964#define PTN_OPT_SIZE 2 965 {NULL, OPT_NOMENU, 0, edit_mbr_size}, 966#define PTN_OPT_END 3 967 {NULL, OPT_NOMENU, OPT_IGNORE, NULL}, /* display end */ 968#define PTN_OPT_ACTIVE 4 969 {NULL, OPT_NOMENU, 0, edit_mbr_active}, 970#define PTN_OPT_INSTALL 5 971 {NULL, OPT_NOMENU, 0, edit_mbr_install}, 972#ifdef BOOTSEL 973#define PTN_OPT_BOOTMENU 6 974 {NULL, OPT_NOMENU, 0, edit_mbr_bootmenu}, 975#define PTN_OPT_BOOTDEFAULT 7 976 {NULL, OPT_NOMENU, 0, edit_mbr_bootdefault}, 977#endif 978 {MSG_askunits, MENU_sizechoice, OPT_SUB, NULL}, 979 }; 980 981 if (ptn_menu == -1) 982 ptn_menu = new_menu(NULL, ptn_opts, nelem(ptn_opts), 983 15, 6, 0, 54, 984 MC_SUBMENU | MC_SCROLL | MC_NOCLEAR, 985 set_ptn_header, set_ptn_label, NULL, 986 NULL, MSG_Partition_OK); 987 if (ptn_menu == -1) 988 return 1; 989 990 mbri->opt = m->cursel; 991 process_menu(ptn_menu, mbri); 992 return 0; 993} 994 995static void 996set_ptn_label(menudesc *m, int line, void *arg) 997{ 998 mbr_info_t *mbri = arg; 999 mbr_info_t *ombri = arg; 1000 struct mbr_partition *mbrp; 1001 int opt; 1002 static const char *yes, *no; 1003 1004 if (yes == NULL) { 1005 yes = msg_string(MSG_Yes); 1006 no = msg_string(MSG_No); 1007 } 1008 1009 opt = mbri->opt; 1010 mbrp = get_mbrp(&mbri, opt); 1011 if (opt >= MBR_PART_COUNT) 1012 opt = 0; 1013 1014 switch (line) { 1015 case PTN_OPT_TYPE: 1016 wprintw(m->mw, msg_string(MSG_ptn_type), 1017 get_partname(mbrp->mbrp_type)); 1018 break; 1019 case PTN_OPT_START: 1020 wprintw(m->mw, msg_string(MSG_ptn_start), 1021 (mbri->sector + mbrp->mbrp_start) / sizemult, multname); 1022 break; 1023 case PTN_OPT_SIZE: 1024 wprintw(m->mw, msg_string(MSG_ptn_size), 1025 (mbri->sector + mbrp->mbrp_start + mbrp->mbrp_size) / 1026 sizemult - 1027 (mbri->sector + mbrp->mbrp_start) / sizemult, multname); 1028 break; 1029 case PTN_OPT_END: 1030 wprintw(m->mw, msg_string(MSG_ptn_end), 1031 (mbri->sector + mbrp->mbrp_start + mbrp->mbrp_size) / 1032 sizemult, multname); 1033 break; 1034 case PTN_OPT_ACTIVE: 1035 wprintw(m->mw, msg_string(MSG_ptn_active), 1036 mbrp->mbrp_flag == MBR_PFLAG_ACTIVE ? yes : no); 1037 break; 1038 case PTN_OPT_INSTALL: 1039 wprintw(m->mw, msg_string(MSG_ptn_install), 1040 mbri->sector + mbrp->mbrp_start == ombri->install && 1041 mbrp->mbrp_type == MBR_PTYPE_NETBSD ? yes : no); 1042 break; 1043#ifdef BOOTSEL 1044 case PTN_OPT_BOOTMENU: 1045 wprintw(m->mw, msg_string(MSG_bootmenu), 1046 mbri->mbrb.mbrbs_nametab[opt]); 1047 break; 1048 case PTN_OPT_BOOTDEFAULT: 1049 wprintw(m->mw, msg_string(MSG_boot_dflt), 1050 ombri->bootsec == mbri->sector + mbrp->mbrp_start ? yes 1051 : no); 1052 break; 1053#endif 1054 } 1055 1056} 1057 1058static void 1059set_ptn_header(menudesc *m, void *arg) 1060{ 1061 mbr_info_t *mbri = arg; 1062 struct mbr_partition *mbrp; 1063 int opt = mbri->opt; 1064 int typ; 1065 1066 mbrp = get_mbrp(&mbri, opt); 1067 if (opt >= MBR_PART_COUNT) 1068 opt = 0; 1069 typ = mbrp->mbrp_type; 1070 1071#define DISABLE(opt,cond) \ 1072 if (cond) \ 1073 m->opts[opt].opt_flags |= OPT_IGNORE; \ 1074 else \ 1075 m->opts[opt].opt_flags &= ~OPT_IGNORE; 1076 1077 /* Can't change type of the extended partition unless it is empty */ 1078 DISABLE(PTN_OPT_TYPE, MBR_IS_EXTENDED(typ) && 1079 (mbri->extended->mbr.mbr_parts[0].mbrp_type != 0 || 1080 mbri->extended->extended != NULL)); 1081 1082 /* It is unnecessary to be able to change the base of an extended ptn */ 1083 DISABLE(PTN_OPT_START, mbri->sector || typ == 0); 1084 1085 /* or the size of a free area */ 1086 DISABLE(PTN_OPT_SIZE, typ == 0); 1087 1088 /* Only 'normal' partitions can be 'Active' */ 1089 DISABLE(PTN_OPT_ACTIVE, mbri->sector != 0 || MBR_IS_EXTENDED(typ) || typ == 0); 1090 1091 /* Can only install into NetBSD partition */ 1092 DISABLE(PTN_OPT_INSTALL, typ != MBR_PTYPE_NETBSD); 1093 1094#ifdef BOOTSEL 1095 /* The extended partition isn't bootable */ 1096 DISABLE(PTN_OPT_BOOTMENU, MBR_IS_EXTENDED(typ) || typ == 0); 1097 1098 if (typ == 0) 1099 mbri->mbrb.mbrbs_nametab[opt][0] = 0; 1100 1101 /* Only partitions with bootmenu names can be made the default */ 1102 DISABLE(PTN_OPT_BOOTDEFAULT, mbri->mbrb.mbrbs_nametab[opt][0] == 0); 1103#endif 1104#undef DISABLE 1105} 1106 1107static void 1108set_mbr_label(menudesc *m, int opt, void *arg) 1109{ 1110 mbr_info_t *mbri = arg; 1111 mbr_info_t *ombri = arg; 1112 struct mbr_partition *mbrp; 1113 uint rstart, rend; 1114 const char *name, *cp, *mounted; 1115 int len; 1116 1117 mbrp = get_mbrp(&mbri, opt); 1118 if (opt >= MBR_PART_COUNT) 1119 opt = 0; 1120 1121 if (mbrp->mbrp_type == 0 && mbri->sector == 0) { 1122 len = snprintf(0, 0, msg_string(MSG_part_row_used), 0, 0, 0); 1123 wprintw(m->mw, "%*s", len, ""); 1124 } else { 1125 rstart = mbri->sector + mbrp->mbrp_start; 1126 rend = (rstart + mbrp->mbrp_size) / sizemult; 1127 rstart = rstart / sizemult; 1128 wprintw(m->mw, msg_string(MSG_part_row_used), 1129 rstart, rend - rstart, 1130 mbrp->mbrp_flag == MBR_PFLAG_ACTIVE ? 'a' : ' ', 1131#ifdef BOOTSEL 1132 ombri->bootsec == mbri->sector + mbrp->mbrp_start ? 'd' : 1133#endif 1134 ' ', 1135 mbri->sector + mbrp->mbrp_start == ombri->install && 1136 mbrp->mbrp_type == MBR_PTYPE_NETBSD ? 'I' : ' '); 1137 } 1138 name = get_partname(mbrp->mbrp_type); 1139 mounted = mbri->last_mounted[opt]; 1140 len = strlen(name); 1141 cp = strchr(name, ','); 1142 if (cp != NULL) 1143 len = cp - name; 1144 if (mounted && *mounted != 0) { 1145 wprintw(m->mw, " %*s (%s)", len, name, mounted); 1146 } else 1147 wprintw(m->mw, " %.*s", len, name); 1148#ifdef BOOTSEL 1149 if (mbri->mbrb.mbrbs_nametab[opt][0] != 0) { 1150 int x, y; 1151 if (opt >= MBR_PART_COUNT) 1152 opt = 0; 1153 getyx(m->mw, y, x); 1154 if (x > 52) { 1155 x = 52; 1156 wmove(m->mw, y, x); 1157 } 1158 wprintw(m->mw, "%*s %s", 53 - x, "", 1159 mbri->mbrb.mbrbs_nametab[opt]); 1160 } 1161#endif 1162} 1163 1164static void 1165set_mbr_header(menudesc *m, void *arg) 1166{ 1167 mbr_info_t *mbri = arg; 1168 static menu_ent *opts; 1169 static int num_opts; 1170 mbr_info_t *ext; 1171 menu_ent *op; 1172 int i; 1173 int left; 1174 1175 msg_display(MSG_editparttable); 1176 1177 msg_table_add(MSG_part_header, (unsigned long)(dlsize/sizemult), 1178 multname, multname, multname, multname); 1179 1180 if (num_opts == 0) { 1181 num_opts = 6; 1182 opts = malloc(6 * sizeof *opts); 1183 if (opts == NULL) { 1184 m->numopts = 0; 1185 return; 1186 } 1187 } 1188 1189 /* First four items are the main partitions */ 1190 for (op = opts, i = 0; i < MBR_PART_COUNT; op++, i++) { 1191 op->opt_name = NULL; 1192 op->opt_menu = OPT_NOMENU; 1193 op->opt_flags = OPT_SUB; 1194 op->opt_action = edit_mbr_entry; 1195 } 1196 left = num_opts - MBR_PART_COUNT; 1197 1198 /* Followed by the extended partitions */ 1199 for (ext = mbri->extended; ext; left--, op++, ext = ext->extended) { 1200 if (left <= 1) { 1201 menu_ent *new = realloc(opts, 1202 (num_opts + 4) * sizeof *opts); 1203 if (new == NULL) 1204 break; 1205 num_opts += 4; 1206 left += 4; 1207 op = new + (op - opts); 1208 opts = new; 1209 } 1210 op->opt_name = NULL; 1211 op->opt_menu = OPT_NOMENU; 1212 op->opt_flags = 0; 1213 op->opt_action = edit_mbr_entry; 1214 } 1215 1216 /* and unit changer */ 1217 op->opt_name = MSG_askunits; 1218 op->opt_menu = MENU_sizechoice; 1219 op->opt_flags = OPT_SUB; 1220 op->opt_action = NULL; 1221 op++; 1222 1223 m->opts = opts; 1224 m->numopts = op - opts; 1225} 1226 1227int 1228mbr_use_wholedisk(mbr_info_t *mbri) 1229{ 1230 struct mbr_sector *mbrs = &mbri->mbr; 1231 mbr_info_t *ext; 1232 struct mbr_partition *part; 1233 1234 part = &mbrs->mbr_parts[0]; 1235 /* Set the partition information for full disk usage. */ 1236 while ((ext = mbri->extended)) { 1237 mbri->extended = ext->extended; 1238 free(ext); 1239 } 1240 memset(part, 0, MBR_PART_COUNT * sizeof *part); 1241#ifdef BOOTSEL 1242 memset(&mbri->mbrb, 0, sizeof mbri->mbrb); 1243#endif 1244 part[0].mbrp_type = MBR_PTYPE_NETBSD; 1245 part[0].mbrp_size = dlsize - ptn_0_offset; 1246 part[0].mbrp_start = ptn_0_offset; 1247 part[0].mbrp_flag = MBR_PFLAG_ACTIVE; 1248 1249 ptstart = ptn_0_offset; 1250 ptsize = dlsize - ptn_0_offset; 1251 return 1; 1252} 1253 1254/* 1255 * Let user change incore Master Boot Record partitions via menu. 1256 */ 1257int 1258edit_mbr(mbr_info_t *mbri) 1259{ 1260 struct mbr_sector *mbrs = &mbri->mbr; 1261 mbr_info_t *ext; 1262 struct mbr_partition *part; 1263 int i, j; 1264 int usefull; 1265 int mbr_menu; 1266 int activepart; 1267 int numbsd; 1268 uint bsdstart, bsdsize; 1269 uint start; 1270 1271 /* Ask full/part */ 1272 1273 part = &mbrs->mbr_parts[0]; 1274 get_ptn_alignment(part); /* update ptn_alignment */ 1275 msg_display(MSG_fullpart, diskdev); 1276 process_menu(MENU_fullpart, &usefull); 1277 1278 /* DOS fdisk label checking and value setting. */ 1279 if (usefull) { 1280 /* Count nonempty, non-BSD partitions. */ 1281 numbsd = 0; 1282 for (i = 0; i < MBR_PART_COUNT; i++) { 1283 j = part[i].mbrp_type; 1284 if (j == 0) 1285 continue; 1286 numbsd++; 1287 if (j != MBR_PTYPE_NETBSD) 1288 numbsd++; 1289 } 1290 1291 /* Ask if we really want to blow away non-NetBSD stuff */ 1292 if (numbsd > 1) { 1293 msg_display(MSG_ovrwrite); 1294 process_menu(MENU_noyes, NULL); 1295 if (!yesno) { 1296 if (logfp) 1297 (void)fprintf(logfp, "User answered no to destroy other data, aborting.\n"); 1298 return 0; 1299 } 1300 } 1301 return(md_mbr_use_wholedisk(mbri)); 1302 } 1303 1304 mbr_menu = new_menu(NULL, NULL, 16, 0, -1, 15, 70, 1305 MC_NOBOX | MC_ALWAYS_SCROLL | MC_NOCLEAR, 1306 set_mbr_header, set_mbr_label, NULL, 1307 NULL, MSG_Partition_table_ok); 1308 if (mbr_menu == -1) 1309 return 0; 1310 1311 /* Default to MB, and use bios geometry for cylinder size */ 1312 set_sizemultname_meg(); 1313 current_cylsize = bhead * bsec; 1314 1315 for (;;) { 1316 ptstart = 0; 1317 ptsize = 0; 1318 process_menu(mbr_menu, mbri); 1319 1320 activepart = 0; 1321 bsdstart = 0; 1322 bsdsize = 0; 1323 for (ext = mbri; ext; ext = ext->extended) { 1324 part = ext->mbr.mbr_parts; 1325 for (i = 0; i < MBR_PART_COUNT; part++, i++) { 1326 if (part->mbrp_flag != 0) 1327 activepart = 1; 1328 if (part->mbrp_type != MBR_PTYPE_NETBSD) 1329 continue; 1330 start = ext->sector + part->mbrp_start; 1331 if (start == mbri->install) { 1332 ptstart = mbri->install; 1333 ptsize = part->mbrp_size; 1334 } 1335 if (bsdstart != 0) 1336 bsdstart = ~0; 1337 else { 1338 bsdstart = start; 1339 bsdsize = part->mbrp_size; 1340 } 1341 } 1342 } 1343 1344 /* Install in only netbsd partition if none tagged */ 1345 if (ptstart == 0 && bsdstart != ~0u) { 1346 ptstart = bsdstart; 1347 ptsize = bsdsize; 1348 } 1349 1350 if (ptstart == 0) { 1351 if (bsdstart == 0) 1352 msg_display(MSG_nobsdpart); 1353 else 1354 msg_display(MSG_multbsdpart, 0); 1355 msg_display_add(MSG_reeditpart, 0); 1356 process_menu(MENU_yesno, NULL); 1357 if (!yesno) 1358 return 0; 1359 continue; 1360 } 1361 1362 if (activepart == 0) { 1363 msg_display(MSG_noactivepart); 1364 process_menu(MENU_yesno, NULL); 1365 if (yesno) 1366 continue; 1367 } 1368 /* the md_check_mbr function has 3 ret codes to deal with 1369 * the different possible states. 0, 1, >1 1370 */ 1371 j = md_check_mbr(mbri); 1372 if (j == 0) 1373 return 0; 1374 if (j == 1) 1375 continue; 1376 1377 break; 1378 } 1379 1380 free_menu(mbr_menu); 1381 1382 return 1; 1383} 1384 1385const char * 1386get_partname(int typ) 1387{ 1388 int j; 1389 static char unknown[32]; 1390 1391 for (j = 0; part_ids[j].id != -1; j++) 1392 if (part_ids[j].id == typ) 1393 return part_ids[j].name; 1394 1395 snprintf(unknown, sizeof unknown, "Unknown (%d)", typ); 1396 return unknown; 1397} 1398 1399#ifdef BOOTSEL 1400static int 1401validate_and_set_names(mbr_info_t *mbri, const struct mbr_bootsel *src, 1402 uint32_t ext_base) 1403{ 1404 size_t i, l; 1405 const unsigned char *p; 1406 1407 /* 1408 * The 16 bit magic used to detect whether mbr_bootsel is valid 1409 * or not is pretty week - collisions have been seen in the wild; 1410 * but maybe it is just foreign tools corruption reminiscents 1411 * of NetBSD MBRs. Anyway, before accepting a boot menu definition, 1412 * make sure it is kinda "sane". 1413 */ 1414 1415 for (i = 0; i < MBR_PART_COUNT; i++) { 1416 /* 1417 * Make sure the name does not contain controll chars 1418 * (not using iscntrl due to minimalistic locale support 1419 * in miniroot environments) and is properly 0-terminated. 1420 */ 1421 for (l = 0, p = (const unsigned char *)&src->mbrbs_nametab[i]; 1422 *p != 0; l++, p++) { 1423 if (l > MBR_BS_PARTNAMESIZE) 1424 return 0; 1425 if (*p < ' ') /* hacky 'iscntrl' */ 1426 return 0; 1427 } 1428 } 1429 1430 memcpy(&mbri->mbrb, src, sizeof(*src)); 1431 1432 if (ext_base == 0) 1433 return mbri->mbrb.mbrbs_defkey - SCAN_1; 1434 return 0; 1435} 1436#endif 1437 1438int 1439read_mbr(const char *disk, mbr_info_t *mbri) 1440{ 1441 struct mbr_partition *mbrp; 1442 struct mbr_sector *mbrs = &mbri->mbr; 1443 mbr_info_t *ext = NULL; 1444 char diskpath[MAXPATHLEN]; 1445 int fd, i; 1446 uint32_t ext_base = 0, next_ext = 0, ext_size = 0; 1447 int rval = -1; 1448#ifdef BOOTSEL 1449 mbr_info_t *ombri = mbri; 1450 int bootkey = 0; 1451#endif 1452 1453 /* 1454 * Fake up a likely 'bios sectors per track' for any extended 1455 * partition headers we might have to produce. 1456 */ 1457 if (bsec == 0) 1458 bsec = dlsec; 1459 ptn_0_offset = bsec; 1460 /* use 1MB default offset on large disks as fdisk(8) */ 1461 if (dlsize > 2048 * 1024 * 128) 1462 ptn_0_offset = 2048; 1463 1464 memset(mbri, 0, sizeof *mbri); 1465 1466 /* Open the disk. */ 1467 fd = opendisk(disk, O_RDONLY, diskpath, sizeof(diskpath), 0); 1468 if (fd < 0) 1469 goto bad_mbr; 1470 1471 for (;;) { 1472 if (pread(fd, mbrs, sizeof *mbrs, 1473 (ext_base + next_ext) * (off_t)MBR_SECSIZE) - sizeof *mbrs != 0) 1474 break; 1475 1476 if (!valid_mbr(mbrs)) 1477 break; 1478 1479 mbrp = &mbrs->mbr_parts[0]; 1480 if (ext_base == 0) { 1481 get_ptn_alignment(mbrp); /* get ptn_0_offset */ 1482 } else { 1483 /* sanity check extended chain */ 1484 if (MBR_IS_EXTENDED(mbrp[0].mbrp_type)) 1485 break; 1486 if (mbrp[1].mbrp_type != 0 && 1487 !MBR_IS_EXTENDED(mbrp[1].mbrp_type)) 1488 break; 1489 if (mbrp[2].mbrp_type != 0 || mbrp[3].mbrp_type != 0) 1490 break; 1491 /* Looks ok, link into extended chain */ 1492 mbri->extended = ext; 1493 ext->prev_ext = next_ext != 0 ? mbri : NULL; 1494 ext->extended = NULL; 1495 mbri = ext; 1496 ext = NULL; 1497 } 1498#if BOOTSEL 1499 if (mbrs->mbr_bootsel_magic == htole16(MBR_MAGIC)) { 1500 /* old bootsel, grab bootsel info */ 1501 bootkey = validate_and_set_names(mbri, 1502 (struct mbr_bootsel *) 1503 ((uint8_t *)mbrs + MBR_BS_OLD_OFFSET), 1504 ext_base); 1505 } else if (mbrs->mbr_bootsel_magic == htole16(MBR_BS_MAGIC)) { 1506 /* new location */ 1507 bootkey = validate_and_set_names(mbri, 1508 &mbrs->mbr_bootsel, ext_base); 1509 } 1510 /* Save original flags for mbr code update tests */ 1511 mbri->oflags = mbri->mbrb.mbrbs_flags; 1512#endif 1513 mbri->sector = next_ext + ext_base; 1514 next_ext = 0; 1515 rval = 0; 1516 for (i = 0; i < MBR_PART_COUNT; mbrp++, i++) { 1517 if (mbrp->mbrp_type == 0) { 1518 /* type is unused, discard scum */ 1519 memset(mbrp, 0, sizeof *mbrp); 1520 continue; 1521 } 1522 mbrp->mbrp_start = le32toh(mbrp->mbrp_start); 1523 mbrp->mbrp_size = le32toh(mbrp->mbrp_size); 1524 if (MBR_IS_EXTENDED(mbrp->mbrp_type)) { 1525 next_ext = mbrp->mbrp_start; 1526 if (ext_base == 0) 1527 ext_size = mbrp->mbrp_size; 1528 } else { 1529 mbri->last_mounted[i] = strdup(get_last_mounted( 1530 fd, mbri->sector + mbrp->mbrp_start, NULL)); 1531#if BOOTSEL 1532 if (ombri->install == 0 && 1533 strcmp(mbri->last_mounted[i], "/") == 0) 1534 ombri->install = mbri->sector + 1535 mbrp->mbrp_start; 1536#endif 1537 } 1538#if BOOTSEL 1539 if (mbri->mbrb.mbrbs_nametab[i][0] != 0 1540 && bootkey-- == 0) 1541 ombri->bootsec = mbri->sector + 1542 mbrp->mbrp_start; 1543#endif 1544 } 1545 1546 if (ext_base != 0) { 1547 /* Is there a gap before the next partition? */ 1548 unsigned int limit = next_ext; 1549 unsigned int base; 1550 if (limit == 0) 1551 limit = ext_size; 1552 mbrp -= MBR_PART_COUNT; 1553 base =mbri->sector + mbrp->mbrp_start + mbrp->mbrp_size; 1554 if (mbrp->mbrp_type != 0 && ext_base + limit != base) { 1555 /* Mock up an extry for the space */ 1556 ext = calloc(1, sizeof *ext); 1557 if (!ext) 1558 break; 1559 ext->sector = base; 1560 ext->mbr.mbr_magic = htole16(MBR_MAGIC); 1561 ext->mbr.mbr_parts[1] = mbrp[1]; 1562 ext->mbr.mbr_parts[0].mbrp_start = ptn_0_offset; 1563 ext->mbr.mbr_parts[0].mbrp_size = 1564 ext_base + limit - base - ptn_0_offset; 1565 mbrp[1].mbrp_type = MBR_PTYPE_EXT; 1566 mbrp[1].mbrp_start = base - ext_base; 1567 mbrp[1].mbrp_size = limit - mbrp[1].mbrp_start; 1568 mbri->extended = ext; 1569 ext->prev_ext = mbri; 1570 ext->extended = NULL; 1571 mbri = ext; 1572 ext = NULL; 1573 } 1574 } 1575 1576 if (next_ext == 0 || ext_base + next_ext <= mbri->sector) 1577 break; 1578 if (ext_base == 0) { 1579 ext_base = next_ext; 1580 next_ext = 0; 1581 } 1582 ext = calloc(sizeof *ext, 1); 1583 if (!ext) 1584 break; 1585 mbrs = &ext->mbr; 1586 } 1587 1588 bad_mbr: 1589 free(ext); 1590 if (fd >= 0) 1591 close(fd); 1592 if (rval == -1) { 1593 memset(&mbrs->mbr_parts, 0, sizeof mbrs->mbr_parts); 1594 mbrs->mbr_magic = htole16(MBR_MAGIC); 1595 } 1596 dump_mbr(ombri, "read"); 1597 return rval; 1598} 1599 1600int 1601write_mbr(const char *disk, mbr_info_t *mbri, int convert) 1602{ 1603 char diskpath[MAXPATHLEN]; 1604 int fd, i, ret = 0; 1605 struct mbr_partition *mbrp; 1606 u_int32_t pstart, psize; 1607#ifdef BOOTSEL 1608 struct mbr_sector *mbrs; 1609#endif 1610 struct mbr_sector mbrsec; 1611 mbr_info_t *ext; 1612 uint sector; 1613 1614 /* Open the disk. */ 1615 fd = opendisk(disk, O_WRONLY, diskpath, sizeof(diskpath), 0); 1616 if (fd < 0) 1617 return -1; 1618 1619#ifdef BOOTSEL 1620 /* 1621 * If the main boot code (appears to) contain the netbsd bootcode, 1622 * copy in all the menu strings and set the default keycode 1623 * to be that for the default partition. 1624 * Unfortunately we can't rely on the user having actually updated 1625 * to the new mbr code :-( 1626 */ 1627 if (mbri->mbr.mbr_bootsel_magic == htole16(MBR_BS_MAGIC) 1628 || mbri->mbr.mbr_bootsel_magic == htole16(MBR_MAGIC)) { 1629 int8_t key = SCAN_1; 1630 uint offset = MBR_BS_OFFSET; 1631 if (mbri->mbr.mbr_bootsel_magic == htole16(MBR_MAGIC)) 1632 offset = MBR_BS_OLD_OFFSET; 1633 mbri->mbrb.mbrbs_defkey = SCAN_ENTER; 1634 if (mbri->mbrb.mbrbs_timeo == 0) 1635 mbri->mbrb.mbrbs_timeo = 182; /* 10 seconds */ 1636 for (ext = mbri; ext != NULL; ext = ext->extended) { 1637 mbrs = &ext->mbr; 1638 mbrp = &mbrs->mbr_parts[0]; 1639 /* Ensure marker is set in each sector */ 1640 mbrs->mbr_bootsel_magic = mbri->mbr.mbr_bootsel_magic; 1641 /* and copy in bootsel parameters */ 1642 *(struct mbr_bootsel *)((uint8_t *)mbrs + offset) = 1643 ext->mbrb; 1644 for (i = 0; i < MBR_PART_COUNT; i++) { 1645 if (ext->mbrb.mbrbs_nametab[i][0] == 0) 1646 continue; 1647 if (ext->sector + mbrp->mbrp_start == 1648 mbri->bootsec) 1649 mbri->mbrb.mbrbs_defkey = key; 1650 key++; 1651 } 1652 } 1653 /* copy main data (again) since we've put the 'key' in */ 1654 *(struct mbr_bootsel *)((uint8_t *)&mbri->mbr + offset) = 1655 mbri->mbrb; 1656 } 1657#endif 1658 1659 for (ext = mbri; ext != NULL; ext = ext->extended) { 1660 sector = ext->sector; 1661 mbrsec = ext->mbr; /* copy sector */ 1662 mbrp = &mbrsec.mbr_parts[0]; 1663 1664 if (sector != 0 && ext->extended != NULL 1665 && ext->extended->mbr.mbr_parts[0].mbrp_type == 0) { 1666 /* We are followed by an empty slot, collapse out */ 1667 ext = ext->extended; 1668 /* Make us describe the next non-empty partition */ 1669 mbrp[1] = ext->mbr.mbr_parts[1]; 1670 } 1671 1672 for (i = 0; i < MBR_PART_COUNT; i++) { 1673 if (mbrp[i].mbrp_start == 0 && mbrp[i].mbrp_size == 0) { 1674 mbrp[i].mbrp_scyl = 0; 1675 mbrp[i].mbrp_shd = 0; 1676 mbrp[i].mbrp_ssect = 0; 1677 mbrp[i].mbrp_ecyl = 0; 1678 mbrp[i].mbrp_ehd = 0; 1679 mbrp[i].mbrp_esect = 0; 1680 continue; 1681 } 1682 pstart = mbrp[i].mbrp_start; 1683 psize = mbrp[i].mbrp_size; 1684 mbrp[i].mbrp_start = htole32(pstart); 1685 mbrp[i].mbrp_size = htole32(psize); 1686 if (convert) { 1687 convert_mbr_chs(bcyl, bhead, bsec, 1688 &mbrp[i].mbrp_scyl, &mbrp[i].mbrp_shd, 1689 &mbrp[i].mbrp_ssect, pstart); 1690 convert_mbr_chs(bcyl, bhead, bsec, 1691 &mbrp[i].mbrp_ecyl, &mbrp[i].mbrp_ehd, 1692 &mbrp[i].mbrp_esect, pstart + psize - 1); 1693 } 1694 } 1695 1696 mbrsec.mbr_magic = htole16(MBR_MAGIC); 1697 if (pwrite(fd, &mbrsec, sizeof mbrsec, 1698 sector * (off_t)MBR_SECSIZE) < 0) { 1699 ret = -1; 1700 break; 1701 } 1702 } 1703 1704 (void)close(fd); 1705 return ret; 1706} 1707 1708int 1709valid_mbr(struct mbr_sector *mbrs) 1710{ 1711 1712 return (le16toh(mbrs->mbr_magic) == MBR_MAGIC); 1713} 1714 1715static void 1716convert_mbr_chs(int cyl, int head, int sec, 1717 uint8_t *cylp, uint8_t *headp, uint8_t *secp, 1718 uint32_t relsecs) 1719{ 1720 unsigned int tcyl, temp, thead, tsec; 1721 1722 temp = head * sec; 1723 tcyl = relsecs / temp; 1724 relsecs -= tcyl * temp; 1725 1726 thead = relsecs / sec; 1727 tsec = relsecs - thead * sec + 1; 1728 1729 if (tcyl > MAXCYL) 1730 tcyl = MAXCYL; 1731 1732 *cylp = MBR_PUT_LSCYL(tcyl); 1733 *headp = thead; 1734 *secp = MBR_PUT_MSCYLANDSEC(tcyl, tsec); 1735} 1736 1737/* 1738 * This function is ONLY to be used as a last resort to provide a 1739 * hint for the user. Ports should provide a more reliable way 1740 * of getting the BIOS geometry. The i386 code, for example, 1741 * uses the BIOS geometry as passed on from the bootblocks, 1742 * and only uses this as a hint to the user when that information 1743 * is not present, or a match could not be made with a NetBSD 1744 * device. 1745 */ 1746 1747int 1748guess_biosgeom_from_mbr(mbr_info_t *mbri, int *cyl, int *head, daddr_t *sec) 1749{ 1750 struct mbr_sector *mbrs = &mbri->mbr; 1751 struct mbr_partition *parts = &mbrs->mbr_parts[0]; 1752 int xcylinders, xheads, i, j; 1753 daddr_t xsectors; 1754 int c1, h1, s1, c2, h2, s2; 1755 daddr_t a1, a2; 1756 uint64_t num, denom; 1757 1758 /* 1759 * The physical parameters may be invalid as bios geometry. 1760 * If we cannot determine the actual bios geometry, we are 1761 * better off picking a likely 'faked' geometry than leaving 1762 * the invalid physical one. 1763 */ 1764 1765 xcylinders = dlcyl; 1766 xheads = dlhead; 1767 xsectors = dlsec; 1768 if (xcylinders > MAXCYL || xheads > MAXHEAD || xsectors > MAXSECTOR) { 1769 xsectors = MAXSECTOR; 1770 xheads = MAXHEAD; 1771 xcylinders = dlsize / (MAXSECTOR * MAXHEAD); 1772 if (xcylinders > MAXCYL) 1773 xcylinders = MAXCYL; 1774 } 1775 *cyl = xcylinders; 1776 *head = xheads; 1777 *sec = xsectors; 1778 1779 xheads = -1; 1780 1781 /* Try to deduce the number of heads from two different mappings. */ 1782 for (i = 0; i < MBR_PART_COUNT * 2 - 1; i++) { 1783 if (get_mapping(parts, i, &c1, &h1, &s1, &a1) < 0) 1784 continue; 1785 a1 -= s1; 1786 for (j = i + 1; j < MBR_PART_COUNT * 2; j++) { 1787 if (get_mapping(parts, j, &c2, &h2, &s2, &a2) < 0) 1788 continue; 1789 a2 -= s2; 1790 num = (uint64_t)h1 * a2 - (quad_t)h2 * a1; 1791 denom = (uint64_t)c2 * a1 - (quad_t)c1 * a2; 1792 if (num != 0 && denom != 0 && num % denom == 0) { 1793 xheads = (int)(num / denom); 1794 xsectors = a1 / (c1 * xheads + h1); 1795 break; 1796 } 1797 } 1798 if (xheads != -1) 1799 break; 1800 } 1801 1802 if (xheads == -1) 1803 return -1; 1804 1805 /* 1806 * Estimate the number of cylinders. 1807 * XXX relies on get_disks having been called. 1808 */ 1809 xcylinders = dlsize / xheads / xsectors; 1810 if (dlsize != xcylinders * xheads * xsectors) 1811 xcylinders++; 1812 1813 /* 1814 * Now verify consistency with each of the partition table entries. 1815 * Be willing to shove cylinders up a little bit to make things work, 1816 * but translation mismatches are fatal. 1817 */ 1818 for (i = 0; i < MBR_PART_COUNT * 2; i++) { 1819 if (get_mapping(parts, i, &c1, &h1, &s1, &a1) < 0) 1820 continue; 1821 if (c1 >= MAXCYL - 1) 1822 /* Ignore anything that is near the CHS limit */ 1823 continue; 1824 if (xsectors * (c1 * xheads + h1) + s1 != a1) 1825 return -1; 1826 } 1827 1828 /* 1829 * Everything checks out. Reset the geometry to use for further 1830 * calculations. 1831 */ 1832 *cyl = MIN(xcylinders, MAXCYL); 1833 *head = xheads; 1834 *sec = xsectors; 1835 return 0; 1836} 1837 1838static int 1839get_mapping(struct mbr_partition *parts, int i, 1840 int *cylinder, int *head, int *sector, daddr_t *absolute) 1841{ 1842 struct mbr_partition *apart = &parts[i / 2]; 1843 1844 if (apart->mbrp_type == 0) 1845 return -1; 1846 if (i % 2 == 0) { 1847 *cylinder = MBR_PCYL(apart->mbrp_scyl, apart->mbrp_ssect); 1848 *head = apart->mbrp_shd; 1849 *sector = MBR_PSECT(apart->mbrp_ssect) - 1; 1850 *absolute = le32toh(apart->mbrp_start); 1851 } else { 1852 *cylinder = MBR_PCYL(apart->mbrp_ecyl, apart->mbrp_esect); 1853 *head = apart->mbrp_ehd; 1854 *sector = MBR_PSECT(apart->mbrp_esect) - 1; 1855 *absolute = le32toh(apart->mbrp_start) 1856 + le32toh(apart->mbrp_size) - 1; 1857 } 1858 /* Sanity check the data against max values */ 1859 if ((((*cylinder * MAXHEAD) + *head) * (uint32_t)MAXSECTOR + *sector) < *absolute) 1860 /* cannot be a CHS mapping */ 1861 return -1; 1862 1863 return 0; 1864} 1865 1866/* 1867 * Determine partition boundary alignment as fdisk(8) does. 1868 */ 1869static void 1870get_ptn_alignment(struct mbr_partition *mbrp0) 1871{ 1872 uint32_t ptn_0_base, ptn_0_limit; 1873 1874 /* Default to using 'traditional' cylinder alignment */ 1875 ptn_alignment = bhead * bsec; 1876 ptn_0_offset = bsec; 1877 1878 if (mbrp0->mbrp_type != 0) { 1879 /* Try to copy offset of first partition */ 1880 ptn_0_base = le32toh(mbrp0->mbrp_start); 1881 ptn_0_limit = ptn_0_base + le32toh(mbrp0->mbrp_size); 1882 if (!(ptn_0_limit & 2047)) { 1883 /* Partition ends on a 1MB boundary, align to 1MB */ 1884 ptn_alignment = 2048; 1885 if (ptn_0_base <= 2048 1886 && !(ptn_0_base & (ptn_0_base - 1))) { 1887 /* ptn_base is a power of 2, use it */ 1888 ptn_0_offset = ptn_0_base; 1889 } 1890 } 1891 } else { 1892 /* Use 1MB offset for large (>128GB) disks */ 1893 if (dlsize > 2048 * 1024 * 128) { 1894 ptn_alignment = 2048; 1895 ptn_0_offset = 2048; 1896 } 1897 } 1898} 1899