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