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