disks.c revision 8331
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.6 1995/05/07 05:58:56 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 * I make some pretty gross assumptions about having a max of 50 chunks 50 * total - 8 slices and 42 partitions. I can't easily display many more 51 * than that on the screen at once! 52 * 53 * For 2.1 I'll revisit this and try to make it more dynamic, but since 54 * this will catch 99.99% of all possible cases, I'm not too worried. 55 */ 56 57#define MAX_CHUNKS 50 58 59/* Where to start printing the freebsd slices */ 60#define CHUNK_SLICE_START_ROW 2 61#define CHUNK_PART_START_ROW 10 62 63/* The smallest filesystem we're willing to create */ 64#define FS_MIN_SIZE 2048 65 66static struct { 67 struct disk *d; 68 struct chunk *c; 69 PartType type; 70} fbsd_chunk_info[MAX_CHUNKS + 1]; 71static int current_chunk; 72 73 74/* If the given disk has a root partition on it, return TRUE */ 75static Boolean 76contains_root_partition(struct disk *d) 77{ 78 struct chunk *c1; 79 80 if (!d->chunks) 81 msgFatal("Disk %s has no chunks!", d->name); 82 c1 = d->chunks->part; 83 while (c1) { 84 if (c1->type == freebsd) { 85 struct chunk *c2 = c1->part; 86 87 while (c2) { 88 if (c2->flags & CHUNK_IS_ROOT) 89 return TRUE; 90 c2 = c2->next; 91 } 92 } 93 c1 = c1->next; 94 } 95 return FALSE; 96} 97 98/* Locate a chunk by position on a specific disk somewhere */ 99static struct chunk * 100find_chunk_by_loc(struct disk *d, u_long offset, u_long size) 101{ 102 struct chunk *c1, *c2; 103 104 if (!d->chunks) 105 msgFatal("No chunk list for %s!", d->name); 106 107 for (c1 = d->chunks->part; c1; c1 = c1->next) { 108 if (c1->type == freebsd) { 109 if (c1->offset == offset && c1->size == size) 110 return c1; 111 for (c2 = c1->part; c2; c2 = c2->next) { 112 if (c2->type == part && c2->offset == offset && 113 c2->size == size) 114 return c2; 115 } 116 } 117 } 118 return NULL; 119} 120 121static Boolean 122check_conflict(char *name) 123{ 124 int i; 125 126 for (i = 0; fbsd_chunk_info[i].d; i++) 127 if (fbsd_chunk_info[i].type == PART_FILESYSTEM && 128 !strcmp(fbsd_chunk_info[i].c->name, name)) 129 return TRUE; 130 return FALSE; 131} 132 133static int 134space_free(struct chunk *c) 135{ 136 struct chunk *c1 = c->part; 137 int sz = c->size; 138 139 while (c1) { 140 if (c1->type != unused) 141 sz -= c1->size; 142 c1 = c1->next; 143 } 144 if (sz < 0) 145 msgFatal("Partitions are larger than actual chunk??"); 146 return sz; 147} 148 149static void 150record_fbsd_chunks(struct disk **disks) 151{ 152 int i, j, p; 153 struct chunk *c1, *c2; 154 155 j = p = 0; 156 for (i = 0; disks[i]; i++) { 157 if (!disks[i]->chunks) 158 msgFatal("No chunk list found for %s!", disks[i]->name); 159 160 /* Put the freebsd chunks first */ 161 for (c1 = disks[i]->chunks->part; c1; c1 = c1->next) { 162 if (c1->type == freebsd) { 163 fbsd_chunk_info[j].type = PART_SLICE; 164 fbsd_chunk_info[j].d = disks[i]; 165 fbsd_chunk_info[j].c = c1; 166 ++j; 167 } 168 } 169 } 170 for (i = 0; disks[i]; i++) { 171 /* Then buzz through and pick up the partitions */ 172 for (c1 = disks[i]->chunks->part; c1; c1 = c1->next) { 173 if (c1->type == freebsd) { 174 for (c2 = c1->part; c2; c2 = c2->next) { 175 if (c2->type == part) { 176 if (c2->subtype == FS_SWAP) 177 fbsd_chunk_info[j].type = PART_SWAP; 178 else 179 fbsd_chunk_info[j].type = PART_FILESYSTEM; 180 fbsd_chunk_info[j].d = disks[i]; 181 fbsd_chunk_info[j].c = c2; 182 ++j; 183 } 184 } 185 } 186 } 187 } 188 fbsd_chunk_info[j].d = NULL; 189 fbsd_chunk_info[j].c = NULL; 190 if (current_chunk >= j) 191 current_chunk = j ? j - 1 : 0; 192} 193 194static PartInfo * 195new_part(char *mpoint, Boolean newfs) 196{ 197 PartInfo *ret; 198 199 ret = (PartInfo *)safe_malloc(sizeof(PartInfo)); 200 strncpy(ret->mountpoint, mpoint, FILENAME_MAX); 201 ret->newfs = newfs; 202 return ret; 203} 204 205PartInfo * 206get_mountpoint(struct chunk *c) 207{ 208 char *val; 209 PartInfo *tmp; 210 211 val = msgGetInput(c && c->private ? 212 ((PartInfo *)c->private)->mountpoint : NULL, 213 "Please specify a mount point for the partition"); 214 if (val) { 215 if (check_conflict(val)) { 216 msgConfirm("You already have a mountpoint for %s assigned!", val); 217 return NULL; 218 } 219 else if (!strcmp(val, "/")) { 220 if (c && c->flags & CHUNK_PAST_1024) { 221msgConfirm("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!"); 222 return NULL; 223 } 224 else if (c) 225 c->flags |= CHUNK_IS_ROOT; 226 } 227 else if (c) 228 c->flags &= ~CHUNK_IS_ROOT; 229 safe_free(c ? c->private : NULL); 230 tmp = new_part(val, TRUE); 231 if (c) { 232 c->private = tmp; 233 c->private_free = safe_free; 234 } 235 return tmp; 236 } 237 return NULL; 238} 239 240static PartType 241get_partition_type(void) 242{ 243 char selection[20]; 244 static unsigned char *fs_types[] = { 245 "FS", 246 "A file system", 247 "Swap", 248 "A swap partition.", 249 }; 250 251 if (!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 (!strcmp(selection, "FS")) 254 return PART_FILESYSTEM; 255 else if (!strcmp(selection, "Swap")) 256 return PART_SWAP; 257 } 258 return PART_NONE; 259} 260 261#define MAX_MOUNT_NAME 12 262 263#define PART_PART_COL 0 264#define PART_MOUNT_COL 8 265#define PART_SIZE_COL (PART_MOUNT_COL + MAX_MOUNT_NAME + 4) 266#define PART_NEWFS_COL (PART_SIZE_COL + 8) 267#define PART_OFF 42 268 269/* How many mounted partitions to display in column before going to next */ 270#define CHUNK_COLUMN_MAX 6 271 272static void 273print_fbsd_chunks(void) 274{ 275 int i, j, srow, prow, pcol; 276 int sz; 277 278 attrset(A_REVERSE); 279 mvaddstr(0, 25, "FreeBSD Partition Editor"); 280 attrset(A_NORMAL); 281 282 for (i = 0; i < 2; i++) { 283 attrset(A_UNDERLINE); 284 mvaddstr(CHUNK_PART_START_ROW - 1, PART_PART_COL + (i * PART_OFF), 285 "Part"); 286 attrset(A_NORMAL); 287 288 attrset(A_UNDERLINE); 289 mvaddstr(CHUNK_PART_START_ROW - 1, PART_MOUNT_COL + (i * PART_OFF), 290 "Mount"); 291 attrset(A_NORMAL); 292 293 attrset(A_UNDERLINE); 294 mvaddstr(CHUNK_PART_START_ROW - 1, PART_SIZE_COL + (i * PART_OFF) + 2, 295 "Size"); 296 attrset(A_NORMAL); 297 298 attrset(A_UNDERLINE); 299 mvaddstr(CHUNK_PART_START_ROW - 1, PART_NEWFS_COL + (i * PART_OFF), 300 "Newfs"); 301 attrset(A_NORMAL); 302 } 303 304 srow = CHUNK_SLICE_START_ROW; 305 prow = CHUNK_PART_START_ROW; 306 307 for (i = 0; fbsd_chunk_info[i].d; i++) { 308 if (i == current_chunk) 309 attrset(A_REVERSE); 310 /* Is it a slice entry displayed at the top? */ 311 if (fbsd_chunk_info[i].type == PART_SLICE) { 312 sz = space_free(fbsd_chunk_info[i].c); 313 mvprintw(srow++, 0, 314 "Disk: %s\tPartition name: %s\tFree: %d blocks (%dMB)", 315 fbsd_chunk_info[i].d->name, 316 fbsd_chunk_info[i].c->name, sz, (sz / 2048)); 317 } 318 /* Otherwise it's a swap or filesystem entry, at the bottom */ 319 else { 320 char onestr[PART_OFF], num[10], *mountpoint, *newfs; 321 322 memset(onestr, ' ', PART_OFF - 1); 323 onestr[PART_OFF - 1] = '\0'; 324 /* Go for two columns */ 325 if (prow == (CHUNK_PART_START_ROW + CHUNK_COLUMN_MAX)) 326 pcol = PART_OFF; 327 else 328 pcol = 0; 329 memcpy(onestr + PART_PART_COL, fbsd_chunk_info[i].c->name, 330 strlen(fbsd_chunk_info[i].c->name)); 331 if (fbsd_chunk_info[i].type == PART_FILESYSTEM) { 332 if (fbsd_chunk_info[i].c->private) { 333 mountpoint = ((PartInfo *)fbsd_chunk_info[i].c->private)->mountpoint; 334 newfs = ((PartInfo *)fbsd_chunk_info[i].c->private)->newfs ? "Y" : "N"; 335 } 336 else { 337 fbsd_chunk_info[i].c->private = new_part("", FALSE); 338 fbsd_chunk_info[i].c->private_free = safe_free; 339 mountpoint = " "; 340 newfs = "N"; 341 } 342 } 343 else { 344 mountpoint = "swap"; 345 newfs = " "; 346 } 347 for (j = 0; j < MAX_MOUNT_NAME && mountpoint[j]; j++) 348 onestr[PART_MOUNT_COL + j] = mountpoint[j]; 349 sprintf(num, "%4ldMB", fbsd_chunk_info[i].c->size ? 350 fbsd_chunk_info[i].c->size / 2048 : 0); 351 memcpy(onestr + PART_SIZE_COL, num, strlen(num)); 352 memcpy(onestr + PART_NEWFS_COL, newfs, strlen(newfs)); 353 mvaddstr(prow, pcol, onestr); 354 ++prow; 355 } 356 if (i == current_chunk) 357 attrset(A_NORMAL); 358 } 359} 360 361static void 362print_command_summary() 363{ 364 mvprintw(17, 0, 365 "The following commands are valid here (upper or lower case):"); 366 mvprintw(19, 0, "C = Create Partition D = Delete Partition M = Mount Partition"); 367 mvprintw(20, 0, "N = Newfs Options T = Toggle Newfs ESC = Finish Partitioning"); 368 mvprintw(21, 0, "The default target will be displayed in "); 369 370 attrset(A_REVERSE); 371 addstr("reverse video"); 372 attrset(A_NORMAL); 373 mvprintw(22, 0, "Use F1 or ? to get more help"); 374 move(0, 0); 375} 376 377void 378partition_disks(struct disk **disks) 379{ 380 int sz, key = 0; 381 Boolean partitioning; 382 char *msg = NULL; 383 PartInfo *p; 384 PartType type; 385 386 dialog_clear(); 387 partitioning = TRUE; 388 keypad(stdscr, TRUE); 389 record_fbsd_chunks(disks); 390 391 while (partitioning) { 392 clear(); 393 print_fbsd_chunks(); 394 print_command_summary(); 395 if (msg) { 396 attrset(A_REVERSE); mvprintw(23, 0, msg); attrset(A_NORMAL); 397 beep(); 398 msg = NULL; 399 } 400 refresh(); 401 key = toupper(getch()); 402 switch (key) { 403 404 case KEY_UP: 405 case '-': 406 if (current_chunk != 0) 407 --current_chunk; 408 break; 409 410 case KEY_DOWN: 411 case '+': 412 case '\r': 413 case '\n': 414 if (fbsd_chunk_info[current_chunk + 1].d) 415 ++current_chunk; 416 break; 417 418 case KEY_HOME: 419 current_chunk = 0; 420 break; 421 422 case KEY_END: 423 while (fbsd_chunk_info[current_chunk + 1].d) 424 ++current_chunk; 425 break; 426 427 case KEY_F(1): 428 case '?': 429 systemDisplayFile("partitioning.hlp"); 430 break; 431 432 case 'C': 433 if (fbsd_chunk_info[current_chunk].type != PART_SLICE) { 434 msg = "Can't create a sub-partition here (that only works in master partitions)"; 435 break; 436 } 437 sz = space_free(fbsd_chunk_info[current_chunk].c); 438 if (sz <= FS_MIN_SIZE) 439 msg = "Not enough space to create additional FreeBSD partition"; 440 else { 441 char *val, *cp, tmp[20]; 442 int size; 443 444 snprintf(tmp, 20, "%d", sz); 445 val = msgGetInput(tmp, "Please specify the size for new FreeBSD partition in blocks, or append\na trailing `M' for megabytes (e.g. 20M)."); 446 if (val && (size = strtol(val, &cp, 0)) > 0) { 447 struct chunk *tmp; 448 u_long flags = 0; 449 450 if (*cp && toupper(*cp) == 'M') 451 size *= 2048; 452 453 type = get_partition_type(); 454 if (type == PART_NONE) 455 break; 456 else if (type == PART_FILESYSTEM) { 457 if ((p = get_mountpoint(NULL)) == NULL) 458 break; 459 else if (!strcmp(p->mountpoint, "/")) 460 flags |= CHUNK_IS_ROOT; 461 else 462 flags &= ~CHUNK_IS_ROOT; 463 } 464 else 465 p = NULL; 466 Create_Chunk(fbsd_chunk_info[current_chunk].d, 467 fbsd_chunk_info[current_chunk].c->offset + 468 sz - size, 469 size, 470 part, 471 (type == PART_SWAP) ? FS_SWAP : freebsd, 472 flags); 473 tmp = find_chunk_by_loc(fbsd_chunk_info[current_chunk].d, 474 fbsd_chunk_info[current_chunk].c->offset + sz - size, size); 475 if (!tmp) 476 msgConfirm("Unable to create the partition. Too big?"); 477 else { 478 tmp->private = p; 479 tmp->private_free = safe_free; 480 record_fbsd_chunks(disks); 481 } 482 } 483 } 484 break; 485 486 case 'D': /* delete */ 487 if (fbsd_chunk_info[current_chunk].type == PART_SLICE) { 488 msg = "Use the Master Partition Editor to delete one of these"; 489 break; 490 } 491 Delete_Chunk(fbsd_chunk_info[current_chunk].d, 492 fbsd_chunk_info[current_chunk].c); 493 record_fbsd_chunks(disks); 494 break; 495 496 case 'M': /* mount */ 497 switch(fbsd_chunk_info[current_chunk].type) { 498 case PART_SLICE: 499 msg = "You can't mount one of these directly!"; 500 break; 501 502 case PART_SWAP: 503 msg = "You don't need to specify a mountpoint for a swap partition."; 504 break; 505 506 case PART_FILESYSTEM: 507 p = get_mountpoint(fbsd_chunk_info[current_chunk].c); 508 if (p) { 509 p->newfs = FALSE; 510 record_fbsd_chunks(disks); 511 } 512 break; 513 514 default: 515 msgFatal("Bogus partition under cursor???"); 516 break; 517 } 518 break; 519 520 case 27: /* ESC */ 521 partitioning = FALSE; 522 break; 523 } 524 } 525} 526 527int 528write_disks(struct disk **disks) 529{ 530 int i; 531 extern u_char boot1[], boot2[]; 532 extern u_char mbr[], bteasy17[]; 533 534 dialog_clear(); 535 if (!msgYesNo("Last Chance! Are you sure you want to write out\nall your changes to disk?")) { 536 for (i = 0; disks[i]; i++) { 537 if (contains_root_partition(disks[i])) 538 Set_Boot_Blocks(disks[i], boot1, boot2); 539 if (i == 0 && !msgYesNo("Would you like to install a boot manager?\n\nThis will allow you to easily select between other operating systems\non the first disk, or boot from a disk other than the first.")) 540 Set_Boot_Mgr(disks[i], bteasy17); 541 else if (i == 0 && !msgYesNo("Would you like to remove an existing boot manager?")) 542 Set_Boot_Mgr(disks[i], mbr); 543 Write_Disk(disks[i]); 544 } 545 return 0; 546 } 547 return 1; 548} 549