label.c revision 8668
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.13 1995/05/21 01:56:02 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 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, u_long size) 182{ 183 PartInfo *ret; 184 u_long target,divisor; 185 186 ret = (PartInfo *)safe_malloc(sizeof(PartInfo)); 187 strncpy(ret->mountpoint, mpoint, FILENAME_MAX); 188 strcpy(ret->newfs_cmd, "newfs"); 189 ret->newfs = newfs; 190 for(target = size; target; target--) { 191 for(divisor = 4096 ; divisor > 1023; divisor--) { 192 if (!(target % divisor)) { 193 sprintf(ret->newfs_cmd+strlen(ret->newfs_cmd), 194 " -u %ld",divisor); 195 return ret; 196 } 197 } 198 } 199 return ret; 200} 201 202/* Get the mountpoint for a partition and save it away */ 203PartInfo * 204get_mountpoint(struct chunk *old) 205{ 206 char *val; 207 PartInfo *tmp; 208 209 val = msgGetInput(old && old->private ? ((PartInfo *)old->private)->mountpoint : NULL, 210 "Please specify a mount point for the partition"); 211 if (val) { 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 else if (*val != '/') { 220 msgConfirm("Mount point must start with a / character"); 221 return NULL; 222 } 223 else if (!strcmp(val, "/")) { 224 if (old) 225 old->flags |= CHUNK_IS_ROOT; 226 } 227 else if (old) 228 old->flags &= ~CHUNK_IS_ROOT; 229 safe_free(old ? old->private : NULL); 230 tmp = new_part(val, TRUE, old->size); 231 if (old) { 232 old->private = tmp; 233 old->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 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 attrset(A_NORMAL); 296 297 for (i = 0; i < 2; i++) { 298 mvaddstr(CHUNK_PART_START_ROW - 2, PART_PART_COL + (i * PART_OFF), "Part"); 299 mvaddstr(CHUNK_PART_START_ROW - 1, PART_PART_COL + (i * PART_OFF), "----"); 300 301 mvaddstr(CHUNK_PART_START_ROW - 2, PART_MOUNT_COL + (i * PART_OFF), "Mount"); 302 mvaddstr(CHUNK_PART_START_ROW - 1, PART_MOUNT_COL + (i * PART_OFF), "-----"); 303 304 mvaddstr(CHUNK_PART_START_ROW - 2, PART_SIZE_COL + (i * PART_OFF) + 2, "Size"); 305 mvaddstr(CHUNK_PART_START_ROW - 1, PART_SIZE_COL + (i * PART_OFF) + 2, "----"); 306 307 mvaddstr(CHUNK_PART_START_ROW - 2, PART_NEWFS_COL + (i * PART_OFF), "Newfs"); 308 mvaddstr(CHUNK_PART_START_ROW - 1, PART_NEWFS_COL + (i * PART_OFF), "-----"); 309 } 310 srow = CHUNK_SLICE_START_ROW; 311 prow = CHUNK_PART_START_ROW; 312 pcol = 0; 313 314 for (i = 0; label_chunk_info[i].d; i++) { 315 if (i == here) 316 attrset(A_REVERSE); 317 /* Is it a slice entry displayed at the top? */ 318 if (label_chunk_info[i].type == PART_SLICE) { 319 sz = space_free(label_chunk_info[i].c); 320 mvprintw(srow++, 0, 321 "Disk: %s\tPartition name: %s\tFree: %d blocks (%dMB)", 322 label_chunk_info[i].d->name, 323 label_chunk_info[i].c->name, sz, (sz / 2048)); 324 } 325 /* Otherwise it's a DOS, swap or filesystem entry, at the bottom */ 326 else { 327 char onestr[PART_OFF], num[10], *mountpoint, *newfs; 328 329 /* 330 * We copy this into a blank-padded string so that it looks like 331 * a solid bar in reverse-video 332 */ 333 memset(onestr, ' ', PART_OFF - 1); 334 onestr[PART_OFF - 1] = '\0'; 335 /* Go for two columns */ 336 if (prow == (CHUNK_PART_START_ROW + CHUNK_COLUMN_MAX)) { 337 pcol = PART_OFF; 338 prow = CHUNK_PART_START_ROW; 339 } 340 memcpy(onestr + PART_PART_COL, label_chunk_info[i].c->name, 341 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) { 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 = 354 new_part(foo, FALSE,label_chunk_info[i].c->size); 355 label_chunk_info[i].c->private_free = safe_free; 356 } 357 mountpoint = ((PartInfo *)label_chunk_info[i].c->private)->mountpoint; 358 newfs = ((PartInfo *)label_chunk_info[i].c->private)->newfs ? "Y" : "N"; 359 } 360 else if (label_chunk_info[i].type == PART_SWAP) { 361 mountpoint = "swap"; 362 newfs = " "; 363 } 364 else if (label_chunk_info[i].type == PART_FAT) { 365 mountpoint = "DOS FAT"; 366 newfs = "*"; 367 } 368 else { 369 mountpoint = "<unknown>"; 370 newfs = "*"; 371 } 372 for (j = 0; j < MAX_MOUNT_NAME && mountpoint[j]; j++) 373 onestr[PART_MOUNT_COL + j] = mountpoint[j]; 374 snprintf(num, 10, "%4ldMB", label_chunk_info[i].c->size ? 375 label_chunk_info[i].c->size / 2048 : 0); 376 memcpy(onestr + PART_SIZE_COL, num, strlen(num)); 377 memcpy(onestr + PART_NEWFS_COL, newfs, strlen(newfs)); 378 onestr[PART_NEWFS_COL + strlen(newfs)] = '\0'; 379 mvaddstr(prow, pcol, onestr); 380 ++prow; 381 } 382 if (i == here) 383 attrset(A_NORMAL); 384 } 385} 386 387static void 388print_command_summary() 389{ 390 mvprintw(17, 0, 391 "The following commands are valid here (upper or lower case):"); 392 mvprintw(19, 0, "C = Create Partition D = Delete Partition M = Mount Partition"); 393 mvprintw(20, 0, "N = Newfs Options T = Toggle Newfs ESC = Exit this screen"); 394 mvprintw(21, 0, "The default target will be displayed in "); 395 396 attrset(A_REVERSE); 397 addstr("reverse video."); 398 attrset(A_NORMAL); 399 mvprintw(22, 0, "Use F1 or ? to get more help, arrow keys to move."); 400 move(0, 0); 401} 402 403int 404diskLabelEditor(char *str) 405{ 406 int sz, key = 0; 407 Boolean labeling; 408 char *msg = NULL; 409 PartInfo *p; 410 PartType type; 411 412 labeling = TRUE; 413 keypad(stdscr, TRUE); 414 record_label_chunks(); 415 416 if (!getenv(DISK_PARTITIONED)) { 417 msgConfirm("You need to partition your disk(s) before you can assign disk labels."); 418 return 0; 419 } 420 clear(); 421 while (labeling) { 422 print_label_chunks(); 423 print_command_summary(); 424 if (msg) { 425 attrset(A_REVERSE); mvprintw(23, 0, msg); attrset(A_NORMAL); 426 beep(); 427 msg = NULL; 428 } 429 refresh(); 430 key = toupper(getch()); 431 switch (key) { 432 433 case KEY_UP: 434 case '-': 435 if (here != 0) 436 --here; 437 break; 438 439 case KEY_DOWN: 440 case '+': 441 case '\r': 442 case '\n': 443 if (label_chunk_info[here + 1].d) 444 ++here; 445 break; 446 447 case KEY_HOME: 448 here = 0; 449 break; 450 451 case KEY_END: 452 while (label_chunk_info[here + 1].d) 453 ++here; 454 break; 455 456 case KEY_F(1): 457 case '?': 458 systemDisplayFile("disklabel.hlp"); 459 break; 460 461 case 'C': 462 if (label_chunk_info[here].type != PART_SLICE) { 463 msg = "You can only do this in a master partition (see top of screen)"; 464 break; 465 } 466 sz = space_free(label_chunk_info[here].c); 467 if (sz <= FS_MIN_SIZE) 468 msg = "Not enough space to create additional FreeBSD partition"; 469 else { 470 char *val, *cp, tmp[20]; 471 int size; 472 473 snprintf(tmp, 20, "%d", sz); 474 val = msgGetInput(tmp, "Please specify the size for new FreeBSD partition in blocks, or append\na trailing `M' for megabytes (e.g. 20M)."); 475 if (val && (size = strtol(val, &cp, 0)) > 0) { 476 struct chunk *tmp; 477 u_long flags = 0; 478 479 if (*cp && toupper(*cp) == 'M') 480 size *= 2048; 481 482 type = get_partition_type(); 483 if (type == PART_NONE) 484 break; 485 else if (type == PART_FILESYSTEM) { 486 if ((p = get_mountpoint(NULL)) == NULL) 487 break; 488 else if (!strcmp(p->mountpoint, "/")) 489 flags |= CHUNK_IS_ROOT; 490 else 491 flags &= ~CHUNK_IS_ROOT; 492 } 493 else 494 p = NULL; 495 496 if ((flags & CHUNK_IS_ROOT) && !(label_chunk_info[here].c->flags & CHUNK_BSD_COMPAT)) { 497 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!"); 498 break; 499 } 500 tmp = Create_Chunk_DWIM(label_chunk_info[here].d, 501 label_chunk_info[here].c, 502 size, part, 503 (type == PART_SWAP) ? FS_SWAP : FS_BSDFFS, 504 flags); 505 if (!tmp) { 506 msgConfirm("Unable to create the partition. Too big?"); 507 break; 508 } 509 else if ((flags & CHUNK_IS_ROOT) && (tmp->flags & CHUNK_PAST_1024)) { 510 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!"); 511 Delete_Chunk(label_chunk_info[here].d, tmp); 512 break; 513 } 514 tmp->private = p; 515 tmp->private_free = safe_free; 516 record_label_chunks(); 517 } 518 } 519 break; 520 521 case 'D': /* delete */ 522 if (label_chunk_info[here].type == PART_SLICE) { 523 msg = MSG_NOT_APPLICABLE; 524 break; 525 } 526 else if (label_chunk_info[here].type == PART_FAT) { 527 msg = "Use the Disk Partition Editor to delete this"; 528 break; 529 } 530 Delete_Chunk(label_chunk_info[here].d, label_chunk_info[here].c); 531 record_label_chunks(); 532 break; 533 534 case 'M': /* mount */ 535 switch(label_chunk_info[here].type) { 536 case PART_SLICE: 537 msg = MSG_NOT_APPLICABLE; 538 break; 539 540 case PART_SWAP: 541 msg = "You don't need to specify a mountpoint for a swap partition."; 542 break; 543 544 case PART_FAT: 545 case PART_FILESYSTEM: 546 p = get_mountpoint(label_chunk_info[here].c); 547 if (p) { 548 p->newfs = FALSE; 549 record_label_chunks(); 550 } 551 break; 552 553 default: 554 msgFatal("Bogus partition under cursor???"); 555 break; 556 } 557 break; 558 559 case 'N': /* Set newfs options */ 560 if (label_chunk_info[here].c->private && 561 ((PartInfo *)label_chunk_info[here].c->private)->newfs) 562 getNewfsCmd(label_chunk_info[here].c->private); 563 else 564 msg = MSG_NOT_APPLICABLE; 565 break; 566 567 case 'T': /* Toggle newfs state */ 568 if (label_chunk_info[here].type == PART_FILESYSTEM && 569 label_chunk_info[here].c->private) 570 ((PartInfo *)label_chunk_info[here].c->private)->newfs = 571 !((PartInfo *)label_chunk_info[here].c->private)->newfs; 572 else 573 msg = MSG_NOT_APPLICABLE; 574 break; 575 576 case 'W': 577 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!")) { 578 int i; 579 Device **devs; 580 581 dialog_clear(); 582 end_dialog(); 583 DialogActive = FALSE; 584 devs = deviceFind(NULL, DEVICE_TYPE_DISK); 585 if (!devs) { 586 msgConfirm("Can't find any disk devicse!"); 587 break; 588 } 589 for (i = 0; devs[i] && ((Disk *)devs[i]->private); i++) { 590 if (devs[i]->enabled) 591 slice_wizard(((Disk *)devs[i]->private)); 592 } 593 DialogActive = TRUE; 594 dialog_clear(); 595 record_label_chunks(); 596 } 597 else 598 msg = "A most prudent choice!"; 599 break; 600 601 case 27: /* ESC */ 602 labeling = FALSE; 603 break; 604 605 default: 606 beep(); 607 msg = "Type F1 or ? for help"; 608 break; 609 } 610 } 611 variable_set2(DISK_LABELLED, "yes"); 612 dialog_clear(); 613 refresh(); 614 return 0; 615} 616 617 618 619