disks.c revision 21711
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 21711 1997-01-15 07:06:39Z 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) && (mbrContents = getBootMgr(d->name)) != NULL) 393 Set_Boot_Mgr(d, mbrContents); 394 395 if (DITEM_STATUS(diskPartitionWrite(NULL)) != DITEM_SUCCESS) 396 msgConfirm("Disk partition write returned an error status!"); 397 else 398 msgConfirm("Wrote FDISK partition information out successfully."); 399 } 400 clear(); 401 break; 402 403 case '|': 404 if (!msgYesNo("Are you SURE you want to go into Wizard mode?\n" 405 "No seat belts whatsoever are provided!")) { 406 clear(); 407 refresh(); 408 slice_wizard(d); 409 variable_set2(DISK_PARTITIONED, "yes"); 410 record_chunks(d); 411 } 412 else 413 msg = "Wise choice!"; 414 clear(); 415 break; 416 417 case '\033': /* ESC */ 418 case 'Q': 419 chunking = FALSE; 420 /* Don't trash the MBR if the first (and therefore only) chunk is marked for a truly dedicated 421 * disk (i.e., the disklabel starts at sector 0), even in cases where the user has requested 422 * booteasy or a "standard" MBR -- both would be fatal in this case. 423 */ 424 if ((d->chunks->part->flags & CHUNK_FORCE_ALL) != CHUNK_FORCE_ALL 425 && (mbrContents = getBootMgr(d->name)) != NULL) 426 Set_Boot_Mgr(d, mbrContents); 427 break; 428 429 default: 430 beep(); 431 msg = "Type F1 or ? for help"; 432 break; 433 } 434 } 435 p = CheckRules(d); 436 if (p) { 437 char buf[FILENAME_MAX]; 438 439 dialog_clear_norefresh(); 440 use_helpline("Press F1 to read more about disk partitioning."); 441 use_helpfile(systemHelpFile("partition", buf)); 442 dialog_mesgbox("Disk partitioning warning:", p, -1, -1); 443 free(p); 444 } 445 restorescr(w); 446} 447 448static int 449partitionHook(dialogMenuItem *selected) 450{ 451 Device **devs = NULL; 452 453 devs = deviceFind(selected->prompt, DEVICE_TYPE_DISK); 454 if (!devs) { 455 msgConfirm("Unable to find disk %s!", selected->prompt); 456 return DITEM_FAILURE; 457 } 458 /* Toggle enabled status? */ 459 if (!devs[0]->enabled) { 460 devs[0]->enabled = TRUE; 461 diskPartition(devs[0], (Disk *)devs[0]->private); 462 } 463 else 464 devs[0]->enabled = FALSE; 465 return DITEM_SUCCESS | DITEM_REDRAW; 466} 467 468static int 469partitionCheck(dialogMenuItem *selected) 470{ 471 Device **devs = NULL; 472 473 devs = deviceFind(selected->prompt, DEVICE_TYPE_DISK); 474 if (!devs || devs[0]->enabled == FALSE) 475 return FALSE; 476 return TRUE; 477} 478 479int 480diskPartitionEditor(dialogMenuItem *self) 481{ 482 DMenu *menu; 483 Device **devs; 484 int i, cnt; 485 char *cp; 486 487 cp = variable_get(VAR_DISK); 488 devs = deviceFind(cp, DEVICE_TYPE_DISK); 489 cnt = deviceCount(devs); 490 if (!cnt) { 491 msgConfirm("No disks found! Please verify that your disk controller is being\n" 492 "properly probed at boot time. See the Hardware Guide on the\n" 493 "Documentation menu for clues on diagnosing this type of problem."); 494 i = DITEM_FAILURE; 495 } 496 else if (cnt == 1) { 497 devs[0]->enabled = TRUE; 498 diskPartition(devs[0], (Disk *)devs[0]->private); 499 i = DITEM_SUCCESS; 500 } 501 else { 502 menu = deviceCreateMenu(&MenuDiskDevices, DEVICE_TYPE_DISK, partitionHook, partitionCheck); 503 if (!menu) { 504 msgConfirm("No devices suitable for installation found!\n\n" 505 "Please verify that your disk controller (and attached drives)\n" 506 "were detected properly. This can be done by pressing the\n" 507 "[Scroll Lock] key and using the Arrow keys to move back to\n" 508 "the boot messages. Press [Scroll Lock] again to return."); 509 i = DITEM_FAILURE; 510 } 511 else { 512 i = dmenuOpenSimple(menu, FALSE) ? DITEM_SUCCESS : DITEM_FAILURE; 513 free(menu); 514 } 515 i = i | DITEM_RECREATE; 516 } 517 return i; 518} 519 520int 521diskPartitionWrite(dialogMenuItem *self) 522{ 523 Device **devs; 524 char *cp; 525 int i; 526 527 if ((cp = variable_get(DISK_PARTITIONED)) && strcmp(cp, "yes")) 528 return DITEM_SUCCESS; 529 else if (!cp) { 530 msgConfirm("You must partition the disk(s) before this option can be used."); 531 return DITEM_FAILURE; 532 } 533 534 devs = deviceFind(NULL, DEVICE_TYPE_DISK); 535 if (!devs) { 536 msgConfirm("Unable to find any disks to write to??"); 537 return DITEM_FAILURE; 538 } 539 if (isDebug()) 540 msgDebug("diskPartitionWrite: Examining %d devices\n", deviceCount(devs)); 541 542 for (i = 0; devs[i]; i++) { 543 Chunk *c1; 544 Disk *d = (Disk *)devs[i]->private; 545 546 if (!devs[i]->enabled) 547 continue; 548 549 Set_Boot_Blocks(d, boot1, boot2); 550 msgNotify("Writing partition information to drive %s", d->name); 551 if (!Fake && Write_Disk(d)) { 552 msgConfirm("ERROR: Unable to write data to disk %s!", d->name); 553 return DITEM_FAILURE; 554 } 555 /* Now scan for bad blocks, if necessary */ 556 for (c1 = d->chunks->part; c1; c1 = c1->next) { 557 if (c1->flags & CHUNK_BAD144) { 558 int ret; 559 560 msgNotify("Running bad block scan on partition %s", c1->name); 561 if (!Fake) { 562 ret = vsystem("bad144 -v /dev/r%s 1234", c1->name); 563 if (ret) 564 msgConfirm("Bad144 init on %s returned status of %d!", c1->name, ret); 565 ret = vsystem("bad144 -v -s /dev/r%s", c1->name); 566 if (ret) 567 msgConfirm("Bad144 scan on %s returned status of %d!", c1->name, ret); 568 } 569 } 570 } 571 } 572 /* Now it's not "yes", but "written" */ 573 variable_set2(DISK_PARTITIONED, "written"); 574 return DITEM_SUCCESS; 575} 576