label.c revision 8549
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: disks.c,v 1.17 1995/05/11 09:01:28 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 120 devs = deviceFind(NULL, DEVICE_TYPE_DISK); 121 if (!devs) { 122 msgConfirm("No disks found!"); 123 return; 124 } 125 126 j = p = 0; 127 for (i = 0; devs[i]; i++) { 128 if (!((Disk *)devs[i]->private)->chunks) 129 msgFatal("No chunk list found for %s!", ((Disk *)devs[i]->private)->name); 130 131 /* Put the freebsd chunks first */ 132 for (c1 = ((Disk *)devs[i]->private)->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 = ((Disk *)devs[i]->private); 136 label_chunk_info[j].c = c1; 137 ++j; 138 } 139 } 140 } 141 for (i = 0; ((Disk *)devs[i]->private); i++) { 142 /* Then buzz through and pick up the partitions */ 143 for (c1 = ((Disk *)devs[i]->private)->chunks->part; c1; c1 = c1->next) { 144 if (c1->type == freebsd) { 145 for (c2 = c1->part; c2; c2 = c2->next) { 146 if (c2->type == part) { 147 if (c2->subtype == FS_SWAP) 148 label_chunk_info[j].type = PART_SWAP; 149 else 150 label_chunk_info[j].type = PART_FILESYSTEM; 151 label_chunk_info[j].d = ((Disk *)devs[i]->private); 152 label_chunk_info[j].c = c2; 153 ++j; 154 } 155 } 156 } 157 else if (c1->type == fat) { 158 label_chunk_info[j].type = PART_FAT; 159 label_chunk_info[j].d = ((Disk *)devs[i]->private); 160 label_chunk_info[j].c = c1; 161 } 162 } 163 } 164 label_chunk_info[j].d = NULL; 165 label_chunk_info[j].c = NULL; 166 if (here >= j) 167 here = j ? j - 1 : 0; 168} 169 170/* A new partition entry */ 171static PartInfo * 172new_part(char *mpoint, Boolean newfs) 173{ 174 PartInfo *ret; 175 176 ret = (PartInfo *)safe_malloc(sizeof(PartInfo)); 177 strncpy(ret->mountpoint, mpoint, FILENAME_MAX); 178 strcpy(ret->newfs_cmd, "newfs"); 179 ret->newfs = newfs; 180 return ret; 181} 182 183/* Get the mountpoint for a partition and save it away */ 184PartInfo * 185get_mountpoint(struct chunk *parent, struct chunk *me) 186{ 187 char *val; 188 PartInfo *tmp; 189 190 val = msgGetInput(me && me->private ? ((PartInfo *)me->private)->mountpoint : NULL, 191 "Please specify a mount point for the partition"); 192 if (val) { 193 /* Is it just the same value? */ 194 if (me && me->private && !strcmp(((PartInfo *)me->private)->mountpoint, val)) 195 return NULL; 196 if (check_conflict(val)) { 197 msgConfirm("You already have a mount point for %s assigned!", val); 198 return NULL; 199 } 200 else if (*val != '/') { 201 msgConfirm("Mount point must start with a / character"); 202 return NULL; 203 } 204 else if (!strcmp(val, "/")) { 205 if (parent) { 206 if (parent->flags & CHUNK_PAST_1024) { 207 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!"); 208 return NULL; 209 } 210 else if (!(parent->flags & CHUNK_BSD_COMPAT)) { 211 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."); 212 return NULL; 213 } 214 } 215 if (me) 216 me->flags |= CHUNK_IS_ROOT; 217 } 218 else if (me) 219 me->flags &= ~CHUNK_IS_ROOT; 220 safe_free(me ? me->private : NULL); 221 tmp = new_part(val, TRUE); 222 if (me) { 223 me->private = tmp; 224 me->private_free = safe_free; 225 } 226 return tmp; 227 } 228 return NULL; 229} 230 231/* Get the type of the new partiton */ 232static PartType 233get_partition_type(void) 234{ 235 char selection[20]; 236 static unsigned char *fs_types[] = { 237 "FS", 238 "A file system", 239 "Swap", 240 "A swap partition.", 241 }; 242 243 if (!dialog_menu("Please choose a partition type", 244 "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)) { 245 if (!strcmp(selection, "FS")) 246 return PART_FILESYSTEM; 247 else if (!strcmp(selection, "Swap")) 248 return PART_SWAP; 249 } 250 return PART_NONE; 251} 252 253/* If the user wants a special newfs command for this, set it */ 254static void 255getNewfsCmd(PartInfo *p) 256{ 257 char *val; 258 259 val = msgGetInput(p->newfs_cmd, 260 "Please enter the newfs command and options you'd like to use in\ncreating this file system."); 261 if (val) 262 strncpy(p->newfs_cmd, val, NEWFS_CMD_MAX); 263} 264 265 266#define MAX_MOUNT_NAME 12 267 268#define PART_PART_COL 0 269#define PART_MOUNT_COL 8 270#define PART_SIZE_COL (PART_MOUNT_COL + MAX_MOUNT_NAME + 3) 271#define PART_NEWFS_COL (PART_SIZE_COL + 7) 272#define PART_OFF 38 273 274/* How many mounted partitions to display in column before going to next */ 275#define CHUNK_COLUMN_MAX 6 276 277/* stick this all up on the screen */ 278static void 279print_label_chunks(void) 280{ 281 int i, j, srow, prow, pcol; 282 int sz; 283 284 attrset(A_REVERSE); 285 mvaddstr(0, 25, "FreeBSD Disklabel Editor"); 286 attrset(A_NORMAL); 287 288 for (i = 0; i < 2; i++) { 289 attrset(A_UNDERLINE); 290 mvaddstr(CHUNK_PART_START_ROW - 1, PART_PART_COL + (i * PART_OFF), 291 "Part"); 292 attrset(A_NORMAL); 293 294 attrset(A_UNDERLINE); 295 mvaddstr(CHUNK_PART_START_ROW - 1, PART_MOUNT_COL + (i * PART_OFF), 296 "Mount"); 297 attrset(A_NORMAL); 298 299 attrset(A_UNDERLINE); 300 mvaddstr(CHUNK_PART_START_ROW - 1, PART_SIZE_COL + (i * PART_OFF) + 2, 301 "Size"); 302 attrset(A_NORMAL); 303 304 attrset(A_UNDERLINE); 305 mvaddstr(CHUNK_PART_START_ROW - 1, PART_NEWFS_COL + (i * PART_OFF), 306 "Newfs"); 307 attrset(A_NORMAL); 308 } 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 = new_part(foo, FALSE); 354 label_chunk_info[i].c->private_free = safe_free; 355 } 356 mountpoint = ((PartInfo *)label_chunk_info[i].c->private)->mountpoint; 357 newfs = ((PartInfo *)label_chunk_info[i].c->private)->newfs ? "Y" : "N"; 358 } 359 else if (label_chunk_info[i].type == PART_SWAP) { 360 mountpoint = "swap"; 361 newfs = " "; 362 } 363 else if (label_chunk_info[i].type == PART_FAT) { 364 mountpoint = "DOS FAT"; 365 newfs = "*"; 366 } 367 else { 368 mountpoint = "<unknown>"; 369 newfs = "*"; 370 } 371 for (j = 0; j < MAX_MOUNT_NAME && mountpoint[j]; j++) 372 onestr[PART_MOUNT_COL + j] = mountpoint[j]; 373 snprintf(num, 10, "%4ldMB", label_chunk_info[i].c->size ? 374 label_chunk_info[i].c->size / 2048 : 0); 375 memcpy(onestr + PART_SIZE_COL, num, strlen(num)); 376 memcpy(onestr + PART_NEWFS_COL, newfs, strlen(newfs)); 377 onestr[PART_NEWFS_COL + strlen(newfs)] = '\0'; 378 mvaddstr(prow, pcol, onestr); 379 ++prow; 380 } 381 if (i == here) 382 attrset(A_NORMAL); 383 } 384} 385 386static void 387print_command_summary() 388{ 389 mvprintw(17, 0, 390 "The following commands are valid here (upper or lower case):"); 391 mvprintw(19, 0, "C = Create Partition D = Delete Partition M = Mount Partition"); 392 mvprintw(20, 0, "N = Newfs Options T = Toggle Newfs ESC = Finish Partitioning"); 393 mvprintw(21, 0, "The default target will be displayed in "); 394 395 attrset(A_REVERSE); 396 addstr("reverse video."); 397 attrset(A_NORMAL); 398 mvprintw(22, 0, "Use F1 or ? to get more help, arrow keys to move."); 399 move(0, 0); 400} 401 402void 403diskLabelEditor(char *str) 404{ 405 int sz, key = 0; 406 Boolean labeling; 407 char *msg = NULL; 408 PartInfo *p; 409 PartType type; 410 411 dialog_clear(); 412 labeling = TRUE; 413 keypad(stdscr, TRUE); 414 record_label_chunks(); 415 416 while (labeling) { 417 clear(); 418 print_label_chunks(); 419 print_command_summary(); 420 if (msg) { 421 attrset(A_REVERSE); mvprintw(23, 0, msg); attrset(A_NORMAL); 422 beep(); 423 msg = NULL; 424 } 425 refresh(); 426 key = toupper(getch()); 427 switch (key) { 428 429 case KEY_UP: 430 case '-': 431 if (here != 0) 432 --here; 433 break; 434 435 case KEY_DOWN: 436 case '+': 437 case '\r': 438 case '\n': 439 if (label_chunk_info[here + 1].d) 440 ++here; 441 break; 442 443 case KEY_HOME: 444 here = 0; 445 break; 446 447 case KEY_END: 448 while (label_chunk_info[here + 1].d) 449 ++here; 450 break; 451 452 case KEY_F(1): 453 case '?': 454 systemDisplayFile("disklabel.hlp"); 455 break; 456 457 case 'C': 458 if (label_chunk_info[here].type != PART_SLICE) { 459 msg = "You can only do this in a master partition (see top of screen)"; 460 break; 461 } 462 sz = space_free(label_chunk_info[here].c); 463 if (sz <= FS_MIN_SIZE) 464 msg = "Not enough space to create additional FreeBSD partition"; 465 else { 466 char *val, *cp, tmp[20]; 467 int size; 468 469 snprintf(tmp, 20, "%d", sz); 470 val = msgGetInput(tmp, "Please specify the size for new FreeBSD partition in blocks, or append\na trailing `M' for megabytes (e.g. 20M)."); 471 if (val && (size = strtol(val, &cp, 0)) > 0) { 472 struct chunk *tmp; 473 u_long flags = 0; 474 475 if (*cp && toupper(*cp) == 'M') 476 size *= 2048; 477 478 type = get_partition_type(); 479 if (type == PART_NONE) 480 break; 481 else if (type == PART_FILESYSTEM) { 482 if ((p = get_mountpoint(label_chunk_info[here].c, NULL)) == NULL) 483 break; 484 else if (!strcmp(p->mountpoint, "/")) 485 flags |= CHUNK_IS_ROOT; 486 else 487 flags &= ~CHUNK_IS_ROOT; 488 } 489 else 490 p = NULL; 491 492 tmp = Create_Chunk_DWIM(label_chunk_info[here].d, 493 label_chunk_info[here].c, 494 size, part, 495 (type == PART_SWAP) ? FS_SWAP : FS_BSDFFS, 496 flags); 497 if (!tmp) 498 msgConfirm("Unable to create the partition. Too big?"); 499 else { 500 tmp->private = p; 501 tmp->private_free = safe_free; 502 record_label_chunks(); 503 } 504 } 505 } 506 break; 507 508 case 'D': /* delete */ 509 if (label_chunk_info[here].type == PART_SLICE) { 510 msg = MSG_NOT_APPLICABLE; 511 break; 512 } 513 else if (label_chunk_info[here].type == PART_FAT) { 514 msg = "Use the Disk Partition Editor to delete this"; 515 break; 516 } 517 Delete_Chunk(label_chunk_info[here].d, label_chunk_info[here].c); 518 record_label_chunks(); 519 break; 520 521 case 'M': /* mount */ 522 switch(label_chunk_info[here].type) { 523 case PART_SLICE: 524 msg = MSG_NOT_APPLICABLE; 525 break; 526 527 case PART_SWAP: 528 msg = "You don't need to specify a mountpoint for a swap partition."; 529 break; 530 531 case PART_DOS: 532 case PART_FILESYSTEM: 533 p = get_mountpoint(NULL, label_chunk_info[here].c); 534 if (p) { 535 p->newfs = FALSE; 536 record_label_chunks(); 537 } 538 break; 539 540 default: 541 msgFatal("Bogus partition under cursor???"); 542 break; 543 } 544 break; 545 546 case 'N': /* Set newfs options */ 547 if (label_chunk_info[here].c->private && 548 ((PartInfo *)label_chunk_info[here].c->private)->newfs) 549 getNewfsCmd(label_chunk_info[here].c->private); 550 else 551 msg = MSG_NOT_APPLICABLE; 552 break; 553 554 case 'T': /* Toggle newfs state */ 555 if (label_chunk_info[here].type == PART_FILESYSTEM && 556 label_chunk_info[here].c->private) 557 ((PartInfo *)label_chunk_info[here].c->private)->newfs = 558 !((PartInfo *)label_chunk_info[here].c->private)->newfs; 559 else 560 msg = MSG_NOT_APPLICABLE; 561 break; 562 563 case 'W': 564 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!")) { 565 int i; 566 Device **devs; 567 568 clear(); 569 dialog_clear(); 570 end_dialog(); 571 DialogActive = FALSE; 572 devs = deviceFind(NULL, DEVICE_TYPE_DISK); 573 if (!devs) { 574 msgConfirm("Can't find any disk devicse!"); 575 break; 576 } 577 for (i = 0; ((Disk *)devs[i]->private); i++) 578 slice_wizard(((Disk *)devs[i]->private)); 579 clear(); 580 dialog_clear(); 581 DialogActive = TRUE; 582 record_label_chunks(); 583 } 584 else 585 msg = "A most prudent choice!"; 586 break; 587 588 case 27: /* ESC */ 589 labeling = FALSE; 590 break; 591 592 default: 593 beep(); 594 msg = "Type F1 or ? for help"; 595 break; 596 } 597 } 598 variable_set2(DISK_LABELLED, "yes"); 599} 600 601 602 603