disks.c revision 17025
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.54 1996/07/05 08:35:52 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_cyl > 65536) || (!d->bios_hd || d->bios_hd > 256) || (!d->bios_sect || d->bios_sect >= 64)) { 74 int sz; 75 76 dialog_clear(); 77 msgConfirm("WARNING: The current geometry for %s is incorrect. Using\n" 78 "a default geometry of 64 heads and 32 sectors. If this geometry\n" 79 "is incorrect or you are unsure as to whether or not it's correct,\n" 80 "please consult the Hardware Guide in the Documentation submenu\n" 81 "or use the (G)eometry command to change it now.", d->name); 82 d->bios_hd = 64; 83 d->bios_sect = 32; 84 sz = 0; 85 for (i = 0; chunk_info[i]; i++) 86 sz += chunk_info[i]->size; 87 if (sz) 88 d->bios_cyl = sz / ONE_MEG; 89 else 90 msgConfirm("Couldn't set geometry! You'll have to do it by hand."); 91 } 92 attrset(A_NORMAL); 93 mvaddstr(0, 0, "Disk name:\t"); 94 clrtobot(); 95 attrset(A_REVERSE); addstr(d->name); attrset(A_NORMAL); 96 attrset(A_REVERSE); mvaddstr(0, 55, "FDISK Partition Editor"); attrset(A_NORMAL); 97 mvprintw(1, 0, 98 "DISK Geometry:\t%lu cyls/%lu heads/%lu sectors", 99 d->bios_cyl, d->bios_hd, d->bios_sect); 100 mvprintw(3, 1, "%10s %10s %10s %8s %8s %8s %8s %8s", 101 "Offset", "Size", "End", "Name", "PType", "Desc", 102 "Subtype", "Flags"); 103 for (i = 0, row = CHUNK_START_ROW; chunk_info[i]; i++, row++) { 104 if (i == current_chunk) 105 attrset(ATTR_SELECTED); 106 mvprintw(row, 2, "%10ld %10lu %10lu %8s %8d %8s %8d\t%-6s", 107 chunk_info[i]->offset, chunk_info[i]->size, 108 chunk_info[i]->end, chunk_info[i]->name, 109 chunk_info[i]->type, chunk_n[chunk_info[i]->type], 110 chunk_info[i]->subtype, ShowChunkFlags(chunk_info[i])); 111 if (i == current_chunk) 112 attrset(A_NORMAL); 113 } 114} 115 116static void 117print_command_summary() 118{ 119 mvprintw(14, 0, "The following commands are supported (in upper or lower case):"); 120 mvprintw(16, 0, "A = Use Entire Disk B = Bad Block Scan C = Create Partition"); 121 mvprintw(17, 0, "D = Delete Partition G = Set Drive Geometry S = Set Bootable"); 122 mvprintw(18, 0, "U = Undo All Changes Q = Finish"); 123 if (!RunningAsInit) 124 mvprintw(18, 46, "W = Write Changes"); 125 mvprintw(21, 0, "Use F1 or ? to get more help, arrow keys to select."); 126 move(0, 0); 127} 128 129static u_char * 130getBootMgr(char *dname) 131{ 132 extern u_char mbr[], bteasy17[]; 133 char str[80]; 134 char *cp; 135 int i = 0; 136 137 cp = variable_get(VAR_BOOTMGR); 138 if (!cp) { 139 /* Figure out what kind of MBR the user wants */ 140 sprintf(str, "Install Boot Manager for drive %s?", dname); 141 MenuMBRType.title = str; 142 i = dmenuOpenSimple(&MenuMBRType, FALSE); 143 } 144 else { 145 if (!strncmp(cp, "boot", 4)) 146 BootMgr = 0; 147 else if (!strcmp(cp, "standard")) 148 BootMgr = 1; 149 else 150 BootMgr = 2; 151 } 152 if (cp || i) { 153 switch (BootMgr) { 154 case 0: 155 return bteasy17; 156 157 case 1: 158 return mbr; 159 160 case 2: 161 default: 162 break; 163 } 164 } 165 return NULL; 166} 167 168void 169diskPartition(Device *dev, Disk *d) 170{ 171 char *p; 172 int key = 0; 173 Boolean chunking; 174 char *msg = NULL; 175 u_char *mbrContents; 176 WINDOW *w; 177 178 chunking = TRUE; 179 keypad(stdscr, TRUE); 180 181 w = savescr(); 182 clear(); 183 record_chunks(d); 184 while (chunking) { 185 print_chunks(d); 186 print_command_summary(); 187 if (msg) { 188 attrset(title_attr); mvprintw(23, 0, msg); attrset(A_NORMAL); 189 beep(); 190 msg = NULL; 191 } 192 193 key = toupper(getch()); 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 278 snprintf(tmp, 20, "%d", chunk_info[current_chunk]->size); 279 val = msgGetInput(tmp, "Please specify the size for new FreeBSD partition in blocks\n" 280 "or append a trailing `M' for megabytes (e.g. 20M)."); 281 if (val && (size = strtol(val, &cp, 0)) > 0) { 282 if (*cp && toupper(*cp) == 'M') 283 size *= ONE_MEG; 284 strcpy(tmp, "165"); 285 val = msgGetInput(tmp, "Enter type of partition to create:\n\n" 286 "Pressing Enter will choose the default, a native FreeBSD\n" 287 "partition (type 165). You can choose other types, 6 for a\n" 288 "DOS partition or 131 for a Linux partition, for example.\n\n" 289 "Note: If you choose a non-FreeBSD partition type, it will not\n" 290 "be formatted or otherwise prepared, it will simply reserve space\n" 291 "for you to use another tool, such as DOS FORMAT, to later format\n" 292 "and use the partition."); 293 if (val && (subtype = strtol(val, NULL, 0)) > 0) { 294 if (subtype==165) 295 partitiontype=freebsd; 296 else if (subtype==6) 297 partitiontype=fat; 298 else 299 partitiontype=unknown; 300 Create_Chunk(d, chunk_info[current_chunk]->offset, size, partitiontype, subtype, 301 (chunk_info[current_chunk]->flags & CHUNK_ALIGN)); 302 variable_set2(DISK_PARTITIONED, "yes"); 303 record_chunks(d); 304 } 305 } 306 } 307 break; 308 309 case '\177': 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(); 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(); 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