disks.c revision 8360
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.12 1995/05/08 10:20:49 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 * 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/* 49 * I make some pretty gross assumptions about having a max of 50 chunks 50 * total - 8 slices and 42 partitions. I can't easily display many more 51 * than that on the screen at once! 52 * 53 * For 2.1 I'll revisit this and try to make it more dynamic, but since 54 * this will catch 99.99% of all possible cases, I'm not too worried. 55 */ 56 57#define MAX_CHUNKS 50 58 59/* Where to start printing the freebsd slices */ 60#define CHUNK_SLICE_START_ROW 2 61#define CHUNK_PART_START_ROW 10 62 63/* The smallest filesystem we're willing to create */ 64#define FS_MIN_SIZE 2048 65 66#define MSG_NOT_APPLICABLE "That option is not applicable here" 67 68static struct { 69 struct disk *d; 70 struct chunk *c; 71 PartType type; 72} fbsd_chunk_info[MAX_CHUNKS + 1]; 73static int current_chunk; 74 75 76/* If the given disk has a root partition on it, return TRUE */ 77static Boolean 78contains_root_partition(struct disk *d) 79{ 80 struct chunk *c1; 81 82 if (!d->chunks) 83 msgFatal("Disk %s has no chunks!", d->name); 84 c1 = d->chunks->part; 85 while (c1) { 86 if (c1->type == freebsd) { 87 struct chunk *c2 = c1->part; 88 89 while (c2) { 90 if (c2->flags & CHUNK_IS_ROOT) 91 return TRUE; 92 c2 = c2->next; 93 } 94 } 95 c1 = c1->next; 96 } 97 return FALSE; 98} 99 100/* Locate a chunk by position on a specific disk somewhere */ 101static struct chunk * 102find_chunk_by_loc(struct disk *d, u_long offset, u_long size) 103{ 104 struct chunk *c1, *c2; 105 106 if (!d->chunks) 107 msgFatal("No chunk list for %s!", d->name); 108 109 for (c1 = d->chunks->part; c1; c1 = c1->next) { 110 if (c1->type == freebsd) { 111 if (c1->offset == offset && c1->size == size) 112 return c1; 113 for (c2 = c1->part; c2; c2 = c2->next) { 114 if (c2->type == part && c2->offset == offset && 115 c2->size == size) 116 return c2; 117 } 118 } 119 } 120 return NULL; 121} 122 123static Boolean 124check_conflict(char *name) 125{ 126 int i; 127 128 for (i = 0; fbsd_chunk_info[i].d; i++) 129 if (fbsd_chunk_info[i].type == PART_FILESYSTEM && 130 fbsd_chunk_info[i].c->private && 131 !strcmp(((PartInfo *)fbsd_chunk_info[i].c->private)->mountpoint, 132 name)) 133 return TRUE; 134 return FALSE; 135} 136 137static int 138space_free(struct chunk *c) 139{ 140 struct chunk *c1 = c->part; 141 int sz = c->size; 142 143 while (c1) { 144 if (c1->type != unused) 145 sz -= c1->size; 146 c1 = c1->next; 147 } 148 if (sz < 0) 149 msgFatal("Partitions are larger than actual chunk??"); 150 return sz; 151} 152 153static void 154record_fbsd_chunks() 155{ 156 int i, j, p; 157 struct chunk *c1, *c2; 158 159 j = p = 0; 160 for (i = 0; Disks[i]; i++) { 161 if (!Disks[i]->chunks) 162 msgFatal("No chunk list found for %s!", Disks[i]->name); 163 164 /* Put the freebsd chunks first */ 165 for (c1 = Disks[i]->chunks->part; c1; c1 = c1->next) { 166 if (c1->type == freebsd) { 167 fbsd_chunk_info[j].type = PART_SLICE; 168 fbsd_chunk_info[j].d = Disks[i]; 169 fbsd_chunk_info[j].c = c1; 170 ++j; 171 } 172 } 173 } 174 for (i = 0; Disks[i]; i++) { 175 /* Then buzz through and pick up the partitions */ 176 for (c1 = Disks[i]->chunks->part; c1; c1 = c1->next) { 177 if (c1->type == freebsd) { 178 for (c2 = c1->part; c2; c2 = c2->next) { 179 if (c2->type == part) { 180 if (c2->subtype == FS_SWAP) 181 fbsd_chunk_info[j].type = PART_SWAP; 182 else 183 fbsd_chunk_info[j].type = PART_FILESYSTEM; 184 fbsd_chunk_info[j].d = Disks[i]; 185 fbsd_chunk_info[j].c = c2; 186 ++j; 187 } 188 } 189 } 190 } 191 } 192 fbsd_chunk_info[j].d = NULL; 193 fbsd_chunk_info[j].c = NULL; 194 if (current_chunk >= j) 195 current_chunk = j ? j - 1 : 0; 196} 197 198static PartInfo * 199new_part(char *mpoint, Boolean newfs) 200{ 201 PartInfo *ret; 202 203 ret = (PartInfo *)safe_malloc(sizeof(PartInfo)); 204 strncpy(ret->mountpoint, mpoint, FILENAME_MAX); 205 strcpy(ret->newfs_cmd, "newfs"); 206 ret->newfs = newfs; 207 return ret; 208} 209 210PartInfo * 211get_mountpoint(struct chunk *c) 212{ 213 char *val; 214 PartInfo *tmp; 215 216 val = msgGetInput(c && c->private ? 217 ((PartInfo *)c->private)->mountpoint : NULL, 218 "Please specify a mount point for the partition"); 219 if (val) { 220 if (check_conflict(val)) { 221 msgConfirm("You already have a mountpoint for %s assigned!", val); 222 return NULL; 223 } 224 else if (!strcmp(val, "/")) { 225 if (c && c->flags & CHUNK_PAST_1024) { 226msgConfirm("This region cannot be used for your root partition as\nit is past the 1024'th cylinder mark and the system would not be\nable to boot from it. Please pick another location for your\nroot partition and try again!"); 227 return NULL; 228 } 229 else if (c) 230 c->flags |= CHUNK_IS_ROOT; 231 } 232 else if (c) 233 c->flags &= ~CHUNK_IS_ROOT; 234 safe_free(c ? c->private : NULL); 235 tmp = new_part(val, TRUE); 236 if (c) { 237 c->private = tmp; 238 c->private_free = safe_free; 239 } 240 return tmp; 241 } 242 return NULL; 243} 244 245static PartType 246get_partition_type(void) 247{ 248 char selection[20]; 249 static unsigned char *fs_types[] = { 250 "FS", 251 "A file system", 252 "Swap", 253 "A swap partition.", 254 }; 255 256 if (!dialog_menu("Please choose a partition type", 257 "If you want to use this partition for swap space, select Swap.\nIf you want to put a filesystem on it, choose FS.", -1, -1, 2, 2, fs_types, selection, NULL, NULL)) { 258 if (!strcmp(selection, "FS")) 259 return PART_FILESYSTEM; 260 else if (!strcmp(selection, "Swap")) 261 return PART_SWAP; 262 } 263 return PART_NONE; 264} 265 266static void 267getNewfsCmd(PartInfo *p) 268{ 269 char *val; 270 271 val = msgGetInput(p->newfs_cmd, 272 "Please enter the newfs command and options you'd like to use in\ncreating this file system."); 273 if (val) 274 strncpy(p->newfs_cmd, val, NEWFS_CMD_MAX); 275} 276 277 278#define MAX_MOUNT_NAME 12 279 280#define PART_PART_COL 0 281#define PART_MOUNT_COL 8 282#define PART_SIZE_COL (PART_MOUNT_COL + MAX_MOUNT_NAME + 3) 283#define PART_NEWFS_COL (PART_SIZE_COL + 7) 284#define PART_OFF 38 285 286/* How many mounted partitions to display in column before going to next */ 287#define CHUNK_COLUMN_MAX 6 288 289static void 290print_fbsd_chunks(void) 291{ 292 int i, j, srow, prow, pcol; 293 int sz; 294 295 attrset(A_REVERSE); 296 mvaddstr(0, 25, "FreeBSD Partition Editor"); 297 attrset(A_NORMAL); 298 299 for (i = 0; i < 2; i++) { 300 attrset(A_UNDERLINE); 301 mvaddstr(CHUNK_PART_START_ROW - 1, PART_PART_COL + (i * PART_OFF), 302 "Part"); 303 attrset(A_NORMAL); 304 305 attrset(A_UNDERLINE); 306 mvaddstr(CHUNK_PART_START_ROW - 1, PART_MOUNT_COL + (i * PART_OFF), 307 "Mount"); 308 attrset(A_NORMAL); 309 310 attrset(A_UNDERLINE); 311 mvaddstr(CHUNK_PART_START_ROW - 1, PART_SIZE_COL + (i * PART_OFF) + 2, 312 "Size"); 313 attrset(A_NORMAL); 314 315 attrset(A_UNDERLINE); 316 mvaddstr(CHUNK_PART_START_ROW - 1, PART_NEWFS_COL + (i * PART_OFF), 317 "Newfs"); 318 attrset(A_NORMAL); 319 } 320 321 srow = CHUNK_SLICE_START_ROW; 322 prow = CHUNK_PART_START_ROW; 323 pcol = 0; 324 325 for (i = 0; fbsd_chunk_info[i].d; i++) { 326 if (i == current_chunk) 327 attrset(A_REVERSE); 328 /* Is it a slice entry displayed at the top? */ 329 if (fbsd_chunk_info[i].type == PART_SLICE) { 330 sz = space_free(fbsd_chunk_info[i].c); 331 mvprintw(srow++, 0, 332 "Disk: %s\tPartition name: %s\tFree: %d blocks (%dMB)", 333 fbsd_chunk_info[i].d->name, 334 fbsd_chunk_info[i].c->name, sz, (sz / 2048)); 335 } 336 /* Otherwise it's a swap or filesystem entry, at the bottom */ 337 else { 338 char onestr[PART_OFF], num[10], *mountpoint, *newfs; 339 340 memset(onestr, ' ', PART_OFF - 1); 341 onestr[PART_OFF - 1] = '\0'; 342 /* Go for two columns */ 343 if (prow == (CHUNK_PART_START_ROW + CHUNK_COLUMN_MAX)) { 344 pcol = PART_OFF; 345 prow = CHUNK_PART_START_ROW; 346 } 347 memcpy(onestr + PART_PART_COL, fbsd_chunk_info[i].c->name, 348 strlen(fbsd_chunk_info[i].c->name)); 349 if (fbsd_chunk_info[i].type == PART_FILESYSTEM) { 350 if (fbsd_chunk_info[i].c->private) { 351 mountpoint = ((PartInfo *)fbsd_chunk_info[i].c->private)->mountpoint; 352 newfs = ((PartInfo *)fbsd_chunk_info[i].c->private)->newfs ? "Y" : "N"; 353 } 354 else { 355 fbsd_chunk_info[i].c->private = new_part("", FALSE); 356 fbsd_chunk_info[i].c->private_free = safe_free; 357 mountpoint = " "; 358 newfs = "N"; 359 } 360 } 361 else { 362 mountpoint = "swap"; 363 newfs = " "; 364 } 365 for (j = 0; j < MAX_MOUNT_NAME && mountpoint[j]; j++) 366 onestr[PART_MOUNT_COL + j] = mountpoint[j]; 367 sprintf(num, "%4ldMB", fbsd_chunk_info[i].c->size ? 368 fbsd_chunk_info[i].c->size / 2048 : 0); 369 memcpy(onestr + PART_SIZE_COL, num, strlen(num)); 370 memcpy(onestr + PART_NEWFS_COL, newfs, strlen(newfs)); 371 mvaddstr(prow, pcol, onestr); 372 ++prow; 373 } 374 if (i == current_chunk) 375 attrset(A_NORMAL); 376 } 377} 378 379static void 380print_command_summary() 381{ 382 mvprintw(17, 0, 383 "The following commands are valid here (upper or lower case):"); 384 mvprintw(19, 0, "C = Create Partition D = Delete Partition M = Mount Partition"); 385 mvprintw(20, 0, "N = Newfs Options T = Toggle Newfs ESC = Finish Partitioning"); 386 mvprintw(21, 0, "The default target will be displayed in "); 387 388 attrset(A_REVERSE); 389 addstr("reverse video"); 390 attrset(A_NORMAL); 391 mvprintw(22, 0, "Use F1 or ? to get more help"); 392 move(0, 0); 393} 394 395void 396partition_disks(void) 397{ 398 int sz, key = 0; 399 Boolean partitioning; 400 char *msg = NULL; 401 PartInfo *p; 402 PartType type; 403 404 dialog_clear(); 405 partitioning = TRUE; 406 keypad(stdscr, TRUE); 407 record_fbsd_chunks(); 408 409 while (partitioning) { 410 clear(); 411 print_fbsd_chunks(); 412 print_command_summary(); 413 if (msg) { 414 attrset(A_REVERSE); mvprintw(23, 0, msg); attrset(A_NORMAL); 415 beep(); 416 msg = NULL; 417 } 418 refresh(); 419 key = toupper(getch()); 420 switch (key) { 421 422 case KEY_UP: 423 case '-': 424 if (current_chunk != 0) 425 --current_chunk; 426 break; 427 428 case KEY_DOWN: 429 case '+': 430 case '\r': 431 case '\n': 432 if (fbsd_chunk_info[current_chunk + 1].d) 433 ++current_chunk; 434 break; 435 436 case KEY_HOME: 437 current_chunk = 0; 438 break; 439 440 case KEY_END: 441 while (fbsd_chunk_info[current_chunk + 1].d) 442 ++current_chunk; 443 break; 444 445 case KEY_F(1): 446 case '?': 447 systemDisplayFile("partitioning.hlp"); 448 break; 449 450 case 'C': 451 if (fbsd_chunk_info[current_chunk].type != PART_SLICE) { 452 msg = "You can only do this in a master partition (see top of screen)"; 453 break; 454 } 455 sz = space_free(fbsd_chunk_info[current_chunk].c); 456 if (sz <= FS_MIN_SIZE) 457 msg = "Not enough space to create additional FreeBSD partition"; 458 else { 459 char *val, *cp, tmp[20]; 460 int size; 461 462 snprintf(tmp, 20, "%d", sz); 463 val = msgGetInput(tmp, "Please specify the size for new FreeBSD partition in blocks, or append\na trailing `M' for megabytes (e.g. 20M)."); 464 if (val && (size = strtol(val, &cp, 0)) > 0) { 465 struct chunk *tmp; 466 u_long flags = 0; 467 468 if (*cp && toupper(*cp) == 'M') 469 size *= 2048; 470 471 type = get_partition_type(); 472 if (type == PART_NONE) 473 break; 474 else if (type == PART_FILESYSTEM) { 475 if ((p = get_mountpoint(NULL)) == NULL) 476 break; 477 else if (!strcmp(p->mountpoint, "/")) 478 flags |= CHUNK_IS_ROOT; 479 else 480 flags &= ~CHUNK_IS_ROOT; 481 } 482 else 483 p = NULL; 484 Create_Chunk(fbsd_chunk_info[current_chunk].d, 485 fbsd_chunk_info[current_chunk].c->offset + 486 sz - size, 487 size, 488 part, 489 (type == PART_SWAP) ? FS_SWAP : FS_BSDFFS, 490 flags); 491 tmp = find_chunk_by_loc(fbsd_chunk_info[current_chunk].d, 492 fbsd_chunk_info[current_chunk].c->offset + sz - size, size); 493 if (!tmp) 494 msgConfirm("Unable to create the partition. Too big?"); 495 else { 496 tmp->private = p; 497 tmp->private_free = safe_free; 498 record_fbsd_chunks(); 499 } 500 } 501 } 502 break; 503 504 case 'D': /* delete */ 505 if (fbsd_chunk_info[current_chunk].type == PART_SLICE) { 506 msg = MSG_NOT_APPLICABLE; 507 break; 508 } 509 Delete_Chunk(fbsd_chunk_info[current_chunk].d, 510 fbsd_chunk_info[current_chunk].c); 511 record_fbsd_chunks(); 512 break; 513 514 case 'M': /* mount */ 515 switch(fbsd_chunk_info[current_chunk].type) { 516 case PART_SLICE: 517 msg = MSG_NOT_APPLICABLE; 518 break; 519 520 case PART_SWAP: 521 msg = "You don't need to specify a mountpoint for a swap partition."; 522 break; 523 524 case PART_FILESYSTEM: 525 p = get_mountpoint(fbsd_chunk_info[current_chunk].c); 526 if (p) { 527 p->newfs = FALSE; 528 record_fbsd_chunks(); 529 } 530 break; 531 532 default: 533 msgFatal("Bogus partition under cursor???"); 534 break; 535 } 536 break; 537 538 case 'N': /* Set newfs options */ 539 if (fbsd_chunk_info[current_chunk].c->private && 540 ((PartInfo *)fbsd_chunk_info[current_chunk].c->private)->newfs) 541 getNewfsCmd(fbsd_chunk_info[current_chunk].c->private); 542 else 543 msg = MSG_NOT_APPLICABLE; 544 break; 545 546 case 'T': /* Toggle newfs state */ 547 if (fbsd_chunk_info[current_chunk].c->private) 548 ((PartInfo *)fbsd_chunk_info[current_chunk].c->private)->newfs = !((PartInfo *)fbsd_chunk_info[current_chunk].c->private)->newfs; 549 else 550 msg = MSG_NOT_APPLICABLE; 551 break; 552 553 case 'W': 554 if (!msgYesNo("Are you sure you want to go into Wizard mode?\n\nThis is an entirely undocumented feature which you are not\nexpected to understand!")) { 555 int i; 556 557 clear(); 558 dialog_clear(); 559 end_dialog(); 560 DialogActive = FALSE; 561 for (i = 0; Disks[i]; i++) 562 slice_wizard(Disks[i]); 563 clear(); 564 dialog_clear(); 565 DialogActive = TRUE; 566 record_fbsd_chunks(); 567 } 568 else 569 msg = "A most prudent choice!"; 570 break; 571 572 case 27: /* ESC */ 573 partitioning = FALSE; 574 break; 575 576 default: 577 beep(); 578 msg = "Type F1 or ? for help"; 579 break; 580 } 581 } 582} 583 584int 585write_disks(void) 586{ 587 int i; 588 extern u_char boot1[], boot2[]; 589 extern u_char mbr[], bteasy17[]; 590 591 dialog_clear(); 592 for (i = 0; Disks[i]; i++) { 593 if (contains_root_partition(Disks[i])) 594 Set_Boot_Blocks(Disks[i], boot1, boot2); 595 dialog_clear(); 596 if (i == 0 && !msgYesNo("Would you like to install a boot manager?\n\nThis will allow you to easily select between other operating systems\non the first disk, or boot from a disk other than the first.")) 597 Set_Boot_Mgr(Disks[i], bteasy17); 598 else { 599 dialog_clear(); 600 if (i == 0 && !msgYesNo("Would you like to remove an existing boot manager?")) 601 Set_Boot_Mgr(Disks[i], mbr); 602 } 603 dialog_clear(); 604 if (!msgYesNo("Last Chance! Are you sure you want to write out\nall these changes to disk?")) { 605 Write_Disk(Disks[i]); 606 return 0; 607 } 608 } 609 return 1; 610} 611