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