label.c revision 8881
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: label.c,v 1.30 1995/05/29 00:50:03 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 * Everything to do with editing the contents of disk labels. 50 */ 51 52/* A nice message we use a lot in the disklabel editor */ 53#define MSG_NOT_APPLICABLE "That option is not applicable here" 54 55/* Where to start printing the freebsd slices */ 56#define CHUNK_SLICE_START_ROW 2 57#define CHUNK_PART_START_ROW 11 58 59/* One MB worth of blocks */ 60#define ONE_MEG 2048 61 62/* The smallest filesystem we're willing to create */ 63#define FS_MIN_SIZE ONE_MEG 64 65/* The smallest root filesystem we're willing to create */ 66#define ROOT_MIN_SIZE (20 * ONE_MEG) 67 68/* All the chunks currently displayed on the screen */ 69static struct { 70 struct chunk *c; 71 PartType type; 72} label_chunk_info[MAX_CHUNKS + 1]; 73static int here; 74 75/* See if we're already using a desired partition name */ 76static Boolean 77check_conflict(char *name) 78{ 79 int i; 80 81 for (i = 0; label_chunk_info[i].c; i++) 82 if (label_chunk_info[i].type == PART_FILESYSTEM && label_chunk_info[i].c->private 83 && !strcmp(((PartInfo *)label_chunk_info[i].c->private)->mountpoint, name)) 84 return TRUE; 85 return FALSE; 86} 87 88/* How much space is in this FreeBSD slice? */ 89static int 90space_free(struct chunk *c) 91{ 92 struct chunk *c1 = c->part; 93 int sz = c->size; 94 95 while (c1) { 96 if (c1->type != unused) 97 sz -= c1->size; 98 c1 = c1->next; 99 } 100 if (sz < 0) 101 msgFatal("Partitions are larger than actual chunk??"); 102 return sz; 103} 104 105/* Snapshot the current situation into the displayed chunks structure */ 106static void 107record_label_chunks() 108{ 109 int i, j, p; 110 struct chunk *c1, *c2; 111 Device **devs; 112 Disk *d; 113 114 devs = deviceFind(NULL, DEVICE_TYPE_DISK); 115 if (!devs) { 116 msgConfirm("No disks found!"); 117 return; 118 } 119 120 j = p = 0; 121 /* First buzz through and pick up the FreeBSD slices */ 122 for (i = 0; devs[i]; i++) { 123 if (!devs[i]->enabled) 124 continue; 125 d = (Disk *)devs[i]->private; 126 if (!d->chunks) 127 msgFatal("No chunk list found for %s!", d->name); 128 129 /* Put the slice entries first */ 130 for (c1 = d->chunks->part; c1; c1 = c1->next) { 131 if (c1->type == freebsd) { 132 label_chunk_info[j].type = PART_SLICE; 133 label_chunk_info[j].c = c1; 134 ++j; 135 } 136 } 137 } 138 /* Now run through again and get the FreeBSD partition entries */ 139 for (i = 0; devs[i]; i++) { 140 if (!devs[i]->enabled) 141 continue; 142 d = (Disk *)devs[i]->private; 143 /* Then buzz through and pick up the partitions */ 144 for (c1 = d->chunks->part; c1; c1 = c1->next) { 145 if (c1->type == freebsd) { 146 for (c2 = c1->part; c2; c2 = c2->next) { 147 if (c2->type == part) { 148 if (c2->subtype == FS_SWAP) 149 label_chunk_info[j].type = PART_SWAP; 150 else 151 label_chunk_info[j].type = PART_FILESYSTEM; 152 label_chunk_info[j].c = c2; 153 ++j; 154 } 155 } 156 } 157 else if (c1->type == fat) { 158 label_chunk_info[j].type = PART_FAT; 159 label_chunk_info[j].c = c1; 160 ++j; 161 } 162 } 163 } 164 label_chunk_info[j].c = NULL; 165 if (here >= j) 166 here = j ? j - 1 : 0; 167} 168 169/* A new partition entry */ 170static PartInfo * 171new_part(char *mpoint, Boolean newfs, u_long size) 172{ 173 PartInfo *ret; 174 u_long target,divisor; 175 176 ret = (PartInfo *)safe_malloc(sizeof(PartInfo)); 177 strncpy(ret->mountpoint, mpoint, FILENAME_MAX); 178 strcpy(ret->newfs_cmd, "newfs"); 179 ret->newfs = newfs; 180 if (!size) 181 return ret; 182 for (target = size; target; target--) { 183 for (divisor = 4096 ; divisor > 1023; divisor--) { 184 if (!(target % divisor)) { 185 sprintf(ret->newfs_cmd + strlen(ret->newfs_cmd), " -u %ld",divisor); 186 return ret; 187 } 188 } 189 } 190 return ret; 191} 192 193/* Get the mountpoint for a partition and save it away */ 194PartInfo * 195get_mountpoint(struct chunk *old) 196{ 197 char *val; 198 PartInfo *tmp; 199 200 if (old && old->private) 201 tmp = old->private; 202 else 203 tmp = NULL; 204 val = msgGetInput(tmp ? tmp->mountpoint : NULL, "Please specify a mount point for the partition"); 205 if (!val || !*val) { 206 if (!old) 207 return NULL; 208 else { 209 free(old->private); 210 old->private = NULL; 211 } 212 return NULL; 213 } 214 215 /* Is it just the same value? */ 216 if (tmp && !strcmp(tmp->mountpoint, val)) 217 return NULL; 218 219 /* Did we use it already? */ 220 if (check_conflict(val)) { 221 msgConfirm("You already have a mount point for %s assigned!", val); 222 return NULL; 223 } 224 225 /* Is it bogus? */ 226 if (*val != '/') { 227 msgConfirm("Mount point must start with a / character"); 228 return NULL; 229 } 230 231 /* Is it going to be mounted on root? */ 232 if (!strcmp(val, "/")) { 233 if (old) 234 old->flags |= CHUNK_IS_ROOT; 235 } 236 else if (old) 237 old->flags &= ~CHUNK_IS_ROOT; 238 239 safe_free(tmp); 240 tmp = new_part(val, TRUE, 0); 241 if (old) { 242 old->private = tmp; 243 old->private_free = safe_free; 244 } 245 return tmp; 246} 247 248/* Get the type of the new partiton */ 249static PartType 250get_partition_type(void) 251{ 252 char selection[20]; 253 int i; 254 255 static unsigned char *fs_types[] = { 256 "FS", 257 "A file system", 258 "Swap", 259 "A swap partition.", 260 }; 261 i = dialog_menu("Please choose a partition type", 262 "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); 263 if (!i) { 264 if (!strcmp(selection, "FS")) 265 return PART_FILESYSTEM; 266 else if (!strcmp(selection, "Swap")) 267 return PART_SWAP; 268 } 269 return PART_NONE; 270} 271 272/* If the user wants a special newfs command for this, set it */ 273static void 274getNewfsCmd(PartInfo *p) 275{ 276 char *val; 277 278 val = msgGetInput(p->newfs_cmd, 279 "Please enter the newfs command and options you'd like to use in\ncreating this file system."); 280 if (val) 281 strncpy(p->newfs_cmd, val, NEWFS_CMD_MAX); 282} 283 284 285#define MAX_MOUNT_NAME 12 286 287#define PART_PART_COL 0 288#define PART_MOUNT_COL 8 289#define PART_SIZE_COL (PART_MOUNT_COL + MAX_MOUNT_NAME + 3) 290#define PART_NEWFS_COL (PART_SIZE_COL + 7) 291#define PART_OFF 38 292 293/* How many mounted partitions to display in column before going to next */ 294#define CHUNK_COLUMN_MAX 5 295 296/* stick this all up on the screen */ 297static void 298print_label_chunks(void) 299{ 300 int i, j, srow, prow, pcol; 301 int sz; 302 303 attrset(A_REVERSE); 304 mvaddstr(0, 25, "FreeBSD Disklabel Editor"); 305 clrtobot(); 306 attrset(A_NORMAL); 307 308 for (i = 0; i < 2; i++) { 309 mvaddstr(CHUNK_PART_START_ROW - 2, PART_PART_COL + (i * PART_OFF), "Part"); 310 mvaddstr(CHUNK_PART_START_ROW - 1, PART_PART_COL + (i * PART_OFF), "----"); 311 312 mvaddstr(CHUNK_PART_START_ROW - 2, PART_MOUNT_COL + (i * PART_OFF), "Mount"); 313 mvaddstr(CHUNK_PART_START_ROW - 1, PART_MOUNT_COL + (i * PART_OFF), "-----"); 314 315 mvaddstr(CHUNK_PART_START_ROW - 2, PART_SIZE_COL + (i * PART_OFF) + 2, "Size"); 316 mvaddstr(CHUNK_PART_START_ROW - 1, PART_SIZE_COL + (i * PART_OFF) + 2, "----"); 317 318 mvaddstr(CHUNK_PART_START_ROW - 2, PART_NEWFS_COL + (i * PART_OFF), "Newfs"); 319 mvaddstr(CHUNK_PART_START_ROW - 1, PART_NEWFS_COL + (i * PART_OFF), "-----"); 320 } 321 srow = CHUNK_SLICE_START_ROW; 322 prow = CHUNK_PART_START_ROW; 323 pcol = 0; 324 325 for (i = 0; label_chunk_info[i].c; i++) { 326 if (i == here) 327 attrset(A_REVERSE); 328 /* Is it a slice entry displayed at the top? */ 329 if (label_chunk_info[i].type == PART_SLICE) { 330 sz = space_free(label_chunk_info[i].c); 331 mvprintw(srow++, 0, "Disk: %s\tPartition name: %s\tFree: %d blocks (%dMB)", 332 label_chunk_info[i].c->disk->name, label_chunk_info[i].c->name, sz, (sz / ONE_MEG)); 333 } 334 /* Otherwise it's a DOS, swap or filesystem entry, at the bottom */ 335 else { 336 char onestr[PART_OFF], num[10], *mountpoint, *newfs; 337 338 /* 339 * We copy this into a blank-padded string so that it looks like 340 * a solid bar in reverse-video 341 */ 342 memset(onestr, ' ', PART_OFF - 1); 343 onestr[PART_OFF - 1] = '\0'; 344 /* Go for two columns */ 345 if (prow == (CHUNK_PART_START_ROW + CHUNK_COLUMN_MAX)) { 346 pcol = PART_OFF; 347 prow = CHUNK_PART_START_ROW; 348 } 349 memcpy(onestr + PART_PART_COL, label_chunk_info[i].c->name, strlen(label_chunk_info[i].c->name)); 350 /* If it's a filesystem, display the mountpoint */ 351 if (label_chunk_info[i].c->private 352 && (label_chunk_info[i].type == PART_FILESYSTEM || label_chunk_info[i].type == PART_FAT)) { 353 mountpoint = ((PartInfo *)label_chunk_info[i].c->private)->mountpoint; 354 if (label_chunk_info[i].type == PART_FAT) 355 newfs = "DOS"; 356 else 357 newfs = ((PartInfo *)label_chunk_info[i].c->private)->newfs ? "Y" : "N"; 358 } 359 else if (label_chunk_info[i].type == PART_SWAP) { 360 mountpoint = "swap"; 361 newfs = " "; 362 } 363 else { 364 mountpoint = "<NONE>"; 365 newfs = "*"; 366 } 367 for (j = 0; j < MAX_MOUNT_NAME && mountpoint[j]; j++) 368 onestr[PART_MOUNT_COL + j] = mountpoint[j]; 369 snprintf(num, 10, "%4ldMB", label_chunk_info[i].c->size ? label_chunk_info[i].c->size / ONE_MEG : 0); 370 memcpy(onestr + PART_SIZE_COL, num, strlen(num)); 371 memcpy(onestr + PART_NEWFS_COL, newfs, strlen(newfs)); 372 onestr[PART_NEWFS_COL + strlen(newfs)] = '\0'; 373 mvaddstr(prow, pcol, onestr); 374 ++prow; 375 } 376 if (i == here) 377 attrset(A_NORMAL); 378 } 379} 380 381static void 382print_command_summary() 383{ 384 mvprintw(17, 0, "The following commands are valid here (upper or lower case):"); 385 mvprintw(19, 0, "C = Create New D = Delete M = Set Mountpoint"); 386 mvprintw(20, 0, "N = Newfs Options T = Toggle Newfs U = Undo ESC = Exit"); 387 mvprintw(21, 0, "The default target will be displayed in "); 388 389 attrset(A_REVERSE); 390 addstr("reverse"); 391 attrset(A_NORMAL); 392 addstr(" video."); 393 mvprintw(22, 0, "Use F1 or ? to get more help, arrow keys to move."); 394 move(0, 0); 395} 396 397int 398diskLabelEditor(char *str) 399{ 400 int sz, i, key = 0; 401 Boolean labeling; 402 char *msg = NULL; 403 PartInfo *p; 404 PartType type; 405 Device **devs; 406 407 labeling = TRUE; 408 keypad(stdscr, TRUE); 409 record_label_chunks(); 410 411 if (!getenv(DISK_PARTITIONED)) { 412 msgConfirm("You need to partition your disk(s) before you can assign disk labels."); 413 return 0; 414 } 415 dialog_clear(); clear(); 416 while (labeling) { 417 clear(); 418 print_label_chunks(); 419 print_command_summary(); 420 if (msg) { 421 attrset(A_REVERSE); mvprintw(23, 0, msg); attrset(A_NORMAL); 422 beep(); 423 msg = NULL; 424 } 425 refresh(); 426 key = toupper(getch()); 427 switch (key) { 428 429 case '\014': /* ^L */ 430 continue; 431 432 case KEY_UP: 433 case '-': 434 if (here != 0) 435 --here; 436 else 437 while (label_chunk_info[here + 1].c) 438 ++here; 439 break; 440 441 case KEY_DOWN: 442 case '+': 443 case '\r': 444 case '\n': 445 if (label_chunk_info[here + 1].c) 446 ++here; 447 else 448 here = 0; 449 break; 450 451 case KEY_HOME: 452 here = 0; 453 break; 454 455 case KEY_END: 456 while (label_chunk_info[here + 1].c) 457 ++here; 458 break; 459 460 case KEY_F(1): 461 case '?': 462 systemDisplayFile("disklabel.hlp"); 463 break; 464 465 case 'C': 466 if (label_chunk_info[here].type != PART_SLICE) { 467 msg = "You can only do this in a master partition (see top of screen)"; 468 break; 469 } 470 else { 471 int i, cnt; 472 473 cnt = i = 0; 474 while (label_chunk_info[i].c) 475 if (label_chunk_info[i++].type != PART_SLICE) 476 cnt++; 477 if (cnt == (CHUNK_COLUMN_MAX * 2)) { 478 msgConfirm("Sorry, I can't fit any more partitions on the screen! You can get around\nthis limitation by partitioning your disks individually rather than all\nat once. This will be fixed just as soon as we get a scrolling partition\nbox written. Sorry for the inconvenience!"); 479 break; 480 } 481 } 482 sz = space_free(label_chunk_info[here].c); 483 if (sz <= FS_MIN_SIZE) { 484 msg = "Not enough space to create additional FreeBSD partition"; 485 break; 486 } 487 { 488 char *val, *cp; 489 int size; 490 struct chunk *tmp; 491 char osize[80]; 492 u_long flags = 0; 493 494 sprintf(osize, "%d", sz); 495 val = msgGetInput(osize, "Please specify the size for new FreeBSD partition in blocks, or\nappend a trailing `M' for megabytes (e.g. 20M) or `C' for cylinders.\n\nSpace free is %d blocks (%dMB)", sz, sz / ONE_MEG); 496 if (!val || (size = strtol(val, &cp, 0)) <= 0) 497 break; 498 499 if (*cp) { 500 if (toupper(*cp) == 'M') 501 size *= ONE_MEG; 502 else if (toupper(*cp) == 'C') 503 size *= (label_chunk_info[here].c->disk->bios_hd * label_chunk_info[here].c->disk->bios_sect); 504 } 505 if (size <= FS_MIN_SIZE) { 506 msgConfirm("The minimum filesystem size is %dMB", FS_MIN_SIZE / ONE_MEG); 507 break; 508 } 509 type = get_partition_type(); 510 if (type == PART_NONE) 511 break; 512 513 if (type == PART_FILESYSTEM) { 514 if ((p = get_mountpoint(NULL)) == NULL) 515 break; 516 else if (!strcmp(p->mountpoint, "/")) 517 flags |= CHUNK_IS_ROOT; 518 else 519 flags &= ~CHUNK_IS_ROOT; 520 } else 521 p = NULL; 522 523 if ((flags & CHUNK_IS_ROOT)) { 524 if (!(label_chunk_info[here].c->flags & CHUNK_BSD_COMPAT)) { 525 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 location. Please choose another location for your root\npartition and try again!"); 526 break; 527 } 528 if (size < ROOT_MIN_SIZE) 529 msgConfirm("Warning: This is smaller than the recommended size for a\nroot partition. For a variety of reasons, root\npartitions should usually be at least %dMB in size", ROOT_MIN_SIZE / ONE_MEG); 530 } 531 tmp = Create_Chunk_DWIM(label_chunk_info[here].c->disk, 532 label_chunk_info[here].c, 533 size, part, 534 (type == PART_SWAP) ? FS_SWAP : FS_BSDFFS, 535 flags); 536 if (!tmp) { 537 msgConfirm("Unable to create the partition. Too big?"); 538 break; 539 } 540 if ((flags & CHUNK_IS_ROOT) && (tmp->flags & CHUNK_PAST_1024)) { 541 msgConfirm("This region cannot be used for your root partition as it starts\nor extends past the 1024'th cylinder mark and is thus a\npoor location to boot from. Please choose another\nlocation for your root partition and try again!"); 542 Delete_Chunk(label_chunk_info[here].c->disk, tmp); 543 break; 544 } 545 if (type != PART_SWAP) { 546 /* This is needed to tell the newfs -u about the size */ 547 tmp->private = new_part(p->mountpoint, p->newfs, tmp->size); 548 tmp->private_free = safe_free; 549 safe_free(p); 550 } else { 551 tmp->private = p; 552 } 553 tmp->private_free = safe_free; 554 record_label_chunks(); 555 } 556 break; 557 558 case 'D': /* delete */ 559 if (label_chunk_info[here].type == PART_SLICE) { 560 msg = MSG_NOT_APPLICABLE; 561 break; 562 } 563 else if (label_chunk_info[here].type == PART_FAT) { 564 msg = "Use the Disk Partition Editor to delete DOS partitions"; 565 break; 566 } 567 Delete_Chunk(label_chunk_info[here].c->disk, label_chunk_info[here].c); 568 record_label_chunks(); 569 break; 570 571 case 'M': /* mount */ 572 switch(label_chunk_info[here].type) { 573 case PART_SLICE: 574 msg = MSG_NOT_APPLICABLE; 575 break; 576 577 case PART_SWAP: 578 msg = "You don't need to specify a mountpoint for a swap partition."; 579 break; 580 581 case PART_FAT: 582 case PART_FILESYSTEM: 583 p = get_mountpoint(label_chunk_info[here].c); 584 if (p) { 585 p->newfs = FALSE; 586 if (label_chunk_info[here].type == PART_FAT 587 && (!strcmp(p->mountpoint, "/") || !strcmp(p->mountpoint, "/usr") 588 || !strcmp(p->mountpoint, "/var"))) { 589 msgConfirm("%s is an invalid mount point for a DOS partition!", p->mountpoint); 590 strcpy(p->mountpoint, "/bogus"); 591 } 592 } 593 record_label_chunks(); 594 break; 595 596 default: 597 msgFatal("Bogus partition under cursor???"); 598 break; 599 } 600 break; 601 602 case 'N': /* Set newfs options */ 603 if (label_chunk_info[here].c->private && 604 ((PartInfo *)label_chunk_info[here].c->private)->newfs) 605 getNewfsCmd(label_chunk_info[here].c->private); 606 else 607 msg = MSG_NOT_APPLICABLE; 608 break; 609 610 case 'T': /* Toggle newfs state */ 611 if (label_chunk_info[here].type == PART_FILESYSTEM && 612 label_chunk_info[here].c->private) { 613 PartInfo *pi = ((PartInfo *)label_chunk_info[here].c->private); 614 label_chunk_info[here].c->private = new_part(pi->mountpoint, !pi->newfs, label_chunk_info[here].c->size); 615 safe_free(pi); 616 label_chunk_info[here].c->private_free = safe_free; 617 } 618 else 619 msg = MSG_NOT_APPLICABLE; 620 break; 621 622 case 'U': 623 devs = deviceFind(NULL, DEVICE_TYPE_DISK); 624 for (i = 0; devs[i]; i++) { 625 if (!devs[i]->enabled) 626 continue; 627 else { 628 char *cp = devs[i]->name; 629 630 Free_Disk(devs[i]->private); 631 devs[i]->private = Open_Disk(cp); 632 } 633 } 634 record_label_chunks(); 635 break; 636 637 case 'W': 638 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!")) { 639 int i; 640 Device **devs; 641 642 dialog_clear(); 643 end_dialog(); 644 DialogActive = FALSE; 645 devs = deviceFind(NULL, DEVICE_TYPE_DISK); 646 if (!devs) { 647 msgConfirm("Can't find any disk devicse!"); 648 break; 649 } 650 for (i = 0; devs[i] && ((Disk *)devs[i]->private); i++) { 651 if (devs[i]->enabled) 652 slice_wizard(((Disk *)devs[i]->private)); 653 } 654 DialogActive = TRUE; 655 dialog_clear(); 656 record_label_chunks(); 657 } 658 else 659 msg = "A most prudent choice!"; 660 break; 661 662 case 27: /* ESC */ 663 labeling = FALSE; 664 break; 665 666 default: 667 beep(); 668 msg = "Type F1 or ? for help"; 669 break; 670 } 671 } 672 variable_set2(DISK_LABELLED, "yes"); 673 dialog_clear(); 674 return 0; 675} 676 677 678 679