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