disks.c revision 8407
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.13 1995/05/08 18:41:37 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 mount point for %s assigned!", val); 222 return NULL; 223 } 224 else if (*val != '/') { 225 msgConfirm("Mount point must start with a / character"); 226 return NULL; 227 } 228 else if (!strcmp(val, "/")) { 229 if (c && c->flags & CHUNK_PAST_1024) { 230msgConfirm("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!"); 231 return NULL; 232 } 233 else if (c) 234 c->flags |= CHUNK_IS_ROOT; 235 } 236 else if (c) 237 c->flags &= ~CHUNK_IS_ROOT; 238 safe_free(c ? c->private : NULL); 239 tmp = new_part(val, TRUE); 240 if (c) { 241 c->private = tmp; 242 c->private_free = safe_free; 243 } 244 return tmp; 245 } 246 return NULL; 247} 248 249static PartType 250get_partition_type(void) 251{ 252 char selection[20]; 253 static unsigned char *fs_types[] = { 254 "FS", 255 "A file system", 256 "Swap", 257 "A swap partition.", 258 }; 259 260 if (!dialog_menu("Please choose a partition type", 261 "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)) { 262 if (!strcmp(selection, "FS")) 263 return PART_FILESYSTEM; 264 else if (!strcmp(selection, "Swap")) 265 return PART_SWAP; 266 } 267 return PART_NONE; 268} 269 270static void 271getNewfsCmd(PartInfo *p) 272{ 273 char *val; 274 275 val = msgGetInput(p->newfs_cmd, 276 "Please enter the newfs command and options you'd like to use in\ncreating this file system."); 277 if (val) 278 strncpy(p->newfs_cmd, val, NEWFS_CMD_MAX); 279} 280 281 282#define MAX_MOUNT_NAME 12 283 284#define PART_PART_COL 0 285#define PART_MOUNT_COL 8 286#define PART_SIZE_COL (PART_MOUNT_COL + MAX_MOUNT_NAME + 3) 287#define PART_NEWFS_COL (PART_SIZE_COL + 7) 288#define PART_OFF 38 289 290/* How many mounted partitions to display in column before going to next */ 291#define CHUNK_COLUMN_MAX 6 292 293static void 294print_fbsd_chunks(void) 295{ 296 int i, j, srow, prow, pcol; 297 int sz; 298 299 attrset(A_REVERSE); 300 mvaddstr(0, 25, "FreeBSD Partition Editor"); 301 attrset(A_NORMAL); 302 303 for (i = 0; i < 2; i++) { 304 attrset(A_UNDERLINE); 305 mvaddstr(CHUNK_PART_START_ROW - 1, PART_PART_COL + (i * PART_OFF), 306 "Part"); 307 attrset(A_NORMAL); 308 309 attrset(A_UNDERLINE); 310 mvaddstr(CHUNK_PART_START_ROW - 1, PART_MOUNT_COL + (i * PART_OFF), 311 "Mount"); 312 attrset(A_NORMAL); 313 314 attrset(A_UNDERLINE); 315 mvaddstr(CHUNK_PART_START_ROW - 1, PART_SIZE_COL + (i * PART_OFF) + 2, 316 "Size"); 317 attrset(A_NORMAL); 318 319 attrset(A_UNDERLINE); 320 mvaddstr(CHUNK_PART_START_ROW - 1, PART_NEWFS_COL + (i * PART_OFF), 321 "Newfs"); 322 attrset(A_NORMAL); 323 } 324 325 srow = CHUNK_SLICE_START_ROW; 326 prow = CHUNK_PART_START_ROW; 327 pcol = 0; 328 329 for (i = 0; fbsd_chunk_info[i].d; i++) { 330 if (i == current_chunk) 331 attrset(A_REVERSE); 332 /* Is it a slice entry displayed at the top? */ 333 if (fbsd_chunk_info[i].type == PART_SLICE) { 334 sz = space_free(fbsd_chunk_info[i].c); 335 mvprintw(srow++, 0, 336 "Disk: %s\tPartition name: %s\tFree: %d blocks (%dMB)", 337 fbsd_chunk_info[i].d->name, 338 fbsd_chunk_info[i].c->name, sz, (sz / 2048)); 339 } 340 /* Otherwise it's a swap or filesystem entry, at the bottom */ 341 else { 342 char onestr[PART_OFF], num[10], *mountpoint, *newfs; 343 344 memset(onestr, ' ', PART_OFF - 1); 345 onestr[PART_OFF - 1] = '\0'; 346 /* Go for two columns */ 347 if (prow == (CHUNK_PART_START_ROW + CHUNK_COLUMN_MAX)) { 348 pcol = PART_OFF; 349 prow = CHUNK_PART_START_ROW; 350 } 351 memcpy(onestr + PART_PART_COL, fbsd_chunk_info[i].c->name, 352 strlen(fbsd_chunk_info[i].c->name)); 353 if (fbsd_chunk_info[i].type == PART_FILESYSTEM) { 354 if (fbsd_chunk_info[i].c->private) { 355 mountpoint = ((PartInfo *)fbsd_chunk_info[i].c->private)->mountpoint; 356 newfs = ((PartInfo *)fbsd_chunk_info[i].c->private)->newfs ? "Y" : "N"; 357 } 358 else { 359 fbsd_chunk_info[i].c->private = new_part("", FALSE); 360 fbsd_chunk_info[i].c->private_free = safe_free; 361 mountpoint = " "; 362 newfs = "N"; 363 } 364 } 365 else { 366 mountpoint = "swap"; 367 newfs = " "; 368 } 369 for (j = 0; j < MAX_MOUNT_NAME && mountpoint[j]; j++) 370 onestr[PART_MOUNT_COL + j] = mountpoint[j]; 371 sprintf(num, "%4ldMB", fbsd_chunk_info[i].c->size ? 372 fbsd_chunk_info[i].c->size / 2048 : 0); 373 memcpy(onestr + PART_SIZE_COL, num, strlen(num)); 374 memcpy(onestr + PART_NEWFS_COL, newfs, strlen(newfs)); 375 mvaddstr(prow, pcol, onestr); 376 ++prow; 377 } 378 if (i == current_chunk) 379 attrset(A_NORMAL); 380 } 381} 382 383static void 384print_command_summary() 385{ 386 mvprintw(17, 0, 387 "The following commands are valid here (upper or lower case):"); 388 mvprintw(19, 0, "C = Create Partition D = Delete Partition M = Mount Partition"); 389 mvprintw(20, 0, "N = Newfs Options T = Toggle Newfs ESC = Finish Partitioning"); 390 mvprintw(21, 0, "The default target will be displayed in "); 391 392 attrset(A_REVERSE); 393 addstr("reverse video"); 394 attrset(A_NORMAL); 395 mvprintw(22, 0, "Use F1 or ? to get more help"); 396 move(0, 0); 397} 398 399void 400partition_disks(void) 401{ 402 int sz, key = 0; 403 Boolean partitioning; 404 char *msg = NULL; 405 PartInfo *p; 406 PartType type; 407 408 dialog_clear(); 409 partitioning = TRUE; 410 keypad(stdscr, TRUE); 411 record_fbsd_chunks(); 412 413 while (partitioning) { 414 clear(); 415 print_fbsd_chunks(); 416 print_command_summary(); 417 if (msg) { 418 attrset(A_REVERSE); mvprintw(23, 0, msg); attrset(A_NORMAL); 419 beep(); 420 msg = NULL; 421 } 422 refresh(); 423 key = toupper(getch()); 424 switch (key) { 425 426 case KEY_UP: 427 case '-': 428 if (current_chunk != 0) 429 --current_chunk; 430 break; 431 432 case KEY_DOWN: 433 case '+': 434 case '\r': 435 case '\n': 436 if (fbsd_chunk_info[current_chunk + 1].d) 437 ++current_chunk; 438 break; 439 440 case KEY_HOME: 441 current_chunk = 0; 442 break; 443 444 case KEY_END: 445 while (fbsd_chunk_info[current_chunk + 1].d) 446 ++current_chunk; 447 break; 448 449 case KEY_F(1): 450 case '?': 451 systemDisplayFile("partitioning.hlp"); 452 break; 453 454 case 'C': 455 if (fbsd_chunk_info[current_chunk].type != PART_SLICE) { 456 msg = "You can only do this in a master partition (see top of screen)"; 457 break; 458 } 459 sz = space_free(fbsd_chunk_info[current_chunk].c); 460 if (sz <= FS_MIN_SIZE) 461 msg = "Not enough space to create additional FreeBSD partition"; 462 else { 463 char *val, *cp, tmp[20]; 464 int size; 465 466 snprintf(tmp, 20, "%d", sz); 467 val = msgGetInput(tmp, "Please specify the size for new FreeBSD partition in blocks, or append\na trailing `M' for megabytes (e.g. 20M)."); 468 if (val && (size = strtol(val, &cp, 0)) > 0) { 469 struct chunk *tmp; 470 u_long flags = 0; 471 472 if (*cp && toupper(*cp) == 'M') 473 size *= 2048; 474 475 type = get_partition_type(); 476 if (type == PART_NONE) 477 break; 478 else if (type == PART_FILESYSTEM) { 479 if ((p = get_mountpoint(NULL)) == NULL) 480 break; 481 else if (!strcmp(p->mountpoint, "/")) 482 flags |= CHUNK_IS_ROOT; 483 else 484 flags &= ~CHUNK_IS_ROOT; 485 } 486 else 487 p = NULL; 488 Create_Chunk(fbsd_chunk_info[current_chunk].d, 489 fbsd_chunk_info[current_chunk].c->offset + 490 sz - size, 491 size, 492 part, 493 (type == PART_SWAP) ? FS_SWAP : FS_BSDFFS, 494 flags); 495 tmp = find_chunk_by_loc(fbsd_chunk_info[current_chunk].d, 496 fbsd_chunk_info[current_chunk].c->offset + sz - size, size); 497 if (!tmp) 498 msgConfirm("Unable to create the partition. Too big?"); 499 else { 500 tmp->private = p; 501 tmp->private_free = safe_free; 502 record_fbsd_chunks(); 503 } 504 } 505 } 506 break; 507 508 case 'D': /* delete */ 509 if (fbsd_chunk_info[current_chunk].type == PART_SLICE) { 510 msg = MSG_NOT_APPLICABLE; 511 break; 512 } 513 Delete_Chunk(fbsd_chunk_info[current_chunk].d, 514 fbsd_chunk_info[current_chunk].c); 515 record_fbsd_chunks(); 516 break; 517 518 case 'M': /* mount */ 519 switch(fbsd_chunk_info[current_chunk].type) { 520 case PART_SLICE: 521 msg = MSG_NOT_APPLICABLE; 522 break; 523 524 case PART_SWAP: 525 msg = "You don't need to specify a mountpoint for a swap partition."; 526 break; 527 528 case PART_FILESYSTEM: 529 p = get_mountpoint(fbsd_chunk_info[current_chunk].c); 530 if (p) { 531 p->newfs = FALSE; 532 record_fbsd_chunks(); 533 } 534 break; 535 536 default: 537 msgFatal("Bogus partition under cursor???"); 538 break; 539 } 540 break; 541 542 case 'N': /* Set newfs options */ 543 if (fbsd_chunk_info[current_chunk].c->private && 544 ((PartInfo *)fbsd_chunk_info[current_chunk].c->private)->newfs) 545 getNewfsCmd(fbsd_chunk_info[current_chunk].c->private); 546 else 547 msg = MSG_NOT_APPLICABLE; 548 break; 549 550 case 'T': /* Toggle newfs state */ 551 if (fbsd_chunk_info[current_chunk].c->private) 552 ((PartInfo *)fbsd_chunk_info[current_chunk].c->private)->newfs = !((PartInfo *)fbsd_chunk_info[current_chunk].c->private)->newfs; 553 else 554 msg = MSG_NOT_APPLICABLE; 555 break; 556 557 case 'W': 558 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!")) { 559 int i; 560 561 clear(); 562 dialog_clear(); 563 end_dialog(); 564 DialogActive = FALSE; 565 for (i = 0; Disks[i]; i++) 566 slice_wizard(Disks[i]); 567 clear(); 568 dialog_clear(); 569 DialogActive = TRUE; 570 record_fbsd_chunks(); 571 } 572 else 573 msg = "A most prudent choice!"; 574 break; 575 576 case 27: /* ESC */ 577 partitioning = FALSE; 578 break; 579 580 default: 581 beep(); 582 msg = "Type F1 or ? for help"; 583 break; 584 } 585 } 586} 587 588int 589write_disks(void) 590{ 591 int i; 592 extern u_char boot1[], boot2[]; 593 extern u_char mbr[], bteasy17[]; 594 595 dialog_clear(); 596 for (i = 0; Disks[i]; i++) { 597 if (contains_root_partition(Disks[i])) 598 Set_Boot_Blocks(Disks[i], boot1, boot2); 599 dialog_clear(); 600 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.")) 601 Set_Boot_Mgr(Disks[i], bteasy17); 602 else { 603 dialog_clear(); 604 if (i == 0 && !msgYesNo("Would you like to remove an existing boot manager?")) 605 Set_Boot_Mgr(Disks[i], mbr); 606 } 607 dialog_clear(); 608 if (!msgYesNo("Last Chance! Are you sure you want to write out\nall these changes to disk?")) { 609 Write_Disk(Disks[i]); 610 return 0; 611 } 612 } 613 return 1; 614} 615