label.c revision 8581
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.4 1995/05/17 15:41:52 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 0 215 if (parent) { 216 if (parent->flags & CHUNK_PAST_1024) { 217 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!"); 218 return NULL; 219 } 220 else if (!(parent->flags & CHUNK_BSD_COMPAT)) { 221 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."); 222 return NULL; 223 } 224 } 225#endif 226 if (me) 227 me->flags |= CHUNK_IS_ROOT; 228 } 229 else if (me) 230 me->flags &= ~CHUNK_IS_ROOT; 231 safe_free(me ? me->private : NULL); 232 tmp = new_part(val, TRUE); 233 if (me) { 234 me->private = tmp; 235 me->private_free = safe_free; 236 } 237 return tmp; 238 } 239 return NULL; 240} 241 242/* Get the type of the new partiton */ 243static PartType 244get_partition_type(void) 245{ 246 char selection[20]; 247 static unsigned char *fs_types[] = { 248 "FS", 249 "A file system", 250 "Swap", 251 "A swap partition.", 252 }; 253 254 if (!dialog_menu("Please choose a partition type", 255 "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)) { 256 if (!strcmp(selection, "FS")) 257 return PART_FILESYSTEM; 258 else if (!strcmp(selection, "Swap")) 259 return PART_SWAP; 260 } 261 return PART_NONE; 262} 263 264/* If the user wants a special newfs command for this, set it */ 265static void 266getNewfsCmd(PartInfo *p) 267{ 268 char *val; 269 270 val = msgGetInput(p->newfs_cmd, 271 "Please enter the newfs command and options you'd like to use in\ncreating this file system."); 272 if (val) 273 strncpy(p->newfs_cmd, val, NEWFS_CMD_MAX); 274} 275 276 277#define MAX_MOUNT_NAME 12 278 279#define PART_PART_COL 0 280#define PART_MOUNT_COL 8 281#define PART_SIZE_COL (PART_MOUNT_COL + MAX_MOUNT_NAME + 3) 282#define PART_NEWFS_COL (PART_SIZE_COL + 7) 283#define PART_OFF 38 284 285/* How many mounted partitions to display in column before going to next */ 286#define CHUNK_COLUMN_MAX 6 287 288/* stick this all up on the screen */ 289static void 290print_label_chunks(void) 291{ 292 int i, j, srow, prow, pcol; 293 int sz; 294 295 dialog_clear(); 296 attrset(A_REVERSE); 297 mvaddstr(0, 25, "FreeBSD Disklabel Editor"); 298 attrset(A_NORMAL); 299 300 for (i = 0; i < 2; i++) { 301 attrset(A_UNDERLINE); 302 mvaddstr(CHUNK_PART_START_ROW - 1, PART_PART_COL + (i * PART_OFF), 303 "Part"); 304 attrset(A_NORMAL); 305 306 attrset(A_UNDERLINE); 307 mvaddstr(CHUNK_PART_START_ROW - 1, PART_MOUNT_COL + (i * PART_OFF), 308 "Mount"); 309 attrset(A_NORMAL); 310 311 attrset(A_UNDERLINE); 312 mvaddstr(CHUNK_PART_START_ROW - 1, PART_SIZE_COL + (i * PART_OFF) + 2, 313 "Size"); 314 attrset(A_NORMAL); 315 316 attrset(A_UNDERLINE); 317 mvaddstr(CHUNK_PART_START_ROW - 1, PART_NEWFS_COL + (i * PART_OFF), 318 "Newfs"); 319 attrset(A_NORMAL); 320 } 321 322 srow = CHUNK_SLICE_START_ROW; 323 prow = CHUNK_PART_START_ROW; 324 pcol = 0; 325 326 for (i = 0; label_chunk_info[i].d; i++) { 327 if (i == here) 328 attrset(A_REVERSE); 329 /* Is it a slice entry displayed at the top? */ 330 if (label_chunk_info[i].type == PART_SLICE) { 331 sz = space_free(label_chunk_info[i].c); 332 mvprintw(srow++, 0, 333 "Disk: %s\tPartition name: %s\tFree: %d blocks (%dMB)", 334 label_chunk_info[i].d->name, 335 label_chunk_info[i].c->name, sz, (sz / 2048)); 336 } 337 /* Otherwise it's a DOS, swap or filesystem entry, at the bottom */ 338 else { 339 char onestr[PART_OFF], num[10], *mountpoint, *newfs; 340 341 /* 342 * We copy this into a blank-padded string so that it looks like 343 * a solid bar in reverse-video 344 */ 345 memset(onestr, ' ', PART_OFF - 1); 346 onestr[PART_OFF - 1] = '\0'; 347 /* Go for two columns */ 348 if (prow == (CHUNK_PART_START_ROW + CHUNK_COLUMN_MAX)) { 349 pcol = PART_OFF; 350 prow = CHUNK_PART_START_ROW; 351 } 352 memcpy(onestr + PART_PART_COL, label_chunk_info[i].c->name, 353 strlen(label_chunk_info[i].c->name)); 354 /* If it's a filesystem, display the mountpoint */ 355 if (label_chunk_info[i].type == PART_FILESYSTEM) { 356 if (label_chunk_info[i].c->private == NULL) { 357 static int mnt = 0; 358 char foo[10]; 359 360 /* 361 * Hmm! A partition that must have already been here. 362 * Fill in a fake mountpoint and register it 363 */ 364 sprintf(foo, "/mnt%d", mnt++); 365 label_chunk_info[i].c->private = new_part(foo, FALSE); 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 / 2048 : 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 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 else { 480 char *val, *cp, tmp[20]; 481 int size; 482 483 snprintf(tmp, 20, "%d", sz); 484 val = msgGetInput(tmp, "Please specify the size for new FreeBSD partition in blocks, or append\na trailing `M' for megabytes (e.g. 20M)."); 485 if (val && (size = strtol(val, &cp, 0)) > 0) { 486 struct chunk *tmp; 487 u_long flags = 0; 488 489 if (*cp && toupper(*cp) == 'M') 490 size *= 2048; 491 492 type = get_partition_type(); 493 if (type == PART_NONE) 494 break; 495 else if (type == PART_FILESYSTEM) { 496 if ((p = get_mountpoint(label_chunk_info[here].c, NULL)) == NULL) 497 break; 498 else if (!strcmp(p->mountpoint, "/")) 499 flags |= CHUNK_IS_ROOT; 500 else 501 flags &= ~CHUNK_IS_ROOT; 502 } 503 else 504 p = NULL; 505 506 tmp = Create_Chunk_DWIM(label_chunk_info[here].d, 507 label_chunk_info[here].c, 508 size, part, 509 (type == PART_SWAP) ? FS_SWAP : FS_BSDFFS, 510 flags); 511 if (!tmp) 512 msgConfirm("Unable to create the partition. Too big?"); 513 else { 514 tmp->private = p; 515 tmp->private_free = safe_free; 516 record_label_chunks(); 517 } 518 } 519 } 520 break; 521 522 case 'D': /* delete */ 523 if (label_chunk_info[here].type == PART_SLICE) { 524 msg = MSG_NOT_APPLICABLE; 525 break; 526 } 527 else if (label_chunk_info[here].type == PART_FAT) { 528 msg = "Use the Disk Partition Editor to delete this"; 529 break; 530 } 531 Delete_Chunk(label_chunk_info[here].d, label_chunk_info[here].c); 532 record_label_chunks(); 533 break; 534 535 case 'M': /* mount */ 536 switch(label_chunk_info[here].type) { 537 case PART_SLICE: 538 msg = MSG_NOT_APPLICABLE; 539 break; 540 541 case PART_SWAP: 542 msg = "You don't need to specify a mountpoint for a swap partition."; 543 break; 544 545 case PART_FAT: 546 case PART_FILESYSTEM: 547 p = get_mountpoint(NULL, label_chunk_info[here].c); 548 if (p) { 549 p->newfs = FALSE; 550 record_label_chunks(); 551 } 552 break; 553 554 default: 555 msgFatal("Bogus partition under cursor???"); 556 break; 557 } 558 break; 559 560 case 'N': /* Set newfs options */ 561 if (label_chunk_info[here].c->private && 562 ((PartInfo *)label_chunk_info[here].c->private)->newfs) 563 getNewfsCmd(label_chunk_info[here].c->private); 564 else 565 msg = MSG_NOT_APPLICABLE; 566 break; 567 568 case 'T': /* Toggle newfs state */ 569 if (label_chunk_info[here].type == PART_FILESYSTEM && 570 label_chunk_info[here].c->private) 571 ((PartInfo *)label_chunk_info[here].c->private)->newfs = 572 !((PartInfo *)label_chunk_info[here].c->private)->newfs; 573 else 574 msg = MSG_NOT_APPLICABLE; 575 break; 576 577 case 'W': 578 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!")) { 579 int i; 580 Device **devs; 581 582 dialog_clear(); 583 end_dialog(); 584 DialogActive = FALSE; 585 devs = deviceFind(NULL, DEVICE_TYPE_DISK); 586 if (!devs) { 587 msgConfirm("Can't find any disk devicse!"); 588 break; 589 } 590 for (i = 0; ((Disk *)devs[i]->private); i++) 591 slice_wizard(((Disk *)devs[i]->private)); 592 dialog_clear(); 593 DialogActive = TRUE; 594 record_label_chunks(); 595 } 596 else 597 msg = "A most prudent choice!"; 598 break; 599 600 case 27: /* ESC */ 601 labeling = FALSE; 602 break; 603 604 default: 605 beep(); 606 msg = "Type F1 or ? for help"; 607 break; 608 } 609 } 610 variable_set2(DISK_LABELLED, "yes"); 611 dialog_clear(); 612 refresh(); 613 return 0; 614} 615 616 617 618