disks.c revision 15695
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.47 1996/04/30 05:23:45 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(item_selected_attr); 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; 275 276 snprintf(tmp, 20, "%d", chunk_info[current_chunk]->size); 277 val = msgGetInput(tmp, "Please specify the size for new FreeBSD partition in blocks, or append\n" 278 "a trailing `M' for megabytes (e.g. 20M)."); 279 if (val && (size = strtol(val, &cp, 0)) > 0) { 280 if (*cp && toupper(*cp) == 'M') 281 size *= ONE_MEG; 282 Create_Chunk(d, chunk_info[current_chunk]->offset, size, freebsd, 3, 283 (chunk_info[current_chunk]->flags & CHUNK_ALIGN)); 284 variable_set2(DISK_PARTITIONED, "yes"); 285 record_chunks(d); 286 } 287 } 288 break; 289 290 case '\177': 291 case 'D': 292 if (chunk_info[current_chunk]->type == unused) 293 msg = "Partition is already unused!"; 294 else { 295 Delete_Chunk(d, chunk_info[current_chunk]); 296 variable_set2(DISK_PARTITIONED, "yes"); 297 record_chunks(d); 298 } 299 break; 300 301 case 'G': { 302 char *val, geometry[80]; 303 304 snprintf(geometry, 80, "%lu/%lu/%lu", d->bios_cyl, d->bios_hd, d->bios_sect); 305 val = msgGetInput(geometry, "Please specify the new geometry in cyl/hd/sect format.\n" 306 "Don't forget to use the two slash (/) separator characters!\n" 307 "It's not possible to parse the field without them."); 308 if (val) { 309 d->bios_cyl = strtol(val, &val, 0); 310 d->bios_hd = strtol(val + 1, &val, 0); 311 d->bios_sect = strtol(val + 1, 0, 0); 312 } 313 } 314 break; 315 316 case 'S': 317 /* Set Bootable */ 318 chunk_info[current_chunk]->flags |= CHUNK_ACTIVE; 319 break; 320 321 case 'U': 322 clear(); 323 if (msgYesNo("Are you SURE you want to Undo everything?")) 324 break; 325 d = Open_Disk(d->name); 326 if (!d) { 327 msgConfirm("Can't reopen disk %s! Internal state is probably corrupted", d->name); 328 break; 329 } 330 Free_Disk(dev->private); 331 dev->private = d; 332 variable_unset(DISK_PARTITIONED); 333 record_chunks(d); 334 break; 335 336 case 'W': 337 if (!msgYesNo("WARNING: This should only be used for modifying an\n" 338 "EXISTING installation - DO NOT USE this option if you\n" 339 "are installing FreeBSD for the first time! This is not\n" 340 "an option for use during the standard install.\n\n" 341 "Are you absolutely sure you want to do this now?")) { 342 WINDOW *save = savescr(); 343 344 variable_set2(DISK_PARTITIONED, "yes"); 345 clear(); 346 347 /* Don't trash the MBR if the first (and therefore only) chunk is marked for a truly dedicated 348 * disk (i.e., the disklabel starts at sector 0), even in cases where the user has requested 349 * booteasy or a "standard" MBR -- both would be fatal in this case. 350 */ 351 if ((d->chunks->part->flags & CHUNK_FORCE_ALL) != CHUNK_FORCE_ALL 352 && (mbrContents = getBootMgr(d->name)) != NULL) 353 Set_Boot_Mgr(d, mbrContents); 354 355 if (DITEM_STATUS(diskPartitionWrite(NULL)) != DITEM_SUCCESS) 356 msgConfirm("Disk partition write returned an error status!"); 357 else 358 msgConfirm("Wrote FDISK partition information out successfully."); 359 restorescr(save); 360 } 361 break; 362 363 case '|': 364 if (!msgYesNo("Are you SURE you want to go into Wizard mode?\n" 365 "No seat belts whatsoever are provided!")) { 366 WINDOW *w; 367 368 w = savescr(); 369 dialog_clear(); 370 end_dialog(); 371 DialogActive = FALSE; 372 slice_wizard(d); 373 variable_set2(DISK_PARTITIONED, "yes"); 374 dialog_clear(); 375 DialogActive = TRUE; 376 record_chunks(d); 377 restorescr(w); 378 } 379 else 380 msg = "Wise choice!"; 381 break; 382 383 case 'Q': 384 chunking = FALSE; 385 clear(); 386 /* Don't trash the MBR if the first (and therefore only) chunk is marked for a truly dedicated 387 * disk (i.e., the disklabel starts at sector 0), even in cases where the user has requested 388 * booteasy or a "standard" MBR -- both would be fatal in this case. 389 */ 390 if ((d->chunks->part->flags & CHUNK_FORCE_ALL) != CHUNK_FORCE_ALL 391 && (mbrContents = getBootMgr(d->name)) != NULL) 392 Set_Boot_Mgr(d, mbrContents); 393 break; 394 395 default: 396 beep(); 397 msg = "Type F1 or ? for help"; 398 break; 399 } 400 } 401 p = CheckRules(d); 402 if (p) { 403 char buf[FILENAME_MAX]; 404 405 dialog_clear(); 406 use_helpline("Press F1 to read more about disk partitioning."); 407 use_helpfile(systemHelpFile("partition", buf)); 408 dialog_mesgbox("Disk partitioning warning:", p, -1, -1); 409 free(p); 410 } 411 restorescr(w); 412} 413 414static int 415partitionHook(dialogMenuItem *selected) 416{ 417 Device **devs = NULL; 418 419 devs = deviceFind(selected->prompt, DEVICE_TYPE_DISK); 420 if (!devs) { 421 msgConfirm("Unable to find disk %s!", selected->prompt); 422 return DITEM_FAILURE; 423 } 424 /* Toggle enabled status? */ 425 if (!devs[0]->enabled) { 426 devs[0]->enabled = TRUE; 427 diskPartition(devs[0], (Disk *)devs[0]->private); 428 } 429 else 430 devs[0]->enabled = FALSE; 431 return DITEM_SUCCESS | DITEM_REDRAW; 432} 433 434static int 435partitionCheck(dialogMenuItem *selected) 436{ 437 Device **devs = NULL; 438 439 devs = deviceFind(selected->prompt, DEVICE_TYPE_DISK); 440 if (!devs || devs[0]->enabled == FALSE) 441 return FALSE; 442 return TRUE; 443} 444 445int 446diskPartitionEditor(dialogMenuItem *self) 447{ 448 DMenu *menu; 449 Device **devs; 450 int i, cnt; 451 char *cp; 452 453 cp = variable_get(VAR_DISK); 454 devs = deviceFind(cp, DEVICE_TYPE_DISK); 455 cnt = deviceCount(devs); 456 if (!cnt) { 457 msgConfirm("No disks found! Please verify that your disk controller is being\n" 458 "properly probed at boot time. See the Hardware Guide on the\n" 459 "Documentation menu for clues on diagnosing this type of problem."); 460 i = DITEM_FAILURE; 461 } 462 else if (cnt == 1) { 463 devs[0]->enabled = TRUE; 464 diskPartition(devs[0], (Disk *)devs[0]->private); 465 i = DITEM_SUCCESS; 466 } 467 else { 468 menu = deviceCreateMenu(&MenuDiskDevices, DEVICE_TYPE_DISK, partitionHook, partitionCheck); 469 if (!menu) { 470 msgConfirm("No devices suitable for installation found!\n\n" 471 "Please verify that your disk controller (and attached drives)\n" 472 "were detected properly. This can be done by pressing the\n" 473 "[Scroll Lock] key and using the Arrow keys to move back to\n" 474 "the boot messages. Press [Scroll Lock] again to return."); 475 i = DITEM_FAILURE; 476 } 477 else { 478 i = dmenuOpenSimple(menu) ? DITEM_SUCCESS : DITEM_FAILURE; 479 free(menu); 480 } 481 i = i | DITEM_RESTORE | DITEM_RECREATE; 482 } 483 return i; 484} 485 486int 487diskPartitionWrite(dialogMenuItem *self) 488{ 489 Device **devs; 490 char *cp; 491 int i; 492 493 if ((cp = variable_get(DISK_PARTITIONED)) && strcmp(cp, "yes")) 494 return DITEM_SUCCESS; 495 else if (!cp) { 496 msgConfirm("You must partition the disk(s) before this option can be used."); 497 return DITEM_FAILURE; 498 } 499 500 devs = deviceFind(NULL, DEVICE_TYPE_DISK); 501 if (!devs) { 502 msgConfirm("Unable to find any disks to write to??"); 503 return DITEM_FAILURE; 504 } 505 if (isDebug()) 506 msgDebug("diskPartitionWrite: Examining %d devices\n", deviceCount(devs)); 507 508 for (i = 0; devs[i]; i++) { 509 Chunk *c1; 510 Disk *d = (Disk *)devs[i]->private; 511 512 if (!devs[i]->enabled) 513 continue; 514 515 Set_Boot_Blocks(d, boot1, boot2); 516 msgNotify("Writing partition information to drive %s", d->name); 517 if (!Fake && Write_Disk(d)) { 518 msgConfirm("ERROR: Unable to write data to disk %s!", d->name); 519 return DITEM_FAILURE; 520 } 521 /* Now scan for bad blocks, if necessary */ 522 for (c1 = d->chunks->part; c1; c1 = c1->next) { 523 if (c1->flags & CHUNK_BAD144) { 524 int ret; 525 526 msgNotify("Running bad block scan on partition %s", c1->name); 527 ret = vsystem("bad144 -v /dev/r%s 1234", c1->name); 528 if (ret) 529 msgConfirm("Bad144 init on %s returned status of %d!", c1->name, ret); 530 ret = vsystem("bad144 -v -s /dev/r%s", c1->name); 531 if (ret) 532 msgConfirm("Bad144 scan on %s returned status of %d!", c1->name, ret); 533 } 534 } 535 } 536 /* Now it's not "yes", but "written" */ 537 variable_set2(DISK_PARTITIONED, "written"); 538 return DITEM_SUCCESS; 539} 540