disks.c revision 18830
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.69 1996/10/06 11:40:30 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 * 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 188 /* Set up the chunk array */ 189 record_chunks(d); 190 191 while (chunking) { 192 char *val, geometry[80]; 193 194 /* Now print our overall state */ 195 print_chunks(d); 196 print_command_summary(); 197 if (msg) { 198 attrset(title_attr); mvprintw(23, 0, msg); attrset(A_NORMAL); 199 beep(); 200 msg = NULL; 201 } 202 else { 203 move(23, 0); 204 clrtoeol(); 205 } 206 207 /* Get command character */ 208 key = getch(); 209 switch (toupper(key)) { 210 case '\014': /* ^L (redraw) */ 211 clear(); 212 msg = NULL; 213 break; 214 215 case KEY_UP: 216 case '-': 217 if (current_chunk != 0) 218 --current_chunk; 219 break; 220 221 case KEY_DOWN: 222 case '+': 223 case '\r': 224 case '\n': 225 if (chunk_info[current_chunk + 1]) 226 ++current_chunk; 227 break; 228 229 case KEY_HOME: 230 current_chunk = 0; 231 break; 232 233 case KEY_END: 234 while (chunk_info[current_chunk + 1]) 235 ++current_chunk; 236 break; 237 238 case KEY_F(1): 239 case '?': 240 systemDisplayHelp("slice"); 241 clear(); 242 break; 243 244 case 'A': 245 rv = msgYesNo("Do you want to do this with a true partition entry\n" 246 "so as to remain cooperative with any future possible\n" 247 "operating systems on the drive(s)?"); 248 if (rv != 0) { 249 rv = !msgYesNo("This is dangerous in that it will make the drive totally\n" 250 "uncooperative with other potential operating systems on the\n" 251 "same disk. It will lead instead to a totally dedicated disk,\n" 252 "starting at the very first sector, bypassing all BIOS geometry\n" 253 "considerations. This precludes the existance of any boot\n" 254 "manager or other stuff in sector 0, since the BSD bootstrap\n" 255 "will live there.\n" 256 "You will run into serious trouble with ST-506 and ESDI drives\n" 257 "and possibly some IDE drives (e.g. drives running under the\n" 258 "control of sort of disk manager). SCSI drives are considerably\n" 259 "less at risk.\n\n" 260 "Do you insist on dedicating the entire disk this way?"); 261 } 262 if (rv == -1) 263 rv = 0; 264 All_FreeBSD(d, rv); 265 if (rv) 266 d->bios_hd = d->bios_sect = d->bios_cyl = 1; 267 else { 268 d->bios_hd = 64; 269 d->bios_sect = 32; 270 d->bios_cyl = Total / ONE_MEG; 271 } 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 partition."; 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 = "Partition 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 partition 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 "partition (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 = "Partition 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 d->bios_cyl = strtol(val, &val, 0); 349 d->bios_hd = strtol(val + 1, &val, 0); 350 d->bios_sect = strtol(val + 1, 0, 0); 351 } 352 clear(); 353 break; 354 355 case 'S': 356 /* Set Bootable */ 357 chunk_info[current_chunk]->flags |= CHUNK_ACTIVE; 358 break; 359 360 case 'U': 361 if ((cp = variable_get(DISK_LABELLED)) && !strcmp(cp, "written")) { 362 msgConfirm("You've already written this information out - you\n" 363 "can't undo it."); 364 } 365 else if (!msgYesNo("Are you SURE you want to Undo everything?")) { 366 d = Open_Disk(d->name); 367 if (!d) { 368 msgConfirm("Can't reopen disk %s! Internal state is probably corrupted", d->name); 369 clear(); 370 break; 371 } 372 Free_Disk(dev->private); 373 dev->private = d; 374 variable_unset(DISK_PARTITIONED); 375 variable_unset(DISK_LABELLED); 376 record_chunks(d); 377 } 378 clear(); 379 break; 380 381 case 'W': 382 if ((cp = variable_get(DISK_LABELLED)) && !strcmp(cp, "written")) { 383 msgConfirm("You've already written this information out - if\n" 384 "you wish to overwrite it, you'll have to restart."); 385 } 386 else if (!msgYesNo("WARNING: This should only be used when modifying an EXISTING\n" 387 "installation. If you are installing FreeBSD for the first time\n" 388 "then you should simply type Q when you're finished here and your\n" 389 "changes will be committed in one batch automatically at the end of\n" 390 "these questions.\n\n" 391 "Are you absolutely sure you want to do this now?")) { 392 variable_set2(DISK_PARTITIONED, "yes"); 393 394 /* Don't trash the MBR if the first (and therefore only) chunk is marked for a truly dedicated 395 * disk (i.e., the disklabel starts at sector 0), even in cases where the user has requested 396 * booteasy or a "standard" MBR -- both would be fatal in this case. 397 */ 398 if ((d->chunks->part->flags & CHUNK_FORCE_ALL) != CHUNK_FORCE_ALL 399 && (mbrContents = getBootMgr(d->name)) != NULL) 400 Set_Boot_Mgr(d, mbrContents); 401 402 if (DITEM_STATUS(diskPartitionWrite(NULL)) != DITEM_SUCCESS) 403 msgConfirm("Disk partition write returned an error status!"); 404 else 405 msgConfirm("Wrote FDISK partition information out successfully."); 406 } 407 clear(); 408 break; 409 410 case '|': 411 if (!msgYesNo("Are you SURE you want to go into Wizard mode?\n" 412 "No seat belts whatsoever are provided!")) { 413 clear(); 414 refresh(); 415 slice_wizard(d); 416 variable_set2(DISK_PARTITIONED, "yes"); 417 record_chunks(d); 418 } 419 else 420 msg = "Wise choice!"; 421 clear(); 422 break; 423 424 case 'Q': 425 chunking = FALSE; 426 /* Don't trash the MBR if the first (and therefore only) chunk is marked for a truly dedicated 427 * disk (i.e., the disklabel starts at sector 0), even in cases where the user has requested 428 * booteasy or a "standard" MBR -- both would be fatal in this case. 429 */ 430 if ((d->chunks->part->flags & CHUNK_FORCE_ALL) != CHUNK_FORCE_ALL 431 && (mbrContents = getBootMgr(d->name)) != NULL) 432 Set_Boot_Mgr(d, mbrContents); 433 break; 434 435 default: 436 beep(); 437 msg = "Type F1 or ? for help"; 438 break; 439 } 440 } 441 p = CheckRules(d); 442 if (p) { 443 char buf[FILENAME_MAX]; 444 445 dialog_clear_norefresh(); 446 use_helpline("Press F1 to read more about disk partitioning."); 447 use_helpfile(systemHelpFile("partition", buf)); 448 dialog_mesgbox("Disk partitioning warning:", p, -1, -1); 449 free(p); 450 } 451 restorescr(w); 452} 453 454static int 455partitionHook(dialogMenuItem *selected) 456{ 457 Device **devs = NULL; 458 459 devs = deviceFind(selected->prompt, DEVICE_TYPE_DISK); 460 if (!devs) { 461 msgConfirm("Unable to find disk %s!", selected->prompt); 462 return DITEM_FAILURE; 463 } 464 /* Toggle enabled status? */ 465 if (!devs[0]->enabled) { 466 devs[0]->enabled = TRUE; 467 diskPartition(devs[0], (Disk *)devs[0]->private); 468 } 469 else 470 devs[0]->enabled = FALSE; 471 return DITEM_SUCCESS | DITEM_REDRAW; 472} 473 474static int 475partitionCheck(dialogMenuItem *selected) 476{ 477 Device **devs = NULL; 478 479 devs = deviceFind(selected->prompt, DEVICE_TYPE_DISK); 480 if (!devs || devs[0]->enabled == FALSE) 481 return FALSE; 482 return TRUE; 483} 484 485int 486diskPartitionEditor(dialogMenuItem *self) 487{ 488 DMenu *menu; 489 Device **devs; 490 int i, cnt; 491 char *cp; 492 493 cp = variable_get(VAR_DISK); 494 devs = deviceFind(cp, DEVICE_TYPE_DISK); 495 cnt = deviceCount(devs); 496 if (!cnt) { 497 msgConfirm("No disks found! Please verify that your disk controller is being\n" 498 "properly probed at boot time. See the Hardware Guide on the\n" 499 "Documentation menu for clues on diagnosing this type of problem."); 500 i = DITEM_FAILURE; 501 } 502 else if (cnt == 1) { 503 devs[0]->enabled = TRUE; 504 diskPartition(devs[0], (Disk *)devs[0]->private); 505 i = DITEM_SUCCESS; 506 } 507 else { 508 menu = deviceCreateMenu(&MenuDiskDevices, DEVICE_TYPE_DISK, partitionHook, partitionCheck); 509 if (!menu) { 510 msgConfirm("No devices suitable for installation found!\n\n" 511 "Please verify that your disk controller (and attached drives)\n" 512 "were detected properly. This can be done by pressing the\n" 513 "[Scroll Lock] key and using the Arrow keys to move back to\n" 514 "the boot messages. Press [Scroll Lock] again to return."); 515 i = DITEM_FAILURE; 516 } 517 else { 518 i = dmenuOpenSimple(menu, FALSE) ? DITEM_SUCCESS : DITEM_FAILURE; 519 free(menu); 520 } 521 i = i | DITEM_RECREATE; 522 } 523 return i; 524} 525 526int 527diskPartitionWrite(dialogMenuItem *self) 528{ 529 Device **devs; 530 char *cp; 531 int i; 532 533 if ((cp = variable_get(DISK_PARTITIONED)) && strcmp(cp, "yes")) 534 return DITEM_SUCCESS; 535 else if (!cp) { 536 msgConfirm("You must partition the disk(s) before this option can be used."); 537 return DITEM_FAILURE; 538 } 539 540 devs = deviceFind(NULL, DEVICE_TYPE_DISK); 541 if (!devs) { 542 msgConfirm("Unable to find any disks to write to??"); 543 return DITEM_FAILURE; 544 } 545 if (isDebug()) 546 msgDebug("diskPartitionWrite: Examining %d devices\n", deviceCount(devs)); 547 548 for (i = 0; devs[i]; i++) { 549 Chunk *c1; 550 Disk *d = (Disk *)devs[i]->private; 551 552 if (!devs[i]->enabled) 553 continue; 554 555 Set_Boot_Blocks(d, boot1, boot2); 556 msgNotify("Writing partition information to drive %s", d->name); 557 if (!Fake && Write_Disk(d)) { 558 msgConfirm("ERROR: Unable to write data to disk %s!", d->name); 559 return DITEM_FAILURE; 560 } 561 /* Now scan for bad blocks, if necessary */ 562 for (c1 = d->chunks->part; c1; c1 = c1->next) { 563 if (c1->flags & CHUNK_BAD144) { 564 int ret; 565 566 msgNotify("Running bad block scan on partition %s", c1->name); 567 if (!Fake) { 568 ret = vsystem("bad144 -v /dev/r%s 1234", c1->name); 569 if (ret) 570 msgConfirm("Bad144 init on %s returned status of %d!", c1->name, ret); 571 ret = vsystem("bad144 -v -s /dev/r%s", c1->name); 572 if (ret) 573 msgConfirm("Bad144 scan on %s returned status of %d!", c1->name, ret); 574 } 575 } 576 } 577 } 578 /* Now it's not "yes", but "written" */ 579 variable_set2(DISK_PARTITIONED, "written"); 580 return DITEM_SUCCESS; 581} 582