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