disks.c revision 15419
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.43 1996/04/28 00:37:28 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(A_REVERSE); 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, 48, "W = Write Changes"); 124 mvprintw(20, 0, "The currently selected partition is displayed in "); 125 attrset(A_REVERSE); addstr("reverse"); attrset(A_NORMAL); addstr(" video."); 126 mvprintw(21, 0, "Use F1 or ? to get more help, arrow keys to move."); 127 move(0, 0); 128} 129 130static u_char * 131getBootMgr(char *dname) 132{ 133 extern u_char mbr[], bteasy17[]; 134 char str[80]; 135 char *cp; 136 int i = 0; 137 138 cp = variable_get(VAR_BOOTMGR); 139 if (!cp) { 140 /* Figure out what kind of MBR the user wants */ 141 sprintf(str, "Install Boot Manager for drive %s?", dname); 142 MenuMBRType.title = str; 143 i = dmenuOpenSimple(&MenuMBRType); 144 } 145 else { 146 if (!strncmp(cp, "boot", 4)) 147 BootMgr = 0; 148 else if (!strcmp(cp, "standard")) 149 BootMgr = 1; 150 else 151 BootMgr = 2; 152 } 153 if (cp || i) { 154 switch (BootMgr) { 155 case 0: 156 return bteasy17; 157 158 case 1: 159 return mbr; 160 161 case 2: 162 default: 163 break; 164 } 165 } 166 return NULL; 167} 168 169void 170diskPartition(Device *dev, Disk *d) 171{ 172 char *p; 173 int key = 0; 174 Boolean chunking; 175 char *msg = NULL; 176 u_char *mbrContents; 177 WINDOW *w; 178 179 chunking = TRUE; 180 keypad(stdscr, TRUE); 181 182 w = savescr(); 183 clear(); 184 record_chunks(d); 185 while (chunking) { 186 print_chunks(d); 187 print_command_summary(); 188 if (msg) { 189 standout(); mvprintw(23, 0, msg); standend(); 190 beep(); 191 msg = NULL; 192 } 193 194 key = toupper(getch()); 195 switch (key) { 196 197 case '\014': /* ^L */ 198 clear(); 199 print_command_summary(); 200 continue; 201 202 case KEY_UP: 203 case '-': 204 if (current_chunk != 0) 205 --current_chunk; 206 break; 207 208 case KEY_DOWN: 209 case '+': 210 case '\r': 211 case '\n': 212 if (chunk_info[current_chunk + 1]) 213 ++current_chunk; 214 break; 215 216 case KEY_HOME: 217 current_chunk = 0; 218 break; 219 220 case KEY_END: 221 while (chunk_info[current_chunk + 1]) 222 ++current_chunk; 223 break; 224 225 case KEY_F(1): 226 case '?': 227 systemDisplayHelp("slice"); 228 break; 229 230 case 'A': { 231 int rv; 232 233 rv = msgYesNo("Do you want to do this with a true partition entry\n" 234 "so as to remain cooperative with any future possible\n" 235 "operating systems on the drive(s)?"); 236 if (rv) { 237 rv = !msgYesNo("This is dangerous in that it will make the drive totally\n" 238 "uncooperative with other potential operating systems on the\n" 239 "same disk. It will lead instead to a totally dedicated disk,\n" 240 "starting at the very first sector, bypassing all BIOS geometry\n" 241 "considerations. This precludes the existance of any boot\n" 242 "manager or other stuff in sector 0, since the BSD bootstrap\n" 243 "will live there.\n" 244 "You will run into serious trouble with ST-506 and ESDI drives\n" 245 "and possibly some IDE drives (e.g. drives running under the\n" 246 "control of sort of disk manager). SCSI drives are considerably\n" 247 "less at risk.\n\n" 248 "Do you insist on dedicating the entire disk this way?"); 249 } 250 All_FreeBSD(d, rv); 251 if (rv) 252 d->bios_hd = d->bios_sect = d->bios_cyl = 1; 253 variable_set2(DISK_PARTITIONED, "yes"); 254 record_chunks(d); 255 } 256 break; 257 258 case 'B': 259 if (chunk_info[current_chunk]->type != freebsd) 260 msg = "Can only scan for bad blocks in FreeBSD partition."; 261 else if (strncmp(d->name, "sd", 2) || 262 !msgYesNo("This typically makes sense only for ESDI, IDE or MFM drives.\n" 263 "Are you sure you want to do this on a SCSI disk?")) { 264 if (chunk_info[current_chunk]->flags & CHUNK_BAD144) 265 chunk_info[current_chunk]->flags &= ~CHUNK_BAD144; 266 else 267 chunk_info[current_chunk]->flags |= CHUNK_BAD144; 268 } 269 break; 270 271 case 'C': 272 if (chunk_info[current_chunk]->type != unused) 273 msg = "Partition in use, delete it first or move to an unused one."; 274 else { 275 char *val, tmp[20], *cp; 276 int size; 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, or append\n" 280 "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 Create_Chunk(d, chunk_info[current_chunk]->offset, size, freebsd, 3, 285 (chunk_info[current_chunk]->flags & CHUNK_ALIGN)); 286 variable_set2(DISK_PARTITIONED, "yes"); 287 record_chunks(d); 288 } 289 } 290 break; 291 292 case '\177': 293 case 'D': 294 if (chunk_info[current_chunk]->type == unused) 295 msg = "Partition is already unused!"; 296 else { 297 Delete_Chunk(d, chunk_info[current_chunk]); 298 variable_set2(DISK_PARTITIONED, "yes"); 299 record_chunks(d); 300 } 301 break; 302 303 case 'G': { 304 char *val, geometry[80]; 305 306 snprintf(geometry, 80, "%lu/%lu/%lu", d->bios_cyl, d->bios_hd, d->bios_sect); 307 val = msgGetInput(geometry, "Please specify the new geometry in cyl/hd/sect format.\n" 308 "Don't forget to use the two slash (/) separator characters!\n" 309 "It's not possible to parse the field without them."); 310 if (val) { 311 d->bios_cyl = strtol(val, &val, 0); 312 d->bios_hd = strtol(val + 1, &val, 0); 313 d->bios_sect = strtol(val + 1, 0, 0); 314 } 315 } 316 break; 317 318 case 'S': 319 /* Set Bootable */ 320 chunk_info[current_chunk]->flags |= CHUNK_ACTIVE; 321 break; 322 323 case 'U': 324 clear(); 325 if (msgYesNo("Are you SURE you want to Undo everything?")) 326 break; 327 d = Open_Disk(d->name); 328 if (!d) { 329 msgConfirm("Can't reopen disk %s! Internal state is probably corrupted", d->name); 330 break; 331 } 332 Free_Disk(dev->private); 333 dev->private = d; 334 variable_unset(DISK_PARTITIONED); 335 record_chunks(d); 336 break; 337 338 case 'W': 339 if (!msgYesNo("WARNING: This should only be used for modifying an\n" 340 "EXISTING installation - DO NOT USE this option if you\n" 341 "are installing FreeBSD for the first time! This is not\n" 342 "an option for use during the standard install.\n\n" 343 "Are you absolutely sure you want to do this now?")) { 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 } 360 break; 361 362 case '|': 363 if (!msgYesNo("Are you SURE you want to go into Wizard mode?\n" 364 "No seat belts whatsoever are provided!")) { 365 WINDOW *w; 366 367 w = savescr(); 368 dialog_clear(); 369 end_dialog(); 370 DialogActive = FALSE; 371 slice_wizard(d); 372 variable_set2(DISK_PARTITIONED, "yes"); 373 dialog_clear(); 374 DialogActive = TRUE; 375 record_chunks(d); 376 restorescr(w); 377 } 378 else 379 msg = "Wise choice!"; 380 break; 381 382 case 'Q': 383 chunking = FALSE; 384 clear(); 385 /* Don't trash the MBR if the first (and therefore only) chunk is marked for a truly dedicated 386 * disk (i.e., the disklabel starts at sector 0), even in cases where the user has requested 387 * booteasy or a "standard" MBR -- both would be fatal in this case. 388 */ 389 if ((d->chunks->part->flags & CHUNK_FORCE_ALL) != CHUNK_FORCE_ALL 390 && (mbrContents = getBootMgr(d->name)) != NULL) 391 Set_Boot_Mgr(d, mbrContents); 392 break; 393 394 default: 395 beep(); 396 msg = "Type F1 or ? for help"; 397 break; 398 } 399 } 400 p = CheckRules(d); 401 if (p) { 402 dialog_clear(); 403 dialog_mesgbox("Disk partitioning warning:", p, -1, -1); 404 free(p); 405 } 406 restorescr(w); 407} 408 409static int 410partitionHook(dialogMenuItem *selected) 411{ 412 Device **devs = NULL; 413 414 devs = deviceFind(selected->prompt, DEVICE_TYPE_DISK); 415 if (!devs) { 416 msgConfirm("Unable to find disk %s!", selected->prompt); 417 return DITEM_FAILURE; 418 } 419 devs[0]->enabled = TRUE; 420 diskPartition(devs[0], (Disk *)devs[0]->private); 421 return DITEM_SUCCESS; 422} 423 424static int 425partitionCheck(dialogMenuItem *selected) 426{ 427 Device **devs = NULL; 428 429 devs = deviceFind(selected->prompt, DEVICE_TYPE_DISK); 430 if (!devs || devs[0]->enabled == FALSE) 431 return FALSE; 432 return TRUE; 433} 434 435int 436diskPartitionEditor(dialogMenuItem *self) 437{ 438 DMenu *menu; 439 Device **devs; 440 int i, cnt; 441 char *cp; 442 443 cp = variable_get(VAR_DISK); 444 devs = deviceFind(cp, DEVICE_TYPE_DISK); 445 cnt = deviceCount(devs); 446 if (!cnt) { 447 msgConfirm("No disks found! Please verify that your disk controller is being\n" 448 "properly probed at boot time. See the Hardware Guide on the\n" 449 "Documentation menu for clues on diagnosing this type of problem."); 450 i = DITEM_FAILURE; 451 } 452 else if (cnt == 1) { 453 devs[0]->enabled = TRUE; 454 diskPartition(devs[0], (Disk *)devs[0]->private); 455 i = DITEM_SUCCESS; 456 } 457 else { 458 menu = deviceCreateMenu(&MenuDiskDevices, DEVICE_TYPE_DISK, partitionHook, partitionCheck); 459 if (!menu) { 460 msgConfirm("No devices suitable for installation found!\n\n" 461 "Please verify that your disk controller (and attached drives)\n" 462 "were detected properly. This can be done by pressing the\n" 463 "[Scroll Lock] key and using the Arrow keys to move back to\n" 464 "the boot messages. Press [Scroll Lock] again to return."); 465 i = DITEM_FAILURE; 466 } 467 else { 468 i = dmenuOpenSimple(menu) ? DITEM_SUCCESS : DITEM_FAILURE; 469 free(menu); 470 } 471 i = i | DITEM_RESTORE | DITEM_RECREATE; 472 } 473 return i; 474} 475 476int 477diskPartitionWrite(dialogMenuItem *self) 478{ 479 Device **devs; 480 char *cp; 481 int i; 482 483 if ((cp = variable_get(DISK_PARTITIONED)) && strcmp(cp, "yes")) 484 return DITEM_SUCCESS; 485 else if (!cp) { 486 msgConfirm("You must partition the disk(s) before this option can be used."); 487 return DITEM_FAILURE; 488 } 489 490 devs = deviceFind(NULL, DEVICE_TYPE_DISK); 491 if (!devs) { 492 msgConfirm("Unable to find any disks to write to??"); 493 return DITEM_FAILURE; 494 } 495 496 for (i = 0; devs[i]; i++) { 497 Chunk *c1; 498 Disk *d = (Disk *)devs[i]->private; 499 500 if (!devs[i]->enabled) 501 continue; 502 503 Set_Boot_Blocks(d, boot1, boot2); 504 msgNotify("Writing partition information to drive %s", d->name); 505 if (Write_Disk(d)) { 506 msgConfirm("ERROR: Unable to write data to disk %s!", d->name); 507 return DITEM_FAILURE; 508 } 509 /* Now scan for bad blocks, if necessary */ 510 for (c1 = d->chunks->part; c1; c1 = c1->next) { 511 if (c1->flags & CHUNK_BAD144) { 512 int ret; 513 514 msgNotify("Running bad block scan on partition %s", c1->name); 515 ret = vsystem("bad144 -v /dev/r%s 1234", c1->name); 516 if (ret) 517 msgConfirm("Bad144 init on %s returned status of %d!", c1->name, ret); 518 ret = vsystem("bad144 -v -s /dev/r%s", c1->name); 519 if (ret) 520 msgConfirm("Bad144 scan on %s returned status of %d!", c1->name, ret); 521 } 522 } 523 } 524 /* Now it's not "yes", but "written" */ 525 variable_set2(DISK_PARTITIONED, "written"); 526 return DITEM_SUCCESS; 527} 528