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