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