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