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