disks.c revision 15242
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.39 1996/04/07 03:52:20 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(A_REVERSE); 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, 48, "W = Write Changes"); 125 mvprintw(20, 0, "The currently selected partition is displayed in "); 126 attrset(A_REVERSE); addstr("reverse"); attrset(A_NORMAL); addstr(" video."); 127 mvprintw(21, 0, "Use F1 or ? to get more help, arrow keys to move."); 128 move(0, 0); 129} 130 131static u_char * 132getBootMgr(char *dname) 133{ 134 extern u_char mbr[], bteasy17[]; 135 char str[80]; 136 char *cp; 137 int i = 0; 138 139 cp = variable_get(VAR_BOOTMGR); 140 if (!cp) { 141 /* Figure out what kind of MBR the user wants */ 142 sprintf(str, "Install Boot Manager for drive %s?", dname); 143 MenuMBRType.title = str; 144 i = dmenuOpenSimple(&MenuMBRType); 145 } 146 else { 147 if (!strncmp(cp, "boot", 4)) 148 BootMgr = 0; 149 else if (!strcmp(cp, "standard")) 150 BootMgr = 1; 151 else 152 BootMgr = 2; 153 } 154 if (cp || i) { 155 switch (BootMgr) { 156 case 0: 157 return bteasy17; 158 159 case 1: 160 return mbr; 161 162 case 2: 163 default: 164 break; 165 } 166 } 167 return NULL; 168} 169 170void 171diskPartition(Device *dev, Disk *d) 172{ 173 char *p; 174 int key = 0; 175 Boolean chunking; 176 char *msg = NULL; 177 u_char *mbrContents; 178 179 chunking = TRUE; 180 keypad(stdscr, TRUE); 181 182 clear(); 183 record_chunks(d); 184 while (chunking) { 185 print_chunks(d); 186 print_command_summary(); 187 if (msg) { 188 standout(); mvprintw(23, 0, msg); standend(); 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; 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, or append\n" 279 "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 Create_Chunk(d, chunk_info[current_chunk]->offset, size, freebsd, 3, 284 (chunk_info[current_chunk]->flags & CHUNK_ALIGN)); 285 variable_set2(DISK_PARTITIONED, "yes"); 286 record_chunks(d); 287 } 288 } 289 break; 290 291 case '\177': 292 case 'D': 293 if (chunk_info[current_chunk]->type == unused) 294 msg = "Partition is already unused!"; 295 else { 296 Delete_Chunk(d, chunk_info[current_chunk]); 297 variable_set2(DISK_PARTITIONED, "yes"); 298 record_chunks(d); 299 } 300 break; 301 302 case 'G': { 303 char *val, geometry[80]; 304 305 snprintf(geometry, 80, "%lu/%lu/%lu", d->bios_cyl, d->bios_hd, d->bios_sect); 306 val = msgGetInput(geometry, "Please specify the new geometry in cyl/hd/sect format.\n" 307 "Don't forget to use the two slash (/) separator characters!\n" 308 "It's not possible to parse the field without them."); 309 if (val) { 310 d->bios_cyl = strtol(val, &val, 0); 311 d->bios_hd = strtol(val + 1, &val, 0); 312 d->bios_sect = strtol(val + 1, 0, 0); 313 } 314 } 315 break; 316 317 case 'S': 318 /* Set Bootable */ 319 chunk_info[current_chunk]->flags |= CHUNK_ACTIVE; 320 break; 321 322 case 'U': 323 clear(); 324 if (msgYesNo("Are you SURE you want to Undo everything?")) 325 break; 326 d = Open_Disk(d->name); 327 if (!d) { 328 dialog_clear(); 329 msgConfirm("Can't reopen disk %s! Internal state is probably corrupted", d->name); 330 return; 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("Are you SURE you want to write this now? You do also\n" 340 "have the option of not modifying the disk until *all*\n" 341 "configuration information has been entered, at which\n" 342 "point you can do it all at once. If you're unsure, then\n" 343 "PLEASE CHOOSE NO at this dialog! This option is DANGEROUS\n" 344 "if you do not know EXACTLY what you are doing!")) { 345 variable_set2(DISK_PARTITIONED, "yes"); 346 clear(); 347 348 /* Don't trash the MBR if the first (and therefore only) chunk is marked for a truly dedicated 349 * disk (i.e., the disklabel starts at sector 0), even in cases where the user has requested 350 * booteasy or a "standard" MBR -- both would be fatal in this case. 351 */ 352 if ((d->chunks->part->flags & CHUNK_FORCE_ALL) != CHUNK_FORCE_ALL 353 && (mbrContents = getBootMgr(d->name)) != NULL) 354 Set_Boot_Mgr(d, mbrContents); 355 356 if (diskPartitionWrite(NULL) != DITEM_SUCCESS) { 357 dialog_clear(); 358 msgConfirm("Disk partition write returned an error status!"); 359 } 360 else { 361 msgConfirm("Wrote FDISK partition information out successfully."); 362 } 363 } 364 break; 365 366 case '|': 367 if (!msgYesNo("Are you SURE you want to go into Wizard mode?\n" 368 "No seat belts whatsoever are provided!")) { 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 } 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 msgConfirm(p); 404 free(p); 405 } 406 dialog_clear(); 407} 408 409static int 410partitionHook(dialogMenuItem *selected) 411{ 412 Device **devs = NULL; 413 WINDOW *w; 414 415 devs = deviceFind(selected->prompt, DEVICE_TYPE_DISK); 416 if (!devs) { 417 dialog_clear(); 418 msgConfirm("Unable to find disk %s!", selected->prompt); 419 return DITEM_FAILURE; 420 } 421 devs[0]->enabled = TRUE; 422 w = savescr(); 423 diskPartition(devs[0], (Disk *)devs[0]->private); 424 restorescr(w); 425 return DITEM_SUCCESS; 426} 427 428static int 429partitionCheck(dialogMenuItem *selected) 430{ 431 Device **devs = NULL; 432 433 devs = deviceFind(selected->prompt, DEVICE_TYPE_DISK); 434 if (!devs || devs[0]->enabled == FALSE) 435 return FALSE; 436 return TRUE; 437} 438 439int 440diskPartitionEditor(dialogMenuItem *self) 441{ 442 DMenu *menu; 443 Device **devs; 444 int i, cnt; 445 char *cp; 446 447 cp = variable_get(VAR_DISK); 448 devs = deviceFind(cp, DEVICE_TYPE_DISK); 449 cnt = deviceCount(devs); 450 if (!cnt) { 451 dialog_clear(); 452 msgConfirm("No disks found! Please verify that your disk controller is being\n" 453 "properly probed at boot time. See the Hardware Guide on the\n" 454 "Documentation menu for clues on diagnosing this type of problem."); 455 i = DITEM_FAILURE; 456 } 457 else if (cnt == 1) { 458 devs[0]->enabled = TRUE; 459 diskPartition(devs[0], (Disk *)devs[0]->private); 460 i = DITEM_SUCCESS; 461 } 462 else { 463 menu = deviceCreateMenu(&MenuDiskDevices, DEVICE_TYPE_DISK, partitionHook, partitionCheck); 464 if (!menu) { 465 dialog_clear(); 466 msgConfirm("No devices suitable for installation found!\n\n" 467 "Please verify that your disk controller (and attached drives)\n" 468 "were detected properly. This can be done by pressing the\n" 469 "[Scroll Lock] key and using the Arrow keys to move back to\n" 470 "the boot messages. Press [Scroll Lock] again to return."); 471 i = DITEM_FAILURE; 472 } 473 else { 474 WINDOW *w; 475 476 w = savescr(); 477 if (dmenuOpenSimple(menu)) 478 i = DITEM_SUCCESS; 479 else 480 i = DITEM_FAILURE; 481 restorescr(w); 482 free(menu); 483 } 484 } 485 return i; 486} 487 488int 489diskPartitionWrite(dialogMenuItem *self) 490{ 491 Device **devs; 492 char *cp; 493 int i; 494 495 if ((cp = variable_get(DISK_PARTITIONED)) && strcmp(cp, "yes")) 496 return DITEM_SUCCESS; 497 else if (!cp) { 498 dialog_clear(); 499 msgConfirm("You must partition the disk(s) before this option can be used."); 500 return DITEM_FAILURE; 501 } 502 503 devs = deviceFind(NULL, DEVICE_TYPE_DISK); 504 if (!devs) { 505 dialog_clear(); 506 msgConfirm("Unable to find any disks to write to??"); 507 return DITEM_FAILURE; 508 } 509 510 for (i = 0; devs[i]; i++) { 511 Chunk *c1; 512 Disk *d = (Disk *)devs[i]->private; 513 514 if (!devs[i]->enabled) 515 continue; 516 517 Set_Boot_Blocks(d, boot1, boot2); 518 msgNotify("Writing partition information to drive %s", d->name); 519 if (Write_Disk(d)) { 520 dialog_clear(); 521 msgConfirm("ERROR: Unable to write data to disk %s!", d->name); 522 return DITEM_FAILURE; 523 } 524 /* Now scan for bad blocks, if necessary */ 525 for (c1 = d->chunks->part; c1; c1 = c1->next) { 526 if (c1->flags & CHUNK_BAD144) { 527 int ret; 528 529 msgNotify("Running bad block scan on partition %s", c1->name); 530 ret = vsystem("bad144 -v /dev/r%s 1234", c1->name); 531 if (ret) { 532 dialog_clear(); 533 msgConfirm("Bad144 init on %s returned status of %d!", c1->name, ret); 534 } 535 ret = vsystem("bad144 -v -s /dev/r%s", c1->name); 536 if (ret) { 537 dialog_clear(); 538 msgConfirm("Bad144 scan on %s returned status of %d!", c1->name, ret); 539 } 540 } 541 } 542 } 543 /* Now it's not "yes", but "written" */ 544 variable_set2(DISK_PARTITIONED, "written"); 545 return DITEM_SUCCESS; 546} 547