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