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