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