disks.c revision 21969
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 21969 1997-01-24 02:26:42Z obrien $ 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, 109 slice_type_name(chunk_info[i]->type, chunk_info[i]->subtype), 110 chunk_info[i]->subtype, ShowChunkFlags(chunk_info[i])); 111 if (i == current_chunk) 112 attrset(A_NORMAL); 113 } 114} 115 116static void 117print_command_summary() 118{ 119 mvprintw(14, 0, "The following commands are supported (in upper or lower case):"); 120 mvprintw(16, 0, "A = Use Entire Disk B = Bad Block Scan C = Create Partition"); 121 mvprintw(17, 0, "D = Delete Partition G = Set Drive Geometry S = Set Bootable"); 122 mvprintw(18, 0, "U = Undo All Changes Q = Finish"); 123 if (!RunningAsInit) 124 mvprintw(18, 46, "W = Write Changes"); 125 mvprintw(21, 0, "Use F1 or ? to get more help, arrow keys to select."); 126 move(0, 0); 127} 128 129static u_char * 130getBootMgr(char *dname) 131{ 132 extern u_char mbr[], bteasy17[]; 133 char str[80]; 134 char *cp; 135 int i = 0; 136 137 cp = variable_get(VAR_BOOTMGR); 138 if (!cp) { 139 /* Figure out what kind of MBR the user wants */ 140 sprintf(str, "Install Boot Manager for drive %s?", dname); 141 MenuMBRType.title = str; 142 i = dmenuOpenSimple(&MenuMBRType, FALSE); 143 } 144 else { 145 if (!strncmp(cp, "boot", 4)) 146 BootMgr = 0; 147 else if (!strcmp(cp, "standard")) 148 BootMgr = 1; 149 else 150 BootMgr = 2; 151 } 152 if (cp || i) { 153 switch (BootMgr) { 154 case 0: 155 return bteasy17; 156 157 case 1: 158 return mbr; 159 160 case 2: 161 default: 162 break; 163 } 164 } 165 return NULL; 166} 167 168void 169diskPartition(Device *dev, Disk *d) 170{ 171 char *cp, *p; 172 int rv, key = 0; 173 Boolean chunking; 174 char *msg = NULL; 175 u_char *mbrContents; 176 WINDOW *w = savescr(); 177 178 chunking = TRUE; 179 keypad(stdscr, TRUE); 180 181 /* Flush both the dialog and curses library views of the screen 182 since we don't always know who called us */ 183 dialog_clear_norefresh(), clear(); 184 current_chunk = 0; 185 186 /* Set up the chunk array */ 187 record_chunks(d); 188 189 while (chunking) { 190 char *val, geometry[80]; 191 192 /* Now print our overall state */ 193 if (d) 194 print_chunks(d); 195 print_command_summary(); 196 if (msg) { 197 attrset(title_attr); mvprintw(23, 0, msg); attrset(A_NORMAL); 198 beep(); 199 msg = NULL; 200 } 201 else { 202 move(23, 0); 203 clrtoeol(); 204 } 205 206 /* Get command character */ 207 key = getch(); 208 switch (toupper(key)) { 209 case '\014': /* ^L (redraw) */ 210 clear(); 211 msg = NULL; 212 break; 213 214 case '\020': /* ^P */ 215 case KEY_UP: 216 case '-': 217 if (current_chunk != 0) 218 --current_chunk; 219 break; 220 221 case '\016': /* ^N */ 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 variable_set2(DISK_PARTITIONED, "yes"); 267 record_chunks(d); 268 clear(); 269 break; 270 271 case 'B': 272 if (chunk_info[current_chunk]->type != freebsd) 273 msg = "Can only scan for bad blocks in FreeBSD partition."; 274 else if (strncmp(d->name, "sd", 2) || 275 !msgYesNo("This typically makes sense only for ESDI, IDE or MFM drives.\n" 276 "Are you sure you want to do this on a SCSI disk?")) { 277 if (chunk_info[current_chunk]->flags & CHUNK_BAD144) 278 chunk_info[current_chunk]->flags &= ~CHUNK_BAD144; 279 else 280 chunk_info[current_chunk]->flags |= CHUNK_BAD144; 281 } 282 clear(); 283 break; 284 285 case 'C': 286 if (chunk_info[current_chunk]->type != unused) 287 msg = "Partition in use, delete it first or move to an unused one."; 288 else { 289 char *val, tmp[20], *cp; 290 int size, subtype; 291 chunk_e partitiontype; 292 293 snprintf(tmp, 20, "%d", chunk_info[current_chunk]->size); 294 val = msgGetInput(tmp, "Please specify the size for new FreeBSD partition in blocks\n" 295 "or append a trailing `M' for megabytes (e.g. 20M)."); 296 if (val && (size = strtol(val, &cp, 0)) > 0) { 297 if (*cp && toupper(*cp) == 'M') 298 size *= ONE_MEG; 299 strcpy(tmp, "165"); 300 val = msgGetInput(tmp, "Enter type of partition to create:\n\n" 301 "Pressing Enter will choose the default, a native FreeBSD\n" 302 "partition (type 165). You can choose other types, 6 for a\n" 303 "DOS partition or 131 for a Linux partition, for example.\n\n" 304 "Note: If you choose a non-FreeBSD partition type, it will not\n" 305 "be formatted or otherwise prepared, it will simply reserve space\n" 306 "for you to use another tool, such as DOS FORMAT, to later format\n" 307 "and use the partition."); 308 if (val && (subtype = strtol(val, NULL, 0)) > 0) { 309 if (subtype==165) 310 partitiontype=freebsd; 311 else if (subtype==6) 312 partitiontype=fat; 313 else 314 partitiontype=unknown; 315 Create_Chunk(d, chunk_info[current_chunk]->offset, size, partitiontype, subtype, 316 (chunk_info[current_chunk]->flags & CHUNK_ALIGN)); 317 variable_set2(DISK_PARTITIONED, "yes"); 318 record_chunks(d); 319 } 320 } 321 clear(); 322 } 323 break; 324 325 case KEY_DC: 326 case 'D': 327 if (chunk_info[current_chunk]->type == unused) 328 msg = "Partition is already unused!"; 329 else { 330 Delete_Chunk(d, chunk_info[current_chunk]); 331 variable_set2(DISK_PARTITIONED, "yes"); 332 record_chunks(d); 333 } 334 break; 335 336 case 'G': 337 snprintf(geometry, 80, "%lu/%lu/%lu", d->bios_cyl, d->bios_hd, d->bios_sect); 338 val = msgGetInput(geometry, "Please specify the new geometry in cyl/hd/sect format.\n" 339 "Don't forget to use the two slash (/) separator characters!\n" 340 "It's not possible to parse the field without them."); 341 if (val) { 342 long nc, nh, ns; 343 nc = strtol(val, &val, 0); 344 nh = strtol(val + 1, &val, 0); 345 ns = strtol(val + 1, 0, 0); 346 Set_Bios_Geom(d, nc, nh, ns); 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 char cp[BUFSIZ]; 363 364 sstrncpy(cp, d->name, sizeof cp); 365 Free_Disk(dev->private); 366 d = Open_Disk(cp); 367 if (!d) 368 msgConfirm("Can't reopen disk %s! Internal state is probably corrupted", cp); 369 dev->private = d; 370 variable_unset(DISK_PARTITIONED); 371 variable_unset(DISK_LABELLED); 372 if (d) 373 record_chunks(d); 374 } 375 clear(); 376 break; 377 378 case 'W': 379 if ((cp = variable_get(DISK_LABELLED)) && !strcmp(cp, "written")) { 380 msgConfirm("You've already written this information out - if\n" 381 "you wish to overwrite it, you'll have to restart."); 382 } 383 else if (!msgYesNo("WARNING: This should only be used when modifying an EXISTING\n" 384 "installation. If you are installing FreeBSD for the first time\n" 385 "then you should simply type Q when you're finished here and your\n" 386 "changes will be committed in one batch automatically at the end of\n" 387 "these questions.\n\n" 388 "Are you absolutely sure you want to do this now?")) { 389 variable_set2(DISK_PARTITIONED, "yes"); 390 391 /* Don't trash the MBR if the first (and therefore only) chunk is marked for a truly dedicated 392 * disk (i.e., the disklabel starts at sector 0), even in cases where the user has requested 393 * booteasy or a "standard" MBR -- both would be fatal in this case. 394 */ 395 if (!(d->chunks->part->flags & CHUNK_FORCE_ALL) && (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 '\033': /* ESC */ 421 case 'Q': 422 chunking = FALSE; 423 /* Don't trash the MBR if the first (and therefore only) chunk is marked for a truly dedicated 424 * disk (i.e., the disklabel starts at sector 0), even in cases where the user has requested 425 * booteasy or a "standard" MBR -- both would be fatal in this case. 426 */ 427 if ((d->chunks->part->flags & CHUNK_FORCE_ALL) != CHUNK_FORCE_ALL 428 && (mbrContents = getBootMgr(d->name)) != NULL) 429 Set_Boot_Mgr(d, mbrContents); 430 break; 431 432 default: 433 beep(); 434 msg = "Type F1 or ? for help"; 435 break; 436 } 437 } 438 p = CheckRules(d); 439 if (p) { 440 char buf[FILENAME_MAX]; 441 442 dialog_clear_norefresh(); 443 use_helpline("Press F1 to read more about disk partitioning."); 444 use_helpfile(systemHelpFile("partition", buf)); 445 dialog_mesgbox("Disk partitioning warning:", p, -1, -1); 446 free(p); 447 } 448 restorescr(w); 449} 450 451static int 452partitionHook(dialogMenuItem *selected) 453{ 454 Device **devs = NULL; 455 456 devs = deviceFind(selected->prompt, DEVICE_TYPE_DISK); 457 if (!devs) { 458 msgConfirm("Unable to find disk %s!", selected->prompt); 459 return DITEM_FAILURE; 460 } 461 /* Toggle enabled status? */ 462 if (!devs[0]->enabled) { 463 devs[0]->enabled = TRUE; 464 diskPartition(devs[0], (Disk *)devs[0]->private); 465 } 466 else 467 devs[0]->enabled = FALSE; 468 return DITEM_SUCCESS | DITEM_REDRAW; 469} 470 471static int 472partitionCheck(dialogMenuItem *selected) 473{ 474 Device **devs = NULL; 475 476 devs = deviceFind(selected->prompt, DEVICE_TYPE_DISK); 477 if (!devs || devs[0]->enabled == FALSE) 478 return FALSE; 479 return TRUE; 480} 481 482int 483diskPartitionEditor(dialogMenuItem *self) 484{ 485 DMenu *menu; 486 Device **devs; 487 int i, cnt; 488 char *cp; 489 490 cp = variable_get(VAR_DISK); 491 devs = deviceFind(cp, DEVICE_TYPE_DISK); 492 cnt = deviceCount(devs); 493 if (!cnt) { 494 msgConfirm("No disks found! Please verify that your disk controller is being\n" 495 "properly probed at boot time. See the Hardware Guide on the\n" 496 "Documentation menu for clues on diagnosing this type of problem."); 497 i = DITEM_FAILURE; 498 } 499 else if (cnt == 1) { 500 devs[0]->enabled = TRUE; 501 diskPartition(devs[0], (Disk *)devs[0]->private); 502 i = DITEM_SUCCESS; 503 } 504 else { 505 menu = deviceCreateMenu(&MenuDiskDevices, DEVICE_TYPE_DISK, partitionHook, partitionCheck); 506 if (!menu) { 507 msgConfirm("No devices suitable for installation found!\n\n" 508 "Please verify that your disk controller (and attached drives)\n" 509 "were detected properly. This can be done by pressing the\n" 510 "[Scroll Lock] key and using the Arrow keys to move back to\n" 511 "the boot messages. Press [Scroll Lock] again to return."); 512 i = DITEM_FAILURE; 513 } 514 else { 515 i = dmenuOpenSimple(menu, FALSE) ? DITEM_SUCCESS : DITEM_FAILURE; 516 free(menu); 517 } 518 i = i | DITEM_RESTORE; 519 } 520 return i; 521} 522 523int 524diskPartitionWrite(dialogMenuItem *self) 525{ 526 Device **devs; 527 char *cp; 528 int i; 529 530 if ((cp = variable_get(DISK_PARTITIONED)) && strcmp(cp, "yes")) 531 return DITEM_SUCCESS; 532 else if (!cp) { 533 msgConfirm("You must partition the disk(s) before this option can be used."); 534 return DITEM_FAILURE; 535 } 536 537 devs = deviceFind(NULL, DEVICE_TYPE_DISK); 538 if (!devs) { 539 msgConfirm("Unable to find any disks to write to??"); 540 return DITEM_FAILURE; 541 } 542 if (isDebug()) 543 msgDebug("diskPartitionWrite: Examining %d devices\n", deviceCount(devs)); 544 545 for (i = 0; devs[i]; i++) { 546 Chunk *c1; 547 Disk *d = (Disk *)devs[i]->private; 548 549 if (!devs[i]->enabled) 550 continue; 551 552 Set_Boot_Blocks(d, boot1, boot2); 553 msgNotify("Writing partition information to drive %s", d->name); 554 if (!Fake && Write_Disk(d)) { 555 msgConfirm("ERROR: Unable to write data to disk %s!", d->name); 556 return DITEM_FAILURE; 557 } 558 /* Now scan for bad blocks, if necessary */ 559 for (c1 = d->chunks->part; c1; c1 = c1->next) { 560 if (c1->flags & CHUNK_BAD144) { 561 int ret; 562 563 msgNotify("Running bad block scan on partition %s", c1->name); 564 if (!Fake) { 565 ret = vsystem("bad144 -v /dev/r%s 1234", c1->name); 566 if (ret) 567 msgConfirm("Bad144 init on %s returned status of %d!", c1->name, ret); 568 ret = vsystem("bad144 -v -s /dev/r%s", c1->name); 569 if (ret) 570 msgConfirm("Bad144 scan on %s returned status of %d!", c1->name, ret); 571 } 572 } 573 } 574 } 575 /* Now it's not "yes", but "written" */ 576 variable_set2(DISK_PARTITIONED, "written"); 577 return DITEM_SUCCESS; 578} 579