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