label.c revision 8820
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.27 1995/05/28 09:31:34 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].type == PART_FILESYSTEM || label_chunk_info[i].type == PART_FAT) { 352 if (label_chunk_info[i].c->private != NULL) 353 mountpoint = ((PartInfo *)label_chunk_info[i].c->private)->mountpoint; 354 else 355 mountpoint = " "; 356 if (label_chunk_info[i].type == PART_FAT) 357 newfs = "DOS"; 358 else 359 newfs = ((PartInfo *)label_chunk_info[i].c->private)->newfs ? "Y" : "N"; 360 } 361 else if (label_chunk_info[i].type == PART_SWAP) { 362 mountpoint = "swap"; 363 newfs = " "; 364 } 365 else { 366 mountpoint = "<NONE>"; 367 newfs = "*"; 368 } 369 for (j = 0; j < MAX_MOUNT_NAME && mountpoint[j]; j++) 370 onestr[PART_MOUNT_COL + j] = mountpoint[j]; 371 snprintf(num, 10, "%4ldMB", label_chunk_info[i].c->size ? label_chunk_info[i].c->size / ONE_MEG : 0); 372 memcpy(onestr + PART_SIZE_COL, num, strlen(num)); 373 memcpy(onestr + PART_NEWFS_COL, newfs, strlen(newfs)); 374 onestr[PART_NEWFS_COL + strlen(newfs)] = '\0'; 375 mvaddstr(prow, pcol, onestr); 376 ++prow; 377 } 378 if (i == here) 379 attrset(A_NORMAL); 380 } 381} 382 383static void 384print_command_summary() 385{ 386 mvprintw(17, 0, "The following commands are valid here (upper or lower case):"); 387 mvprintw(19, 0, "C = Create New D = Delete M = Set Mountpoint"); 388 mvprintw(20, 0, "N = Newfs Options T = Toggle Newfs U = Undo ESC = Exit"); 389 mvprintw(21, 0, "The default target will be displayed in "); 390 391 attrset(A_REVERSE); 392 addstr("reverse"); 393 attrset(A_NORMAL); 394 addstr(" video."); 395 mvprintw(22, 0, "Use F1 or ? to get more help, arrow keys to move."); 396 move(0, 0); 397} 398 399int 400diskLabelEditor(char *str) 401{ 402 int sz, key = 0; 403 Boolean labeling; 404 char *msg = NULL; 405 PartInfo *p; 406 PartType type; 407 408 labeling = TRUE; 409 keypad(stdscr, TRUE); 410 record_label_chunks(); 411 412 if (!getenv(DISK_PARTITIONED)) { 413 msgConfirm("You need to partition your disk(s) before you can assign disk labels."); 414 return 0; 415 } 416 dialog_clear(); clear(); 417 while (labeling) { 418 clear(); 419 print_label_chunks(); 420 print_command_summary(); 421 if (msg) { 422 attrset(A_REVERSE); mvprintw(23, 0, msg); attrset(A_NORMAL); 423 beep(); 424 msg = NULL; 425 } 426 refresh(); 427 key = toupper(getch()); 428 switch (key) { 429 430 case '\014': /* ^L */ 431 continue; 432 433 case KEY_UP: 434 case '-': 435 if (here != 0) 436 --here; 437 else 438 while (label_chunk_info[here + 1].c) 439 ++here; 440 break; 441 442 case KEY_DOWN: 443 case '+': 444 case '\r': 445 case '\n': 446 if (label_chunk_info[here + 1].c) 447 ++here; 448 else 449 here = 0; 450 break; 451 452 case KEY_HOME: 453 here = 0; 454 break; 455 456 case KEY_END: 457 while (label_chunk_info[here + 1].c) 458 ++here; 459 break; 460 461 case KEY_F(1): 462 case '?': 463 systemDisplayFile("disklabel.hlp"); 464 break; 465 466 case 'C': 467 if (label_chunk_info[here].type != PART_SLICE) { 468 msg = "You can only do this in a master partition (see top of screen)"; 469 break; 470 } 471 sz = space_free(label_chunk_info[here].c); 472 if (sz <= FS_MIN_SIZE) { 473 msg = "Not enough space to create additional FreeBSD partition"; 474 break; 475 } 476 { 477 char *val, *cp; 478 int size; 479 struct chunk *tmp; 480 char osize[80]; 481 u_long flags = 0; 482 483 sprintf(osize, "%d", sz); 484 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); 485 if (!val || (size = strtol(val, &cp, 0)) <= 0) 486 break; 487 488 if (*cp) { 489 if (toupper(*cp) == 'M') 490 size *= ONE_MEG; 491 else if (toupper(*cp) == 'C') 492 size *= (label_chunk_info[here].c->disk->bios_hd * label_chunk_info[here].c->disk->bios_sect); 493 } 494 if (size <= FS_MIN_SIZE) { 495 msgConfirm("The minimum filesystem size is %dMB", FS_MIN_SIZE / ONE_MEG); 496 break; 497 } 498 type = get_partition_type(); 499 if (type == PART_NONE) 500 break; 501 502 if (type == PART_FILESYSTEM) { 503 if ((p = get_mountpoint(NULL)) == NULL) 504 break; 505 else if (!strcmp(p->mountpoint, "/")) 506 flags |= CHUNK_IS_ROOT; 507 else 508 flags &= ~CHUNK_IS_ROOT; 509 } else 510 p = NULL; 511 512 if ((flags & CHUNK_IS_ROOT)) { 513 if (!(label_chunk_info[here].c->flags & CHUNK_BSD_COMPAT)) { 514 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!"); 515 break; 516 } 517 if (size < ROOT_MIN_SIZE) 518 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); 519 } 520 tmp = Create_Chunk_DWIM(label_chunk_info[here].c->disk, 521 label_chunk_info[here].c, 522 size, part, 523 (type == PART_SWAP) ? FS_SWAP : FS_BSDFFS, 524 flags); 525 if (!tmp) { 526 msgConfirm("Unable to create the partition. Too big?"); 527 break; 528 } 529 if ((flags & CHUNK_IS_ROOT) && (tmp->flags & CHUNK_PAST_1024)) { 530 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!"); 531 Delete_Chunk(label_chunk_info[here].c->disk, tmp); 532 break; 533 } 534 if (type != PART_SWAP) { 535 /* This is needed to tell the newfs -u about the size */ 536 tmp->private = new_part(p->mountpoint, p->newfs, tmp->size); 537 tmp->private_free = safe_free; 538 safe_free(p); 539 } else { 540 tmp->private = p; 541 } 542 tmp->private_free = safe_free; 543 record_label_chunks(); 544 } 545 break; 546 547 case 'D': /* delete */ 548 if (label_chunk_info[here].type == PART_SLICE) { 549 msg = MSG_NOT_APPLICABLE; 550 break; 551 } 552 else if (label_chunk_info[here].type == PART_FAT) { 553 msg = "Use the Disk Partition Editor to delete DOS partitions"; 554 break; 555 } 556 Delete_Chunk(label_chunk_info[here].c->disk, label_chunk_info[here].c); 557 record_label_chunks(); 558 break; 559 560 case 'M': /* mount */ 561 switch(label_chunk_info[here].type) { 562 case PART_SLICE: 563 msg = MSG_NOT_APPLICABLE; 564 break; 565 566 case PART_SWAP: 567 msg = "You don't need to specify a mountpoint for a swap partition."; 568 break; 569 570 case PART_FAT: 571 case PART_FILESYSTEM: 572 p = get_mountpoint(label_chunk_info[here].c); 573 if (p) { 574 p->newfs = FALSE; 575 if (label_chunk_info[here].type == PART_FAT 576 && (!strcmp(p->mountpoint, "/") || !strcmp(p->mountpoint, "/usr") 577 || !strcmp(p->mountpoint, "/var"))) { 578 msgConfirm("%s is an invalid mount point for a DOS partition!", p->mountpoint); 579 strcpy(p->mountpoint, "/bogus"); 580 } 581 } 582 record_label_chunks(); 583 break; 584 585 default: 586 msgFatal("Bogus partition under cursor???"); 587 break; 588 } 589 break; 590 591 case 'N': /* Set newfs options */ 592 if (label_chunk_info[here].c->private && 593 ((PartInfo *)label_chunk_info[here].c->private)->newfs) 594 getNewfsCmd(label_chunk_info[here].c->private); 595 else 596 msg = MSG_NOT_APPLICABLE; 597 break; 598 599 case 'T': /* Toggle newfs state */ 600 if (label_chunk_info[here].type == PART_FILESYSTEM && 601 label_chunk_info[here].c->private) { 602 PartInfo *pi = ((PartInfo *)label_chunk_info[here].c->private); 603 label_chunk_info[here].c->private = new_part(pi->mountpoint, !pi->newfs, label_chunk_info[here].c->size); 604 safe_free(pi); 605 label_chunk_info[here].c->private_free = safe_free; 606 } 607 else 608 msg = MSG_NOT_APPLICABLE; 609 break; 610 611 case 'U': 612 Free_Disk(label_chunk_info[here].c->disk); 613 label_chunk_info[here].c->disk = Open_Disk(label_chunk_info[here].c->disk->name); 614 if (!label_chunk_info[here].c->disk) 615 msgFatal("Can't reopen disk in undo!"); 616 record_label_chunks(); 617 618 case 'W': 619 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!")) { 620 int i; 621 Device **devs; 622 623 dialog_clear(); 624 end_dialog(); 625 DialogActive = FALSE; 626 devs = deviceFind(NULL, DEVICE_TYPE_DISK); 627 if (!devs) { 628 msgConfirm("Can't find any disk devicse!"); 629 break; 630 } 631 for (i = 0; devs[i] && ((Disk *)devs[i]->private); i++) { 632 if (devs[i]->enabled) 633 slice_wizard(((Disk *)devs[i]->private)); 634 } 635 DialogActive = TRUE; 636 dialog_clear(); 637 record_label_chunks(); 638 } 639 else 640 msg = "A most prudent choice!"; 641 break; 642 643 case 27: /* ESC */ 644 labeling = FALSE; 645 break; 646 647 default: 648 beep(); 649 msg = "Type F1 or ? for help"; 650 break; 651 } 652 } 653 variable_set2(DISK_LABELLED, "yes"); 654 dialog_clear(); 655 return 0; 656} 657 658 659 660