disks.c revision 15091
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.38 1996/03/24 18:57:35 joerg 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 * 3. All advertising materials mentioning features or use of this software 23 * must display the following acknowledgement: 24 * This product includes software developed by Jordan Hubbard 25 * for the FreeBSD Project. 26 * 4. The name of Jordan Hubbard or the FreeBSD project may not be used to 27 * endorse or promote products derived from this software without specific 28 * prior written permission. 29 * 30 * THIS SOFTWARE IS PROVIDED BY JORDAN HUBBARD ``AS IS'' AND 31 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 32 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 33 * ARE DISCLAIMED. IN NO EVENT SHALL JORDAN HUBBARD OR HIS PETS BE LIABLE 34 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 35 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 36 * OR SERVICES; LOSS OF USE, DATA, LIFE OR PROFITS; OR BUSINESS INTERRUPTION) 37 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 38 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 39 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 40 * SUCH DAMAGE. 41 * 42 */ 43 44#include "sysinstall.h" 45#include <ctype.h> 46#include <sys/disklabel.h> 47 48/* Where we start displaying chunk information on the screen */ 49#define CHUNK_START_ROW 5 50 51/* Where we keep track of MBR chunks */ 52static struct chunk *chunk_info[16]; 53static int current_chunk; 54 55static void 56record_chunks(Disk *d) 57{ 58 struct chunk *c1; 59 int i = 0; 60 int last_free = 0; 61 if (!d->chunks) 62 msgFatal("No chunk list found for %s!", d->name); 63 current_chunk = 0; 64 for (c1 = d->chunks->part; c1; c1 = c1->next) { 65 if (c1->type == unused && c1->size > last_free) { 66 last_free = c1->size; 67 current_chunk = i; 68 } 69 chunk_info[i++] = c1; 70 } 71 chunk_info[i] = NULL; 72} 73 74static void 75print_chunks(Disk *d) 76{ 77 int row; 78 int i; 79 80 if ((!d->bios_cyl || d->bios_cyl > 65536) || (!d->bios_hd || d->bios_hd > 256) || (!d->bios_sect || d->bios_sect >= 64)) { 81 int sz; 82 83 dialog_clear(); 84 msgConfirm("WARNING: The current geometry for %s is incorrect. Using\n" 85 "a default geometry of 64 heads and 32 sectors. If this geometry\n" 86 "is incorrect or you are unsure as to whether or not it's correct,\n" 87 "please consult the Hardware Guide in the Documentation submenu\n" 88 "or use the (G)eometry command to change it now.", d->name); 89 d->bios_hd = 64; 90 d->bios_sect = 32; 91 sz = 0; 92 for (i = 0; chunk_info[i]; i++) 93 sz += chunk_info[i]->size; 94 if (sz) 95 d->bios_cyl = sz / ONE_MEG; 96 else 97 msgConfirm("Couldn't set geometry! You'll have to do it by hand."); 98 } 99 attrset(A_NORMAL); 100 mvaddstr(0, 0, "Disk name:\t"); 101 clrtobot(); 102 attrset(A_REVERSE); addstr(d->name); attrset(A_NORMAL); 103 attrset(A_REVERSE); mvaddstr(0, 55, "FDISK Partition Editor"); attrset(A_NORMAL); 104 mvprintw(1, 0, 105 "DISK Geometry:\t%lu cyls/%lu heads/%lu sectors", 106 d->bios_cyl, d->bios_hd, d->bios_sect); 107 mvprintw(3, 1, "%10s %10s %10s %8s %8s %8s %8s %8s", 108 "Offset", "Size", "End", "Name", "PType", "Desc", 109 "Subtype", "Flags"); 110 for (i = 0, row = CHUNK_START_ROW; chunk_info[i]; i++, row++) { 111 if (i == current_chunk) 112 attrset(A_REVERSE); 113 mvprintw(row, 2, "%10ld %10lu %10lu %8s %8d %8s %8d\t%-6s", 114 chunk_info[i]->offset, chunk_info[i]->size, 115 chunk_info[i]->end, chunk_info[i]->name, 116 chunk_info[i]->type, chunk_n[chunk_info[i]->type], 117 chunk_info[i]->subtype, ShowChunkFlags(chunk_info[i])); 118 if (i == current_chunk) 119 attrset(A_NORMAL); 120 } 121} 122 123static void 124print_command_summary() 125{ 126 mvprintw(14, 0, "The following commands are supported (in upper or lower case):"); 127 mvprintw(16, 0, "A = Use Entire Disk B = Bad Block Scan C = Create Partition"); 128 mvprintw(17, 0, "D = Delete Partition G = Set Drive Geometry S = Set Bootable"); 129 mvprintw(18, 0, "U = Undo All Changes Q = Finish"); 130 if (!RunningAsInit) 131 mvprintw(18, 48, "W = Write Changes"); 132 mvprintw(20, 0, "The currently selected partition is displayed in "); 133 attrset(A_REVERSE); addstr("reverse"); attrset(A_NORMAL); addstr(" video."); 134 mvprintw(21, 0, "Use F1 or ? to get more help, arrow keys to move."); 135 move(0, 0); 136} 137 138static u_char * 139getBootMgr(char *dname) 140{ 141 extern u_char mbr[], bteasy17[]; 142 char str[80]; 143 char *cp; 144 int i = 0; 145 146 cp = variable_get(VAR_BOOTMGR); 147 if (!cp) { 148 /* Figure out what kind of MBR the user wants */ 149 sprintf(str, "Install Boot Manager for drive %s?", dname); 150 MenuMBRType.title = str; 151 dialog_clear(); 152 i = dmenuOpenSimple(&MenuMBRType); 153 } 154 else { 155 if (!strncmp(cp, "boot", 4)) 156 BootMgr = 0; 157 else if (!strcmp(cp, "standard")) 158 BootMgr = 1; 159 else 160 BootMgr = 2; 161 } 162 if (cp || i) { 163 switch (BootMgr) { 164 case 0: 165 return bteasy17; 166 167 case 1: 168 return mbr; 169 170 case 2: 171 default: 172 break; 173 } 174 } 175 return NULL; 176} 177 178void 179diskPartition(Device *dev, Disk *d) 180{ 181 char *p; 182 int key = 0; 183 Boolean chunking; 184 char *msg = NULL; 185 u_char *mbrContents; 186 187 chunking = TRUE; 188 keypad(stdscr, TRUE); 189 190 clear(); 191 record_chunks(d); 192 while (chunking) { 193 print_chunks(d); 194 print_command_summary(); 195 if (msg) { 196 standout(); mvprintw(23, 0, msg); standend(); 197 beep(); 198 msg = NULL; 199 } 200 201 key = toupper(getch()); 202 switch (key) { 203 204 case '\014': /* ^L */ 205 clear(); 206 print_command_summary(); 207 continue; 208 209 case KEY_UP: 210 case '-': 211 if (current_chunk != 0) 212 --current_chunk; 213 break; 214 215 case KEY_DOWN: 216 case '+': 217 case '\r': 218 case '\n': 219 if (chunk_info[current_chunk + 1]) 220 ++current_chunk; 221 break; 222 223 case KEY_HOME: 224 current_chunk = 0; 225 break; 226 227 case KEY_END: 228 while (chunk_info[current_chunk + 1]) 229 ++current_chunk; 230 break; 231 232 case KEY_F(1): 233 case '?': 234 systemDisplayHelp("slice"); 235 break; 236 237 case 'A': { 238 int rv; 239 240 rv = msgYesNo("Do you want to do this with a true partition entry\n" 241 "so as to remain cooperative with any future possible\n" 242 "operating systems on the drive(s)?"); 243 if (rv) { 244 rv = !msgYesNo("This is dangerous in that it will make the drive totally\n" 245 "uncooperative with other potential operating systems on the\n" 246 "same disk. It will lead instead to a totally dedicated disk,\n" 247 "starting at the very first sector, bypassing all BIOS geometry\n" 248 "considerations. This precludes the existance of any boot\n" 249 "manager or other stuff in sector 0, since the BSD bootstrap\n" 250 "will live there.\n" 251 "You will run into serious trouble with ST-506 and ESDI drives\n" 252 "and possibly some IDE drives (e.g. drives running under the\n" 253 "control of sort of disk manager). SCSI drives are considerably\n" 254 "less at risk.\n\n" 255 "Do you insist on dedicating the entire disk this way?"); 256 } 257 All_FreeBSD(d, rv); 258 if (rv) 259 d->bios_hd = d->bios_sect = d->bios_cyl = 1; 260 variable_set2(DISK_PARTITIONED, "yes"); 261 record_chunks(d); 262 } 263 break; 264 265 case 'B': 266 if (chunk_info[current_chunk]->type != freebsd) 267 msg = "Can only scan for bad blocks in FreeBSD partition."; 268 else if (strncmp(d->name, "sd", 2) || 269 !msgYesNo("This typically makes sense only for ESDI, IDE or MFM drives.\n" 270 "Are you sure you want to do this on a SCSI disk?")) { 271 if (chunk_info[current_chunk]->flags & CHUNK_BAD144) 272 chunk_info[current_chunk]->flags &= ~CHUNK_BAD144; 273 else 274 chunk_info[current_chunk]->flags |= CHUNK_BAD144; 275 } 276 break; 277 278 case 'C': 279 if (chunk_info[current_chunk]->type != unused) 280 msg = "Partition in use, delete it first or move to an unused one."; 281 else { 282 char *val, tmp[20], *cp; 283 int size; 284 285 snprintf(tmp, 20, "%d", chunk_info[current_chunk]->size); 286 val = msgGetInput(tmp, "Please specify the size for new FreeBSD partition in blocks, or append\n" 287 "a trailing `M' for megabytes (e.g. 20M)."); 288 if (val && (size = strtol(val, &cp, 0)) > 0) { 289 if (*cp && toupper(*cp) == 'M') 290 size *= ONE_MEG; 291 Create_Chunk(d, chunk_info[current_chunk]->offset, size, freebsd, 3, 292 (chunk_info[current_chunk]->flags & CHUNK_ALIGN)); 293 variable_set2(DISK_PARTITIONED, "yes"); 294 record_chunks(d); 295 } 296 } 297 break; 298 299 case '\177': 300 case 'D': 301 if (chunk_info[current_chunk]->type == unused) 302 msg = "Partition is already unused!"; 303 else { 304 Delete_Chunk(d, chunk_info[current_chunk]); 305 variable_set2(DISK_PARTITIONED, "yes"); 306 record_chunks(d); 307 } 308 break; 309 310 case 'G': { 311 char *val, geometry[80]; 312 313 snprintf(geometry, 80, "%lu/%lu/%lu", d->bios_cyl, d->bios_hd, d->bios_sect); 314 val = msgGetInput(geometry, "Please specify the new geometry in cyl/hd/sect format.\n" 315 "Don't forget to use the two slash (/) separator characters!\n" 316 "It's not possible to parse the field without them."); 317 if (val) { 318 d->bios_cyl = strtol(val, &val, 0); 319 d->bios_hd = strtol(val + 1, &val, 0); 320 d->bios_sect = strtol(val + 1, 0, 0); 321 } 322 } 323 break; 324 325 case 'S': 326 /* Set Bootable */ 327 chunk_info[current_chunk]->flags |= CHUNK_ACTIVE; 328 break; 329 330 case 'U': 331 clear(); 332 if (msgYesNo("Are you SURE you want to Undo everything?")) 333 break; 334 d = Open_Disk(d->name); 335 if (!d) { 336 dialog_clear(); 337 msgConfirm("Can't reopen disk %s! Internal state is probably corrupted", d->name); 338 return; 339 } 340 Free_Disk(dev->private); 341 dev->private = d; 342 variable_unset(DISK_PARTITIONED); 343 record_chunks(d); 344 break; 345 346 case 'W': 347 if (!msgYesNo("Are you SURE you want to write this now? You do also\n" 348 "have the option of not modifying the disk until *all*\n" 349 "configuration information has been entered, at which\n" 350 "point you can do it all at once. If you're unsure, then\n" 351 "PLEASE CHOOSE NO at this dialog! This option is DANGEROUS\n" 352 "if you do not know EXACTLY what you are doing!")) { 353 variable_set2(DISK_PARTITIONED, "yes"); 354 clear(); 355 356 /* Don't trash the MBR if the first (and therefore only) chunk is marked for a truly dedicated 357 * disk (i.e., the disklabel starts at sector 0), even in cases where the user has requested 358 * booteasy or a "standard" MBR -- both would be fatal in this case. 359 */ 360 if ((d->chunks->part->flags & CHUNK_FORCE_ALL) != CHUNK_FORCE_ALL 361 && (mbrContents = getBootMgr(d->name)) != NULL) 362 Set_Boot_Mgr(d, mbrContents); 363 364 if (diskPartitionWrite(NULL) != RET_SUCCESS) { 365 dialog_clear(); 366 msgConfirm("Disk partition write returned an error status!"); 367 } 368 else { 369 msgConfirm("Wrote FDISK partition information out successfully."); 370 } 371 } 372 break; 373 374 case '|': 375 if (!msgYesNo("Are you SURE you want to go into Wizard mode?\n" 376 "No seat belts whatsoever are provided!")) { 377 dialog_clear(); 378 end_dialog(); 379 DialogActive = FALSE; 380 slice_wizard(d); 381 variable_set2(DISK_PARTITIONED, "yes"); 382 dialog_clear(); 383 DialogActive = TRUE; 384 record_chunks(d); 385 } 386 else 387 msg = "Wise choice!"; 388 break; 389 390 case 'Q': 391 chunking = FALSE; 392 clear(); 393 /* Don't trash the MBR if the first (and therefore only) chunk is marked for a truly dedicated 394 * disk (i.e., the disklabel starts at sector 0), even in cases where the user has requested 395 * booteasy or a "standard" MBR -- both would be fatal in this case. 396 */ 397 if ((d->chunks->part->flags & CHUNK_FORCE_ALL) != CHUNK_FORCE_ALL 398 && (mbrContents = getBootMgr(d->name)) != NULL) 399 Set_Boot_Mgr(d, mbrContents); 400 break; 401 402 default: 403 beep(); 404 msg = "Type F1 or ? for help"; 405 break; 406 } 407 } 408 p = CheckRules(d); 409 if (p) { 410 dialog_clear(); 411 msgConfirm(p); 412 free(p); 413 } 414 dialog_clear(); 415} 416 417static int 418partitionHook(char *str) 419{ 420 Device **devs = NULL; 421 422 /* Clip garbage off the ends */ 423 string_prune(str); 424 str = string_skipwhite(str); 425 /* Try and open all the disks */ 426 while (str) { 427 char *cp; 428 429 cp = index(str, '\n'); 430 if (cp) 431 *cp++ = 0; 432 if (!*str) { 433 beep(); 434 return 0; 435 } 436 devs = deviceFind(str, DEVICE_TYPE_DISK); 437 if (!devs) { 438 dialog_clear(); 439 msgConfirm("Unable to find disk %s!", str); 440 return 0; 441 } 442 devs[0]->enabled = TRUE; 443 diskPartition(devs[0], (Disk *)devs[0]->private); 444 str = cp; 445 } 446 return devs ? 1 : 0; 447} 448 449int 450diskPartitionEditor(dialogMenuItem *self) 451{ 452 DMenu *menu; 453 Device **devs; 454 int i, cnt; 455 char *cp, *str; 456 457 cp = variable_get(VAR_DISK); 458 str= variable_get(SYSTEM_STATE); 459 devs = deviceFind(cp, DEVICE_TYPE_DISK); 460 cnt = deviceCount(devs); 461 if (!cnt) { 462 dialog_clear(); 463 msgConfirm("No disks found! Please verify that your disk controller is being\n" 464 "properly probed at boot time. See the Hardware Guide on the\n" 465 "Documentation menu for clues on diagnosing this type of problem."); 466 i = RET_FAIL; 467 } 468 else if (cnt == 1) { 469 devs[0]->enabled = TRUE; 470 diskPartition(devs[0], (Disk *)devs[0]->private); 471 i = RET_SUCCESS; 472 variable_set2(DISK_SELECTED, "yes"); 473 } 474 else { 475 menu = deviceCreateMenu(&MenuDiskDevices, DEVICE_TYPE_DISK, partitionHook); 476 if (!menu) { 477 dialog_clear(); 478 msgConfirm("No devices suitable for installation found!\n\n" 479 "Please verify that your disk controller (and attached drives)\n" 480 "were detected properly. This can be done by pressing the\n" 481 "[Scroll Lock] key and using the Arrow keys to move back to\n" 482 "the boot messages. Press [Scroll Lock] again to return."); 483 i = RET_FAIL; 484 } 485 else { 486 if (!dmenuOpenSimple(menu)) 487 i = RET_FAIL; 488 else { 489 i = RET_SUCCESS; 490 variable_set2(DISK_SELECTED, "yes"); 491 } 492 free(menu); 493 } 494 } 495 return i; 496} 497 498int 499diskPartitionWrite(dialogMenuItem *self) 500{ 501 Device **devs; 502 char *cp; 503 int i; 504 505 if ((cp = variable_get(DISK_PARTITIONED)) && strcmp(cp, "yes")) 506 return RET_SUCCESS; 507 else if (!cp) { 508 dialog_clear(); 509 msgConfirm("You must partition the disk(s) before this option can be used."); 510 return RET_FAIL; 511 } 512 513 devs = deviceFind(NULL, DEVICE_TYPE_DISK); 514 if (!devs) { 515 dialog_clear(); 516 msgConfirm("Unable to find any disks to write to??"); 517 return RET_FAIL; 518 } 519 520 for (i = 0; devs[i]; i++) { 521 Chunk *c1; 522 Disk *d = (Disk *)devs[i]->private; 523 524 if (!devs[i]->enabled) 525 continue; 526 527 Set_Boot_Blocks(d, boot1, boot2); 528 msgNotify("Writing partition information to drive %s", d->name); 529 if (Write_Disk(d)) { 530 dialog_clear(); 531 msgConfirm("ERROR: Unable to write data to disk %s!", d->name); 532 return RET_FAIL; 533 } 534 /* Now scan for bad blocks, if necessary */ 535 for (c1 = d->chunks->part; c1; c1 = c1->next) { 536 if (c1->flags & CHUNK_BAD144) { 537 int ret; 538 539 msgNotify("Running bad block scan on partition %s", c1->name); 540 ret = vsystem("bad144 -v /dev/r%s 1234", c1->name); 541 if (ret) { 542 dialog_clear(); 543 msgConfirm("Bad144 init on %s returned status of %d!", c1->name, ret); 544 } 545 ret = vsystem("bad144 -v -s /dev/r%s", c1->name); 546 if (ret) { 547 dialog_clear(); 548 msgConfirm("Bad144 scan on %s returned status of %d!", c1->name, ret); 549 } 550 } 551 } 552 } 553 /* Now it's not "yes", but "written" */ 554 variable_set2(DISK_PARTITIONED, "written"); 555 return RET_SUCCESS; 556} 557