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