disks.c revision 19992
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.72 1996/11/07 16:40:10 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_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 KEY_UP: 213 case '-': 214 if (current_chunk != 0) 215 --current_chunk; 216 break; 217 218 case KEY_DOWN: 219 case '+': 220 case '\r': 221 case '\n': 222 if (chunk_info[current_chunk + 1]) 223 ++current_chunk; 224 break; 225 226 case KEY_HOME: 227 current_chunk = 0; 228 break; 229 230 case KEY_END: 231 while (chunk_info[current_chunk + 1]) 232 ++current_chunk; 233 break; 234 235 case KEY_F(1): 236 case '?': 237 systemDisplayHelp("slice"); 238 clear(); 239 break; 240 241 case 'A': 242 rv = msgYesNo("Do you want to do this with a true partition entry\n" 243 "so as to remain cooperative with any future possible\n" 244 "operating systems on the drive(s)?"); 245 if (rv != 0) { 246 rv = !msgYesNo("This is dangerous in that it will make the drive totally\n" 247 "uncooperative with other potential operating systems on the\n" 248 "same disk. It will lead instead to a totally dedicated disk,\n" 249 "starting at the very first sector, bypassing all BIOS geometry\n" 250 "considerations. This precludes the existance of any boot\n" 251 "manager or other stuff in sector 0, since the BSD bootstrap\n" 252 "will live there.\n" 253 "You will run into serious trouble with ST-506 and ESDI drives\n" 254 "and possibly some IDE drives (e.g. drives running under the\n" 255 "control of sort of disk manager). SCSI drives are considerably\n" 256 "less at risk.\n\n" 257 "Do you insist on dedicating the entire disk this way?"); 258 } 259 if (rv == -1) 260 rv = 0; 261 All_FreeBSD(d, rv); 262 variable_set2(DISK_PARTITIONED, "yes"); 263 record_chunks(d); 264 clear(); 265 break; 266 267 case 'B': 268 if (chunk_info[current_chunk]->type != freebsd) 269 msg = "Can only scan for bad blocks in FreeBSD partition."; 270 else if (strncmp(d->name, "sd", 2) || 271 !msgYesNo("This typically makes sense only for ESDI, IDE or MFM drives.\n" 272 "Are you sure you want to do this on a SCSI disk?")) { 273 if (chunk_info[current_chunk]->flags & CHUNK_BAD144) 274 chunk_info[current_chunk]->flags &= ~CHUNK_BAD144; 275 else 276 chunk_info[current_chunk]->flags |= CHUNK_BAD144; 277 } 278 clear(); 279 break; 280 281 case 'C': 282 if (chunk_info[current_chunk]->type != unused) 283 msg = "Partition in use, delete it first or move to an unused one."; 284 else { 285 char *val, tmp[20], *cp; 286 int size, subtype; 287 chunk_e partitiontype; 288 289 snprintf(tmp, 20, "%d", chunk_info[current_chunk]->size); 290 val = msgGetInput(tmp, "Please specify the size for new FreeBSD partition in blocks\n" 291 "or append a trailing `M' for megabytes (e.g. 20M)."); 292 if (val && (size = strtol(val, &cp, 0)) > 0) { 293 if (*cp && toupper(*cp) == 'M') 294 size *= ONE_MEG; 295 strcpy(tmp, "165"); 296 val = msgGetInput(tmp, "Enter type of partition to create:\n\n" 297 "Pressing Enter will choose the default, a native FreeBSD\n" 298 "partition (type 165). You can choose other types, 6 for a\n" 299 "DOS partition or 131 for a Linux partition, for example.\n\n" 300 "Note: If you choose a non-FreeBSD partition type, it will not\n" 301 "be formatted or otherwise prepared, it will simply reserve space\n" 302 "for you to use another tool, such as DOS FORMAT, to later format\n" 303 "and use the partition."); 304 if (val && (subtype = strtol(val, NULL, 0)) > 0) { 305 if (subtype==165) 306 partitiontype=freebsd; 307 else if (subtype==6) 308 partitiontype=fat; 309 else 310 partitiontype=unknown; 311 Create_Chunk(d, chunk_info[current_chunk]->offset, size, partitiontype, subtype, 312 (chunk_info[current_chunk]->flags & CHUNK_ALIGN)); 313 variable_set2(DISK_PARTITIONED, "yes"); 314 record_chunks(d); 315 } 316 } 317 clear(); 318 } 319 break; 320 321 case KEY_DC: 322 case 'D': 323 if (chunk_info[current_chunk]->type == unused) 324 msg = "Partition is already unused!"; 325 else { 326 Delete_Chunk(d, chunk_info[current_chunk]); 327 variable_set2(DISK_PARTITIONED, "yes"); 328 record_chunks(d); 329 } 330 break; 331 332 case 'G': 333 snprintf(geometry, 80, "%lu/%lu/%lu", d->bios_cyl, d->bios_hd, d->bios_sect); 334 val = msgGetInput(geometry, "Please specify the new geometry in cyl/hd/sect format.\n" 335 "Don't forget to use the two slash (/) separator characters!\n" 336 "It's not possible to parse the field without them."); 337 if (val) { 338 long nc, nh, ns; 339 nc = strtol(val, &val, 0); 340 nh = strtol(val + 1, &val, 0); 341 ns = strtol(val + 1, 0, 0); 342 Set_Bios_Geom(d, nc, nh, ns); 343 } 344 clear(); 345 break; 346 347 case 'S': 348 /* Set Bootable */ 349 chunk_info[current_chunk]->flags |= CHUNK_ACTIVE; 350 break; 351 352 case 'U': 353 if ((cp = variable_get(DISK_LABELLED)) && !strcmp(cp, "written")) { 354 msgConfirm("You've already written this information out - you\n" 355 "can't undo it."); 356 } 357 else if (!msgYesNo("Are you SURE you want to Undo everything?")) { 358 d = Open_Disk(d->name); 359 if (!d) { 360 msgConfirm("Can't reopen disk %s! Internal state is probably corrupted", d->name); 361 clear(); 362 break; 363 } 364 Free_Disk(dev->private); 365 dev->private = d; 366 variable_unset(DISK_PARTITIONED); 367 variable_unset(DISK_LABELLED); 368 record_chunks(d); 369 } 370 clear(); 371 break; 372 373 case 'W': 374 if ((cp = variable_get(DISK_LABELLED)) && !strcmp(cp, "written")) { 375 msgConfirm("You've already written this information out - if\n" 376 "you wish to overwrite it, you'll have to restart."); 377 } 378 else if (!msgYesNo("WARNING: This should only be used when modifying an EXISTING\n" 379 "installation. If you are installing FreeBSD for the first time\n" 380 "then you should simply type Q when you're finished here and your\n" 381 "changes will be committed in one batch automatically at the end of\n" 382 "these questions.\n\n" 383 "Are you absolutely sure you want to do this now?")) { 384 variable_set2(DISK_PARTITIONED, "yes"); 385 386 /* Don't trash the MBR if the first (and therefore only) chunk is marked for a truly dedicated 387 * disk (i.e., the disklabel starts at sector 0), even in cases where the user has requested 388 * booteasy or a "standard" MBR -- both would be fatal in this case. 389 */ 390 if ((d->chunks->part->flags & CHUNK_FORCE_ALL) != CHUNK_FORCE_ALL 391 && (mbrContents = getBootMgr(d->name)) != NULL) 392 Set_Boot_Mgr(d, mbrContents); 393 394 if (DITEM_STATUS(diskPartitionWrite(NULL)) != DITEM_SUCCESS) 395 msgConfirm("Disk partition write returned an error status!"); 396 else 397 msgConfirm("Wrote FDISK partition information out successfully."); 398 } 399 clear(); 400 break; 401 402 case '|': 403 if (!msgYesNo("Are you SURE you want to go into Wizard mode?\n" 404 "No seat belts whatsoever are provided!")) { 405 clear(); 406 refresh(); 407 slice_wizard(d); 408 variable_set2(DISK_PARTITIONED, "yes"); 409 record_chunks(d); 410 } 411 else 412 msg = "Wise choice!"; 413 clear(); 414 break; 415 416 case 'Q': 417 chunking = FALSE; 418 /* Don't trash the MBR if the first (and therefore only) chunk is marked for a truly dedicated 419 * disk (i.e., the disklabel starts at sector 0), even in cases where the user has requested 420 * booteasy or a "standard" MBR -- both would be fatal in this case. 421 */ 422 if ((d->chunks->part->flags & CHUNK_FORCE_ALL) != CHUNK_FORCE_ALL 423 && (mbrContents = getBootMgr(d->name)) != NULL) 424 Set_Boot_Mgr(d, mbrContents); 425 break; 426 427 default: 428 beep(); 429 msg = "Type F1 or ? for help"; 430 break; 431 } 432 } 433 p = CheckRules(d); 434 if (p) { 435 char buf[FILENAME_MAX]; 436 437 dialog_clear_norefresh(); 438 use_helpline("Press F1 to read more about disk partitioning."); 439 use_helpfile(systemHelpFile("partition", buf)); 440 dialog_mesgbox("Disk partitioning warning:", p, -1, -1); 441 free(p); 442 } 443 restorescr(w); 444} 445 446static int 447partitionHook(dialogMenuItem *selected) 448{ 449 Device **devs = NULL; 450 451 devs = deviceFind(selected->prompt, DEVICE_TYPE_DISK); 452 if (!devs) { 453 msgConfirm("Unable to find disk %s!", selected->prompt); 454 return DITEM_FAILURE; 455 } 456 /* Toggle enabled status? */ 457 if (!devs[0]->enabled) { 458 devs[0]->enabled = TRUE; 459 diskPartition(devs[0], (Disk *)devs[0]->private); 460 } 461 else 462 devs[0]->enabled = FALSE; 463 return DITEM_SUCCESS | DITEM_REDRAW; 464} 465 466static int 467partitionCheck(dialogMenuItem *selected) 468{ 469 Device **devs = NULL; 470 471 devs = deviceFind(selected->prompt, DEVICE_TYPE_DISK); 472 if (!devs || devs[0]->enabled == FALSE) 473 return FALSE; 474 return TRUE; 475} 476 477int 478diskPartitionEditor(dialogMenuItem *self) 479{ 480 DMenu *menu; 481 Device **devs; 482 int i, cnt; 483 char *cp; 484 485 cp = variable_get(VAR_DISK); 486 devs = deviceFind(cp, DEVICE_TYPE_DISK); 487 cnt = deviceCount(devs); 488 if (!cnt) { 489 msgConfirm("No disks found! Please verify that your disk controller is being\n" 490 "properly probed at boot time. See the Hardware Guide on the\n" 491 "Documentation menu for clues on diagnosing this type of problem."); 492 i = DITEM_FAILURE; 493 } 494 else if (cnt == 1) { 495 devs[0]->enabled = TRUE; 496 diskPartition(devs[0], (Disk *)devs[0]->private); 497 i = DITEM_SUCCESS; 498 } 499 else { 500 menu = deviceCreateMenu(&MenuDiskDevices, DEVICE_TYPE_DISK, partitionHook, partitionCheck); 501 if (!menu) { 502 msgConfirm("No devices suitable for installation found!\n\n" 503 "Please verify that your disk controller (and attached drives)\n" 504 "were detected properly. This can be done by pressing the\n" 505 "[Scroll Lock] key and using the Arrow keys to move back to\n" 506 "the boot messages. Press [Scroll Lock] again to return."); 507 i = DITEM_FAILURE; 508 } 509 else { 510 i = dmenuOpenSimple(menu, FALSE) ? DITEM_SUCCESS : DITEM_FAILURE; 511 free(menu); 512 } 513 i = i | DITEM_RECREATE; 514 } 515 return i; 516} 517 518int 519diskPartitionWrite(dialogMenuItem *self) 520{ 521 Device **devs; 522 char *cp; 523 int i; 524 525 if ((cp = variable_get(DISK_PARTITIONED)) && strcmp(cp, "yes")) 526 return DITEM_SUCCESS; 527 else if (!cp) { 528 msgConfirm("You must partition the disk(s) before this option can be used."); 529 return DITEM_FAILURE; 530 } 531 532 devs = deviceFind(NULL, DEVICE_TYPE_DISK); 533 if (!devs) { 534 msgConfirm("Unable to find any disks to write to??"); 535 return DITEM_FAILURE; 536 } 537 if (isDebug()) 538 msgDebug("diskPartitionWrite: Examining %d devices\n", deviceCount(devs)); 539 540 for (i = 0; devs[i]; i++) { 541 Chunk *c1; 542 Disk *d = (Disk *)devs[i]->private; 543 544 if (!devs[i]->enabled) 545 continue; 546 547 Set_Boot_Blocks(d, boot1, boot2); 548 msgNotify("Writing partition information to drive %s", d->name); 549 if (!Fake && Write_Disk(d)) { 550 msgConfirm("ERROR: Unable to write data to disk %s!", d->name); 551 return DITEM_FAILURE; 552 } 553 /* Now scan for bad blocks, if necessary */ 554 for (c1 = d->chunks->part; c1; c1 = c1->next) { 555 if (c1->flags & CHUNK_BAD144) { 556 int ret; 557 558 msgNotify("Running bad block scan on partition %s", c1->name); 559 if (!Fake) { 560 ret = vsystem("bad144 -v /dev/r%s 1234", c1->name); 561 if (ret) 562 msgConfirm("Bad144 init on %s returned status of %d!", c1->name, ret); 563 ret = vsystem("bad144 -v -s /dev/r%s", c1->name); 564 if (ret) 565 msgConfirm("Bad144 scan on %s returned status of %d!", c1->name, ret); 566 } 567 } 568 } 569 } 570 /* Now it's not "yes", but "written" */ 571 variable_set2(DISK_PARTITIONED, "written"); 572 return DITEM_SUCCESS; 573} 574