label.c revision 8702
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.20 1995/05/21 18:24:33 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/* 56 * I make some pretty gross assumptions about having a max of 50 chunks 57 * total - 8 slices and 42 partitions. I can't easily display many more 58 * than that on the screen at once! 59 * 60 * For 2.1 I'll revisit this and try to make it more dynamic, but since 61 * this will catch 99.99% of all possible cases, I'm not too worried. 62 */ 63#define MAX_CHUNKS 50 64 65/* Where to start printing the freebsd slices */ 66#define CHUNK_SLICE_START_ROW 2 67#define CHUNK_PART_START_ROW 11 68 69/* One MB worth of blocks */ 70#define ONE_MEG 2048 71 72/* The smallest filesystem we're willing to create */ 73#define FS_MIN_SIZE ONE_MEG 74 75/* The smallest root filesystem we're willing to create */ 76#define ROOT_MIN_SIZE (20 * ONE_MEG) 77 78/* All the chunks currently displayed on the screen */ 79static struct { 80 struct disk *d; 81 struct chunk *c; 82 PartType type; 83} label_chunk_info[MAX_CHUNKS + 1]; 84static int here; 85 86/* See if we're already using a desired partition name */ 87static Boolean 88check_conflict(char *name) 89{ 90 int i; 91 92 for (i = 0; label_chunk_info[i].d; i++) 93 if (label_chunk_info[i].type == PART_FILESYSTEM 94 && label_chunk_info[i].c->private 95 && !strcmp(((PartInfo *)label_chunk_info[i].c->private)->mountpoint, name)) 96 return TRUE; 97 return FALSE; 98} 99 100/* How much space is in this FreeBSD slice? */ 101static int 102space_free(struct chunk *c) 103{ 104 struct chunk *c1 = c->part; 105 int sz = c->size; 106 107 while (c1) { 108 if (c1->type != unused) 109 sz -= c1->size; 110 c1 = c1->next; 111 } 112 if (sz < 0) 113 msgFatal("Partitions are larger than actual chunk??"); 114 return sz; 115} 116 117/* Snapshot the current situation into the displayed chunks structure */ 118static void 119record_label_chunks() 120{ 121 int i, j, p; 122 struct chunk *c1, *c2; 123 Device **devs; 124 Disk *d; 125 126 devs = deviceFind(NULL, DEVICE_TYPE_DISK); 127 if (!devs) { 128 msgConfirm("No disks found!"); 129 return; 130 } 131 132 j = p = 0; 133 /* First buzz through and pick up the FreeBSD slices */ 134 for (i = 0; devs[i]; i++) { 135 if (!devs[i]->enabled) 136 continue; 137 d = (Disk *)devs[i]->private; 138 if (!d->chunks) 139 msgFatal("No chunk list found for %s!", d->name); 140 141 /* Put the slice entries first */ 142 for (c1 = d->chunks->part; c1; c1 = c1->next) { 143 if (c1->type == freebsd) { 144 label_chunk_info[j].type = PART_SLICE; 145 label_chunk_info[j].d = d; 146 label_chunk_info[j].c = c1; 147 ++j; 148 } 149 } 150 } 151 /* Now run through again and get the FreeBSD partition entries */ 152 for (i = 0; devs[i]; i++) { 153 if (!devs[i]->enabled) 154 continue; 155 d = (Disk *)devs[i]->private; 156 /* Then buzz through and pick up the partitions */ 157 for (c1 = d->chunks->part; c1; c1 = c1->next) { 158 if (c1->type == freebsd) { 159 for (c2 = c1->part; c2; c2 = c2->next) { 160 if (c2->type == part) { 161 if (c2->subtype == FS_SWAP) 162 label_chunk_info[j].type = PART_SWAP; 163 else 164 label_chunk_info[j].type = PART_FILESYSTEM; 165 label_chunk_info[j].d = d; 166 label_chunk_info[j].c = c2; 167 ++j; 168 } 169 } 170 } 171 else if (c1->type == fat) { 172 label_chunk_info[j].type = PART_FAT; 173 label_chunk_info[j].d = d; 174 label_chunk_info[j].c = c1; 175 ++j; 176 } 177 } 178 } 179 label_chunk_info[j].d = NULL; 180 label_chunk_info[j].c = NULL; 181 if (here >= j) 182 here = j ? j - 1 : 0; 183} 184 185/* A new partition entry */ 186static PartInfo * 187new_part(char *mpoint, Boolean newfs, u_long size) 188{ 189 PartInfo *ret; 190 u_long target,divisor; 191 192 ret = (PartInfo *)safe_malloc(sizeof(PartInfo)); 193 strncpy(ret->mountpoint, mpoint, FILENAME_MAX); 194 strcpy(ret->newfs_cmd, "newfs"); 195 ret->newfs = newfs; 196 if (!size) 197 return ret; 198 for(target = size; target; target--) { 199 for(divisor = 4096 ; divisor > 1023; divisor--) { 200 if (!(target % divisor)) { 201 sprintf(ret->newfs_cmd+strlen(ret->newfs_cmd), 202 " -u %ld",divisor); 203 return ret; 204 } 205 } 206 } 207 return ret; 208} 209 210/* Get the mountpoint for a partition and save it away */ 211PartInfo * 212get_mountpoint(struct chunk *old) 213{ 214 char *val; 215 PartInfo *tmp; 216 217 val = msgGetInput(old && old->private ? ((PartInfo *)old->private)->mountpoint : NULL, 218 "Please specify a mount point for the partition"); 219 if (!val) 220 return NULL; 221 222 /* Is it just the same value? */ 223 if (old && old->private && !strcmp(((PartInfo *)old->private)->mountpoint, val)) 224 return NULL; 225 if (check_conflict(val)) { 226 msgConfirm("You already have a mount point for %s assigned!", val); 227 return NULL; 228 } 229 if (*val != '/') { 230 msgConfirm("Mount point must start with a / character"); 231 return NULL; 232 } 233 if (!strcmp(val, "/")) { 234 if (old) 235 old->flags |= CHUNK_IS_ROOT; 236 } else if (old) { 237 old->flags &= ~CHUNK_IS_ROOT; 238 } 239 safe_free(old ? old->private : NULL); 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].d; 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, 332 "Disk: %s\tPartition name: %s\tFree: %d blocks (%dMB)", 333 label_chunk_info[i].d->name, 334 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, 352 strlen(label_chunk_info[i].c->name)); 353 /* If it's a filesystem, display the mountpoint */ 354 if (label_chunk_info[i].type == PART_FILESYSTEM) { 355 if (label_chunk_info[i].c->private == NULL) { 356 static int mnt = 0; 357 char foo[10]; 358 359 /* 360 * Hmm! A partition that must have already been here. 361 * Fill in a fake mountpoint and register it 362 */ 363 sprintf(foo, "/mnt%d", mnt++); 364 label_chunk_info[i].c->private = 365 new_part(foo, FALSE,label_chunk_info[i].c->size); 366 label_chunk_info[i].c->private_free = safe_free; 367 } 368 mountpoint = ((PartInfo *)label_chunk_info[i].c->private)->mountpoint; 369 newfs = ((PartInfo *)label_chunk_info[i].c->private)->newfs ? "Y" : "N"; 370 } 371 else if (label_chunk_info[i].type == PART_SWAP) { 372 mountpoint = "swap"; 373 newfs = " "; 374 } 375 else if (label_chunk_info[i].type == PART_FAT) { 376 mountpoint = "DOS FAT"; 377 newfs = "*"; 378 } 379 else { 380 mountpoint = "<unknown>"; 381 newfs = "*"; 382 } 383 for (j = 0; j < MAX_MOUNT_NAME && mountpoint[j]; j++) 384 onestr[PART_MOUNT_COL + j] = mountpoint[j]; 385 snprintf(num, 10, "%4ldMB", label_chunk_info[i].c->size ? 386 label_chunk_info[i].c->size / ONE_MEG : 0); 387 memcpy(onestr + PART_SIZE_COL, num, strlen(num)); 388 memcpy(onestr + PART_NEWFS_COL, newfs, strlen(newfs)); 389 onestr[PART_NEWFS_COL + strlen(newfs)] = '\0'; 390 mvaddstr(prow, pcol, onestr); 391 ++prow; 392 } 393 if (i == here) 394 attrset(A_NORMAL); 395 } 396} 397 398static void 399print_command_summary() 400{ 401 mvprintw(17, 0, 402 "The following commands are valid here (upper or lower case):"); 403 mvprintw(19, 0, "C = Create Partition D = Delete Partition M = Mount Partition"); 404 mvprintw(20, 0, "N = Newfs Options T = Toggle Newfs ESC = Exit this screen"); 405 mvprintw(21, 0, "The default target will be displayed in "); 406 407 attrset(A_REVERSE); 408 addstr("reverse video."); 409 attrset(A_NORMAL); 410 mvprintw(22, 0, "Use F1 or ? to get more help, arrow keys to move."); 411 move(0, 0); 412} 413 414int 415diskLabelEditor(char *str) 416{ 417 int sz, key = 0; 418 Boolean labeling; 419 char *msg = NULL; 420 PartInfo *p; 421 PartType type; 422 423 labeling = TRUE; 424 keypad(stdscr, TRUE); 425 record_label_chunks(); 426 427 if (!getenv(DISK_PARTITIONED)) { 428 msgConfirm("You need to partition your disk(s) before you can assign disk labels."); 429 return 0; 430 } 431 dialog_clear(); clear(); 432 while (labeling) { 433 clear(); 434 print_label_chunks(); 435 print_command_summary(); 436 if (msg) { 437 attrset(A_REVERSE); mvprintw(23, 0, msg); attrset(A_NORMAL); 438 beep(); 439 msg = NULL; 440 } 441 refresh(); 442 key = toupper(getch()); 443 switch (key) { 444 445 case KEY_UP: 446 case '-': 447 if (here != 0) 448 --here; 449 break; 450 451 case KEY_DOWN: 452 case '+': 453 case '\r': 454 case '\n': 455 if (label_chunk_info[here + 1].d) 456 ++here; 457 break; 458 459 case KEY_HOME: 460 here = 0; 461 break; 462 463 case KEY_END: 464 while (label_chunk_info[here + 1].d) 465 ++here; 466 break; 467 468 case KEY_F(1): 469 case '?': 470 systemDisplayFile("disklabel.hlp"); 471 break; 472 473 case 'C': 474 if (label_chunk_info[here].type != PART_SLICE) { 475 msg = "You can only do this in a master partition (see top of screen)"; 476 break; 477 } 478 sz = space_free(label_chunk_info[here].c); 479 if (sz <= FS_MIN_SIZE) { 480 msg = "Not enough space to create additional FreeBSD partition"; 481 break; 482 } 483 { 484 char *val, *cp, tmpb[20]; 485 int size; 486 struct chunk *tmp; 487 u_long flags = 0; 488 489 snprintf(tmpb, 20, "%d", sz); 490 val = msgGetInput(tmpb, "Please specify the size for new FreeBSD partition in blocks, or append\na trailing `M' for megabytes (e.g. 20M)."); 491 if (!val || (size = strtol(val, &cp, 0)) <= 0) 492 break; 493 494 if (sz <= FS_MIN_SIZE) { 495 msgConfirm("The minimum filesystem size is %dMB", FS_MIN_SIZE / ONE_MEG); 496 break; 497 } 498 if (*cp && toupper(*cp) == 'M') 499 size *= ONE_MEG; 500 501 type = get_partition_type(); 502 if (type == PART_NONE) 503 break; 504 505 if (type == PART_FILESYSTEM) { 506 if ((p = get_mountpoint(NULL)) == NULL) 507 break; 508 else if (!strcmp(p->mountpoint, "/")) 509 flags |= CHUNK_IS_ROOT; 510 else 511 flags &= ~CHUNK_IS_ROOT; 512 } else 513 p = NULL; 514 515 if ((flags & CHUNK_IS_ROOT)) { 516 if (!(label_chunk_info[here].c->flags & CHUNK_BSD_COMPAT)) { 517 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!"); 518 break; 519 } 520 if (size < ROOT_MIN_SIZE) { 521 msgConfirm("This is too small a size for a root partition. For a variety of\nreasons, root partitions should be at least %dMB in size", ROOT_MIN_SIZE / ONE_MEG); 522 break; 523 } 524 } 525 tmp = Create_Chunk_DWIM(label_chunk_info[here].d, 526 label_chunk_info[here].c, 527 size, part, 528 (type == PART_SWAP) ? FS_SWAP : FS_BSDFFS, 529 flags); 530 if (!tmp) { 531 msgConfirm("Unable to create the partition. Too big?"); 532 break; 533 } 534 if ((flags & CHUNK_IS_ROOT) && (tmp->flags & CHUNK_PAST_1024)) { 535 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!"); 536 Delete_Chunk(label_chunk_info[here].d, tmp); 537 break; 538 } 539 if (type != PART_SWAP) { 540 /* This is needed to tell the newfs -u about the size */ 541 tmp->private = new_part(p->mountpoint,p->newfs,tmp->size); 542 safe_free(p); 543 } else { 544 tmp->private = p; 545 } 546 tmp->private_free = safe_free; 547 record_label_chunks(); 548 } 549 break; 550 551 case 'D': /* delete */ 552 if (label_chunk_info[here].type == PART_SLICE) { 553 msg = MSG_NOT_APPLICABLE; 554 break; 555 } 556 else if (label_chunk_info[here].type == PART_FAT) { 557 msg = "Use the Disk Partition Editor to delete this"; 558 break; 559 } 560 Delete_Chunk(label_chunk_info[here].d, label_chunk_info[here].c); 561 record_label_chunks(); 562 break; 563 564 case 'M': /* mount */ 565 switch(label_chunk_info[here].type) { 566 case PART_SLICE: 567 msg = MSG_NOT_APPLICABLE; 568 break; 569 570 case PART_SWAP: 571 msg = "You don't need to specify a mountpoint for a swap partition."; 572 break; 573 574 case PART_FAT: 575 case PART_FILESYSTEM: 576 p = get_mountpoint(label_chunk_info[here].c); 577 if (p) { 578 p->newfs = FALSE; 579 record_label_chunks(); 580 } 581 break; 582 583 default: 584 msgFatal("Bogus partition under cursor???"); 585 break; 586 } 587 break; 588 589 case 'N': /* Set newfs options */ 590 if (label_chunk_info[here].c->private && 591 ((PartInfo *)label_chunk_info[here].c->private)->newfs) 592 getNewfsCmd(label_chunk_info[here].c->private); 593 else 594 msg = MSG_NOT_APPLICABLE; 595 break; 596 597 case 'T': /* Toggle newfs state */ 598 if (label_chunk_info[here].type == PART_FILESYSTEM && 599 label_chunk_info[here].c->private) { 600 PartInfo *pi = ((PartInfo *) 601 label_chunk_info[here].c->private); 602 label_chunk_info[here].c->private = new_part( 603 pi->mountpoint, 604 !pi->newfs, 605 label_chunk_info[here].c->size); 606 safe_free(pi); 607 } 608 else 609 msg = MSG_NOT_APPLICABLE; 610 break; 611 612 case 'W': 613 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!")) { 614 int i; 615 Device **devs; 616 617 dialog_clear(); 618 end_dialog(); 619 DialogActive = FALSE; 620 devs = deviceFind(NULL, DEVICE_TYPE_DISK); 621 if (!devs) { 622 msgConfirm("Can't find any disk devicse!"); 623 break; 624 } 625 for (i = 0; devs[i] && ((Disk *)devs[i]->private); i++) { 626 if (devs[i]->enabled) 627 slice_wizard(((Disk *)devs[i]->private)); 628 } 629 DialogActive = TRUE; 630 dialog_clear(); 631 record_label_chunks(); 632 } 633 else 634 msg = "A most prudent choice!"; 635 break; 636 637 case 27: /* ESC */ 638 labeling = FALSE; 639 break; 640 641 default: 642 beep(); 643 msg = "Type F1 or ? for help"; 644 break; 645 } 646 } 647 variable_set2(DISK_LABELLED, "yes"); 648 dialog_clear(); 649 return 0; 650} 651 652 653 654