disks.c revision 13592
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.34 1995/12/11 16:32:31 jkh 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 W = Write Changes"); 130 mvprintw(20, 0, "The currently selected partition is displayed in "); 131 attrset(A_REVERSE); addstr("reverse"); attrset(A_NORMAL); addstr(" video."); 132 mvprintw(21, 0, "Use F1 or ? to get more help, arrow keys to move."); 133 move(0, 0); 134} 135 136/* Partition a disk based wholly on which variables are set */ 137static void 138scriptPartition(Device *dev, Disk *d) 139{ 140 char *cp; 141 int i, sz; 142 143 record_chunks(d); 144 cp = variable_get(VAR_GEOMETRY); 145 if (cp) { 146 msgDebug("Setting geometry from script to: %s\n", cp); 147 d->bios_cyl = strtol(cp, &cp, 0); 148 d->bios_hd = strtol(cp + 1, &cp, 0); 149 d->bios_sect = strtol(cp + 1, 0, 0); 150 } 151 152 cp = variable_get(VAR_DISKSPACE); 153 if (cp) { 154 if (!strcmp(cp, "free")) { 155 /* Do free disk space case */ 156 for (i = 0; chunk_info[i]; i++) { 157 /* If a chunk is at least 10MB in size, use it. */ 158 if (chunk_info[i]->type == unused && chunk_info[i]->size > (10 * ONE_MEG)) { 159 Create_Chunk(d, chunk_info[i]->offset, chunk_info[i]->size, freebsd, 3, 160 (chunk_info[i]->flags & CHUNK_ALIGN)); 161 variable_set2(DISK_PARTITIONED, "yes"); 162 break; 163 } 164 } 165 if (!chunk_info[i]) { 166 dialog_clear(); 167 msgConfirm("Unable to find any free space on this disk!"); 168 return; 169 } 170 } 171 else if (!strcmp(cp, "all")) { 172 /* Do all disk space case */ 173 msgDebug("Warning: Devoting all of disk %s to FreeBSD.\n", d->name); 174 175 All_FreeBSD(d, FALSE); 176 } 177 else if (!strcmp(cp, "exclusive")) { 178 /* Do really-all-the-disk-space case */ 179 msgDebug("Warning: Devoting all of disk %s to FreeBSD.\n", d->name); 180 181 All_FreeBSD(d, TRUE); 182 } 183 else if ((sz = strtol(cp, &cp, 0))) { 184 /* Look for sz bytes free */ 185 if (*cp && toupper(*cp) == 'M') 186 sz *= ONE_MEG; 187 for (i = 0; chunk_info[i]; i++) { 188 /* If a chunk is at least sz MB, use it. */ 189 if (chunk_info[i]->type == unused && chunk_info[i]->size >= sz) { 190 Create_Chunk(d, chunk_info[i]->offset, sz, freebsd, 3, (chunk_info[i]->flags & CHUNK_ALIGN)); 191 variable_set2(DISK_PARTITIONED, "yes"); 192 break; 193 } 194 } 195 if (!chunk_info[i]) { 196 dialog_clear(); 197 msgConfirm("Unable to find %d free blocks on this disk!", sz); 198 return; 199 } 200 } 201 else if (!strcmp(cp, "existing")) { 202 /* Do existing FreeBSD case */ 203 for (i = 0; chunk_info[i]; i++) { 204 if (chunk_info[i]->type == freebsd) 205 break; 206 } 207 if (!chunk_info[i]) { 208 dialog_clear(); 209 msgConfirm("Unable to find any existing FreeBSD partitions on this disk!"); 210 return; 211 } 212 } 213 else { 214 dialog_clear(); 215 msgConfirm("`%s' is an invalid value for %s - is config file valid?", cp, VAR_DISKSPACE); 216 return; 217 } 218 variable_set2(DISK_PARTITIONED, "yes"); 219 } 220} 221 222static u_char * 223getBootMgr(char *dname) 224{ 225 extern u_char mbr[], bteasy17[]; 226 char str[80]; 227 char *cp; 228 int i = 0; 229 230 cp = variable_get(VAR_BOOTMGR); 231 if (!cp) { 232 /* Figure out what kind of MBR the user wants */ 233 sprintf(str, "Install Boot Manager for drive %s?", dname); 234 MenuMBRType.title = str; 235 dialog_clear(); 236 i = dmenuOpenSimple(&MenuMBRType); 237 } 238 else { 239 if (!strncmp(cp, "boot", 4)) 240 BootMgr = 0; 241 else if (!strcmp(cp, "standard")) 242 BootMgr = 1; 243 else 244 BootMgr = 2; 245 } 246 if (cp || i) { 247 switch (BootMgr) { 248 case 0: 249 return bteasy17; 250 251 case 1: 252 return mbr; 253 254 case 2: 255 default: 256 break; 257 } 258 } 259 return NULL; 260} 261 262void 263diskPartition(Device *dev, Disk *d) 264{ 265 char *p; 266 int key = 0; 267 Boolean chunking; 268 char *msg = NULL; 269 u_char *mbrContents; 270 271 chunking = TRUE; 272 keypad(stdscr, TRUE); 273 274 clear(); 275 record_chunks(d); 276 while (chunking) { 277 print_chunks(d); 278 print_command_summary(); 279 if (msg) { 280 standout(); mvprintw(23, 0, msg); standend(); 281 beep(); 282 msg = NULL; 283 } 284 285 key = toupper(getch()); 286 switch (key) { 287 288 case '\014': /* ^L */ 289 clear(); 290 print_command_summary(); 291 continue; 292 293 case KEY_UP: 294 case '-': 295 if (current_chunk != 0) 296 --current_chunk; 297 break; 298 299 case KEY_DOWN: 300 case '+': 301 case '\r': 302 case '\n': 303 if (chunk_info[current_chunk + 1]) 304 ++current_chunk; 305 break; 306 307 case KEY_HOME: 308 current_chunk = 0; 309 break; 310 311 case KEY_END: 312 while (chunk_info[current_chunk + 1]) 313 ++current_chunk; 314 break; 315 316 case KEY_F(1): 317 case '?': 318 systemDisplayHelp("slice"); 319 break; 320 321 case 'A': { 322 int rv; 323 324 rv = msgYesNo("Do you want to do this with a true partition entry\n" 325 "so as to remain cooperative with any future possible\n" 326 "operating systems on the drive(s)?"); 327 if (rv) { 328 rv = !msgYesNo("This is dangerous in that it will make the drive totally\n" 329 "uncooperative with other potential operating systems on the\n" 330 "same disk. It will lead instead to a totally dedicated disk,\n" 331 "starting at the very first sector, bypassing all BIOS geometry\n" 332 "considerations. This precludes the existance of any boot\n" 333 "manager or other stuff in sector 0, since the BSD bootstrap\n" 334 "will live there.\n" 335 "You will run into serious trouble with ST-506 and ESDI drives\n" 336 "and possibly some IDE drives (e.g. drives running under the\n" 337 "control of sort of disk manager). SCSI drives are considerably\n" 338 "less at risk.\n\n" 339 "Do you insist on dedicating the entire disk this way?"); 340 } 341 All_FreeBSD(d, rv); 342 if (rv) 343 d->bios_hd = d->bios_sect = d->bios_cyl = 1; 344 variable_set2(DISK_PARTITIONED, "yes"); 345 record_chunks(d); 346 } 347 break; 348 349 case 'B': 350 if (chunk_info[current_chunk]->type != freebsd) 351 msg = "Can only scan for bad blocks in FreeBSD partition."; 352 else if (strncmp(d->name, "sd", 2) || 353 !msgYesNo("This typically makes sense only for ESDI, IDE or MFM drives.\n" 354 "Are you sure you want to do this on a SCSI disk?")) { 355 if (chunk_info[current_chunk]->flags & CHUNK_BAD144) 356 chunk_info[current_chunk]->flags &= ~CHUNK_BAD144; 357 else 358 chunk_info[current_chunk]->flags |= CHUNK_BAD144; 359 } 360 break; 361 362 case 'C': 363 if (chunk_info[current_chunk]->type != unused) 364 msg = "Partition in use, delete it first or move to an unused one."; 365 else { 366 char *val, tmp[20], *cp; 367 int size; 368 369 snprintf(tmp, 20, "%d", chunk_info[current_chunk]->size); 370 val = msgGetInput(tmp, "Please specify the size for new FreeBSD partition in blocks, or append\n" 371 "a trailing `M' for megabytes (e.g. 20M)."); 372 if (val && (size = strtol(val, &cp, 0)) > 0) { 373 if (*cp && toupper(*cp) == 'M') 374 size *= ONE_MEG; 375 Create_Chunk(d, chunk_info[current_chunk]->offset, size, freebsd, 3, 376 (chunk_info[current_chunk]->flags & CHUNK_ALIGN)); 377 variable_set2(DISK_PARTITIONED, "yes"); 378 record_chunks(d); 379 } 380 } 381 break; 382 383 case 'D': 384 if (chunk_info[current_chunk]->type == unused) 385 msg = "Partition is already unused!"; 386 else { 387 Delete_Chunk(d, chunk_info[current_chunk]); 388 variable_set2(DISK_PARTITIONED, "yes"); 389 record_chunks(d); 390 } 391 break; 392 393 case 'G': { 394 char *val, geometry[80]; 395 396 snprintf(geometry, 80, "%lu/%lu/%lu", d->bios_cyl, d->bios_hd, d->bios_sect); 397 val = msgGetInput(geometry, "Please specify the new geometry in cyl/hd/sect format.\n" 398 "Don't forget to use the two slash (/) separator characters!\n" 399 "It's not possible to parse the field without them."); 400 if (val) { 401 d->bios_cyl = strtol(val, &val, 0); 402 d->bios_hd = strtol(val + 1, &val, 0); 403 d->bios_sect = strtol(val + 1, 0, 0); 404 } 405 } 406 break; 407 408 case 'S': 409 /* Set Bootable */ 410 chunk_info[current_chunk]->flags |= CHUNK_ACTIVE; 411 break; 412 413 case 'U': 414 clear(); 415 if (msgYesNo("Are you SURE you want to Undo everything?")) 416 break; 417 d = Open_Disk(d->name); 418 if (!d) { 419 dialog_clear(); 420 msgConfirm("Can't reopen disk %s! Internal state is probably corrupted", d->name); 421 return; 422 } 423 Free_Disk(dev->private); 424 dev->private = d; 425 variable_unset(DISK_PARTITIONED); 426 record_chunks(d); 427 break; 428 429 case 'W': 430 if (!msgYesNo("Are you SURE you want to write this now? You do also\n" 431 "have the option of not modifying the disk until *all*\n" 432 "configuration information has been entered, at which\n" 433 "point you can do it all at once. If you're unsure, then\n" 434 "PLEASE CHOOSE NO at this dialog! This option is DANGEROUS\n" 435 "if you do not know EXACTLY what you are doing!")) { 436 variable_set2(DISK_PARTITIONED, "yes"); 437 clear(); 438 439 /* Don't trash the MBR if the first (and therefore only) chunk is marked for a truly dedicated 440 * disk (i.e., the disklabel starts at sector 0), even in cases where the user has requested 441 * booteasy or a "standard" MBR -- both would be fatal in this case. 442 */ 443 if ((d->chunks->part->flags & CHUNK_FORCE_ALL) != CHUNK_FORCE_ALL 444 && (mbrContents = getBootMgr(d->name)) != NULL) 445 Set_Boot_Mgr(d, mbrContents); 446 447 if (diskPartitionWrite(NULL) != RET_SUCCESS) { 448 dialog_clear(); 449 msgConfirm("Disk partition write returned an error status!"); 450 } 451 else { 452 msgConfirm("Wrote FDISK partition information out successfully."); 453 } 454 } 455 break; 456 457 case '|': 458 if (!msgYesNo("Are you SURE you want to go into Wizard mode?\n" 459 "No seat belts whatsoever are provided!")) { 460 dialog_clear(); 461 end_dialog(); 462 DialogActive = FALSE; 463 slice_wizard(d); 464 variable_set2(DISK_PARTITIONED, "yes"); 465 dialog_clear(); 466 DialogActive = TRUE; 467 record_chunks(d); 468 } 469 else 470 msg = "Wise choice!"; 471 break; 472 473 case 'Q': 474 chunking = FALSE; 475 clear(); 476 /* Don't trash the MBR if the first (and therefore only) chunk is marked for a truly dedicated 477 * disk (i.e., the disklabel starts at sector 0), even in cases where the user has requested 478 * booteasy or a "standard" MBR -- both would be fatal in this case. 479 */ 480 if ((d->chunks->part->flags & CHUNK_FORCE_ALL) != CHUNK_FORCE_ALL 481 && (mbrContents = getBootMgr(d->name)) != NULL) 482 Set_Boot_Mgr(d, mbrContents); 483 break; 484 485 default: 486 beep(); 487 msg = "Type F1 or ? for help"; 488 break; 489 } 490 } 491 p = CheckRules(d); 492 if (p) { 493 dialog_clear(); 494 msgConfirm(p); 495 free(p); 496 } 497 dialog_clear(); 498} 499 500static int 501partitionHook(char *str) 502{ 503 Device **devs = NULL; 504 505 /* Clip garbage off the ends */ 506 string_prune(str); 507 str = string_skipwhite(str); 508 /* Try and open all the disks */ 509 while (str) { 510 char *cp; 511 512 cp = index(str, '\n'); 513 if (cp) 514 *cp++ = 0; 515 if (!*str) { 516 beep(); 517 return 0; 518 } 519 devs = deviceFind(str, DEVICE_TYPE_DISK); 520 if (!devs) { 521 dialog_clear(); 522 msgConfirm("Unable to find disk %s!", str); 523 return 0; 524 } 525 devs[0]->enabled = TRUE; 526 diskPartition(devs[0], (Disk *)devs[0]->private); 527 str = cp; 528 } 529 return devs ? 1 : 0; 530} 531 532int 533diskPartitionEditor(char *str) 534{ 535 DMenu *menu; 536 Device **devs; 537 int i, cnt; 538 char *cp; 539 540 cp = variable_get(VAR_DISK); 541 devs = deviceFind(cp, DEVICE_TYPE_DISK); 542 cnt = deviceCount(devs); 543 if (!cnt) { 544 dialog_clear(); 545 msgConfirm("No disks found! Please verify that your disk controller is being\n" 546 "properly probed at boot time. See the Hardware Guide on the\n" 547 "Documentation menu for clues on diagnosing this type of problem."); 548 i = RET_FAIL; 549 } 550 else if (cnt == 1) { 551 devs[0]->enabled = TRUE; 552 if (str && !strcmp(str, "script")) 553 scriptPartition(devs[0], (Disk *)devs[0]->private); 554 else 555 diskPartition(devs[0], (Disk *)devs[0]->private); 556 i = RET_SUCCESS; 557 variable_set2(DISK_SELECTED, "yes"); 558 } 559 else { 560 menu = deviceCreateMenu(&MenuDiskDevices, DEVICE_TYPE_DISK, partitionHook); 561 if (!menu) { 562 dialog_clear(); 563 msgConfirm("No devices suitable for installation found!\n\n" 564 "Please verify that your disk controller (and attached drives)\n" 565 "were detected properly. This can be done by pressing the\n" 566 "[Scroll Lock] key and using the Arrow keys to move back to\n" 567 "the boot messages. Press [Scroll Lock] again to return."); 568 i = RET_FAIL; 569 } 570 else { 571 if (!dmenuOpenSimple(menu)) 572 i = RET_FAIL; 573 else { 574 i = RET_SUCCESS; 575 variable_set2(DISK_SELECTED, "yes"); 576 } 577 free(menu); 578 } 579 } 580 return i; 581} 582 583int 584diskPartitionWrite(char *str) 585{ 586 extern u_char boot1[], boot2[]; 587 Device **devs; 588 char *cp; 589 int i; 590 591 if ((cp = variable_get(DISK_PARTITIONED)) && strcmp(cp, "yes")) 592 return RET_SUCCESS; 593 else if (!cp) { 594 dialog_clear(); 595 msgConfirm("You must partition the disk(s) before this option can be used."); 596 return RET_FAIL; 597 } 598 599 devs = deviceFind(NULL, DEVICE_TYPE_DISK); 600 if (!devs) { 601 dialog_clear(); 602 msgConfirm("Unable to find any disks to write to??"); 603 return RET_FAIL; 604 } 605 606 for (i = 0; devs[i]; i++) { 607 Chunk *c1; 608 Disk *d = (Disk *)devs[i]->private; 609 610 if (!devs[i]->enabled) 611 continue; 612 613 Set_Boot_Blocks(d, boot1, boot2); 614 msgNotify("Writing partition information to drive %s", d->name); 615 if (Write_Disk(d)) { 616 dialog_clear(); 617 msgConfirm("ERROR: Unable to write data to disk %s!", d->name); 618 return RET_FAIL; 619 } 620 /* Now scan for bad blocks, if necessary */ 621 for (c1 = d->chunks->part; c1; c1 = c1->next) { 622 if (c1->flags & CHUNK_BAD144) { 623 int ret; 624 625 msgNotify("Running bad block scan on partition %s", c1->name); 626 ret = vsystem("bad144 -v /dev/r%s 1234", c1->name); 627 if (ret) { 628 dialog_clear(); 629 msgConfirm("Bad144 init on %s returned status of %d!", c1->name, ret); 630 } 631 ret = vsystem("bad144 -v -s /dev/r%s", c1->name); 632 if (ret) { 633 dialog_clear(); 634 msgConfirm("Bad144 scan on %s returned status of %d!", c1->name, ret); 635 } 636 } 637 } 638 } 639 /* Now it's not "yes", but "written" */ 640 variable_set2(DISK_PARTITIONED, "written"); 641 return RET_SUCCESS; 642} 643