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