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