1 /* $NetBSD: mbr.c,v 1.5 2015/05/10 10:14:02 martin 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 86 struct 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 112 static int get_mapping(struct mbr_partition *, int, int *, int *, int *, 113 daddr_t *); 114 static void convert_mbr_chs(int, int, int, uint8_t *, uint8_t *, 115 uint8_t *, uint32_t); 116 static void get_ptn_alignment(struct mbr_partition *); 117 118 static unsigned int ptn_alignment; 119 static 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 168 void 169 dump_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 */ 193 int 194 set_bios_geom_with_mbr_guess(void) 195 { 196 int cyl, head; 197 daddr_t sec; 198 199 read_mbr(pm->diskdev, &mbr); 200 msg_display(MSG_nobiosgeom, pm->dlcyl, pm->dlhead, pm->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 */ 211 void 212 set_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 = pm->dlsize / bsec / bhead; 231 if (pm->dlsize != bcyl * bsec * bhead) 232 bcyl++; 233 } 234 235 #ifdef notdef 236 void 237 disp_cur_geom(void) 238 { 239 240 msg_display_add(MSG_realgeom, pm->dlcyl, pm->dlhead, pm->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 */ 254 static void 255 remove_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 = pm->oldlabel; p < pm->oldlabel + nelem(pm->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 277 static int 278 find_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 = pm->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 307 static struct mbr_partition * 308 get_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 320 static int 321 err_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 339 static int 340 set_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 478 static void 479 set_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 497 static int 498 edit_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 522 static int 523 edit_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 677 static int 678 edit_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 863 static int 864 edit_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 889 static int 890 edit_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 912 static int 913 edit_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 936 static int 937 edit_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 950 static void set_ptn_label(menudesc *m, int line, void *arg); 951 static void set_ptn_header(menudesc *m, void *arg); 952 953 static int 954 edit_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 995 static void 996 set_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 1058 static void 1059 set_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 1107 static void 1108 set_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 1152 getyx(m->mw, y, x); 1153 if (x > 52) { 1154 x = 52; 1155 wmove(m->mw, y, x); 1156 } 1157 wprintw(m->mw, "%*s %s", 53 - x, "", 1158 mbri->mbrb.mbrbs_nametab[opt]); 1159 } 1160 #endif 1161 } 1162 1163 static void 1164 set_mbr_header(menudesc *m, void *arg) 1165 { 1166 mbr_info_t *mbri = arg; 1167 static menu_ent *opts; 1168 static int num_opts; 1169 mbr_info_t *ext; 1170 menu_ent *op; 1171 int i; 1172 int left; 1173 1174 msg_display(MSG_editparttable); 1175 1176 msg_table_add(MSG_part_header, (unsigned long)(pm->dlsize/sizemult), 1177 multname, multname, multname, multname); 1178 1179 if (num_opts == 0) { 1180 num_opts = 6; 1181 opts = malloc(6 * sizeof *opts); 1182 if (opts == NULL) { 1183 m->numopts = 0; 1184 return; 1185 } 1186 } 1187 1188 /* First four items are the main partitions */ 1189 for (op = opts, i = 0; i < MBR_PART_COUNT; op++, i++) { 1190 op->opt_name = NULL; 1191 op->opt_menu = OPT_NOMENU; 1192 op->opt_flags = OPT_SUB; 1193 op->opt_action = edit_mbr_entry; 1194 } 1195 left = num_opts - MBR_PART_COUNT; 1196 1197 /* Followed by the extended partitions */ 1198 for (ext = mbri->extended; ext; left--, op++, ext = ext->extended) { 1199 if (left <= 1) { 1200 menu_ent *new = realloc(opts, 1201 (num_opts + 4) * sizeof *opts); 1202 if (new == NULL) 1203 break; 1204 num_opts += 4; 1205 left += 4; 1206 op = new + (op - opts); 1207 opts = new; 1208 } 1209 op->opt_name = NULL; 1210 op->opt_menu = OPT_NOMENU; 1211 op->opt_flags = 0; 1212 op->opt_action = edit_mbr_entry; 1213 } 1214 1215 /* and unit changer */ 1216 op->opt_name = MSG_askunits; 1217 op->opt_menu = MENU_sizechoice; 1218 op->opt_flags = OPT_SUB; 1219 op->opt_action = NULL; 1220 op++; 1221 1222 m->opts = opts; 1223 m->numopts = op - opts; 1224 } 1225 1226 int 1227 mbr_use_wholedisk(mbr_info_t *mbri) 1228 { 1229 struct mbr_sector *mbrs = &mbri->mbr; 1230 mbr_info_t *ext; 1231 struct mbr_partition *part; 1232 1233 part = &mbrs->mbr_parts[0]; 1234 /* Set the partition information for full disk usage. */ 1235 while ((ext = mbri->extended)) { 1236 mbri->extended = ext->extended; 1237 free(ext); 1238 } 1239 memset(part, 0, MBR_PART_COUNT * sizeof *part); 1240 #ifdef BOOTSEL 1241 memset(&mbri->mbrb, 0, sizeof mbri->mbrb); 1242 #endif 1243 part[0].mbrp_type = MBR_PTYPE_NETBSD; 1244 part[0].mbrp_size = pm->dlsize - ptn_0_offset; 1245 part[0].mbrp_start = ptn_0_offset; 1246 part[0].mbrp_flag = MBR_PFLAG_ACTIVE; 1247 1248 pm->ptstart = ptn_0_offset; 1249 pm->ptsize = pm->dlsize - ptn_0_offset; 1250 return 1; 1251 } 1252 1253 /* 1254 * Let user change incore Master Boot Record partitions via menu. 1255 */ 1256 int 1257 edit_mbr(mbr_info_t *mbri) 1258 { 1259 struct mbr_sector *mbrs = &mbri->mbr; 1260 mbr_info_t *ext; 1261 struct mbr_partition *part; 1262 int i, j; 1263 int usefull; 1264 int mbr_menu; 1265 int activepart; 1266 int numbsd; 1267 uint bsdstart, bsdsize; 1268 uint start; 1269 1270 /* Ask full/part */ 1271 1272 part = &mbrs->mbr_parts[0]; 1273 get_ptn_alignment(part); /* update ptn_alignment */ 1274 if (partman_go) 1275 usefull = 0; 1276 else { 1277 msg_display(MSG_fullpart, pm->diskdev); 1278 process_menu(MENU_fullpart, &usefull); 1279 } 1280 1281 /* DOS fdisk label checking and value setting. */ 1282 if (usefull) { 1283 /* Count nonempty, non-BSD partitions. */ 1284 numbsd = 0; 1285 for (i = 0; i < MBR_PART_COUNT; i++) { 1286 j = part[i].mbrp_type; 1287 if (j == 0) 1288 continue; 1289 numbsd++; 1290 if (j != MBR_PTYPE_NETBSD) 1291 numbsd++; 1292 } 1293 1294 /* Ask if we really want to blow away non-NetBSD stuff */ 1295 if (numbsd > 1) { 1296 msg_display(MSG_ovrwrite); 1297 if (!ask_noyes(NULL)) { 1298 if (logfp) 1299 (void)fprintf(logfp, "User answered no to destroy other data, aborting.\n"); 1300 return 0; 1301 } 1302 } 1303 return(md_mbr_use_wholedisk(mbri)); 1304 } 1305 1306 mbr_menu = new_menu(NULL, NULL, 16, 0, -1, 15, 70, 1307 MC_NOBOX | MC_ALWAYS_SCROLL | MC_NOCLEAR, 1308 set_mbr_header, set_mbr_label, NULL, 1309 NULL, MSG_Partition_table_ok); 1310 if (mbr_menu == -1) 1311 return 0; 1312 1313 /* Default to MB, and use bios geometry for cylinder size */ 1314 set_sizemultname_meg(); 1315 pm->current_cylsize = bhead * bsec; 1316 1317 for (;;) { 1318 pm->ptstart = 0; 1319 pm->ptsize = 0; 1320 process_menu(mbr_menu, mbri); 1321 1322 activepart = 0; 1323 bsdstart = 0; 1324 bsdsize = 0; 1325 for (ext = mbri; ext; ext = ext->extended) { 1326 part = ext->mbr.mbr_parts; 1327 for (i = 0; i < MBR_PART_COUNT; part++, i++) { 1328 if (part->mbrp_flag != 0) 1329 activepart = 1; 1330 if (part->mbrp_type != MBR_PTYPE_NETBSD) 1331 continue; 1332 start = ext->sector + part->mbrp_start; 1333 if (start == mbri->install) { 1334 pm->ptstart = mbri->install; 1335 pm->ptsize = part->mbrp_size; 1336 } 1337 if (bsdstart != 0) 1338 bsdstart = ~0; 1339 else { 1340 bsdstart = start; 1341 bsdsize = part->mbrp_size; 1342 } 1343 } 1344 } 1345 1346 /* Install in only netbsd partition if none tagged */ 1347 if (pm->ptstart == 0 && bsdstart != ~0u) { 1348 pm->ptstart = bsdstart; 1349 pm->ptsize = bsdsize; 1350 } 1351 1352 if (pm->ptstart == 0) { 1353 if (bsdstart == 0) 1354 msg_display(MSG_nobsdpart); 1355 else 1356 msg_display(MSG_multbsdpart, 0); 1357 msg_display_add(MSG_reeditpart, 0); 1358 if (!ask_yesno(NULL)) 1359 return 0; 1360 continue; 1361 } 1362 1363 if (activepart == 0) { 1364 msg_display(MSG_noactivepart); 1365 if (ask_yesno(NULL)) 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 1385 const char * 1386 get_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 1400 static int 1401 validate_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 1438 int 1439 read_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 = pm->dlsec; 1459 if (bhead == 0) 1460 bhead = pm->dlhead; 1461 if (bcyl == 0) 1462 bhead = pm->dlcyl; 1463 1464 ptn_0_offset = bsec; 1465 /* use 1MB default offset on large disks as fdisk(8) */ 1466 if (pm->dlsize > 2048 * 1024 * 128) 1467 ptn_0_offset = 2048; 1468 1469 memset(mbri, 0, sizeof *mbri); 1470 1471 /* Open the disk. */ 1472 fd = opendisk(disk, O_RDONLY, diskpath, sizeof(diskpath), 0); 1473 if (fd < 0) 1474 goto bad_mbr; 1475 1476 for (;;) { 1477 if (pread(fd, mbrs, sizeof *mbrs, 1478 (ext_base + next_ext) * (off_t)MBR_SECSIZE) - sizeof *mbrs != 0) 1479 break; 1480 1481 if (!valid_mbr(mbrs)) 1482 break; 1483 1484 mbrp = &mbrs->mbr_parts[0]; 1485 if (ext_base == 0) { 1486 get_ptn_alignment(mbrp); /* get ptn_0_offset */ 1487 } else { 1488 /* sanity check extended chain */ 1489 if (MBR_IS_EXTENDED(mbrp[0].mbrp_type)) 1490 break; 1491 if (mbrp[1].mbrp_type != 0 && 1492 !MBR_IS_EXTENDED(mbrp[1].mbrp_type)) 1493 break; 1494 if (mbrp[2].mbrp_type != 0 || mbrp[3].mbrp_type != 0) 1495 break; 1496 /* Looks ok, link into extended chain */ 1497 mbri->extended = ext; 1498 ext->prev_ext = next_ext != 0 ? mbri : NULL; 1499 ext->extended = NULL; 1500 mbri = ext; 1501 ext = NULL; 1502 } 1503 #if BOOTSEL 1504 if (mbrs->mbr_bootsel_magic == htole16(MBR_MAGIC)) { 1505 /* old bootsel, grab bootsel info */ 1506 bootkey = validate_and_set_names(mbri, 1507 (struct mbr_bootsel *) 1508 ((uint8_t *)mbrs + MBR_BS_OLD_OFFSET), 1509 ext_base); 1510 } else if (mbrs->mbr_bootsel_magic == htole16(MBR_BS_MAGIC)) { 1511 /* new location */ 1512 bootkey = validate_and_set_names(mbri, 1513 &mbrs->mbr_bootsel, ext_base); 1514 } 1515 /* Save original flags for mbr code update tests */ 1516 mbri->oflags = mbri->mbrb.mbrbs_flags; 1517 #endif 1518 mbri->sector = next_ext + ext_base; 1519 next_ext = 0; 1520 rval = 0; 1521 for (i = 0; i < MBR_PART_COUNT; mbrp++, i++) { 1522 if (mbrp->mbrp_type == 0) { 1523 /* type is unused, discard scum */ 1524 memset(mbrp, 0, sizeof *mbrp); 1525 continue; 1526 } 1527 mbrp->mbrp_start = le32toh(mbrp->mbrp_start); 1528 mbrp->mbrp_size = le32toh(mbrp->mbrp_size); 1529 if (MBR_IS_EXTENDED(mbrp->mbrp_type)) { 1530 next_ext = mbrp->mbrp_start; 1531 if (ext_base == 0) 1532 ext_size = mbrp->mbrp_size; 1533 } else { 1534 mbri->last_mounted[i] = strdup(get_last_mounted( 1535 fd, mbri->sector + mbrp->mbrp_start, NULL)); 1536 #if BOOTSEL 1537 if (ombri->install == 0 && 1538 strcmp(mbri->last_mounted[i], "/") == 0) 1539 ombri->install = mbri->sector + 1540 mbrp->mbrp_start; 1541 #endif 1542 } 1543 #if BOOTSEL 1544 if (mbri->mbrb.mbrbs_nametab[i][0] != 0 1545 && bootkey-- == 0) 1546 ombri->bootsec = mbri->sector + 1547 mbrp->mbrp_start; 1548 #endif 1549 } 1550 1551 if (ext_base != 0) { 1552 /* Is there a gap before the next partition? */ 1553 unsigned int limit = next_ext; 1554 unsigned int base; 1555 if (limit == 0) 1556 limit = ext_size; 1557 mbrp -= MBR_PART_COUNT; 1558 base =mbri->sector + mbrp->mbrp_start + mbrp->mbrp_size; 1559 if (mbrp->mbrp_type != 0 && ext_base + limit != base) { 1560 /* Mock up an extry for the space */ 1561 ext = calloc(1, sizeof *ext); 1562 if (!ext) 1563 break; 1564 ext->sector = base; 1565 ext->mbr.mbr_magic = htole16(MBR_MAGIC); 1566 ext->mbr.mbr_parts[1] = mbrp[1]; 1567 ext->mbr.mbr_parts[0].mbrp_start = ptn_0_offset; 1568 ext->mbr.mbr_parts[0].mbrp_size = 1569 ext_base + limit - base - ptn_0_offset; 1570 mbrp[1].mbrp_type = MBR_PTYPE_EXT; 1571 mbrp[1].mbrp_start = base - ext_base; 1572 mbrp[1].mbrp_size = limit - mbrp[1].mbrp_start; 1573 mbri->extended = ext; 1574 ext->prev_ext = mbri; 1575 ext->extended = NULL; 1576 mbri = ext; 1577 ext = NULL; 1578 } 1579 } 1580 1581 if (next_ext == 0 || ext_base + next_ext <= mbri->sector) 1582 break; 1583 if (ext_base == 0) { 1584 ext_base = next_ext; 1585 next_ext = 0; 1586 } 1587 ext = calloc(sizeof *ext, 1); 1588 if (!ext) 1589 break; 1590 mbrs = &ext->mbr; 1591 } 1592 1593 bad_mbr: 1594 free(ext); 1595 if (fd >= 0) 1596 close(fd); 1597 if (rval == -1) { 1598 memset(&mbrs->mbr_parts, 0, sizeof mbrs->mbr_parts); 1599 mbrs->mbr_magic = htole16(MBR_MAGIC); 1600 } 1601 dump_mbr(ombri, "read"); 1602 return rval; 1603 } 1604 1605 int 1606 write_mbr(const char *disk, mbr_info_t *mbri, int convert) 1607 { 1608 char diskpath[MAXPATHLEN]; 1609 int fd, i, ret = 0; 1610 struct mbr_partition *mbrp; 1611 u_int32_t pstart, psize; 1612 #ifdef BOOTSEL 1613 struct mbr_sector *mbrs; 1614 #endif 1615 struct mbr_sector mbrsec; 1616 mbr_info_t *ext; 1617 uint sector; 1618 1619 /* Open the disk. */ 1620 fd = opendisk(disk, O_WRONLY, diskpath, sizeof(diskpath), 0); 1621 if (fd < 0) 1622 return -1; 1623 1624 #ifdef BOOTSEL 1625 /* 1626 * If the main boot code (appears to) contain the netbsd bootcode, 1627 * copy in all the menu strings and set the default keycode 1628 * to be that for the default partition. 1629 * Unfortunately we can't rely on the user having actually updated 1630 * to the new mbr code :-( 1631 */ 1632 if (mbri->mbr.mbr_bootsel_magic == htole16(MBR_BS_MAGIC) 1633 || mbri->mbr.mbr_bootsel_magic == htole16(MBR_MAGIC)) { 1634 int8_t key = SCAN_1; 1635 uint offset = MBR_BS_OFFSET; 1636 if (mbri->mbr.mbr_bootsel_magic == htole16(MBR_MAGIC)) 1637 offset = MBR_BS_OLD_OFFSET; 1638 mbri->mbrb.mbrbs_defkey = SCAN_ENTER; 1639 if (mbri->mbrb.mbrbs_timeo == 0) 1640 mbri->mbrb.mbrbs_timeo = 182; /* 10 seconds */ 1641 for (ext = mbri; ext != NULL; ext = ext->extended) { 1642 mbrs = &ext->mbr; 1643 mbrp = &mbrs->mbr_parts[0]; 1644 /* Ensure marker is set in each sector */ 1645 mbrs->mbr_bootsel_magic = mbri->mbr.mbr_bootsel_magic; 1646 /* and copy in bootsel parameters */ 1647 *(struct mbr_bootsel *)((uint8_t *)mbrs + offset) = 1648 ext->mbrb; 1649 for (i = 0; i < MBR_PART_COUNT; i++) { 1650 if (ext->mbrb.mbrbs_nametab[i][0] == 0) 1651 continue; 1652 if (ext->sector + mbrp->mbrp_start == 1653 mbri->bootsec) 1654 mbri->mbrb.mbrbs_defkey = key; 1655 key++; 1656 } 1657 } 1658 /* copy main data (again) since we've put the 'key' in */ 1659 *(struct mbr_bootsel *)((uint8_t *)&mbri->mbr + offset) = 1660 mbri->mbrb; 1661 } 1662 #endif 1663 1664 for (ext = mbri; ext != NULL; ext = ext->extended) { 1665 sector = ext->sector; 1666 mbrsec = ext->mbr; /* copy sector */ 1667 mbrp = &mbrsec.mbr_parts[0]; 1668 1669 if (sector != 0 && ext->extended != NULL 1670 && ext->extended->mbr.mbr_parts[0].mbrp_type == 0) { 1671 /* We are followed by an empty slot, collapse out */ 1672 ext = ext->extended; 1673 /* Make us describe the next non-empty partition */ 1674 mbrp[1] = ext->mbr.mbr_parts[1]; 1675 } 1676 1677 for (i = 0; i < MBR_PART_COUNT; i++) { 1678 if (mbrp[i].mbrp_start == 0 && mbrp[i].mbrp_size == 0) { 1679 mbrp[i].mbrp_scyl = 0; 1680 mbrp[i].mbrp_shd = 0; 1681 mbrp[i].mbrp_ssect = 0; 1682 mbrp[i].mbrp_ecyl = 0; 1683 mbrp[i].mbrp_ehd = 0; 1684 mbrp[i].mbrp_esect = 0; 1685 continue; 1686 } 1687 pstart = mbrp[i].mbrp_start; 1688 psize = mbrp[i].mbrp_size; 1689 mbrp[i].mbrp_start = htole32(pstart); 1690 mbrp[i].mbrp_size = htole32(psize); 1691 if (convert) { 1692 convert_mbr_chs(bcyl, bhead, bsec, 1693 &mbrp[i].mbrp_scyl, &mbrp[i].mbrp_shd, 1694 &mbrp[i].mbrp_ssect, pstart); 1695 convert_mbr_chs(bcyl, bhead, bsec, 1696 &mbrp[i].mbrp_ecyl, &mbrp[i].mbrp_ehd, 1697 &mbrp[i].mbrp_esect, pstart + psize - 1); 1698 } 1699 } 1700 1701 mbrsec.mbr_magic = htole16(MBR_MAGIC); 1702 if (pwrite(fd, &mbrsec, sizeof mbrsec, 1703 sector * (off_t)MBR_SECSIZE) < 0) { 1704 ret = -1; 1705 break; 1706 } 1707 } 1708 1709 (void)close(fd); 1710 return ret; 1711 } 1712 1713 int 1714 valid_mbr(struct mbr_sector *mbrs) 1715 { 1716 1717 return (le16toh(mbrs->mbr_magic) == MBR_MAGIC); 1718 } 1719 1720 static void 1721 convert_mbr_chs(int cyl, int head, int sec, 1722 uint8_t *cylp, uint8_t *headp, uint8_t *secp, 1723 uint32_t relsecs) 1724 { 1725 unsigned int tcyl, temp, thead, tsec; 1726 1727 temp = head * sec; 1728 tcyl = relsecs / temp; 1729 relsecs -= tcyl * temp; 1730 1731 thead = relsecs / sec; 1732 tsec = relsecs - thead * sec + 1; 1733 1734 if (tcyl > MAXCYL) 1735 tcyl = MAXCYL; 1736 1737 *cylp = MBR_PUT_LSCYL(tcyl); 1738 *headp = thead; 1739 *secp = MBR_PUT_MSCYLANDSEC(tcyl, tsec); 1740 } 1741 1742 /* 1743 * This function is ONLY to be used as a last resort to provide a 1744 * hint for the user. Ports should provide a more reliable way 1745 * of getting the BIOS geometry. The i386 code, for example, 1746 * uses the BIOS geometry as passed on from the bootblocks, 1747 * and only uses this as a hint to the user when that information 1748 * is not present, or a match could not be made with a NetBSD 1749 * device. 1750 */ 1751 1752 int 1753 guess_biosgeom_from_mbr(mbr_info_t *mbri, int *cyl, int *head, daddr_t *sec) 1754 { 1755 struct mbr_sector *mbrs = &mbri->mbr; 1756 struct mbr_partition *parts = &mbrs->mbr_parts[0]; 1757 int xcylinders, xheads, i, j; 1758 daddr_t xsectors; 1759 int c1, h1, s1, c2, h2, s2; 1760 daddr_t a1, a2; 1761 uint64_t num, denom; 1762 1763 /* 1764 * The physical parameters may be invalid as bios geometry. 1765 * If we cannot determine the actual bios geometry, we are 1766 * better off picking a likely 'faked' geometry than leaving 1767 * the invalid physical one. 1768 */ 1769 1770 xcylinders = pm->dlcyl; 1771 xheads = pm->dlhead; 1772 xsectors = pm->dlsec; 1773 if (xcylinders > MAXCYL || xheads > MAXHEAD || xsectors > MAXSECTOR) { 1774 xsectors = MAXSECTOR; 1775 xheads = MAXHEAD; 1776 xcylinders = pm->dlsize / (MAXSECTOR * MAXHEAD); 1777 if (xcylinders > MAXCYL) 1778 xcylinders = MAXCYL; 1779 } 1780 *cyl = xcylinders; 1781 *head = xheads; 1782 *sec = xsectors; 1783 1784 xheads = -1; 1785 1786 /* Try to deduce the number of heads from two different mappings. */ 1787 for (i = 0; i < MBR_PART_COUNT * 2 - 1; i++) { 1788 if (get_mapping(parts, i, &c1, &h1, &s1, &a1) < 0) 1789 continue; 1790 a1 -= s1; 1791 for (j = i + 1; j < MBR_PART_COUNT * 2; j++) { 1792 if (get_mapping(parts, j, &c2, &h2, &s2, &a2) < 0) 1793 continue; 1794 a2 -= s2; 1795 num = (uint64_t)h1 * a2 - (quad_t)h2 * a1; 1796 denom = (uint64_t)c2 * a1 - (quad_t)c1 * a2; 1797 if (num != 0 && denom != 0 && num % denom == 0) { 1798 xheads = (int)(num / denom); 1799 xsectors = a1 / (c1 * xheads + h1); 1800 break; 1801 } 1802 } 1803 if (xheads != -1) 1804 break; 1805 } 1806 1807 if (xheads == -1) 1808 return -1; 1809 1810 /* 1811 * Estimate the number of cylinders. 1812 * XXX relies on get_disks having been called. 1813 */ 1814 xcylinders = pm->dlsize / xheads / xsectors; 1815 if (pm->dlsize != xcylinders * xheads * xsectors) 1816 xcylinders++; 1817 1818 /* 1819 * Now verify consistency with each of the partition table entries. 1820 * Be willing to shove cylinders up a little bit to make things work, 1821 * but translation mismatches are fatal. 1822 */ 1823 for (i = 0; i < MBR_PART_COUNT * 2; i++) { 1824 if (get_mapping(parts, i, &c1, &h1, &s1, &a1) < 0) 1825 continue; 1826 if (c1 >= MAXCYL - 1) 1827 /* Ignore anything that is near the CHS limit */ 1828 continue; 1829 if (xsectors * (c1 * xheads + h1) + s1 != a1) 1830 return -1; 1831 } 1832 1833 /* 1834 * Everything checks out. Reset the geometry to use for further 1835 * calculations. 1836 */ 1837 *cyl = MIN(xcylinders, MAXCYL); 1838 *head = xheads; 1839 *sec = xsectors; 1840 return 0; 1841 } 1842 1843 static int 1844 get_mapping(struct mbr_partition *parts, int i, 1845 int *cylinder, int *head, int *sector, daddr_t *absolute) 1846 { 1847 struct mbr_partition *apart = &parts[i / 2]; 1848 1849 if (apart->mbrp_type == 0) 1850 return -1; 1851 if (i % 2 == 0) { 1852 *cylinder = MBR_PCYL(apart->mbrp_scyl, apart->mbrp_ssect); 1853 *head = apart->mbrp_shd; 1854 *sector = MBR_PSECT(apart->mbrp_ssect) - 1; 1855 *absolute = le32toh(apart->mbrp_start); 1856 } else { 1857 *cylinder = MBR_PCYL(apart->mbrp_ecyl, apart->mbrp_esect); 1858 *head = apart->mbrp_ehd; 1859 *sector = MBR_PSECT(apart->mbrp_esect) - 1; 1860 *absolute = le32toh(apart->mbrp_start) 1861 + le32toh(apart->mbrp_size) - 1; 1862 } 1863 /* Sanity check the data against max values */ 1864 if ((((*cylinder * MAXHEAD) + *head) * (uint32_t)MAXSECTOR + *sector) < *absolute) 1865 /* cannot be a CHS mapping */ 1866 return -1; 1867 1868 return 0; 1869 } 1870 1871 /* 1872 * Determine partition boundary alignment as fdisk(8) does. 1873 */ 1874 static void 1875 get_ptn_alignment(struct mbr_partition *mbrp0) 1876 { 1877 uint32_t ptn_0_base, ptn_0_limit; 1878 1879 /* Default to using 'traditional' cylinder alignment */ 1880 ptn_alignment = bhead * bsec; 1881 ptn_0_offset = bsec; 1882 1883 if (mbrp0->mbrp_type != 0) { 1884 /* Try to copy offset of first partition */ 1885 ptn_0_base = le32toh(mbrp0->mbrp_start); 1886 ptn_0_limit = ptn_0_base + le32toh(mbrp0->mbrp_size); 1887 if (!(ptn_0_limit & 2047)) { 1888 /* Partition ends on a 1MB boundary, align to 1MB */ 1889 ptn_alignment = 2048; 1890 if (ptn_0_base <= 2048 1891 && !(ptn_0_base & (ptn_0_base - 1))) { 1892 /* ptn_base is a power of 2, use it */ 1893 ptn_0_offset = ptn_0_base; 1894 } 1895 } 1896 } else { 1897 /* Use 1MB offset for large (>128GB) disks */ 1898 if (pm->dlsize > 2048 * 1024 * 128) { 1899 ptn_alignment = 2048; 1900 ptn_0_offset = 2048; 1901 } 1902 } 1903 } 1904