disks.c revision 14670
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 * $Id: disks.c,v 1.35 1996/01/23 21:05:22 joerg Exp $ 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 * 3. All advertising materials mentioning features or use of this software 23 * must display the following acknowledgement: 24 * This product includes software developed by Jordan Hubbard 25 * for the FreeBSD Project. 26 * 4. The name of Jordan Hubbard or the FreeBSD project may not be used to 27 * endorse or promote products derived from this software without specific 28 * prior written permission. 29 * 30 * THIS SOFTWARE IS PROVIDED BY JORDAN HUBBARD ``AS IS'' AND 31 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 32 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 33 * ARE DISCLAIMED. IN NO EVENT SHALL JORDAN HUBBARD OR HIS PETS BE LIABLE 34 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 35 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 36 * OR SERVICES; LOSS OF USE, DATA, LIFE OR PROFITS; OR BUSINESS INTERRUPTION) 37 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 38 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 39 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 40 * SUCH DAMAGE. 41 * 42 */ 43 44#include "sysinstall.h" 45#include <ctype.h> 46#include <sys/disklabel.h> 47 48/* Where we start displaying chunk information on the screen */ 49#define CHUNK_START_ROW 5 50 51/* Where we keep track of MBR chunks */ 52static struct chunk *chunk_info[16]; 53static int current_chunk; 54 55static void 56record_chunks(Disk *d) 57{ 58 struct chunk *c1; 59 int i = 0; 60 int last_free = 0; 61 if (!d->chunks) 62 msgFatal("No chunk list found for %s!", d->name); 63 current_chunk = 0; 64 for (c1 = d->chunks->part; c1; c1 = c1->next) { 65 if (c1->type == unused && c1->size > last_free) { 66 last_free = c1->size; 67 current_chunk = i; 68 } 69 chunk_info[i++] = c1; 70 } 71 chunk_info[i] = NULL; 72} 73 74static void 75print_chunks(Disk *d) 76{ 77 int row; 78 int i; 79 80 if ((!d->bios_cyl || d->bios_cyl > 65536) || (!d->bios_hd || d->bios_hd > 256) || (!d->bios_sect || d->bios_sect >= 64)) { 81 int sz; 82 83 dialog_clear(); 84 msgConfirm("WARNING: The current geometry for %s is incorrect. Using\n" 85 "a default geometry of 64 heads and 32 sectors. If this geometry\n" 86 "is incorrect or you are unsure as to whether or not it's correct,\n" 87 "please consult the Hardware Guide in the Documentation submenu\n" 88 "or use the (G)eometry command to change it now.", d->name); 89 d->bios_hd = 64; 90 d->bios_sect = 32; 91 sz = 0; 92 for (i = 0; chunk_info[i]; i++) 93 sz += chunk_info[i]->size; 94 if (sz) 95 d->bios_cyl = sz / ONE_MEG; 96 else 97 msgConfirm("Couldn't set geometry! You'll have to do it by hand."); 98 } 99 attrset(A_NORMAL); 100 mvaddstr(0, 0, "Disk name:\t"); 101 clrtobot(); 102 attrset(A_REVERSE); addstr(d->name); attrset(A_NORMAL); 103 attrset(A_REVERSE); mvaddstr(0, 55, "FDISK Partition Editor"); attrset(A_NORMAL); 104 mvprintw(1, 0, 105 "DISK Geometry:\t%lu cyls/%lu heads/%lu sectors", 106 d->bios_cyl, d->bios_hd, d->bios_sect); 107 mvprintw(3, 1, "%10s %10s %10s %8s %8s %8s %8s %8s", 108 "Offset", "Size", "End", "Name", "PType", "Desc", 109 "Subtype", "Flags"); 110 for (i = 0, row = CHUNK_START_ROW; chunk_info[i]; i++, row++) { 111 if (i == current_chunk) 112 attrset(A_REVERSE); 113 mvprintw(row, 2, "%10ld %10lu %10lu %8s %8d %8s %8d\t%-6s", 114 chunk_info[i]->offset, chunk_info[i]->size, 115 chunk_info[i]->end, chunk_info[i]->name, 116 chunk_info[i]->type, chunk_n[chunk_info[i]->type], 117 chunk_info[i]->subtype, ShowChunkFlags(chunk_info[i])); 118 if (i == current_chunk) 119 attrset(A_NORMAL); 120 } 121} 122 123static void 124print_command_summary() 125{ 126 mvprintw(14, 0, "The following commands are supported (in upper or lower case):"); 127 mvprintw(16, 0, "A = Use Entire Disk B = Bad Block Scan C = Create Partition"); 128 mvprintw(17, 0, "D = Delete Partition G = Set Drive Geometry S = Set Bootable"); 129 mvprintw(18, 0, "U = Undo All Changes Q = Finish"); 130 if (!RunningAsInit) 131 mvprintw(18, 48, "W = Write Changes"); 132 mvprintw(20, 0, "The currently selected partition is displayed in "); 133 attrset(A_REVERSE); addstr("reverse"); attrset(A_NORMAL); addstr(" video."); 134 mvprintw(21, 0, "Use F1 or ? to get more help, arrow keys to move."); 135 move(0, 0); 136} 137 138/* Partition a disk based wholly on which variables are set */ 139static void 140scriptPartition(Device *dev, Disk *d) 141{ 142 char *cp; 143 int i, sz; 144 145 record_chunks(d); 146 cp = variable_get(VAR_GEOMETRY); 147 if (cp) { 148 msgDebug("Setting geometry from script to: %s\n", cp); 149 d->bios_cyl = strtol(cp, &cp, 0); 150 d->bios_hd = strtol(cp + 1, &cp, 0); 151 d->bios_sect = strtol(cp + 1, 0, 0); 152 } 153 154 cp = variable_get(VAR_DISKSPACE); 155 if (cp) { 156 if (!strcmp(cp, "free")) { 157 /* Do free disk space case */ 158 for (i = 0; chunk_info[i]; i++) { 159 /* If a chunk is at least 10MB in size, use it. */ 160 if (chunk_info[i]->type == unused && chunk_info[i]->size > (10 * ONE_MEG)) { 161 Create_Chunk(d, chunk_info[i]->offset, chunk_info[i]->size, freebsd, 3, 162 (chunk_info[i]->flags & CHUNK_ALIGN)); 163 variable_set2(DISK_PARTITIONED, "yes"); 164 break; 165 } 166 } 167 if (!chunk_info[i]) { 168 dialog_clear(); 169 msgConfirm("Unable to find any free space on this disk!"); 170 return; 171 } 172 } 173 else if (!strcmp(cp, "all")) { 174 /* Do all disk space case */ 175 msgDebug("Warning: Devoting all of disk %s to FreeBSD.\n", d->name); 176 177 All_FreeBSD(d, FALSE); 178 } 179 else if (!strcmp(cp, "exclusive")) { 180 /* Do really-all-the-disk-space case */ 181 msgDebug("Warning: Devoting all of disk %s to FreeBSD.\n", d->name); 182 183 All_FreeBSD(d, TRUE); 184 } 185 else if ((sz = strtol(cp, &cp, 0))) { 186 /* Look for sz bytes free */ 187 if (*cp && toupper(*cp) == 'M') 188 sz *= ONE_MEG; 189 for (i = 0; chunk_info[i]; i++) { 190 /* If a chunk is at least sz MB, use it. */ 191 if (chunk_info[i]->type == unused && chunk_info[i]->size >= sz) { 192 Create_Chunk(d, chunk_info[i]->offset, sz, freebsd, 3, (chunk_info[i]->flags & CHUNK_ALIGN)); 193 variable_set2(DISK_PARTITIONED, "yes"); 194 break; 195 } 196 } 197 if (!chunk_info[i]) { 198 dialog_clear(); 199 msgConfirm("Unable to find %d free blocks on this disk!", sz); 200 return; 201 } 202 } 203 else if (!strcmp(cp, "existing")) { 204 /* Do existing FreeBSD case */ 205 for (i = 0; chunk_info[i]; i++) { 206 if (chunk_info[i]->type == freebsd) 207 break; 208 } 209 if (!chunk_info[i]) { 210 dialog_clear(); 211 msgConfirm("Unable to find any existing FreeBSD partitions on this disk!"); 212 return; 213 } 214 } 215 else { 216 dialog_clear(); 217 msgConfirm("`%s' is an invalid value for %s - is config file valid?", cp, VAR_DISKSPACE); 218 return; 219 } 220 variable_set2(DISK_PARTITIONED, "yes"); 221 } 222} 223 224static u_char * 225getBootMgr(char *dname) 226{ 227 extern u_char mbr[], bteasy17[]; 228 char str[80]; 229 char *cp; 230 int i = 0; 231 232 cp = variable_get(VAR_BOOTMGR); 233 if (!cp) { 234 /* Figure out what kind of MBR the user wants */ 235 sprintf(str, "Install Boot Manager for drive %s?", dname); 236 MenuMBRType.title = str; 237 dialog_clear(); 238 i = dmenuOpenSimple(&MenuMBRType); 239 } 240 else { 241 if (!strncmp(cp, "boot", 4)) 242 BootMgr = 0; 243 else if (!strcmp(cp, "standard")) 244 BootMgr = 1; 245 else 246 BootMgr = 2; 247 } 248 if (cp || i) { 249 switch (BootMgr) { 250 case 0: 251 return bteasy17; 252 253 case 1: 254 return mbr; 255 256 case 2: 257 default: 258 break; 259 } 260 } 261 return NULL; 262} 263 264void 265diskPartition(Device *dev, Disk *d) 266{ 267 char *p; 268 int key = 0; 269 Boolean chunking; 270 char *msg = NULL; 271 u_char *mbrContents; 272 273 chunking = TRUE; 274 keypad(stdscr, TRUE); 275 276 clear(); 277 record_chunks(d); 278 while (chunking) { 279 print_chunks(d); 280 print_command_summary(); 281 if (msg) { 282 standout(); mvprintw(23, 0, msg); standend(); 283 beep(); 284 msg = NULL; 285 } 286 287 key = toupper(getch()); 288 switch (key) { 289 290 case '\014': /* ^L */ 291 clear(); 292 print_command_summary(); 293 continue; 294 295 case KEY_UP: 296 case '-': 297 if (current_chunk != 0) 298 --current_chunk; 299 break; 300 301 case KEY_DOWN: 302 case '+': 303 case '\r': 304 case '\n': 305 if (chunk_info[current_chunk + 1]) 306 ++current_chunk; 307 break; 308 309 case KEY_HOME: 310 current_chunk = 0; 311 break; 312 313 case KEY_END: 314 while (chunk_info[current_chunk + 1]) 315 ++current_chunk; 316 break; 317 318 case KEY_F(1): 319 case '?': 320 systemDisplayHelp("slice"); 321 break; 322 323 case 'A': { 324 int rv; 325 326 rv = msgYesNo("Do you want to do this with a true partition entry\n" 327 "so as to remain cooperative with any future possible\n" 328 "operating systems on the drive(s)?"); 329 if (rv) { 330 rv = !msgYesNo("This is dangerous in that it will make the drive totally\n" 331 "uncooperative with other potential operating systems on the\n" 332 "same disk. It will lead instead to a totally dedicated disk,\n" 333 "starting at the very first sector, bypassing all BIOS geometry\n" 334 "considerations. This precludes the existance of any boot\n" 335 "manager or other stuff in sector 0, since the BSD bootstrap\n" 336 "will live there.\n" 337 "You will run into serious trouble with ST-506 and ESDI drives\n" 338 "and possibly some IDE drives (e.g. drives running under the\n" 339 "control of sort of disk manager). SCSI drives are considerably\n" 340 "less at risk.\n\n" 341 "Do you insist on dedicating the entire disk this way?"); 342 } 343 All_FreeBSD(d, rv); 344 if (rv) 345 d->bios_hd = d->bios_sect = d->bios_cyl = 1; 346 variable_set2(DISK_PARTITIONED, "yes"); 347 record_chunks(d); 348 } 349 break; 350 351 case 'B': 352 if (chunk_info[current_chunk]->type != freebsd) 353 msg = "Can only scan for bad blocks in FreeBSD partition."; 354 else if (strncmp(d->name, "sd", 2) || 355 !msgYesNo("This typically makes sense only for ESDI, IDE or MFM drives.\n" 356 "Are you sure you want to do this on a SCSI disk?")) { 357 if (chunk_info[current_chunk]->flags & CHUNK_BAD144) 358 chunk_info[current_chunk]->flags &= ~CHUNK_BAD144; 359 else 360 chunk_info[current_chunk]->flags |= CHUNK_BAD144; 361 } 362 break; 363 364 case 'C': 365 if (chunk_info[current_chunk]->type != unused) 366 msg = "Partition in use, delete it first or move to an unused one."; 367 else { 368 char *val, tmp[20], *cp; 369 int size; 370 371 snprintf(tmp, 20, "%d", chunk_info[current_chunk]->size); 372 val = msgGetInput(tmp, "Please specify the size for new FreeBSD partition in blocks, or append\n" 373 "a trailing `M' for megabytes (e.g. 20M)."); 374 if (val && (size = strtol(val, &cp, 0)) > 0) { 375 if (*cp && toupper(*cp) == 'M') 376 size *= ONE_MEG; 377 Create_Chunk(d, chunk_info[current_chunk]->offset, size, freebsd, 3, 378 (chunk_info[current_chunk]->flags & CHUNK_ALIGN)); 379 variable_set2(DISK_PARTITIONED, "yes"); 380 record_chunks(d); 381 } 382 } 383 break; 384 385 case 'D': 386 if (chunk_info[current_chunk]->type == unused) 387 msg = "Partition is already unused!"; 388 else { 389 Delete_Chunk(d, chunk_info[current_chunk]); 390 variable_set2(DISK_PARTITIONED, "yes"); 391 record_chunks(d); 392 } 393 break; 394 395 case 'G': { 396 char *val, geometry[80]; 397 398 snprintf(geometry, 80, "%lu/%lu/%lu", d->bios_cyl, d->bios_hd, d->bios_sect); 399 val = msgGetInput(geometry, "Please specify the new geometry in cyl/hd/sect format.\n" 400 "Don't forget to use the two slash (/) separator characters!\n" 401 "It's not possible to parse the field without them."); 402 if (val) { 403 d->bios_cyl = strtol(val, &val, 0); 404 d->bios_hd = strtol(val + 1, &val, 0); 405 d->bios_sect = strtol(val + 1, 0, 0); 406 } 407 } 408 break; 409 410 case 'S': 411 /* Set Bootable */ 412 chunk_info[current_chunk]->flags |= CHUNK_ACTIVE; 413 break; 414 415 case 'U': 416 clear(); 417 if (msgYesNo("Are you SURE you want to Undo everything?")) 418 break; 419 d = Open_Disk(d->name); 420 if (!d) { 421 dialog_clear(); 422 msgConfirm("Can't reopen disk %s! Internal state is probably corrupted", d->name); 423 return; 424 } 425 Free_Disk(dev->private); 426 dev->private = d; 427 variable_unset(DISK_PARTITIONED); 428 record_chunks(d); 429 break; 430 431 case 'W': 432 if (!msgYesNo("Are you SURE you want to write this now? You do also\n" 433 "have the option of not modifying the disk until *all*\n" 434 "configuration information has been entered, at which\n" 435 "point you can do it all at once. If you're unsure, then\n" 436 "PLEASE CHOOSE NO at this dialog! This option is DANGEROUS\n" 437 "if you do not know EXACTLY what you are doing!")) { 438 variable_set2(DISK_PARTITIONED, "yes"); 439 clear(); 440 441 /* Don't trash the MBR if the first (and therefore only) chunk is marked for a truly dedicated 442 * disk (i.e., the disklabel starts at sector 0), even in cases where the user has requested 443 * booteasy or a "standard" MBR -- both would be fatal in this case. 444 */ 445 if ((d->chunks->part->flags & CHUNK_FORCE_ALL) != CHUNK_FORCE_ALL 446 && (mbrContents = getBootMgr(d->name)) != NULL) 447 Set_Boot_Mgr(d, mbrContents); 448 449 if (diskPartitionWrite(NULL) != RET_SUCCESS) { 450 dialog_clear(); 451 msgConfirm("Disk partition write returned an error status!"); 452 } 453 else { 454 msgConfirm("Wrote FDISK partition information out successfully."); 455 } 456 } 457 break; 458 459 case '|': 460 if (!msgYesNo("Are you SURE you want to go into Wizard mode?\n" 461 "No seat belts whatsoever are provided!")) { 462 dialog_clear(); 463 end_dialog(); 464 DialogActive = FALSE; 465 slice_wizard(d); 466 variable_set2(DISK_PARTITIONED, "yes"); 467 dialog_clear(); 468 DialogActive = TRUE; 469 record_chunks(d); 470 } 471 else 472 msg = "Wise choice!"; 473 break; 474 475 case 'Q': 476 chunking = FALSE; 477 clear(); 478 /* Don't trash the MBR if the first (and therefore only) chunk is marked for a truly dedicated 479 * disk (i.e., the disklabel starts at sector 0), even in cases where the user has requested 480 * booteasy or a "standard" MBR -- both would be fatal in this case. 481 */ 482 if ((d->chunks->part->flags & CHUNK_FORCE_ALL) != CHUNK_FORCE_ALL 483 && (mbrContents = getBootMgr(d->name)) != NULL) 484 Set_Boot_Mgr(d, mbrContents); 485 break; 486 487 default: 488 beep(); 489 msg = "Type F1 or ? for help"; 490 break; 491 } 492 } 493 p = CheckRules(d); 494 if (p) { 495 dialog_clear(); 496 msgConfirm(p); 497 free(p); 498 } 499 dialog_clear(); 500} 501 502static int 503partitionHook(char *str) 504{ 505 Device **devs = NULL; 506 507 /* Clip garbage off the ends */ 508 string_prune(str); 509 str = string_skipwhite(str); 510 /* Try and open all the disks */ 511 while (str) { 512 char *cp; 513 514 cp = index(str, '\n'); 515 if (cp) 516 *cp++ = 0; 517 if (!*str) { 518 beep(); 519 return 0; 520 } 521 devs = deviceFind(str, DEVICE_TYPE_DISK); 522 if (!devs) { 523 dialog_clear(); 524 msgConfirm("Unable to find disk %s!", str); 525 return 0; 526 } 527 devs[0]->enabled = TRUE; 528 diskPartition(devs[0], (Disk *)devs[0]->private); 529 str = cp; 530 } 531 return devs ? 1 : 0; 532} 533 534int 535diskPartitionEditor(char *str) 536{ 537 DMenu *menu; 538 Device **devs; 539 int i, cnt; 540 char *cp; 541 542 cp = variable_get(VAR_DISK); 543 devs = deviceFind(cp, DEVICE_TYPE_DISK); 544 cnt = deviceCount(devs); 545 if (!cnt) { 546 dialog_clear(); 547 msgConfirm("No disks found! Please verify that your disk controller is being\n" 548 "properly probed at boot time. See the Hardware Guide on the\n" 549 "Documentation menu for clues on diagnosing this type of problem."); 550 i = RET_FAIL; 551 } 552 else if (cnt == 1) { 553 devs[0]->enabled = TRUE; 554 if (str && !strcmp(str, "script")) 555 scriptPartition(devs[0], (Disk *)devs[0]->private); 556 else 557 diskPartition(devs[0], (Disk *)devs[0]->private); 558 i = RET_SUCCESS; 559 variable_set2(DISK_SELECTED, "yes"); 560 } 561 else { 562 menu = deviceCreateMenu(&MenuDiskDevices, DEVICE_TYPE_DISK, partitionHook); 563 if (!menu) { 564 dialog_clear(); 565 msgConfirm("No devices suitable for installation found!\n\n" 566 "Please verify that your disk controller (and attached drives)\n" 567 "were detected properly. This can be done by pressing the\n" 568 "[Scroll Lock] key and using the Arrow keys to move back to\n" 569 "the boot messages. Press [Scroll Lock] again to return."); 570 i = RET_FAIL; 571 } 572 else { 573 if (!dmenuOpenSimple(menu)) 574 i = RET_FAIL; 575 else { 576 i = RET_SUCCESS; 577 variable_set2(DISK_SELECTED, "yes"); 578 } 579 free(menu); 580 } 581 } 582 return i; 583} 584 585int 586diskPartitionWrite(char *str) 587{ 588 extern u_char boot1[], boot2[]; 589 Device **devs; 590 char *cp; 591 int i; 592 593 if ((cp = variable_get(DISK_PARTITIONED)) && strcmp(cp, "yes")) 594 return RET_SUCCESS; 595 else if (!cp) { 596 dialog_clear(); 597 msgConfirm("You must partition the disk(s) before this option can be used."); 598 return RET_FAIL; 599 } 600 601 devs = deviceFind(NULL, DEVICE_TYPE_DISK); 602 if (!devs) { 603 dialog_clear(); 604 msgConfirm("Unable to find any disks to write to??"); 605 return RET_FAIL; 606 } 607 608 for (i = 0; devs[i]; i++) { 609 Chunk *c1; 610 Disk *d = (Disk *)devs[i]->private; 611 612 if (!devs[i]->enabled) 613 continue; 614 615 Set_Boot_Blocks(d, boot1, boot2); 616 msgNotify("Writing partition information to drive %s", d->name); 617 if (Write_Disk(d)) { 618 dialog_clear(); 619 msgConfirm("ERROR: Unable to write data to disk %s!", d->name); 620 return RET_FAIL; 621 } 622 /* Now scan for bad blocks, if necessary */ 623 for (c1 = d->chunks->part; c1; c1 = c1->next) { 624 if (c1->flags & CHUNK_BAD144) { 625 int ret; 626 627 msgNotify("Running bad block scan on partition %s", c1->name); 628 ret = vsystem("bad144 -v /dev/r%s 1234", c1->name); 629 if (ret) { 630 dialog_clear(); 631 msgConfirm("Bad144 init on %s returned status of %d!", c1->name, ret); 632 } 633 ret = vsystem("bad144 -v -s /dev/r%s", c1->name); 634 if (ret) { 635 dialog_clear(); 636 msgConfirm("Bad144 scan on %s returned status of %d!", c1->name, ret); 637 } 638 } 639 } 640 } 641 /* Now it's not "yes", but "written" */ 642 variable_set2(DISK_PARTITIONED, "written"); 643 return RET_SUCCESS; 644} 645