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