disks.c revision 12781
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.33 1995/12/07 10:33:39 peter 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.\n" 333 "You will run into serious trouble with ST-506 and ESDI drives\n" 334 "and possibly some IDE drives (e.g. drives running under the\n" 335 "control of sort of disk manager). SCSI drives are considerably\n" 336 "less at risk.\n\n" 337 "Do you insist on dedicating the entire disk this way?"); 338 } 339 All_FreeBSD(d, rv); 340 if (rv) 341 d->bios_hd = d->bios_sect = d->bios_cyl = 1; 342 variable_set2(DISK_PARTITIONED, "yes"); 343 record_chunks(d); 344 } 345 break; 346 347 case 'B': 348 if (chunk_info[current_chunk]->type != freebsd) 349 msg = "Can only scan for bad blocks in FreeBSD partition."; 350 else if (strncmp(d->name, "sd", 2) || 351 !msgYesNo("This typically makes sense only for ESDI, IDE or MFM drives.\n" 352 "Are you sure you want to do this on a SCSI disk?")) { 353 if (chunk_info[current_chunk]->flags & CHUNK_BAD144) 354 chunk_info[current_chunk]->flags &= ~CHUNK_BAD144; 355 else 356 chunk_info[current_chunk]->flags |= CHUNK_BAD144; 357 } 358 break; 359 360 case 'C': 361 if (chunk_info[current_chunk]->type != unused) 362 msg = "Partition in use, delete it first or move to an unused one."; 363 else { 364 char *val, tmp[20], *cp; 365 int size; 366 367 snprintf(tmp, 20, "%d", chunk_info[current_chunk]->size); 368 val = msgGetInput(tmp, "Please specify the size for new FreeBSD partition in blocks, or append\n" 369 "a trailing `M' for megabytes (e.g. 20M)."); 370 if (val && (size = strtol(val, &cp, 0)) > 0) { 371 if (*cp && toupper(*cp) == 'M') 372 size *= ONE_MEG; 373 Create_Chunk(d, chunk_info[current_chunk]->offset, size, freebsd, 3, 374 (chunk_info[current_chunk]->flags & CHUNK_ALIGN)); 375 variable_set2(DISK_PARTITIONED, "yes"); 376 record_chunks(d); 377 } 378 } 379 break; 380 381 case 'D': 382 if (chunk_info[current_chunk]->type == unused) 383 msg = "Partition is already unused!"; 384 else { 385 Delete_Chunk(d, chunk_info[current_chunk]); 386 variable_set2(DISK_PARTITIONED, "yes"); 387 record_chunks(d); 388 } 389 break; 390 391 case 'G': { 392 char *val, geometry[80]; 393 394 snprintf(geometry, 80, "%lu/%lu/%lu", d->bios_cyl, d->bios_hd, d->bios_sect); 395 val = msgGetInput(geometry, "Please specify the new geometry in cyl/hd/sect format.\n" 396 "Don't forget to use the two slash (/) separator characters!\n" 397 "It's not possible to parse the field without them."); 398 if (val) { 399 d->bios_cyl = strtol(val, &val, 0); 400 d->bios_hd = strtol(val + 1, &val, 0); 401 d->bios_sect = strtol(val + 1, 0, 0); 402 } 403 } 404 break; 405 406 case 'S': 407 /* Set Bootable */ 408 chunk_info[current_chunk]->flags |= CHUNK_ACTIVE; 409 break; 410 411 case 'U': 412 clear(); 413 if (msgYesNo("Are you SURE you want to Undo everything?")) 414 break; 415 d = Open_Disk(d->name); 416 if (!d) { 417 dialog_clear(); 418 msgConfirm("Can't reopen disk %s! Internal state is probably corrupted", d->name); 419 return; 420 } 421 Free_Disk(dev->private); 422 dev->private = d; 423 variable_unset(DISK_PARTITIONED); 424 record_chunks(d); 425 break; 426 427 case 'W': 428 if (!msgYesNo("Are you SURE you want to write this now? You do also\n" 429 "have the option of not modifying the disk until *all*\n" 430 "configuration information has been entered, at which\n" 431 "point you can do it all at once. If you're unsure, then\n" 432 "PLEASE CHOOSE NO at this dialog! This option is DANGEROUS\n" 433 "if you do not know EXACTLY what you are doing!")) { 434 variable_set2(DISK_PARTITIONED, "yes"); 435 clear(); 436 437 /* Don't trash the MBR if the first (and therefore only) chunk is marked for a truly dedicated 438 * disk (i.e., the disklabel starts at sector 0), even in cases where the user has requested 439 * booteasy or a "standard" MBR -- both would be fatal in this case. 440 */ 441 if ((d->chunks->part->flags & CHUNK_FORCE_ALL) != CHUNK_FORCE_ALL 442 && (mbrContents = getBootMgr(d->name)) != NULL) 443 Set_Boot_Mgr(d, mbrContents); 444 445 if (diskPartitionWrite(NULL) != RET_SUCCESS) { 446 dialog_clear(); 447 msgConfirm("Disk partition write returned an error status!"); 448 } 449 else { 450 msgConfirm("Wrote FDISK partition information out successfully."); 451 } 452 } 453 break; 454 455 case '|': 456 if (!msgYesNo("Are you SURE you want to go into Wizard mode?\n" 457 "No seat belts whatsoever are provided!")) { 458 dialog_clear(); 459 end_dialog(); 460 DialogActive = FALSE; 461 slice_wizard(d); 462 variable_set2(DISK_PARTITIONED, "yes"); 463 dialog_clear(); 464 DialogActive = TRUE; 465 record_chunks(d); 466 } 467 else 468 msg = "Wise choice!"; 469 break; 470 471 case 'Q': 472 chunking = FALSE; 473 clear(); 474 /* Don't trash the MBR if the first (and therefore only) chunk is marked for a truly dedicated 475 * disk (i.e., the disklabel starts at sector 0), even in cases where the user has requested 476 * booteasy or a "standard" MBR -- both would be fatal in this case. 477 */ 478 if ((d->chunks->part->flags & CHUNK_FORCE_ALL) != CHUNK_FORCE_ALL 479 && (mbrContents = getBootMgr(d->name)) != NULL) 480 Set_Boot_Mgr(d, mbrContents); 481 break; 482 483 default: 484 beep(); 485 msg = "Type F1 or ? for help"; 486 break; 487 } 488 } 489 p = CheckRules(d); 490 if (p) { 491 dialog_clear(); 492 msgConfirm(p); 493 free(p); 494 } 495 dialog_clear(); 496} 497 498static int 499partitionHook(char *str) 500{ 501 Device **devs = NULL; 502 503 /* Clip garbage off the ends */ 504 string_prune(str); 505 str = string_skipwhite(str); 506 /* Try and open all the disks */ 507 while (str) { 508 char *cp; 509 510 cp = index(str, '\n'); 511 if (cp) 512 *cp++ = 0; 513 if (!*str) { 514 beep(); 515 return 0; 516 } 517 devs = deviceFind(str, DEVICE_TYPE_DISK); 518 if (!devs) { 519 dialog_clear(); 520 msgConfirm("Unable to find disk %s!", str); 521 return 0; 522 } 523 devs[0]->enabled = TRUE; 524 diskPartition(devs[0], (Disk *)devs[0]->private); 525 str = cp; 526 } 527 return devs ? 1 : 0; 528} 529 530int 531diskPartitionEditor(char *str) 532{ 533 DMenu *menu; 534 Device **devs; 535 int i, cnt; 536 char *cp; 537 538 cp = variable_get(VAR_DISK); 539 devs = deviceFind(cp, DEVICE_TYPE_DISK); 540 cnt = deviceCount(devs); 541 if (!cnt) { 542 dialog_clear(); 543 msgConfirm("No disks found! Please verify that your disk controller is being\n" 544 "properly probed at boot time. See the Hardware Guide on the\n" 545 "Documentation menu for clues on diagnosing this type of problem."); 546 i = RET_FAIL; 547 } 548 else if (cnt == 1) { 549 devs[0]->enabled = TRUE; 550 if (str && !strcmp(str, "script")) 551 scriptPartition(devs[0], (Disk *)devs[0]->private); 552 else 553 diskPartition(devs[0], (Disk *)devs[0]->private); 554 i = RET_SUCCESS; 555 variable_set2(DISK_SELECTED, "yes"); 556 } 557 else { 558 menu = deviceCreateMenu(&MenuDiskDevices, DEVICE_TYPE_DISK, partitionHook); 559 if (!menu) { 560 dialog_clear(); 561 msgConfirm("No devices suitable for installation found!\n\n" 562 "Please verify that your disk controller (and attached drives)\n" 563 "were detected properly. This can be done by pressing the\n" 564 "[Scroll Lock] key and using the Arrow keys to move back to\n" 565 "the boot messages. Press [Scroll Lock] again to return."); 566 i = RET_FAIL; 567 } 568 else { 569 if (!dmenuOpenSimple(menu)) 570 i = RET_FAIL; 571 else { 572 i = RET_SUCCESS; 573 variable_set2(DISK_SELECTED, "yes"); 574 } 575 free(menu); 576 } 577 } 578 return i; 579} 580 581int 582diskPartitionWrite(char *str) 583{ 584 extern u_char boot1[], boot2[]; 585 Device **devs; 586 char *cp; 587 int i; 588 589 if ((cp = variable_get(DISK_PARTITIONED)) && strcmp(cp, "yes")) 590 return RET_SUCCESS; 591 else if (!cp) { 592 dialog_clear(); 593 msgConfirm("You must partition the disk(s) before this option can be used."); 594 return RET_FAIL; 595 } 596 597 devs = deviceFind(NULL, DEVICE_TYPE_DISK); 598 if (!devs) { 599 dialog_clear(); 600 msgConfirm("Unable to find any disks to write to??"); 601 return RET_FAIL; 602 } 603 604 for (i = 0; devs[i]; i++) { 605 Chunk *c1; 606 Disk *d = (Disk *)devs[i]->private; 607 608 if (!devs[i]->enabled) 609 continue; 610 611 Set_Boot_Blocks(d, boot1, boot2); 612 msgNotify("Writing partition information to drive %s", d->name); 613 if (Write_Disk(d)) { 614 dialog_clear(); 615 msgConfirm("ERROR: Unable to write data to disk %s!", d->name); 616 return RET_FAIL; 617 } 618 /* Now scan for bad blocks, if necessary */ 619 for (c1 = d->chunks->part; c1; c1 = c1->next) { 620 if (c1->flags & CHUNK_BAD144) { 621 int ret; 622 623 msgNotify("Running bad block scan on partition %s", c1->name); 624 ret = vsystem("bad144 -v /dev/r%s 1234", c1->name); 625 if (ret) { 626 dialog_clear(); 627 msgConfirm("Bad144 init on %s returned status of %d!", c1->name, ret); 628 } 629 ret = vsystem("bad144 -v -s /dev/r%s", c1->name); 630 if (ret) { 631 dialog_clear(); 632 msgConfirm("Bad144 scan on %s returned status of %d!", c1->name, ret); 633 } 634 } 635 } 636 } 637 /* Now it's not "yes", but "written" */ 638 variable_set2(DISK_PARTITIONED, "written"); 639 return RET_SUCCESS; 640} 641