disks.c revision 144334
1/* 2 * The new sysinstall program. 3 * 4 * This is probably the last program in the `sysinstall' line - the next 5 * generation being essentially a complete rewrite. 6 * 7 * $FreeBSD: head/usr.sbin/sade/disks.c 144334 2005-03-30 13:03:33Z nyan $ 8 * 9 * Copyright (c) 1995 10 * Jordan Hubbard. All rights reserved. 11 * 12 * Redistribution and use in source and binary forms, with or without 13 * modification, are permitted provided that the following conditions 14 * are met: 15 * 1. Redistributions of source code must retain the above copyright 16 * notice, this list of conditions and the following disclaimer, 17 * verbatim and that no modifications are made prior to this 18 * point in the file. 19 * 2. Redistributions in binary form must reproduce the above copyright 20 * notice, this list of conditions and the following disclaimer in the 21 * documentation and/or other materials provided with the distribution. 22 * 23 * THIS SOFTWARE IS PROVIDED BY JORDAN HUBBARD ``AS IS'' AND 24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 26 * ARE DISCLAIMED. IN NO EVENT SHALL JORDAN HUBBARD OR HIS PETS BE LIABLE 27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 29 * OR SERVICES; LOSS OF USE, DATA, LIFE OR PROFITS; OR BUSINESS INTERRUPTION) 30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 33 * SUCH DAMAGE. 34 * 35 */ 36 37#include "sysinstall.h" 38#include <ctype.h> 39#include <fcntl.h> 40#include <inttypes.h> 41#include <libdisk.h> 42#include <sys/stat.h> 43#include <sys/disklabel.h> 44 45#ifdef WITH_SLICES 46enum size_units_t { UNIT_BLOCKS, UNIT_KILO, UNIT_MEG, UNIT_GIG, UNIT_SIZE }; 47 48#ifdef PC98 49#define SUBTYPE_FREEBSD 50324 50#define SUBTYPE_FAT 37218 51#else 52#define SUBTYPE_FREEBSD 165 53#define SUBTYPE_FAT 6 54#endif 55#define SUBTYPE_EFI 239 56 57#ifdef PC98 58#define OTHER_SLICE_VALUES \ 59 "Other popular values are 37218 for a\n" \ 60 "DOS FAT partition.\n\n" 61#else 62#define OTHER_SLICE_VALUES \ 63 "Other popular values are 6 for a\n" \ 64 "DOS FAT partition, 131 for a Linux ext2fs partition, or\n" \ 65 "130 for a Linux swap partition.\n\n" 66#endif 67#define NON_FREEBSD_NOTE \ 68 "Note: If you choose a non-FreeBSD partition type, it will not\n" \ 69 "be formatted or otherwise prepared, it will simply reserve space\n" \ 70 "for you to use another tool, such as DOS format, to later format\n" \ 71 "and actually use the partition." 72 73/* Where we start displaying chunk information on the screen */ 74#define CHUNK_START_ROW 5 75 76/* Where we keep track of MBR chunks */ 77#define CHUNK_INFO_ENTRIES 16 78static struct chunk *chunk_info[CHUNK_INFO_ENTRIES]; 79static int current_chunk; 80 81static void diskPartitionNonInteractive(Device *dev); 82static u_char * bootalloc(char *name, size_t *size); 83 84static void 85record_chunks(Disk *d) 86{ 87 struct chunk *c1 = NULL; 88 int i = 0; 89 daddr_t last_free = 0; 90 91 if (!d->chunks) 92 msgFatal("No chunk list found for %s!", d->name); 93 94 for (c1 = d->chunks->part; c1; c1 = c1->next) { 95 if (c1->type == unused && c1->size > last_free) { 96 last_free = c1->size; 97 current_chunk = i; 98 } 99 chunk_info[i++] = c1; 100 } 101 chunk_info[i] = NULL; 102 if (current_chunk >= i) 103 current_chunk = i - 1; 104} 105 106static daddr_t Total; 107 108static void 109print_chunks(Disk *d, int u) 110{ 111 int row; 112 int i; 113 daddr_t sz; 114 char *szstr; 115 116 szstr = (u == UNIT_GIG ? "GB" : (u == UNIT_MEG ? "MB" : 117 (u == UNIT_KILO ? "KB" : "ST"))); 118 119 Total = 0; 120 for (i = 0; chunk_info[i]; i++) 121 Total += chunk_info[i]->size; 122#ifdef PC98 123 if (d->bios_cyl >= 65536 || d->bios_hd > 256 || d->bios_sect >= 256) { 124#else 125 if (d->bios_cyl > 65536 || d->bios_hd > 256 || d->bios_sect >= 64) { 126#endif 127 dialog_clear_norefresh(); 128 msgConfirm("WARNING: A geometry of %lu/%lu/%lu for %s is incorrect. Using\n" 129 "a more likely geometry. If this geometry is incorrect or you\n" 130 "are unsure as to whether or not it's correct, please consult\n" 131 "the Hardware Guide in the Documentation submenu or use the\n" 132 "(G)eometry command to change it now.\n\n" 133 "Remember: you need to enter whatever your BIOS thinks the\n" 134 "geometry is! For IDE, it's what you were told in the BIOS\n" 135 "setup. For SCSI, it's the translation mode your controller is\n" 136 "using. Do NOT use a ``physical geometry''.", 137 d->bios_cyl, d->bios_hd, d->bios_sect, d->name); 138 Sanitize_Bios_Geom(d); 139 } 140 attrset(A_NORMAL); 141 mvaddstr(0, 0, "Disk name:\t"); 142 clrtobot(); 143 attrset(A_REVERSE); addstr(d->name); attrset(A_NORMAL); 144 attrset(A_REVERSE); mvaddstr(0, 55, "FDISK Partition Editor"); attrset(A_NORMAL); 145 mvprintw(1, 0, 146 "DISK Geometry:\t%lu cyls/%lu heads/%lu sectors = %jd sectors (%jdMB)", 147 d->bios_cyl, d->bios_hd, d->bios_sect, 148 (intmax_t)d->bios_cyl * d->bios_hd * d->bios_sect, 149 (intmax_t)d->bios_cyl * d->bios_hd * d->bios_sect / (1024/512) / 1024); 150 mvprintw(3, 0, "%6s %10s(%s) %10s %8s %6s %10s %8s %8s", 151 "Offset", "Size", szstr, "End", "Name", "PType", "Desc", 152 "Subtype", "Flags"); 153 for (i = 0, row = CHUNK_START_ROW; chunk_info[i]; i++, row++) { 154 switch(u) { 155 default: /* fall thru */ 156 case UNIT_BLOCKS: 157 sz = chunk_info[i]->size; 158 break; 159 case UNIT_KILO: 160 sz = chunk_info[i]->size / (1024/512); 161 break; 162 case UNIT_MEG: 163 sz = chunk_info[i]->size / (1024/512) / 1024; 164 break; 165 case UNIT_GIG: 166 sz = chunk_info[i]->size / (1024/512) / 1024 / 1024; 167 break; 168 } 169 if (i == current_chunk) 170 attrset(ATTR_SELECTED); 171 mvprintw(row, 0, "%10jd %10jd %10jd %8s %6d %10s %8d\t%-6s", 172 (intmax_t)chunk_info[i]->offset, (intmax_t)sz, 173 (intmax_t)chunk_info[i]->end, chunk_info[i]->name, 174 chunk_info[i]->type, 175 slice_type_name(chunk_info[i]->type, chunk_info[i]->subtype), 176 chunk_info[i]->subtype, ShowChunkFlags(chunk_info[i])); 177 if (i == current_chunk) 178 attrset(A_NORMAL); 179 } 180} 181 182static void 183print_command_summary() 184{ 185 mvprintw(14, 0, "The following commands are supported (in upper or lower case):"); 186 mvprintw(16, 0, "A = Use Entire Disk G = set Drive Geometry C = Create Slice F = `DD' mode"); 187 mvprintw(17, 0, "D = Delete Slice Z = Toggle Size Units S = Set Bootable | = Wizard m."); 188 mvprintw(18, 0, "T = Change Type U = Undo All Changes Q = Finish"); 189 if (!RunningAsInit) 190 mvprintw(18, 47, "W = Write Changes"); 191 mvprintw(21, 0, "Use F1 or ? to get more help, arrow keys to select."); 192 move(0, 0); 193} 194 195#ifdef PC98 196static void 197getBootMgr(char *dname, u_char **bootipl, size_t *bootipl_size, 198 u_char **bootmenu, size_t *bootmenu_size) 199{ 200 static u_char *boot0; 201 static size_t boot0_size; 202 static u_char *boot05; 203 static size_t boot05_size; 204 205 char str[80]; 206 char *cp; 207 int i = 0; 208 209 cp = variable_get(VAR_BOOTMGR); 210 if (!cp) { 211 /* Figure out what kind of IPL the user wants */ 212 sprintf(str, "Install Boot Manager for drive %s?", dname); 213 MenuIPLType.title = str; 214 i = dmenuOpenSimple(&MenuIPLType, FALSE); 215 } else { 216 if (!strncmp(cp, "boot", 4)) 217 BootMgr = 0; 218 else 219 BootMgr = 1; 220 } 221 if (cp || i) { 222 switch (BootMgr) { 223 case 0: 224 if (!boot0) boot0 = bootalloc("boot0", &boot0_size); 225 *bootipl = boot0; 226 *bootipl_size = boot0_size; 227 if (!boot05) boot05 = bootalloc("boot0.5", &boot05_size); 228 *bootmenu = boot05; 229 *bootmenu_size = boot05_size; 230 return; 231 case 1: 232 default: 233 break; 234 } 235 } 236 *bootipl = NULL; 237 *bootipl_size = 0; 238 *bootmenu = NULL; 239 *bootmenu_size = 0; 240} 241#else 242static void 243getBootMgr(char *dname, u_char **bootCode, size_t *bootCodeSize) 244{ 245#if defined(__i386__) || defined(__amd64__) /* only meaningful on x86 */ 246 static u_char *mbr, *boot0; 247 static size_t mbr_size, boot0_size; 248 char str[80]; 249 char *cp; 250 int i = 0; 251 252 cp = variable_get(VAR_BOOTMGR); 253 if (!cp) { 254 /* Figure out what kind of MBR the user wants */ 255 sprintf(str, "Install Boot Manager for drive %s?", dname); 256 MenuMBRType.title = str; 257 i = dmenuOpenSimple(&MenuMBRType, FALSE); 258 } 259 else { 260 if (!strncmp(cp, "boot", 4)) 261 BootMgr = 0; 262 else if (!strcmp(cp, "standard")) 263 BootMgr = 1; 264 else 265 BootMgr = 2; 266 } 267 if (cp || i) { 268 switch (BootMgr) { 269 case 0: 270 if (!boot0) boot0 = bootalloc("boot0", &boot0_size); 271 *bootCode = boot0; 272 *bootCodeSize = boot0_size; 273 return; 274 case 1: 275 if (!mbr) mbr = bootalloc("mbr", &mbr_size); 276 *bootCode = mbr; 277 *bootCodeSize = mbr_size; 278 return; 279 case 2: 280 default: 281 break; 282 } 283 } 284#endif 285 *bootCode = NULL; 286 *bootCodeSize = 0; 287} 288#endif 289#endif /* WITH_SLICES */ 290 291int 292diskGetSelectCount(Device ***devs) 293{ 294 int i, cnt, enabled; 295 char *cp; 296 Device **dp; 297 298 cp = variable_get(VAR_DISK); 299 dp = *devs = deviceFind(cp, DEVICE_TYPE_DISK); 300 cnt = deviceCount(dp); 301 if (!cnt) 302 return -1; 303 for (i = 0, enabled = 0; i < cnt; i++) { 304 if (dp[i]->enabled) 305 ++enabled; 306 } 307 return enabled; 308} 309 310#ifdef WITH_SLICES 311void 312diskPartition(Device *dev) 313{ 314 char *cp, *p; 315 int rv, key = 0; 316 int i; 317 Boolean chunking; 318 char *msg = NULL; 319#ifdef PC98 320 u_char *bootipl; 321 size_t bootipl_size; 322 u_char *bootmenu; 323 size_t bootmenu_size; 324#else 325 u_char *mbrContents; 326 size_t mbrSize; 327#endif 328 WINDOW *w = savescr(); 329 Disk *d = (Disk *)dev->private; 330 int size_unit; 331 332 size_unit = UNIT_BLOCKS; 333 chunking = TRUE; 334 keypad(stdscr, TRUE); 335 336 /* Flush both the dialog and curses library views of the screen 337 since we don't always know who called us */ 338 dialog_clear_norefresh(), clear(); 339 current_chunk = 0; 340 341 /* Set up the chunk array */ 342 record_chunks(d); 343 344 while (chunking) { 345 char *val, geometry[80]; 346 347 /* Now print our overall state */ 348 if (d) 349 print_chunks(d, size_unit); 350 print_command_summary(); 351 if (msg) { 352 attrset(title_attr); mvprintw(23, 0, msg); attrset(A_NORMAL); 353 beep(); 354 msg = NULL; 355 } 356 else { 357 move(23, 0); 358 clrtoeol(); 359 } 360 361 /* Get command character */ 362 key = getch(); 363 switch (toupper(key)) { 364 case '\014': /* ^L (redraw) */ 365 clear(); 366 msg = NULL; 367 break; 368 369 case '\020': /* ^P */ 370 case KEY_UP: 371 case '-': 372 if (current_chunk != 0) 373 --current_chunk; 374 break; 375 376 case '\016': /* ^N */ 377 case KEY_DOWN: 378 case '+': 379 case '\r': 380 case '\n': 381 if (chunk_info[current_chunk + 1]) 382 ++current_chunk; 383 break; 384 385 case KEY_HOME: 386 current_chunk = 0; 387 break; 388 389 case KEY_END: 390 while (chunk_info[current_chunk + 1]) 391 ++current_chunk; 392 break; 393 394 case KEY_F(1): 395 case '?': 396 systemDisplayHelp("slice"); 397 clear(); 398 break; 399 400 case 'A': 401 case 'F': /* Undocumented magic Dangerously Dedicated mode */ 402#if !defined(__i386__) && !defined(__amd64__) 403 rv = 1; 404#else /* The rest is only relevant on x86 */ 405 cp = variable_get(VAR_DEDICATE_DISK); 406 if (cp && !strcasecmp(cp, "always")) 407 rv = 1; 408 else if (toupper(key) == 'A') 409 rv = 0; 410 else { 411 rv = msgYesNo("Do you want to do this with a true partition entry\n" 412 "so as to remain cooperative with any future possible\n" 413 "operating systems on the drive(s)?\n" 414 "(See also the section about ``dangerously dedicated''\n" 415 "disks in the FreeBSD FAQ.)"); 416 if (rv == -1) 417 rv = 0; 418 } 419#endif 420 All_FreeBSD(d, rv); 421 variable_set2(DISK_PARTITIONED, "yes", 0); 422 record_chunks(d); 423 clear(); 424 break; 425 426 case 'C': 427 if (chunk_info[current_chunk]->type != unused) 428 msg = "Slice in use, delete it first or move to an unused one."; 429 else { 430 char *val, tmp[20], name[16], *cp; 431 daddr_t size; 432 int subtype; 433 chunk_e partitiontype; 434#ifdef PC98 435 snprintf(name, sizeof (name), "%s", "FreeBSD"); 436 val = msgGetInput(name, 437 "Please specify the name for new FreeBSD slice."); 438 if (val) 439 strncpy(name, val, sizeof (name)); 440#else 441 name[0] = '\0'; 442#endif 443 snprintf(tmp, 20, "%jd", (intmax_t)chunk_info[current_chunk]->size); 444 val = msgGetInput(tmp, "Please specify the size for new FreeBSD slice in blocks\n" 445 "or append a trailing `M' for megabytes (e.g. 20M)."); 446 if (val && (size = strtoimax(val, &cp, 0)) > 0) { 447 if (*cp && toupper(*cp) == 'M') 448 size *= ONE_MEG; 449 else if (*cp && toupper(*cp) == 'G') 450 size *= ONE_GIG; 451 sprintf(tmp, "%d", SUBTYPE_FREEBSD); 452 val = msgGetInput(tmp, "Enter type of partition to create:\n\n" 453 "Pressing Enter will choose the default, a native FreeBSD\n" 454 "slice (type %u). " 455 OTHER_SLICE_VALUES 456 NON_FREEBSD_NOTE, SUBTYPE_FREEBSD); 457 if (val && (subtype = strtol(val, NULL, 0)) > 0) { 458 if (subtype == SUBTYPE_FREEBSD) 459 partitiontype = freebsd; 460 else if (subtype == SUBTYPE_FAT) 461 partitiontype = fat; 462 else if (subtype == SUBTYPE_EFI) 463 partitiontype = efi; 464 else 465#ifdef PC98 466 partitiontype = pc98; 467#else 468 partitiontype = mbr; 469#endif 470 Create_Chunk(d, chunk_info[current_chunk]->offset, size, partitiontype, subtype, 471 (chunk_info[current_chunk]->flags & CHUNK_ALIGN), name); 472 variable_set2(DISK_PARTITIONED, "yes", 0); 473 record_chunks(d); 474 } 475 } 476 clear(); 477 } 478 break; 479 480 case KEY_DC: 481 case 'D': 482 if (chunk_info[current_chunk]->type == unused) 483 msg = "Slice is already unused!"; 484 else { 485 Delete_Chunk(d, chunk_info[current_chunk]); 486 variable_set2(DISK_PARTITIONED, "yes", 0); 487 record_chunks(d); 488 } 489 break; 490 491 case 'T': 492 if (chunk_info[current_chunk]->type == unused) 493 msg = "Slice is currently unused (use create instead)"; 494 else { 495 char *val, tmp[20]; 496 int subtype; 497 chunk_e partitiontype; 498 499 sprintf(tmp, "%d", chunk_info[current_chunk]->subtype); 500 val = msgGetInput(tmp, "New partition type:\n\n" 501 "Pressing Enter will use the current type. To choose a native\n" 502 "FreeBSD slice enter %u. " 503 OTHER_SLICE_VALUES 504 NON_FREEBSD_NOTE, SUBTYPE_FREEBSD); 505 if (val && (subtype = strtol(val, NULL, 0)) > 0) { 506 if (subtype == SUBTYPE_FREEBSD) 507 partitiontype = freebsd; 508 else if (subtype == SUBTYPE_FAT) 509 partitiontype = fat; 510 else if (subtype == SUBTYPE_EFI) 511 partitiontype = efi; 512 else 513#ifdef PC98 514 partitiontype = pc98; 515#else 516 partitiontype = mbr; 517#endif 518 chunk_info[current_chunk]->type = partitiontype; 519 chunk_info[current_chunk]->subtype = subtype; 520 } 521 } 522 break; 523 524 case 'G': 525 snprintf(geometry, 80, "%lu/%lu/%lu", d->bios_cyl, d->bios_hd, d->bios_sect); 526 val = msgGetInput(geometry, "Please specify the new geometry in cyl/hd/sect format.\n" 527 "Don't forget to use the two slash (/) separator characters!\n" 528 "It's not possible to parse the field without them."); 529 if (val) { 530 long nc, nh, ns; 531 nc = strtol(val, &val, 0); 532 nh = strtol(val + 1, &val, 0); 533 ns = strtol(val + 1, 0, 0); 534 Set_Bios_Geom(d, nc, nh, ns); 535 } 536 clear(); 537 break; 538 539 case 'S': 540 /* Clear active states so we won't have two */ 541 for (i = 0; (chunk_info[i] != NULL) && (i < CHUNK_INFO_ENTRIES); i++) 542 chunk_info[i]->flags &= !CHUNK_ACTIVE; 543 544 /* Set Bootable */ 545 chunk_info[current_chunk]->flags |= CHUNK_ACTIVE; 546 break; 547 548 case 'U': 549 if (!variable_cmp(DISK_LABELLED, "written")) { 550 msgConfirm("You've already written this information out - you\n" 551 "can't undo it."); 552 } 553 else if (!msgNoYes("Are you SURE you want to Undo everything?")) { 554 char cp[BUFSIZ]; 555 556 sstrncpy(cp, d->name, sizeof cp); 557 Free_Disk(dev->private); 558 d = Open_Disk(cp); 559 if (!d) 560 msgConfirm("Can't reopen disk %s! Internal state is probably corrupted", cp); 561 dev->private = d; 562 variable_unset(DISK_PARTITIONED); 563 variable_unset(DISK_LABELLED); 564 if (d) 565 record_chunks(d); 566 } 567 clear(); 568 break; 569 570 case 'W': 571 if (!msgNoYes("WARNING: This should only be used when modifying an EXISTING\n" 572 "installation. If you are installing FreeBSD for the first time\n" 573 "then you should simply type Q when you're finished here and your\n" 574 "changes will be committed in one batch automatically at the end of\n" 575 "these questions. If you're adding a disk, you should NOT write\n" 576 "from this screen, you should do it from the label editor.\n\n" 577 "Are you absolutely sure you want to do this now?")) { 578 variable_set2(DISK_PARTITIONED, "yes", 0); 579 580#ifdef PC98 581 /* 582 * Don't trash the IPL if the first (and therefore only) chunk 583 * is marked for a truly dedicated disk (i.e., the disklabel 584 * starts at sector 0), even in cases where the user has 585 * requested a FreeBSD Boot Manager -- both would be fatal in 586 * this case. 587 */ 588 /* 589 * Don't offer to update the IPL on this disk if the first 590 * "real" chunk looks like a FreeBSD "all disk" partition, 591 * or the disk is entirely FreeBSD. 592 */ 593 if ((d->chunks->part->type != freebsd) || 594 (d->chunks->part->offset > 1)) 595 getBootMgr(d->name, &bootipl, &bootipl_size, 596 &bootmenu, &bootmenu_size); 597 else { 598 bootipl = NULL; 599 bootipl_size = 0; 600 bootmenu = NULL; 601 bootmenu_size = 0; 602 } 603 Set_Boot_Mgr(d, bootipl, bootipl_size, bootmenu, bootmenu_size); 604#else 605 /* 606 * Don't trash the MBR if the first (and therefore only) chunk 607 * is marked for a truly dedicated disk (i.e., the disklabel 608 * starts at sector 0), even in cases where the user has 609 * requested booteasy or a "standard" MBR -- both would be 610 * fatal in this case. 611 */ 612 /* 613 * Don't offer to update the MBR on this disk if the first 614 * "real" chunk looks like a FreeBSD "all disk" partition, 615 * or the disk is entirely FreeBSD. 616 */ 617 if ((d->chunks->part->type != freebsd) || 618 (d->chunks->part->offset > 1)) 619 getBootMgr(d->name, &mbrContents, &mbrSize); 620 else { 621 mbrContents = NULL; 622 mbrSize = 0; 623 } 624 Set_Boot_Mgr(d, mbrContents, mbrSize); 625#endif 626 627 if (DITEM_STATUS(diskPartitionWrite(NULL)) != DITEM_SUCCESS) 628 msgConfirm("Disk partition write returned an error status!"); 629 else 630 msgConfirm("Wrote FDISK partition information out successfully."); 631 } 632 clear(); 633 break; 634 635 case '|': 636 if (!msgNoYes("Are you SURE you want to go into Wizard mode?\n" 637 "No seat belts whatsoever are provided!")) { 638 clear(); 639 refresh(); 640 slice_wizard(d); 641 variable_set2(DISK_PARTITIONED, "yes", 0); 642 record_chunks(d); 643 } 644 else 645 msg = "Wise choice!"; 646 clear(); 647 break; 648 649 case '\033': /* ESC */ 650 case 'Q': 651 chunking = FALSE; 652#ifdef PC98 653 /* 654 * Don't trash the IPL if the first (and therefore only) chunk 655 * is marked for a truly dedicated disk (i.e., the disklabel 656 * starts at sector 0), even in cases where the user has requested 657 * a FreeBSD Boot Manager -- both would be fatal in this case. 658 */ 659 /* 660 * Don't offer to update the IPL on this disk if the first "real" 661 * chunk looks like a FreeBSD "all disk" partition, or the disk is 662 * entirely FreeBSD. 663 */ 664 if ((d->chunks->part->type != freebsd) || 665 (d->chunks->part->offset > 1)) { 666 if (variable_cmp(DISK_PARTITIONED, "written")) { 667 getBootMgr(d->name, &bootipl, &bootipl_size, 668 &bootmenu, &bootmenu_size); 669 if (bootipl != NULL && bootmenu != NULL) 670 Set_Boot_Mgr(d, bootipl, bootipl_size, 671 bootmenu, bootmenu_size); 672 } 673 } 674#else 675 /* 676 * Don't trash the MBR if the first (and therefore only) chunk 677 * is marked for a truly dedicated disk (i.e., the disklabel 678 * starts at sector 0), even in cases where the user has requested 679 * booteasy or a "standard" MBR -- both would be fatal in this case. 680 */ 681 /* 682 * Don't offer to update the MBR on this disk if the first "real" 683 * chunk looks like a FreeBSD "all disk" partition, or the disk is 684 * entirely FreeBSD. 685 */ 686 if ((d->chunks->part->type != freebsd) || 687 (d->chunks->part->offset > 1)) { 688 if (variable_cmp(DISK_PARTITIONED, "written")) { 689 getBootMgr(d->name, &mbrContents, &mbrSize); 690 if (mbrContents != NULL) 691 Set_Boot_Mgr(d, mbrContents, mbrSize); 692 } 693 } 694#endif 695 break; 696 697 case 'Z': 698 size_unit = (size_unit + 1) % UNIT_SIZE; 699 break; 700 701 default: 702 beep(); 703 msg = "Type F1 or ? for help"; 704 break; 705 } 706 } 707 p = CheckRules(d); 708 if (p) { 709 char buf[FILENAME_MAX]; 710 711 use_helpline("Press F1 to read more about disk slices."); 712 use_helpfile(systemHelpFile("partition", buf)); 713 if (!variable_get(VAR_NO_WARN)) 714 dialog_mesgbox("Disk slicing warning:", p, -1, -1); 715 free(p); 716 } 717 restorescr(w); 718} 719#endif /* WITH_SLICES */ 720 721static u_char * 722bootalloc(char *name, size_t *size) 723{ 724 char buf[FILENAME_MAX]; 725 struct stat sb; 726 727 snprintf(buf, sizeof buf, "/boot/%s", name); 728 if (stat(buf, &sb) != -1) { 729 int fd; 730 731 fd = open(buf, O_RDONLY); 732 if (fd != -1) { 733 u_char *cp; 734 735 cp = malloc(sb.st_size); 736 if (read(fd, cp, sb.st_size) != sb.st_size) { 737 free(cp); 738 close(fd); 739 msgDebug("bootalloc: couldn't read %ld bytes from %s\n", (long)sb.st_size, buf); 740 return NULL; 741 } 742 close(fd); 743 if (size != NULL) 744 *size = sb.st_size; 745 return cp; 746 } 747 msgDebug("bootalloc: couldn't open %s\n", buf); 748 } 749 else 750 msgDebug("bootalloc: can't stat %s\n", buf); 751 return NULL; 752} 753 754#ifdef WITH_SLICES 755static int 756partitionHook(dialogMenuItem *selected) 757{ 758 Device **devs = NULL; 759 760 devs = deviceFind(selected->prompt, DEVICE_TYPE_DISK); 761 if (!devs) { 762 msgConfirm("Unable to find disk %s!", selected->prompt); 763 return DITEM_FAILURE; 764 } 765 /* Toggle enabled status? */ 766 if (!devs[0]->enabled) { 767 devs[0]->enabled = TRUE; 768 diskPartition(devs[0]); 769 } 770 else 771 devs[0]->enabled = FALSE; 772 return DITEM_SUCCESS; 773} 774 775static int 776partitionCheck(dialogMenuItem *selected) 777{ 778 Device **devs = NULL; 779 780 devs = deviceFind(selected->prompt, DEVICE_TYPE_DISK); 781 if (!devs || devs[0]->enabled == FALSE) 782 return FALSE; 783 return TRUE; 784} 785 786int 787diskPartitionEditor(dialogMenuItem *self) 788{ 789 DMenu *menu; 790 Device **devs; 791 int i, cnt, devcnt; 792 793 cnt = diskGetSelectCount(&devs); 794 devcnt = deviceCount(devs); 795 if (cnt == -1) { 796 msgConfirm("No disks found! Please verify that your disk controller is being\n" 797 "properly probed at boot time. See the Hardware Guide on the\n" 798 "Documentation menu for clues on diagnosing this type of problem."); 799 return DITEM_FAILURE; 800 } 801 else if (cnt) { 802 /* Some are already selected */ 803 for (i = 0; i < devcnt; i++) { 804 if (devs[i]->enabled) { 805 if (variable_get(VAR_NONINTERACTIVE) && 806 !variable_get(VAR_DISKINTERACTIVE)) 807 diskPartitionNonInteractive(devs[i]); 808 else 809 diskPartition(devs[i]); 810 } 811 } 812 } 813 else { 814 /* No disks are selected, fall-back case now */ 815 if (devcnt == 1) { 816 devs[0]->enabled = TRUE; 817 if (variable_get(VAR_NONINTERACTIVE) && 818 !variable_get(VAR_DISKINTERACTIVE)) 819 diskPartitionNonInteractive(devs[0]); 820 else 821 diskPartition(devs[0]); 822 return DITEM_SUCCESS; 823 } 824 else { 825 menu = deviceCreateMenu(&MenuDiskDevices, DEVICE_TYPE_DISK, partitionHook, partitionCheck); 826 if (!menu) { 827 msgConfirm("No devices suitable for installation found!\n\n" 828 "Please verify that your disk controller (and attached drives)\n" 829 "were detected properly. This can be done by pressing the\n" 830 "[Scroll Lock] key and using the Arrow keys to move back to\n" 831 "the boot messages. Press [Scroll Lock] again to return."); 832 return DITEM_FAILURE; 833 } 834 else { 835 i = dmenuOpenSimple(menu, FALSE) ? DITEM_SUCCESS : DITEM_FAILURE; 836 free(menu); 837 } 838 return i; 839 } 840 } 841 return DITEM_SUCCESS; 842} 843#endif /* WITH_SLICES */ 844 845int 846diskPartitionWrite(dialogMenuItem *self) 847{ 848 Device **devs; 849 int i; 850 851 if (!variable_cmp(DISK_PARTITIONED, "written")) 852 return DITEM_SUCCESS; 853 854 devs = deviceFind(NULL, DEVICE_TYPE_DISK); 855 if (!devs) { 856 msgConfirm("Unable to find any disks to write to??"); 857 return DITEM_FAILURE; 858 } 859 if (isDebug()) 860 msgDebug("diskPartitionWrite: Examining %d devices\n", deviceCount(devs)); 861 for (i = 0; devs[i]; i++) { 862 Disk *d = (Disk *)devs[i]->private; 863 static u_char *boot1; 864#if defined(__i386__) || defined(__amd64__) 865 static u_char *boot2; 866#endif 867 868 if (!devs[i]->enabled) 869 continue; 870 871#if defined(__i386__) || defined(__amd64__) 872 if (!boot1) boot1 = bootalloc("boot1", NULL); 873 if (!boot2) boot2 = bootalloc("boot2", NULL); 874 Set_Boot_Blocks(d, boot1, boot2); 875#elif !defined(__ia64__) 876 if (!boot1) boot1 = bootalloc("boot1", NULL); 877 Set_Boot_Blocks(d, boot1, NULL); 878#endif 879 880 msgNotify("Writing partition information to drive %s", d->name); 881 if (!Fake && Write_Disk(d)) { 882 msgConfirm("ERROR: Unable to write data to disk %s!", d->name); 883 return DITEM_FAILURE; 884 } 885 } 886 /* Now it's not "yes", but "written" */ 887 variable_set2(DISK_PARTITIONED, "written", 0); 888 return DITEM_SUCCESS | DITEM_RESTORE; 889} 890 891#ifdef WITH_SLICES 892/* Partition a disk based wholly on which variables are set */ 893static void 894diskPartitionNonInteractive(Device *dev) 895{ 896 char *cp; 897 int i, all_disk = 0; 898 daddr_t sz; 899#ifdef PC98 900 u_char *bootipl; 901 size_t bootipl_size; 902 u_char *bootmenu; 903 size_t bootmenu_size; 904#else 905 u_char *mbrContents; 906 size_t mbrSize; 907#endif 908 Disk *d = (Disk *)dev->private; 909 910 record_chunks(d); 911 cp = variable_get(VAR_GEOMETRY); 912 if (cp) { 913 msgDebug("Setting geometry from script to: %s\n", cp); 914 d->bios_cyl = strtol(cp, &cp, 0); 915 d->bios_hd = strtol(cp + 1, &cp, 0); 916 d->bios_sect = strtol(cp + 1, 0, 0); 917 } 918 919 cp = variable_get(VAR_PARTITION); 920 if (cp) { 921 if (!strcmp(cp, "free")) { 922 /* Do free disk space case */ 923 for (i = 0; chunk_info[i]; i++) { 924 /* If a chunk is at least 10MB in size, use it. */ 925 if (chunk_info[i]->type == unused && chunk_info[i]->size > (10 * ONE_MEG)) { 926 Create_Chunk(d, chunk_info[i]->offset, chunk_info[i]->size, 927 freebsd, 3, 928 (chunk_info[i]->flags & CHUNK_ALIGN), 929 "FreeBSD"); 930 variable_set2(DISK_PARTITIONED, "yes", 0); 931 break; 932 } 933 } 934 if (!chunk_info[i]) { 935 msgConfirm("Unable to find any free space on this disk!"); 936 return; 937 } 938 } 939 else if (!strcmp(cp, "all")) { 940 /* Do all disk space case */ 941 msgDebug("Warning: Devoting all of disk %s to FreeBSD.\n", d->name); 942 943 All_FreeBSD(d, FALSE); 944 } 945 else if (!strcmp(cp, "exclusive")) { 946 /* Do really-all-the-disk-space case */ 947 msgDebug("Warning: Devoting all of disk %s to FreeBSD.\n", d->name); 948 949 All_FreeBSD(d, all_disk = TRUE); 950 } 951 else if ((sz = strtoimax(cp, &cp, 0))) { 952 /* Look for sz bytes free */ 953 if (*cp && toupper(*cp) == 'M') 954 sz *= ONE_MEG; 955 else if (*cp && toupper(*cp) == 'G') 956 sz *= ONE_GIG; 957 for (i = 0; chunk_info[i]; i++) { 958 /* If a chunk is at least sz MB, use it. */ 959 if (chunk_info[i]->type == unused && chunk_info[i]->size >= sz) { 960 Create_Chunk(d, chunk_info[i]->offset, sz, freebsd, 3, 961 (chunk_info[i]->flags & CHUNK_ALIGN), 962 "FreeBSD"); 963 variable_set2(DISK_PARTITIONED, "yes", 0); 964 break; 965 } 966 } 967 if (!chunk_info[i]) { 968 msgConfirm("Unable to find %jd free blocks on this disk!", 969 (intmax_t)sz); 970 return; 971 } 972 } 973 else if (!strcmp(cp, "existing")) { 974 /* Do existing FreeBSD case */ 975 for (i = 0; chunk_info[i]; i++) { 976 if (chunk_info[i]->type == freebsd) 977 break; 978 } 979 if (!chunk_info[i]) { 980 msgConfirm("Unable to find any existing FreeBSD partitions on this disk!"); 981 return; 982 } 983 } 984 else { 985 msgConfirm("`%s' is an invalid value for %s - is config file valid?", cp, VAR_PARTITION); 986 return; 987 } 988 if (!all_disk) { 989#ifdef PC98 990 getBootMgr(d->name, &bootipl, &bootipl_size, 991 &bootmenu, &bootmenu_size); 992 Set_Boot_Mgr(d, bootipl, bootipl_size, bootmenu, bootmenu_size); 993#else 994 getBootMgr(d->name, &mbrContents, &mbrSize); 995 Set_Boot_Mgr(d, mbrContents, mbrSize); 996#endif 997 } 998 variable_set2(DISK_PARTITIONED, "yes", 0); 999 } 1000} 1001#endif /* WITH_SLICES */ 1002