1 /* $OpenBSD: editor.c,v 1.224 2009/11/12 16:21:03 deraadt Exp $ */ 2 3 /* 4 * Copyright (c) 1997-2000 Todd C. Miller <Todd.Miller@courtesan.com> 5 * 6 * Permission to use, copy, modify, and distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 */ 18 19 #include <sys/types.h> 20 #include <sys/param.h> 21 #include <sys/stat.h> 22 #include <sys/ioctl.h> 23 #include <sys/sysctl.h> 24 #define DKTYPENAMES 25 #include <sys/disklabel.h> 26 27 #include <ufs/ffs/fs.h> 28 29 #include <ctype.h> 30 #include <err.h> 31 #include <errno.h> 32 #include <string.h> 33 #include <libgen.h> 34 #include <stdio.h> 35 #include <stdlib.h> 36 #include <unistd.h> 37 38 #include "extern.h" 39 #include "pathnames.h" 40 41 /* flags for getuint() */ 42 #define DO_CONVERSIONS 0x00000001 43 #define DO_ROUNDING 0x00000002 44 45 #ifndef NUMBOOT 46 #define NUMBOOT 0 47 #endif 48 49 /* structure to describe a portion of a disk */ 50 struct diskchunk { 51 u_int64_t start; 52 u_int64_t stop; 53 }; 54 55 /* used when sorting mountpoints in mpsave() */ 56 struct mountinfo { 57 char *mountpoint; 58 int partno; 59 }; 60 61 /* used when allocating all space according to recommendations */ 62 63 struct space_allocation { 64 daddr64_t minsz; /* starts as blocks, xlated to sectors. */ 65 daddr64_t maxsz; /* starts as blocks, xlated to sectors. */ 66 int rate; /* % of extra space to use */ 67 char *mp; 68 }; 69 70 /* entries for swap and var are changed by editor_allocspace() */ 71 const struct space_allocation alloc_big[] = { 72 { MEG(80), GIG(1), 5, "/" }, 73 { MEG(80), MEG(256), 5, "swap" }, 74 { MEG(120), GIG(4), 8, "/tmp" }, 75 { MEG(80), GIG(4), 13, "/var" }, 76 { MEG(900), GIG(2), 5, "/usr" }, 77 { MEG(512), GIG(1), 3, "/usr/X11R6" }, 78 { GIG(2), GIG(6), 5, "/usr/local" }, 79 { GIG(1), GIG(2), 3, "/usr/src" }, 80 { GIG(1), GIG(2), 3, "/usr/obj" }, 81 { GIG(1), GIG(300), 50, "/home" } 82 /* Anything beyond this leave for the user to decide */ 83 }; 84 85 const struct space_allocation alloc_medium[] = { 86 { MEG(800), GIG(2), 5, "/" }, 87 { MEG(80), MEG(256), 10, "swap" }, 88 { MEG(900), GIG(3), 78, "/usr" }, 89 { MEG(256), GIG(2), 7, "/home" } 90 }; 91 92 const struct space_allocation alloc_small[] = { 93 { MEG(700), GIG(4), 95, "/" }, 94 { MEG(1), MEG(256), 5, "swap" } 95 }; 96 97 const struct space_allocation alloc_stupid[] = { 98 { MEG(1), MEG(2048), 100, "/" } 99 }; 100 101 #ifndef nitems 102 #define nitems(_a) (sizeof((_a)) / sizeof((_a)[0])) 103 #endif 104 105 const struct { 106 const struct space_allocation *table; 107 int sz; 108 } alloc_table[] = { 109 { alloc_big, nitems(alloc_big) }, 110 { alloc_medium, nitems(alloc_medium) }, 111 { alloc_small, nitems(alloc_small) }, 112 { alloc_stupid, nitems(alloc_stupid) } 113 }; 114 115 void edit_parms(struct disklabel *); 116 void editor_add(struct disklabel *, char *); 117 void editor_change(struct disklabel *, char *); 118 u_int64_t editor_countfree(struct disklabel *); 119 void editor_delete(struct disklabel *, char *); 120 void editor_help(char *); 121 void editor_modify(struct disklabel *, char *); 122 void editor_name(struct disklabel *, char *); 123 char *getstring(char *, char *, char *); 124 u_int64_t getuint(struct disklabel *, char *, char *, u_int64_t, u_int64_t, u_int64_t, int); 125 int has_overlap(struct disklabel *); 126 int partition_cmp(const void *, const void *); 127 struct partition **sort_partitions(struct disklabel *); 128 void getdisktype(struct disklabel *, char *, char *); 129 void find_bounds(struct disklabel *); 130 void set_bounds(struct disklabel *); 131 struct diskchunk *free_chunks(struct disklabel *); 132 void mpcopy(char **, char **); 133 int micmp(const void *, const void *); 134 int mpequal(char **, char **); 135 int get_bsize(struct disklabel *, int); 136 int get_fsize(struct disklabel *, int); 137 int get_fstype(struct disklabel *, int); 138 int get_mp(struct disklabel *, int); 139 int get_offset(struct disklabel *, int); 140 int get_size(struct disklabel *, int); 141 void get_geometry(int, struct disklabel **); 142 void set_geometry(struct disklabel *, struct disklabel *, struct disklabel *, 143 char *); 144 void zero_partitions(struct disklabel *); 145 u_int64_t max_partition_size(struct disklabel *, int); 146 void display_edit(struct disklabel *, char, u_int64_t); 147 148 static u_int64_t starting_sector; 149 static u_int64_t ending_sector; 150 static int expert; 151 152 /* 153 * Simple partition editor. 154 */ 155 int 156 editor(struct disklabel *lp, int f) 157 { 158 struct disklabel origlabel, lastlabel, tmplabel, label = *lp; 159 struct disklabel *disk_geop = NULL; 160 struct partition *pp; 161 FILE *fp; 162 char buf[BUFSIZ], *cmd, *arg; 163 char **omountpoints = NULL; 164 char **origmountpoints = NULL, **tmpmountpoints = NULL; 165 int i, error = 0; 166 167 /* Alloc and init mount point info */ 168 if (!(omountpoints = calloc(MAXPARTITIONS, sizeof(char *))) || 169 !(origmountpoints = calloc(MAXPARTITIONS, sizeof(char *))) || 170 !(tmpmountpoints = calloc(MAXPARTITIONS, sizeof(char *)))) 171 errx(4, "out of memory"); 172 173 /* Don't allow disk type of "unknown" */ 174 getdisktype(&label, "You need to specify a type for this disk.", specname); 175 176 /* Get the on-disk geometries if possible */ 177 get_geometry(f, &disk_geop); 178 179 /* How big is the OpenBSD portion of the disk? */ 180 find_bounds(&label); 181 182 /* Make sure there is no partition overlap. */ 183 if (has_overlap(&label)) 184 errx(1, "can't run when there is partition overlap."); 185 186 /* If we don't have a 'c' partition, create one. */ 187 pp = &label.d_partitions[RAW_PART]; 188 if (label.d_npartitions < 3 || DL_GETPSIZE(pp) == 0) { 189 puts("No 'c' partition found, adding one that spans the disk."); 190 if (label.d_npartitions < 3) 191 label.d_npartitions = 3; 192 DL_SETPOFFSET(pp, 0); 193 DL_SETPSIZE(pp, DL_GETDSIZE(&label)); 194 pp->p_fstype = FS_UNUSED; 195 pp->p_fragblock = pp->p_cpg = 0; 196 } 197 198 #ifdef SUN_CYLCHECK 199 if (label.d_flags & D_VENDOR) { 200 puts("This platform requires that partition offsets/sizes " 201 "be on cylinder boundaries.\n" 202 "Partition offsets/sizes will be rounded to the " 203 "nearest cylinder automatically."); 204 } 205 #endif 206 207 /* Set d_bbsize and d_sbsize as necessary */ 208 if (label.d_bbsize == 0) 209 label.d_bbsize = BBSIZE; 210 if (label.d_sbsize == 0) 211 label.d_sbsize = SBSIZE; 212 213 /* Interleave must be >= 1 */ 214 if (label.d_interleave == 0) 215 label.d_interleave = 1; 216 217 /* Save the (U|u)ndo labels and mountpoints. */ 218 mpcopy(origmountpoints, mountpoints); 219 origlabel = label; 220 lastlabel = label; 221 222 puts("Label editor (enter '?' for help at any prompt)"); 223 for (;;) { 224 fputs("> ", stdout); 225 if (fgets(buf, sizeof(buf), stdin) == NULL) { 226 putchar('\n'); 227 buf[0] = 'q'; 228 buf[1] = '\0'; 229 } 230 if ((cmd = strtok(buf, " \t\r\n")) == NULL) 231 continue; 232 arg = strtok(NULL, " \t\r\n"); 233 234 if ((*cmd != 'u') && (*cmd != 'U')) { 235 /* 236 * Save undo info in case the command tries to make 237 * changes but decides not to. 238 */ 239 tmplabel = lastlabel; 240 lastlabel = label; 241 mpcopy(tmpmountpoints, omountpoints); 242 mpcopy(omountpoints, mountpoints); 243 } 244 245 switch (*cmd) { 246 case '?': 247 case 'h': 248 editor_help(arg ? arg : ""); 249 break; 250 251 case 'A': 252 if (ioctl(f, DIOCGPDINFO, &label) == 0) { 253 aflag = 1; 254 editor_allocspace(&label); 255 } else 256 label = lastlabel; 257 break; 258 case 'a': 259 editor_add(&label, arg); 260 break; 261 262 case 'b': 263 set_bounds(&label); 264 break; 265 266 case 'c': 267 editor_change(&label, arg); 268 break; 269 270 case 'D': 271 if (ioctl(f, DIOCGPDINFO, &label) == 0) { 272 dflag = 1; 273 for (i=0; i<MAXPARTITIONS; i++) { 274 free(mountpoints[i]); 275 mountpoints[i] = NULL; 276 } 277 } else 278 warn("unable to get default partition table"); 279 break; 280 281 case 'd': 282 editor_delete(&label, arg); 283 break; 284 285 case 'e': 286 edit_parms(&label); 287 break; 288 289 case 'g': 290 set_geometry(&label, disk_geop, lp, arg); 291 break; 292 293 case 'm': 294 editor_modify(&label, arg); 295 break; 296 297 case 'n': 298 if (!fstabfile) { 299 fputs("This option is not valid when run " 300 "without the -f flag.\n", stderr); 301 break; 302 } 303 editor_name(&label, arg); 304 break; 305 306 case 'p': 307 display_edit(&label, arg ? *arg : 0, editor_countfree(&label)); 308 break; 309 310 case 'l': 311 display(stdout, &label, arg ? *arg : 0, 0); 312 break; 313 314 case 'M': { 315 sig_t opipe = signal(SIGPIPE, SIG_IGN); 316 char *pager, *comm = NULL; 317 extern const u_char manpage[]; 318 extern const int manpage_sz; 319 320 if ((pager = getenv("PAGER")) == NULL || *pager == '\0') 321 pager = _PATH_LESS; 322 323 if (asprintf(&comm, "gunzip -qc|%s", pager) != -1 && 324 (fp = popen(comm, "w")) != NULL) { 325 (void) fwrite(manpage, manpage_sz, 1, fp); 326 pclose(fp); 327 } else 328 warn("unable to execute %s", pager); 329 330 free(comm); 331 (void)signal(SIGPIPE, opipe); 332 break; 333 } 334 335 case 'q': 336 if (donothing) { 337 puts("In no change mode, not writing label."); 338 goto done; 339 } 340 /* Save mountpoint info if there is any. */ 341 mpsave(&label); 342 343 /* 344 * If we haven't changed the original label, and it 345 * wasn't a default label or an auto-allocated label, 346 * there is no need to do anything before exiting. Note 347 * that 'w' will reset dflag and aflag to allow 'q' to 348 * exit without further questions. 349 */ 350 if (!dflag && !aflag && 351 memcmp(lp, &label, sizeof(label)) == 0) { 352 puts("No label changes."); 353 goto done; 354 } 355 do { 356 arg = getstring("Write new label?", 357 "Write the modified label to disk?", 358 "y"); 359 } while (arg && tolower(*arg) != 'y' && tolower(*arg) != 'n'); 360 if (arg && tolower(*arg) == 'y') { 361 if (writelabel(f, bootarea, &label) == 0) { 362 *lp = label; 363 goto done; 364 } 365 warnx("unable to write label"); 366 } 367 error = 1; 368 goto done; 369 /* NOTREACHED */ 370 break; 371 372 case 'r': { 373 struct diskchunk *chunks; 374 int i; 375 /* Display free space. */ 376 chunks = free_chunks(&label); 377 for (i = 0; chunks[i].start != 0 || chunks[i].stop != 0; 378 i++) 379 fprintf(stderr, "Free sectors: %16llu - %16llu " 380 "(%16llu)\n", 381 chunks[i].start, chunks[i].stop - 1, 382 chunks[i].stop - chunks[i].start); 383 fprintf(stderr, "Total free sectors: %llu.\n", 384 editor_countfree(&label)); 385 break; 386 } 387 388 case 's': 389 if (arg == NULL) { 390 arg = getstring("Filename", 391 "Name of the file to save label into.", 392 NULL); 393 if (arg == NULL || *arg == '\0') 394 break; 395 } 396 if ((fp = fopen(arg, "w")) == NULL) { 397 warn("cannot open %s", arg); 398 } else { 399 display(fp, &label, 0, 1); 400 (void)fclose(fp); 401 } 402 break; 403 404 case 'U': 405 /* 406 * If we allow 'U' repeatedly, information would be lost. This way 407 * multiple 'U's followed by 'u' would undo the 'U's. 408 */ 409 if (memcmp(&label, &origlabel, sizeof(label)) || 410 !mpequal(mountpoints, origmountpoints)) { 411 tmplabel = label; 412 label = origlabel; 413 lastlabel = tmplabel; 414 mpcopy(tmpmountpoints, mountpoints); 415 mpcopy(mountpoints, origmountpoints); 416 mpcopy(omountpoints, tmpmountpoints); 417 } 418 puts("Original label and mount points restored."); 419 break; 420 421 case 'u': 422 tmplabel = label; 423 label = lastlabel; 424 lastlabel = tmplabel; 425 mpcopy(tmpmountpoints, mountpoints); 426 mpcopy(mountpoints, omountpoints); 427 mpcopy(omountpoints, tmpmountpoints); 428 puts("Last change undone."); 429 break; 430 431 case 'w': 432 if (donothing) { 433 puts("In no change mode, not writing label."); 434 break; 435 } 436 437 /* Write label to disk. */ 438 if (writelabel(f, bootarea, &label) != 0) 439 warnx("unable to write label"); 440 else { 441 dflag = aflag = 0; 442 *lp = label; 443 } 444 break; 445 446 case 'X': 447 expert = !expert; 448 printf("%s expert mode\n", expert ? "Entering" : 449 "Exiting"); 450 break; 451 452 case 'x': 453 goto done; 454 break; 455 456 case 'z': 457 zero_partitions(&label); 458 break; 459 460 case '\n': 461 break; 462 463 default: 464 printf("Unknown option: %c ('?' for help)\n", *cmd); 465 break; 466 } 467 468 /* 469 * If no changes were made to label or mountpoints, then 470 * restore undo info. 471 */ 472 if (memcmp(&label, &lastlabel, sizeof(label)) == 0 && 473 (mpequal(mountpoints, omountpoints))) { 474 lastlabel = tmplabel; 475 mpcopy(omountpoints, tmpmountpoints); 476 } 477 } 478 done: 479 free(omountpoints); 480 free(origmountpoints); 481 free(tmpmountpoints); 482 if (disk_geop) 483 free(disk_geop); 484 return(error); 485 } 486 487 int64_t 488 getphysmem(void) 489 { 490 int64_t physmem; 491 size_t sz = sizeof(physmem); 492 int mib[] = { CTL_HW, HW_PHYSMEM64 }; 493 if (sysctl(mib, 2, &physmem, &sz, NULL, (size_t)0) == -1) 494 errx(4, "can't get mem size"); 495 return physmem; 496 } 497 498 /* 499 * Allocate all disk space according to standard recommendations for a 500 * root disk. 501 */ 502 void 503 editor_allocspace(struct disklabel *lp_org) 504 { 505 struct disklabel *lp, label; 506 struct space_allocation *alloc; 507 struct space_allocation *ap; 508 struct partition *pp; 509 struct diskchunk *chunks; 510 daddr64_t secs, chunkstart, chunksize, cylsecs, totsecs, xtrasecs; 511 char **partmp; 512 int i, j, lastalloc, index = 0; 513 int64_t physmem; 514 515 physmem = getphysmem() / lp_org->d_secsize; 516 517 /* How big is the OpenBSD portion of the disk? */ 518 find_bounds(lp_org); 519 520 cylsecs = lp_org->d_secpercyl; 521 again: 522 lp = &label; 523 for (i=0; i<MAXPARTITIONS; i++) { 524 free(mountpoints[i]); 525 mountpoints[i] = NULL; 526 } 527 memcpy(lp, lp_org, sizeof(struct disklabel)); 528 lp->d_npartitions = MAXPARTITIONS; 529 lastalloc = alloc_table[index].sz; 530 alloc = malloc(lastalloc * sizeof(struct space_allocation)); 531 if (alloc == NULL) 532 errx(4, "out of memory"); 533 memcpy(alloc, alloc_table[index].table, 534 lastalloc * sizeof(struct space_allocation)); 535 536 /* bump max swap based on phys mem, little physmem gets 2x swap */ 537 if (index == 0) { 538 if (physmem < 256LL * 1024 * 1024 / lp->d_secsize) 539 alloc[1].maxsz = 2 * physmem; 540 else 541 alloc[1].maxsz += physmem; 542 /* bump max /var to make room for 2 crash dumps */ 543 alloc[3].maxsz += 2 * physmem; 544 } 545 546 xtrasecs = totsecs = editor_countfree(lp); 547 548 for (i = 0; i < lastalloc; i++) { 549 alloc[i].minsz = DL_BLKTOSEC(lp, alloc[i].minsz); 550 alloc[i].maxsz = DL_BLKTOSEC(lp, alloc[i].maxsz); 551 if (xtrasecs > alloc[i].minsz) 552 xtrasecs -= alloc[i].minsz; 553 else 554 xtrasecs = 0; 555 } 556 557 for (i = 0; i < lastalloc; i++) { 558 /* Find next available partition. */ 559 for (j = 0; j < MAXPARTITIONS; j++) 560 if (DL_GETPSIZE(&lp->d_partitions[j]) == 0) 561 break; 562 if (j == MAXPARTITIONS) 563 return; 564 pp = &lp->d_partitions[j]; 565 partmp = &mountpoints[j]; 566 ap = &alloc[i]; 567 568 /* Figure out the size of the partition. */ 569 if (i == lastalloc - 1) { 570 if (totsecs > ap->maxsz) 571 secs = ap->maxsz; 572 else 573 secs = totsecs; 574 #ifdef SUN_CYLCHECK 575 goto cylinderalign; 576 #endif 577 } else { 578 secs = ap->minsz; 579 if (xtrasecs > 0) 580 secs += (xtrasecs / 100) * ap->rate; 581 if (secs > ap->maxsz) 582 secs = ap->maxsz; 583 #ifdef SUN_CYLCHECK 584 cylinderalign: 585 secs = ((secs + cylsecs - 1) / cylsecs) * cylsecs; 586 #endif 587 totsecs -= secs; 588 #ifdef SUN_CYLCHECK 589 while (totsecs < 0) { 590 secs -= cylsecs; 591 totsecs += cylsecs; 592 } 593 #endif 594 } 595 596 /* Find largest chunk of free space. */ 597 chunks = free_chunks(lp); 598 chunkstart = 0; 599 chunksize = 0; 600 for (j = 0; chunks[j].start != 0 || chunks[j].stop != 0; j++) 601 if ((chunks[j].stop - chunks[j].start) > chunksize) { 602 chunkstart = chunks[j].start; 603 chunksize = chunks[j].stop - chunks[j].start; 604 } 605 #ifdef SUN_CYLCHECK 606 if (lp->d_flags & D_VENDOR) { 607 /* Align chunk to cylinder boundaries. */ 608 chunksize -= chunksize % cylsecs; 609 chunkstart = ((chunkstart + cylsecs - 1) / cylsecs) * 610 cylsecs; 611 } 612 #endif 613 /* See if partition can fit into chunk. */ 614 if (secs > chunksize) { 615 totsecs += secs - chunksize; 616 secs = chunksize; 617 } 618 if (secs < ap->minsz) { 619 /* It did not work out, try next strategy */ 620 free(alloc); 621 if (++index < nitems(alloc_table)) 622 goto again; 623 else 624 return; 625 } 626 627 /* Everything seems ok so configure the partition. */ 628 DL_SETPSIZE(pp, secs); 629 DL_SETPOFFSET(pp, chunkstart); 630 #if defined (__sparc__) && !defined(__sparc64__) 631 /* can't boot from > 8k boot blocks */ 632 pp->p_fragblock = 633 DISKLABELV1_FFS_FRAGBLOCK(i == 0 ? 1024 : 2048, 8); 634 #else 635 pp->p_fragblock = DISKLABELV1_FFS_FRAGBLOCK(2048, 8); 636 #endif 637 pp->p_cpg = 1; 638 if (ap->mp[0] != '/') 639 pp->p_fstype = FS_SWAP; 640 else { 641 pp->p_fstype = FS_BSDFFS; 642 free(*partmp); 643 if ((*partmp = strdup(ap->mp)) == NULL) 644 errx(4, "out of memory"); 645 } 646 } 647 648 free(alloc); 649 memcpy(lp_org, lp, sizeof(struct disklabel)); 650 } 651 652 /* 653 * Add a new partition. 654 */ 655 void 656 editor_add(struct disklabel *lp, char *p) 657 { 658 struct partition *pp; 659 struct diskchunk *chunks; 660 char buf[2]; 661 int i, partno; 662 u_int64_t freesectors, new_offset, new_size; 663 664 freesectors = editor_countfree(lp); 665 666 /* XXX - prompt user to steal space from another partition instead */ 667 #ifdef SUN_CYLCHECK 668 if ((lp->d_flags & D_VENDOR) && freesectors < lp->d_secpercyl) { 669 fputs("No space left, you need to shrink a partition " 670 "(need at least one full cylinder)\n", 671 stderr); 672 return; 673 } 674 #endif 675 if (freesectors == 0) { 676 fputs("No space left, you need to shrink a partition\n", 677 stderr); 678 return; 679 } 680 681 if (p == NULL) { 682 /* 683 * Use the first unused partition that is not 'c' as the 684 * default partition in the prompt string. 685 */ 686 pp = &lp->d_partitions[0]; 687 buf[0] = buf[1] = '\0'; 688 for (partno = 0; partno < MAXPARTITIONS; partno++, pp++) { 689 if (DL_GETPSIZE(pp) == 0 && partno != RAW_PART) { 690 buf[0] = partno + 'a'; 691 p = &buf[0]; 692 break; 693 } 694 } 695 p = getstring("partition", 696 "The letter of the new partition, a - p.", p); 697 } 698 if (p == NULL) { 699 fputs("Command aborted\n", stderr); 700 return; 701 } 702 partno = p[0] - 'a'; 703 if (partno < 0 || partno == RAW_PART || partno >= MAXPARTITIONS) { 704 fprintf(stderr, "Partition must be between 'a' and '%c' " 705 "(excluding 'c').\n", 'a' + MAXPARTITIONS - 1); 706 return; 707 } 708 pp = &lp->d_partitions[partno]; 709 710 if (pp->p_fstype != FS_UNUSED && DL_GETPSIZE(pp) != 0) { 711 fprintf(stderr, "Partition '%c' exists. Delete it first.\n", 712 p[0]); 713 return; 714 } 715 716 /* 717 * Increase d_npartitions if necessary. Ensure all new partitions are 718 * zero'ed to avoid inadvertant overlaps. 719 */ 720 for(; lp->d_npartitions <= partno; lp->d_npartitions++) 721 memset(&lp->d_partitions[lp->d_npartitions], 0, sizeof(*pp)); 722 723 /* Make sure selected partition is zero'd too. */ 724 memset(pp, 0, sizeof(*pp)); 725 chunks = free_chunks(lp); 726 727 /* 728 * Since we know there's free space, there must be at least one 729 * chunk. So find the largest chunk and assume we want to add the 730 * partition in that free space. 731 */ 732 new_size = new_offset = 0; 733 for (i = 0; chunks[i].start != 0 || chunks[i].stop != 0; i++) { 734 if (chunks[i].stop - chunks[i].start > new_size) { 735 new_size = chunks[i].stop - chunks[i].start; 736 new_offset = chunks[i].start; 737 } 738 } 739 DL_SETPSIZE(pp, new_size); 740 DL_SETPOFFSET(pp, new_offset); 741 pp->p_fstype = partno == 1 ? FS_SWAP : FS_BSDFFS; 742 #if defined (__sparc__) && !defined(__sparc64__) 743 /* can't boot from > 8k boot blocks */ 744 pp->p_fragblock = 745 DISKLABELV1_FFS_FRAGBLOCK(partno == 0 ? 1024 : 2048, 8); 746 #else 747 pp->p_fragblock = DISKLABELV1_FFS_FRAGBLOCK(2048, 8); 748 #endif 749 pp->p_cpg = 1; 750 751 if (get_offset(lp, partno) == 0 && 752 get_size(lp, partno) == 0 && 753 get_fstype(lp, partno) == 0 && 754 get_mp(lp, partno) == 0 && 755 get_fsize(lp, partno) == 0 && 756 get_bsize(lp, partno) == 0) 757 return; 758 759 /* Bailed out at some point, so effectively delete the partition. */ 760 DL_SETPSIZE(pp, 0); 761 } 762 763 /* 764 * Set the mountpoint of an existing partition ('name'). 765 */ 766 void 767 editor_name(struct disklabel *lp, char *p) 768 { 769 struct partition *pp; 770 int partno; 771 772 /* Change which partition? */ 773 if (p == NULL) { 774 p = getstring("partition to name", 775 "The letter of the partition to name, a - p.", NULL); 776 } 777 if (p == NULL) { 778 fputs("Command aborted\n", stderr); 779 return; 780 } 781 partno = p[0] - 'a'; 782 if (partno < 0 || partno == RAW_PART || partno >= lp->d_npartitions) { 783 fprintf(stderr, "Partition must be between 'a' and '%c' " 784 "(excluding 'c').\n", 'a' + lp->d_npartitions - 1); 785 return; 786 } 787 pp = &lp->d_partitions[partno]; 788 789 if (pp->p_fstype == FS_UNUSED && DL_GETPSIZE(pp) == 0) { 790 fprintf(stderr, "Partition '%c' is not in use.\n", p[0]); 791 return; 792 } 793 794 /* Not all fstypes can be named */ 795 if (pp->p_fstype == FS_UNUSED || pp->p_fstype == FS_SWAP || 796 pp->p_fstype == FS_BOOT || pp->p_fstype == FS_OTHER || 797 pp->p_fstype == FS_RAID) { 798 fprintf(stderr, "You cannot name a filesystem of type %s.\n", 799 fstypenames[lp->d_partitions[partno].p_fstype]); 800 return; 801 } 802 803 get_mp(lp, partno); 804 } 805 806 /* 807 * Change an existing partition. 808 */ 809 void 810 editor_modify(struct disklabel *lp, char *p) 811 { 812 struct partition origpart, *pp; 813 int partno; 814 815 /* Change which partition? */ 816 if (p == NULL) { 817 p = getstring("partition to modify", 818 "The letter of the partition to modify, a - p.", NULL); 819 } 820 if (p == NULL) { 821 fputs("Command aborted\n", stderr); 822 return; 823 } 824 partno = p[0] - 'a'; 825 if (partno < 0 || partno == RAW_PART || partno >= lp->d_npartitions) { 826 fprintf(stderr, "Partition must be between 'a' and '%c' " 827 "(excluding 'c').\n", 'a' + lp->d_npartitions - 1); 828 return; 829 } 830 pp = &lp->d_partitions[partno]; 831 832 if (pp->p_fstype == FS_UNUSED && DL_GETPSIZE(pp) == 0) { 833 fprintf(stderr, "Partition '%c' is not in use.\n", p[0]); 834 return; 835 } 836 837 origpart = *pp; 838 839 if (get_offset(lp, partno) == 0 && 840 get_size(lp, partno) == 0 && 841 get_fstype(lp, partno) == 0 && 842 get_mp(lp, partno) == 0 && 843 get_fsize(lp, partno) == 0 && 844 get_bsize(lp, partno) == 0) 845 return; 846 847 /* Bailed out at some point, so undo any changes. */ 848 *pp = origpart; 849 } 850 851 /* 852 * Delete an existing partition. 853 */ 854 void 855 editor_delete(struct disklabel *lp, char *p) 856 { 857 struct partition *pp; 858 int partno; 859 860 if (p == NULL) { 861 p = getstring("partition to delete", 862 "The letter of the partition to delete, a - p, or '*'.", 863 NULL); 864 } 865 if (p == NULL) { 866 fputs("Command aborted\n", stderr); 867 return; 868 } 869 if (p[0] == '*') { 870 zero_partitions(lp); 871 return; 872 } 873 partno = p[0] - 'a'; 874 if (partno < 0 || partno == RAW_PART || partno >= lp->d_npartitions) { 875 fprintf(stderr, "Partition must be between 'a' and '%c' " 876 "(excluding 'c').\n", 'a' + lp->d_npartitions - 1); 877 return; 878 } 879 pp = &lp->d_partitions[partno]; 880 881 if (pp->p_fstype == FS_UNUSED && DL_GETPSIZE(pp) == 0) { 882 fprintf(stderr, "Partition '%c' is not in use.\n", p[0]); 883 return; 884 } 885 886 /* Really delete it (as opposed to just setting to "unused") */ 887 memset(pp, 0, sizeof(*pp)); 888 free(mountpoints[partno]); 889 mountpoints[partno] = NULL; 890 } 891 892 /* 893 * Change the size of an existing partition. 894 */ 895 void 896 editor_change(struct disklabel *lp, char *p) 897 { 898 struct partition *pp; 899 int partno; 900 901 if (p == NULL) { 902 p = getstring("partition to change size", 903 "The letter of the partition to change size, a - p.", NULL); 904 } 905 if (p == NULL) { 906 fputs("Command aborted\n", stderr); 907 return; 908 } 909 partno = p[0] - 'a'; 910 if (partno < 0 || partno == RAW_PART || partno >= lp->d_npartitions) { 911 fprintf(stderr, "Partition must be between 'a' and '%c' " 912 "(excluding 'c').\n", 'a' + lp->d_npartitions - 1); 913 return; 914 } 915 pp = &lp->d_partitions[partno]; 916 917 if (DL_GETPSIZE(pp) == 0) { 918 fprintf(stderr, "Partition '%c' is not in use.\n", p[0]); 919 return; 920 } 921 922 printf("Partition %c is currently %llu sectors in size, and can have " 923 "a maximum\nsize of %llu sectors.\n", 924 p[0], DL_GETPSIZE(pp), max_partition_size(lp, partno)); 925 926 /* Get new size */ 927 get_size(lp, partno); 928 } 929 930 /* 931 * Sort the partitions based on starting offset. 932 * This assumes there can be no overlap. 933 */ 934 int 935 partition_cmp(const void *e1, const void *e2) 936 { 937 struct partition *p1 = *(struct partition **)e1; 938 struct partition *p2 = *(struct partition **)e2; 939 u_int64_t o1 = DL_GETPOFFSET(p1); 940 u_int64_t o2 = DL_GETPOFFSET(p2); 941 942 if (o1 < o2) 943 return -1; 944 else if (o1 > o2) 945 return 1; 946 else 947 return 0; 948 } 949 950 char * 951 getstring(char *prompt, char *helpstring, char *oval) 952 { 953 static char buf[BUFSIZ]; 954 int n; 955 956 buf[0] = '\0'; 957 do { 958 printf("%s: [%s] ", prompt, oval ? oval : ""); 959 if (fgets(buf, sizeof(buf), stdin) == NULL) { 960 buf[0] = '\0'; 961 if (feof(stdin)) { 962 clearerr(stdin); 963 putchar('\n'); 964 return(NULL); 965 } 966 } 967 n = strlen(buf); 968 if (n > 0 && buf[n-1] == '\n') 969 buf[--n] = '\0'; 970 if (buf[0] == '?') 971 puts(helpstring); 972 else if (oval != NULL && buf[0] == '\0') 973 strlcpy(buf, oval, sizeof(buf)); 974 } while (buf[0] == '?'); 975 976 return(&buf[0]); 977 } 978 979 /* 980 * Returns ULLONG_MAX on error 981 * Usually only called by helper functions. 982 */ 983 u_int64_t 984 getuint(struct disklabel *lp, char *prompt, char *helpstring, 985 u_int64_t oval, u_int64_t maxval, u_int64_t offset, int flags) 986 { 987 char buf[BUFSIZ], *endptr, *p, operator = '\0'; 988 u_int64_t rval = oval; 989 size_t n; 990 int mult = 1; 991 double d, percent = 1.0; 992 993 /* We only care about the remainder */ 994 offset = offset % lp->d_secpercyl; 995 996 buf[0] = '\0'; 997 do { 998 printf("%s: [%llu] ", prompt, oval); 999 if (fgets(buf, sizeof(buf), stdin) == NULL) { 1000 buf[0] = '\0'; 1001 if (feof(stdin)) { 1002 clearerr(stdin); 1003 putchar('\n'); 1004 return(ULLONG_MAX - 1); 1005 } 1006 } 1007 n = strlen(buf); 1008 if (n > 0 && buf[n-1] == '\n') 1009 buf[--n] = '\0'; 1010 if (buf[0] == '?') 1011 puts(helpstring); 1012 } while (buf[0] == '?'); 1013 1014 if (buf[0] == '*' && buf[1] == '\0') { 1015 rval = maxval; 1016 } else { 1017 /* deal with units */ 1018 if (buf[0] != '\0' && n > 0) { 1019 if ((flags & DO_CONVERSIONS)) { 1020 switch (tolower(buf[n-1])) { 1021 1022 case 'c': 1023 mult = lp->d_secpercyl; 1024 buf[--n] = '\0'; 1025 break; 1026 case 'b': 1027 mult = -lp->d_secsize; 1028 buf[--n] = '\0'; 1029 break; 1030 case 'k': 1031 if (lp->d_secsize > 1024) 1032 mult = -lp->d_secsize / 1024; 1033 else 1034 mult = 1024 / lp->d_secsize; 1035 buf[--n] = '\0'; 1036 break; 1037 case 'm': 1038 mult = 1048576 / lp->d_secsize; 1039 buf[--n] = '\0'; 1040 break; 1041 case 'g': 1042 mult = 1073741824 / lp->d_secsize; 1043 buf[--n] = '\0'; 1044 break; 1045 case '%': 1046 buf[--n] = '\0'; 1047 percent = strtod(buf, NULL) / 100.0; 1048 snprintf(buf, sizeof(buf), "%lld", 1049 DL_GETDSIZE(lp)); 1050 break; 1051 case '&': 1052 buf[--n] = '\0'; 1053 percent = strtod(buf, NULL) / 100.0; 1054 snprintf(buf, sizeof(buf), "%lld", 1055 maxval); 1056 break; 1057 } 1058 } 1059 1060 /* Did they give us an operator? */ 1061 p = &buf[0]; 1062 if (*p == '+' || *p == '-') 1063 operator = *p++; 1064 1065 endptr = p; 1066 errno = 0; 1067 d = strtod(p, &endptr); 1068 if (errno == ERANGE) 1069 rval = ULLONG_MAX; /* too big/small */ 1070 else if (*endptr != '\0') { 1071 errno = EINVAL; /* non-numbers in str */ 1072 rval = ULLONG_MAX; 1073 } else { 1074 /* XXX - should check for overflow */ 1075 if (mult > 0) 1076 rval = d * mult * percent; 1077 else 1078 /* Negative mult means divide (fancy) */ 1079 rval = d / (-mult) * percent; 1080 1081 /* Apply the operator */ 1082 if (operator == '+') 1083 rval += oval; 1084 else if (operator == '-') 1085 rval = oval - rval; 1086 } 1087 } 1088 } 1089 if ((flags & DO_ROUNDING) && rval != ULLONG_MAX) { 1090 /* Round to nearest cylinder unless given in sectors */ 1091 if ( 1092 #ifdef SUN_CYLCHECK 1093 ((lp->d_flags & D_VENDOR) || mult != 1) && 1094 #else 1095 mult != 1 && 1096 #endif 1097 (rval + offset) % lp->d_secpercyl != 0) { 1098 u_int64_t cyls; 1099 1100 /* Round to higher cylinder but no more than maxval */ 1101 cyls = (rval / lp->d_secpercyl) + 1; 1102 if ((cyls * lp->d_secpercyl) - offset > maxval) 1103 cyls--; 1104 rval = (cyls * lp->d_secpercyl) - offset; 1105 printf("Rounding to cylinder: %llu\n", rval); 1106 } 1107 } 1108 1109 return(rval); 1110 } 1111 1112 /* 1113 * Check for partition overlap in lp and prompt the user to resolve the overlap 1114 * if any is found. Returns 1 if unable to resolve, else 0. 1115 */ 1116 int 1117 has_overlap(struct disklabel *lp) 1118 { 1119 struct partition **spp; 1120 int c, i, j; 1121 char buf[BUFSIZ]; 1122 1123 /* Get a sorted list of the in-use partitions. */ 1124 spp = sort_partitions(lp); 1125 1126 /* If there are less than two partitions in use, there is no overlap. */ 1127 if (spp[1] == NULL) 1128 return(0); 1129 1130 /* Now that we have things sorted by starting sector check overlap */ 1131 for (i = 0; spp[i] != NULL; i++) { 1132 for (j = i + 1; spp[j] != NULL; j++) { 1133 /* `if last_sec_in_part + 1 > first_sec_in_next_part' */ 1134 if (DL_GETPOFFSET(spp[i]) + DL_GETPSIZE(spp[i]) > DL_GETPOFFSET(spp[j])) { 1135 /* Overlap! Convert to real part numbers. */ 1136 i = ((char *)spp[i] - (char *)lp->d_partitions) 1137 / sizeof(**spp); 1138 j = ((char *)spp[j] - (char *)lp->d_partitions) 1139 / sizeof(**spp); 1140 printf("\nError, partitions %c and %c overlap:\n", 1141 'a' + i, 'a' + j); 1142 printf("# %16.16s %16.16s fstype " 1143 "[fsize bsize cpg]\n", "size", "offset"); 1144 display_partition(stdout, lp, i, 0); 1145 display_partition(stdout, lp, j, 0); 1146 1147 /* Get partition to disable or ^D */ 1148 do { 1149 printf("Disable which one? (^D to abort) [%c %c] ", 1150 'a' + i, 'a' + j); 1151 buf[0] = '\0'; 1152 if (!fgets(buf, sizeof(buf), stdin)) { 1153 putchar('\n'); 1154 return(1); /* ^D */ 1155 } 1156 c = buf[0] - 'a'; 1157 } while (buf[1] != '\n' && buf[1] != '\0' && 1158 c != i && c != j); 1159 1160 /* Mark the selected one as unused */ 1161 lp->d_partitions[c].p_fstype = FS_UNUSED; 1162 return (has_overlap(lp)); 1163 } 1164 } 1165 } 1166 1167 return(0); 1168 } 1169 1170 void 1171 edit_parms(struct disklabel *lp) 1172 { 1173 char *p; 1174 u_int64_t freesectors, ui; 1175 struct disklabel oldlabel = *lp; 1176 1177 printf("Changing device parameters for %s:\n", specname); 1178 1179 /* disk type */ 1180 for (;;) { 1181 p = getstring("disk type", 1182 "What kind of disk is this? Usually SCSI, ESDI, ST506, or " 1183 "floppy (use ESDI for IDE).", dktypenames[lp->d_type]); 1184 if (p == NULL) { 1185 fputs("Command aborted\n", stderr); 1186 return; 1187 } 1188 if (strcasecmp(p, "IDE") == 0) 1189 ui = DTYPE_ESDI; 1190 else 1191 for (ui = 1; ui < DKMAXTYPES && 1192 strcasecmp(p, dktypenames[ui]); ui++) 1193 ; 1194 if (ui < DKMAXTYPES) { 1195 break; 1196 } else { 1197 printf("\"%s\" is not a valid disk type.\n", p); 1198 fputs("Valid types are: ", stdout); 1199 for (ui = 1; ui < DKMAXTYPES; ui++) { 1200 printf("\"%s\"", dktypenames[ui]); 1201 if (ui < DKMAXTYPES - 1) 1202 fputs(", ", stdout); 1203 } 1204 putchar('\n'); 1205 } 1206 } 1207 lp->d_type = ui; 1208 1209 /* pack/label id */ 1210 p = getstring("label name", 1211 "15 char string that describes this label, usually the disk name.", 1212 lp->d_packname); 1213 if (p == NULL) { 1214 fputs("Command aborted\n", stderr); 1215 *lp = oldlabel; /* undo damage */ 1216 return; 1217 } 1218 strncpy(lp->d_packname, p, sizeof(lp->d_packname)); /* checked */ 1219 1220 /* sectors/track */ 1221 for (;;) { 1222 ui = getuint(lp, "sectors/track", 1223 "The Number of sectors per track.", lp->d_nsectors, 1224 lp->d_nsectors, 0, 0); 1225 if (ui == ULLONG_MAX - 1) { 1226 fputs("Command aborted\n", stderr); 1227 *lp = oldlabel; /* undo damage */ 1228 return; 1229 } if (ui == ULLONG_MAX) 1230 fputs("Invalid entry\n", stderr); 1231 else 1232 break; 1233 } 1234 lp->d_nsectors = ui; 1235 1236 /* tracks/cylinder */ 1237 for (;;) { 1238 ui = getuint(lp, "tracks/cylinder", 1239 "The number of tracks per cylinder.", lp->d_ntracks, 1240 lp->d_ntracks, 0, 0); 1241 if (ui == ULLONG_MAX - 1) { 1242 fputs("Command aborted\n", stderr); 1243 *lp = oldlabel; /* undo damage */ 1244 return; 1245 } else if (ui == ULLONG_MAX) 1246 fputs("Invalid entry\n", stderr); 1247 else 1248 break; 1249 } 1250 lp->d_ntracks = ui; 1251 1252 /* sectors/cylinder */ 1253 for (;;) { 1254 ui = getuint(lp, "sectors/cylinder", 1255 "The number of sectors per cylinder (Usually sectors/track " 1256 "* tracks/cylinder).", lp->d_secpercyl, lp->d_secpercyl, 1257 0, 0); 1258 if (ui == ULLONG_MAX - 1) { 1259 fputs("Command aborted\n", stderr); 1260 *lp = oldlabel; /* undo damage */ 1261 return; 1262 } else if (ui == ULLONG_MAX) 1263 fputs("Invalid entry\n", stderr); 1264 else 1265 break; 1266 } 1267 lp->d_secpercyl = ui; 1268 1269 /* number of cylinders */ 1270 for (;;) { 1271 ui = getuint(lp, "number of cylinders", 1272 "The total number of cylinders on the disk.", 1273 lp->d_ncylinders, lp->d_ncylinders, 0, 0); 1274 if (ui == ULLONG_MAX - 1) { 1275 fputs("Command aborted\n", stderr); 1276 *lp = oldlabel; /* undo damage */ 1277 return; 1278 } else if (ui == ULLONG_MAX) 1279 fputs("Invalid entry\n", stderr); 1280 else 1281 break; 1282 } 1283 lp->d_ncylinders = ui; 1284 1285 /* total sectors */ 1286 for (;;) { 1287 u_int64_t nsec = MAX(DL_GETDSIZE(lp), 1288 (u_int64_t)lp->d_ncylinders * lp->d_secpercyl); 1289 ui = getuint(lp, "total sectors", 1290 "The total number of sectors on the disk.", 1291 nsec, nsec, 0, 0); 1292 if (ui == ULLONG_MAX - 1) { 1293 fputs("Command aborted\n", stderr); 1294 *lp = oldlabel; /* undo damage */ 1295 return; 1296 } else if (ui == ULLONG_MAX) 1297 fputs("Invalid entry\n", stderr); 1298 else if (ui > DL_GETDSIZE(lp) && 1299 ending_sector == DL_GETDSIZE(lp)) { 1300 puts("You may want to increase the size of the 'c' " 1301 "partition."); 1302 break; 1303 } else if (ui < DL_GETDSIZE(lp) && 1304 ending_sector == DL_GETDSIZE(lp)) { 1305 /* shrink free count */ 1306 freesectors = editor_countfree(lp); 1307 if (DL_GETDSIZE(lp) - ui > freesectors) 1308 fprintf(stderr, 1309 "Not enough free space to shrink by %llu " 1310 "sectors (only %llu sectors left)\n", 1311 DL_GETDSIZE(lp) - ui, freesectors); 1312 else 1313 break; 1314 } else 1315 break; 1316 } 1317 /* Adjust ending_sector if necessary. */ 1318 if (ending_sector > ui) 1319 ending_sector = ui; 1320 DL_SETDSIZE(lp, ui); 1321 1322 /* rpm */ 1323 for (;;) { 1324 ui = getuint(lp, "rpm", 1325 "The rotational speed of the disk in revolutions per minute.", 1326 lp->d_rpm, lp->d_rpm, 0, 0); 1327 if (ui == ULLONG_MAX - 1) { 1328 fputs("Command aborted\n", stderr); 1329 *lp = oldlabel; /* undo damage */ 1330 return; 1331 } else if (ui == ULLONG_MAX) 1332 fputs("Invalid entry\n", stderr); 1333 else 1334 break; 1335 } 1336 lp->d_rpm = ui; 1337 1338 /* interleave */ 1339 for (;;) { 1340 ui = getuint(lp, "interleave", 1341 "The physical sector interleave, set when formatting. Almost always 1.", 1342 lp->d_interleave, lp->d_interleave, 0, 0); 1343 if (ui == ULLONG_MAX - 1) { 1344 fputs("Command aborted\n", stderr); 1345 *lp = oldlabel; /* undo damage */ 1346 return; 1347 } else if (ui == ULLONG_MAX || ui == 0) 1348 fputs("Invalid entry\n", stderr); 1349 else 1350 break; 1351 } 1352 lp->d_interleave = ui; 1353 } 1354 1355 struct partition ** 1356 sort_partitions(struct disklabel *lp) 1357 { 1358 static struct partition *spp[MAXPARTITIONS+2]; 1359 int i, npartitions; 1360 1361 memset(spp, 0, sizeof(spp)); 1362 1363 for (npartitions = 0, i = 0; i < lp->d_npartitions; i++) { 1364 if (lp->d_partitions[i].p_fstype != FS_UNUSED && 1365 lp->d_partitions[i].p_fstype != FS_BOOT && 1366 DL_GETPSIZE(&lp->d_partitions[i]) != 0) 1367 spp[npartitions++] = &lp->d_partitions[i]; 1368 } 1369 1370 /* 1371 * Sort the partitions based on starting offset. 1372 * This is safe because we guarantee no overlap. 1373 */ 1374 if (npartitions > 1) 1375 if (heapsort((void *)spp, npartitions, sizeof(spp[0]), 1376 partition_cmp)) 1377 err(4, "failed to sort partition table"); 1378 1379 return(spp); 1380 } 1381 1382 /* 1383 * Get a valid disk type if necessary. 1384 */ 1385 void 1386 getdisktype(struct disklabel *lp, char *banner, char *dev) 1387 { 1388 int i; 1389 char *s, *def = "SCSI"; 1390 struct dtypes { 1391 char *dev; 1392 char *type; 1393 } dtypes[] = { 1394 { "sd", "SCSI" }, 1395 { "rz", "SCSI" }, 1396 { "wd", "IDE" }, 1397 { "fd", "FLOPPY" }, 1398 { "xd", "SMD" }, 1399 { "xy", "SMD" }, 1400 { "hd", "HP-IB" }, 1401 { "ccd", "CCD" }, 1402 { "vnd", "VND" }, 1403 { "svnd", "VND" }, 1404 { NULL, NULL } 1405 }; 1406 1407 if ((s = basename(dev)) != NULL) { 1408 if (*s == 'r') 1409 s++; 1410 i = strcspn(s, "0123456789"); 1411 s[i] = '\0'; 1412 dev = s; 1413 for (i = 0; dtypes[i].dev != NULL; i++) { 1414 if (strcmp(dev, dtypes[i].dev) == 0) { 1415 def = dtypes[i].type; 1416 break; 1417 } 1418 } 1419 } 1420 1421 if (lp->d_type > DKMAXTYPES || lp->d_type == 0) { 1422 puts(banner); 1423 puts("Possible values are:"); 1424 printf("\"IDE\", "); 1425 for (i = 1; i < DKMAXTYPES; i++) { 1426 printf("\"%s\"", dktypenames[i]); 1427 if (i < DKMAXTYPES - 1) 1428 fputs(", ", stdout); 1429 } 1430 putchar('\n'); 1431 1432 for (;;) { 1433 s = getstring("Disk type", 1434 "What kind of disk is this? Usually SCSI, IDE, " 1435 "ESDI, CCD, ST506, or floppy.", def); 1436 if (s == NULL) 1437 continue; 1438 if (strcasecmp(s, "IDE") == 0) { 1439 lp->d_type = DTYPE_ESDI; 1440 return; 1441 } 1442 for (i = 1; i < DKMAXTYPES; i++) 1443 if (strcasecmp(s, dktypenames[i]) == 0) { 1444 lp->d_type = i; 1445 return; 1446 } 1447 printf("\"%s\" is not a valid disk type.\n", s); 1448 fputs("Valid types are: ", stdout); 1449 for (i = 1; i < DKMAXTYPES; i++) { 1450 printf("\"%s\"", dktypenames[i]); 1451 if (i < DKMAXTYPES - 1) 1452 fputs(", ", stdout); 1453 } 1454 putchar('\n'); 1455 } 1456 } 1457 } 1458 1459 /* 1460 * Get beginning and ending sectors of the OpenBSD portion of the disk 1461 * from the user. 1462 */ 1463 void 1464 set_bounds(struct disklabel *lp) 1465 { 1466 u_int64_t ui, start_temp; 1467 1468 /* Starting sector */ 1469 do { 1470 ui = getuint(lp, "Starting sector", 1471 "The start of the OpenBSD portion of the disk.", 1472 starting_sector, DL_GETDSIZE(lp), 0, 0); 1473 if (ui == ULLONG_MAX - 1) { 1474 fputs("Command aborted\n", stderr); 1475 return; 1476 } 1477 } while (ui >= DL_GETDSIZE(lp)); 1478 start_temp = ui; 1479 1480 /* Size */ 1481 do { 1482 ui = getuint(lp, "Size ('*' for entire disk)", 1483 "The size of the OpenBSD portion of the disk ('*' for the " 1484 "entire disk).", ending_sector - starting_sector, 1485 DL_GETDSIZE(lp) - start_temp, 0, 0); 1486 if (ui == ULLONG_MAX - 1) { 1487 fputs("Command aborted\n", stderr); 1488 return; 1489 } 1490 } while (ui > DL_GETDSIZE(lp) - start_temp); 1491 ending_sector = start_temp + ui; 1492 starting_sector = start_temp; 1493 } 1494 1495 /* 1496 * Return a list of the "chunks" of free space available 1497 */ 1498 struct diskchunk * 1499 free_chunks(struct disklabel *lp) 1500 { 1501 struct partition **spp; 1502 static struct diskchunk chunks[MAXPARTITIONS + 2]; 1503 u_int64_t start, stop; 1504 int i, numchunks; 1505 1506 /* Sort the in-use partitions based on offset */ 1507 spp = sort_partitions(lp); 1508 1509 /* If there are no partitions, it's all free. */ 1510 if (spp[0] == NULL) { 1511 chunks[0].start = starting_sector; 1512 chunks[0].stop = ending_sector; 1513 chunks[1].start = chunks[1].stop = 0; 1514 return(chunks); 1515 } 1516 1517 /* Find chunks of free space */ 1518 numchunks = 0; 1519 if (DL_GETPOFFSET(spp[0]) > starting_sector) { 1520 chunks[0].start = starting_sector; 1521 chunks[0].stop = DL_GETPOFFSET(spp[0]); 1522 numchunks++; 1523 } 1524 for (i = 0; spp[i] != NULL; i++) { 1525 start = DL_GETPOFFSET(spp[i]) + DL_GETPSIZE(spp[i]); 1526 if (start < starting_sector) 1527 start = starting_sector; 1528 else if (start > ending_sector) 1529 start = ending_sector; 1530 if (spp[i + 1] != NULL) 1531 stop = DL_GETPOFFSET(spp[i+1]); 1532 else 1533 stop = ending_sector; 1534 if (stop < starting_sector) 1535 stop = starting_sector; 1536 else if (stop > ending_sector) 1537 stop = ending_sector; 1538 if (start < stop) { 1539 chunks[numchunks].start = start; 1540 chunks[numchunks].stop = stop; 1541 numchunks++; 1542 } 1543 } 1544 1545 /* Terminate and return */ 1546 chunks[numchunks].start = chunks[numchunks].stop = 0; 1547 return(chunks); 1548 } 1549 1550 void 1551 find_bounds(struct disklabel *lp) 1552 { 1553 starting_sector = DL_GETBSTART(lp); 1554 ending_sector = DL_GETBEND(lp); 1555 1556 if (ending_sector) { 1557 if (verbose) 1558 printf("Treating sectors %llu-%llu as the OpenBSD" 1559 " portion of the disk.\nYou can use the 'b'" 1560 " command to change this.\n\n", starting_sector, 1561 ending_sector); 1562 } else { 1563 #if (NUMBOOT == 1) 1564 /* Boot blocks take up the first cylinder */ 1565 starting_sector = lp->d_secpercyl; 1566 if (verbose) 1567 printf("Reserving the first data cylinder for boot" 1568 " blocks.\nYou can use the 'b' command to change" 1569 " this.\n\n"); 1570 #endif 1571 } 1572 } 1573 1574 /* 1575 * Calculate free space. 1576 */ 1577 u_int64_t 1578 editor_countfree(struct disklabel *lp) 1579 { 1580 struct diskchunk *chunks; 1581 u_int64_t freesectors = 0; 1582 int i; 1583 1584 chunks = free_chunks(lp); 1585 1586 for (i = 0; chunks[i].start != 0 || chunks[i].stop != 0; i++) 1587 freesectors += chunks[i].stop - chunks[i].start; 1588 1589 return (freesectors); 1590 } 1591 1592 void 1593 editor_help(char *arg) 1594 { 1595 1596 /* XXX - put these strings in a table instead? */ 1597 switch (*arg) { 1598 case 'p': 1599 puts( 1600 "The 'p' command prints the current partitions. By default, it prints size\n" 1601 "and offset in sectors (a sector is usually 512 bytes). The 'p' command\n" 1602 "takes an optional units argument. Possible values are 'b' for bytes, 'c'\n" 1603 "for cylinders, 'k' for kilobytes, 'm' for megabytes, and 'g' for gigabytes.\n"); 1604 break; 1605 case 'l': 1606 puts( 1607 "The 'l' command prints the header of the disk label. By default, it prints\n" 1608 "size and offset in sectors (a sector is usually 512 bytes). The 'p' command\n" 1609 "takes an optional units argument. Possible values are 'b' for bytes, 'c'\n" 1610 "for cylinders, 'k' for kilobytes, 'm' for megabytes, and 'g' for gigabytes.\n"); 1611 break; 1612 case 'M': 1613 puts( 1614 "The 'M' command pipes the entire OpenBSD manual page for disk label through\n" 1615 "the pager specified by the PAGER environment variable or 'less' if PAGER is\n" 1616 "not set. It is especially useful during install when the normal system\n" 1617 "manual is not available.\n"); 1618 break; 1619 case 'e': 1620 puts( 1621 "The 'e' command is used to edit the disk drive parameters. These include\n" 1622 "the number of sectors/track, tracks/cylinder, sectors/cylinder, number of\n" 1623 "cylinders on the disk , total sectors on the disk, rpm, interleave, disk\n" 1624 "type, and a descriptive label string. You should not change these unless\n" 1625 "you know what you are doing\n"); 1626 break; 1627 case 'a': 1628 puts( 1629 "The 'a' command adds new partitions to the disk. It takes as an optional\n" 1630 "argument the partition letter to add. If you do not specify a partition\n" 1631 "letter, you will be prompted for it; the next available letter will be the\n" 1632 "default answer\n"); 1633 break; 1634 case 'b': 1635 puts( 1636 "The 'b' command is used to change the boundaries of the OpenBSD portion of\n" 1637 "the disk. This is only useful on disks with an fdisk partition. By default,\n" 1638 "on a disk with an fdisk partition, the boundaries are set to be the first\n" 1639 "and last sectors of the OpenBSD fdisk partition. You should only change\n" 1640 "these if your fdisk partition table is incorrect or you have a disk larger\n" 1641 "than 8gig, since 8gig is the maximum size an fdisk partition can be. You\n" 1642 "may enter '*' at the 'Size' prompt to indicate the entire size of the disk\n" 1643 "(minus the starting sector). Use this option with care; if you extend the\n" 1644 "boundaries such that they overlap with another operating system you will\n" 1645 "corrupt the other operating system's data.\n"); 1646 break; 1647 case 'c': 1648 puts( 1649 "The 'c' command is used to change the size of an existing partition. It\n" 1650 "takes as an optional argument the partition letter to change. If you do not\n" 1651 "specify a partition letter, you will be prompted for one. You may add a '+'\n" 1652 "or '-' prefix to the new size to increase or decrease the existing value\n" 1653 "instead of entering an absolute value. You may also use a suffix to indicate\n" 1654 "the units the values is in terms of. Possible suffixes are 'b' for bytes,\n" 1655 "'c' for cylinders, 'k' for kilobytes, 'm' for megabytes, 'g' for gigabytes or\n" 1656 "no suffix for sectors (usually 512 bytes). You may also enter '*' to change\n" 1657 "the size to be the total number of free sectors remaining.\n"); 1658 break; 1659 case 'D': 1660 puts( 1661 "The 'D' command will set the disk label to the default values as reported\n" 1662 "by the disk itself. This similates the case where there is no disk label.\n"); 1663 break; 1664 case 'd': 1665 puts( 1666 "The 'd' command is used to delete an existing partition. It takes as an\n" 1667 "optional argument the partition letter to change. If you do not specify a\n" 1668 "partition letter, you will be prompted for one. You may not delete the ``c''\n" 1669 "partition as 'c' must always exist and by default is marked as 'unused' (so\n" 1670 "it does not take up any space).\n"); 1671 break; 1672 case 'g': 1673 puts( 1674 "The 'g' command is used select which disk geometry to use, the disk or a\n" 1675 "user geometry. It takes as an optional argument ``d'' or ``u''. If \n" 1676 "you do not specify the type as an argument, you will be prompted for it.\n"); 1677 break; 1678 case 'm': 1679 puts( 1680 "The 'm' command is used to modify an existing partition. It takes as an\n" 1681 "optional argument the partition letter to change. If you do not specify a\n" 1682 "partition letter, you will be prompted for one. This option allows the user\n" 1683 "to change the filesystem type, starting offset, partition size, block fragment\n" 1684 "size, block size, and cylinders per group for the specified partition (not all\n" 1685 "parameters are configurable for non-BSD partitions).\n"); 1686 break; 1687 case 'n': 1688 puts( 1689 "The 'n' command is used to set the mount point for a partition (ie: name it).\n" 1690 "It takes as an optional argument the partition letter to name. If you do\n" 1691 "not specify a partition letter, you will be prompted for one. This option\n" 1692 "is only valid if disklabel was invoked with the -f flag.\n"); 1693 break; 1694 case 'r': 1695 puts( 1696 "The 'r' command is used to recalculate and display details about\n" 1697 "the available free space.\n"); 1698 break; 1699 case 'u': 1700 puts( 1701 "The 'u' command will undo (or redo) the last change. Entering 'u' once will\n" 1702 "undo your last change. Entering it again will restore the change.\n"); 1703 break; 1704 case 's': 1705 puts( 1706 "The 's' command is used to save a copy of the label to a file in ascii format\n" 1707 "(suitable for loading via disklabel's [-R] option). It takes as an optional\n" 1708 "argument the filename to save the label to. If you do not specify a filename,\n" 1709 "you will be prompted for one.\n"); 1710 break; 1711 case 'w': 1712 puts( 1713 "The 'w' command will write the current label to disk. This option will\n" 1714 "commit any changes to the on-disk label.\n"); 1715 break; 1716 case 'q': 1717 puts( 1718 "The 'q' command quits the label editor. If any changes have been made you\n" 1719 "will be asked whether or not to save the changes to the on-disk label.\n"); 1720 break; 1721 case 'X': 1722 puts( 1723 "The 'X' command toggles disklabel in to/out of 'expert mode'. By default,\n" 1724 "some settings are reserved for experts only (such as the block and fragment\n" 1725 "size on ffs partitions).\n"); 1726 break; 1727 case 'x': 1728 puts( 1729 "The 'x' command exits the label editor without saving any changes to the\n" 1730 "on-disk label.\n"); 1731 break; 1732 case 'z': 1733 puts( 1734 "The 'z' command zeroes out the existing partition table, leaving only the 'c'\n" 1735 "partition. The drive parameters are not changed.\n"); 1736 break; 1737 default: 1738 puts("Available commands:"); 1739 puts( 1740 " ? [cmd] - show help n [part] - set mount point\n" 1741 " A - auto partition all space p [unit] - print partitions\n" 1742 " a [part] - add partition q - quit & save changes\n" 1743 " b - set OpenBSD boundaries s [path] - save label to file\n" 1744 " c [part] - change partition size r - display free space\n" 1745 " D - reset label to default U - undo all changes\n" 1746 " d [part] - delete partition u - undo last change\n" 1747 " e - edit drive parameters w - write label to disk\n" 1748 " g [d|u] - [d]isk or [u]ser geometry X - toggle expert mode\n" 1749 " l [unit] - print disk label header x - exit & lose changes\n" 1750 " M - disklabel(8) man page z - delete all partitions\n" 1751 " m [part] - modify partition\n" 1752 "\n" 1753 "Suffixes can be used to indicate units other than sectors:\n" 1754 "\t'b' (bytes), 'k' (kilobytes), 'm' (megabytes), 'g' (gigabytes)\n" 1755 "\t'c' (cylinders), '%' (% of total disk), '&' (% of free space).\n" 1756 "Values in non-sector units are truncated to the nearest cylinder boundary."); 1757 break; 1758 } 1759 } 1760 1761 void 1762 mpcopy(char **to, char **from) 1763 { 1764 int i; 1765 char *top; 1766 1767 for (i = 0; i < MAXPARTITIONS; i++) { 1768 if (from[i] != NULL) { 1769 int len = strlen(from[i]) + 1; 1770 1771 top = realloc(to[i], len); 1772 if (top == NULL) 1773 errx(4, "out of memory"); 1774 to[i] = top; 1775 (void)strlcpy(to[i], from[i], len); 1776 } else if (to[i] != NULL) { 1777 free(to[i]); 1778 to[i] = NULL; 1779 } 1780 } 1781 } 1782 1783 int 1784 mpequal(char **mp1, char **mp2) 1785 { 1786 int i; 1787 1788 for (i = 0; i < MAXPARTITIONS; i++) { 1789 if (mp1[i] == NULL && mp2[i] == NULL) 1790 continue; 1791 1792 if ((mp1[i] != NULL && mp2[i] == NULL) || 1793 (mp1[i] == NULL && mp2[i] != NULL) || 1794 (strcmp(mp1[i], mp2[i]) != 0)) 1795 return(0); 1796 } 1797 return(1); 1798 } 1799 1800 void 1801 mpsave(struct disklabel *lp) 1802 { 1803 int i, j; 1804 char bdev[MAXPATHLEN], *p; 1805 struct mountinfo mi[MAXPARTITIONS]; 1806 FILE *fp; 1807 1808 if (!fstabfile) 1809 return; 1810 1811 memset(&mi, 0, sizeof(mi)); 1812 1813 for (i = 0; i < MAXPARTITIONS; i++) { 1814 if (mountpoints[i] != NULL) { 1815 mi[i].mountpoint = mountpoints[i]; 1816 mi[i].partno = i; 1817 } 1818 } 1819 1820 /* Convert specname to bdev */ 1821 if (strncmp(_PATH_DEV, specname, sizeof(_PATH_DEV) - 1) == 0 && 1822 specname[sizeof(_PATH_DEV) - 1] == 'r') { 1823 snprintf(bdev, sizeof(bdev), "%s%s", _PATH_DEV, 1824 &specname[sizeof(_PATH_DEV)]); 1825 } else { 1826 if ((p = strrchr(specname, '/')) == NULL || *(++p) != 'r') 1827 return; 1828 *p = '\0'; 1829 snprintf(bdev, sizeof(bdev), "%s%s", specname, p + 1); 1830 *p = 'r'; 1831 } 1832 bdev[strlen(bdev) - 1] = '\0'; 1833 1834 /* Sort mountpoints so we don't try to mount /usr/local before /usr */ 1835 qsort((void *)mi, MAXPARTITIONS, sizeof(struct mountinfo), micmp); 1836 1837 if (fp = fopen(fstabfile, "w")) { 1838 for (i = 0; i < MAXPARTITIONS && mi[i].mountpoint; i++) { 1839 j = mi[i].partno; 1840 fprintf(fp, "%s%c %s %s rw 1 %d\n", bdev, 'a' + j, 1841 mi[i].mountpoint, 1842 fstypesnames[lp->d_partitions[j].p_fstype], 1843 j == 0 ? 1 : 2); 1844 } 1845 fclose(fp); 1846 } 1847 } 1848 1849 int 1850 get_offset(struct disklabel *lp, int partno) 1851 { 1852 struct diskchunk *chunks; 1853 struct partition *pp = &lp->d_partitions[partno]; 1854 u_int64_t ui, maxsize; 1855 int i, fstype; 1856 1857 ui = getuint(lp, "offset", 1858 "Starting sector for this partition.", 1859 DL_GETPOFFSET(pp), 1860 DL_GETPOFFSET(pp), 0, DO_CONVERSIONS | 1861 (pp->p_fstype == FS_BSDFFS ? DO_ROUNDING : 0)); 1862 1863 if (ui == ULLONG_MAX - 1) 1864 fputs("Command aborted\n", stderr); 1865 else if (ui == ULLONG_MAX) 1866 fputs("Invalid entry\n", stderr); 1867 else if (ui < starting_sector || ui >= ending_sector) 1868 fprintf(stderr, "The offset must be >= %llu and < %llu, " 1869 "the limits of the OpenBSD portion\n" 1870 "of the disk. The 'b' command can change these limits.\n", 1871 starting_sector, ending_sector); 1872 #ifdef SUN_AAT0 1873 else if (partno == 0 && ui != 0) 1874 fprintf(stderr, "This architecture requires that " 1875 "partition 'a' start at sector 0.\n"); 1876 #endif 1877 else { 1878 fstype = pp->p_fstype; 1879 pp->p_fstype = FS_UNUSED; 1880 chunks = free_chunks(lp); 1881 pp->p_fstype = fstype; 1882 for (i = 0; chunks[i].start != 0 || chunks[i].stop != 0; i++) { 1883 if (ui < chunks[i].start || ui >= chunks[i].stop) 1884 continue; 1885 DL_SETPOFFSET(pp, ui); 1886 maxsize = chunks[i].stop - DL_GETPOFFSET(pp); 1887 if (DL_GETPSIZE(pp) > maxsize) 1888 DL_SETPSIZE(pp, maxsize); 1889 return (0); 1890 } 1891 fputs("The offset must be in a free area.\n", stderr); 1892 } 1893 1894 /* Partition offset was not set. */ 1895 return (1); 1896 } 1897 1898 int 1899 get_size(struct disklabel *lp, int partno) 1900 { 1901 struct partition *pp = &lp->d_partitions[partno]; 1902 u_int64_t maxsize, ui; 1903 1904 maxsize = max_partition_size(lp, partno); 1905 1906 ui = getuint(lp, "size", "Size of the partition. " 1907 "You may also say +/- amount for a relative change.", 1908 DL_GETPSIZE(pp), maxsize, DL_GETPOFFSET(pp), 1909 DO_CONVERSIONS | ((pp->p_fstype == FS_BSDFFS || 1910 pp->p_fstype == FS_SWAP) ? DO_ROUNDING : 0)); 1911 1912 if (ui == ULLONG_MAX - 1) 1913 fputs("Command aborted\n", stderr); 1914 else if (ui == ULLONG_MAX) 1915 fputs("Invalid entry\n", stderr); 1916 else if (ui == 0) 1917 fputs("The size must be > 0\n", stderr); 1918 else if (ui + DL_GETPOFFSET(pp) > ending_sector) 1919 fprintf(stderr, "The size can't be more than " 1920 "%llu sectors, or the partition would\n" 1921 "extend beyond the last sector (%llu) of the " 1922 "OpenBSD portion of\nthe disk. " 1923 "The 'b' command can change this limit.\n", 1924 ending_sector - DL_GETPOFFSET(pp), ending_sector); 1925 else if (ui > maxsize) 1926 fprintf(stderr,"Sorry, there are only %llu sectors left\n", 1927 maxsize); 1928 else { 1929 DL_SETPSIZE(pp, ui); 1930 return (0); 1931 } 1932 1933 /* Partition size was not set. */ 1934 return (1); 1935 } 1936 1937 int 1938 get_fsize(struct disklabel *lp, int partno) 1939 { 1940 u_int64_t ui, fsize, frag; 1941 struct partition *pp = &lp->d_partitions[partno]; 1942 1943 if (!expert || pp->p_fstype != FS_BSDFFS) 1944 return (0); 1945 1946 fsize = DISKLABELV1_FFS_FSIZE(pp->p_fragblock); 1947 frag = DISKLABELV1_FFS_FRAG(pp->p_fragblock); 1948 if (fsize == 0) 1949 frag = 8; 1950 1951 for (;;) { 1952 ui = getuint(lp, "fragment size", 1953 "Size of fs block fragments. Usually 2048 or 512.", 1954 fsize, fsize, 0, 0); 1955 if (ui == ULLONG_MAX - 1) { 1956 fputs("Command aborted\n", stderr); 1957 return(1); 1958 } else if (ui == ULLONG_MAX) 1959 fputs("Invalid entry\n", stderr); 1960 else 1961 break; 1962 } 1963 if (ui == 0) 1964 puts("Zero fragment size implies zero block size"); 1965 pp->p_fragblock = DISKLABELV1_FFS_FRAGBLOCK(ui, frag); 1966 return(0); 1967 } 1968 1969 int 1970 get_bsize(struct disklabel *lp, int partno) 1971 { 1972 u_int64_t ui, bsize, frag, fsize; 1973 struct partition *pp = &lp->d_partitions[partno]; 1974 1975 if (!expert || pp->p_fstype != FS_BSDFFS) 1976 return (0); 1977 1978 /* Avoid dividing by zero... */ 1979 if (pp->p_fragblock == 0) 1980 return(1); 1981 1982 bsize = DISKLABELV1_FFS_BSIZE(pp->p_fragblock); 1983 fsize = DISKLABELV1_FFS_FSIZE(pp->p_fragblock); 1984 frag = DISKLABELV1_FFS_FRAG(pp->p_fragblock); 1985 1986 for (;;) { 1987 ui = getuint(lp, "block size", 1988 "Size of filesystem blocks. Usually 16384 or 4096.", 1989 fsize * frag, fsize * frag, 1990 0, 0); 1991 1992 /* sanity checks */ 1993 if (ui == ULLONG_MAX - 1) { 1994 fputs("Command aborted\n", stderr); 1995 return(1); 1996 } else if (ui == ULLONG_MAX) 1997 fputs("Invalid entry\n", stderr); 1998 else if (ui < getpagesize()) 1999 fprintf(stderr, 2000 "Error: block size must be at least as big " 2001 "as page size (%d).\n", getpagesize()); 2002 else if (ui % fsize != 0) 2003 fputs("Error: block size must be a multiple of the " 2004 "fragment size.\n", stderr); 2005 else if (ui / fsize < 1) 2006 fputs("Error: block size must be at least as big as " 2007 "fragment size.\n", stderr); 2008 else 2009 break; 2010 } 2011 pp->p_fragblock = DISKLABELV1_FFS_FRAGBLOCK(ui / frag, frag); 2012 return(0); 2013 } 2014 2015 int 2016 get_fstype(struct disklabel *lp, int partno) 2017 { 2018 char *p; 2019 u_int64_t ui; 2020 struct partition *pp = &lp->d_partitions[partno]; 2021 2022 if (pp->p_fstype < FSMAXTYPES) { 2023 p = getstring("FS type", 2024 "Filesystem type (usually 4.2BSD or swap)", 2025 fstypenames[pp->p_fstype]); 2026 if (p == NULL) { 2027 fputs("Command aborted\n", stderr); 2028 return(1); 2029 } 2030 for (ui = 0; ui < FSMAXTYPES; ui++) { 2031 if (!strcasecmp(p, fstypenames[ui])) { 2032 pp->p_fstype = ui; 2033 break; 2034 } 2035 } 2036 if (ui >= FSMAXTYPES) { 2037 printf("Unrecognized filesystem type '%s', treating as 'unknown'\n", p); 2038 pp->p_fstype = FS_OTHER; 2039 } 2040 } else { 2041 for (;;) { 2042 ui = getuint(lp, "FS type (decimal)", 2043 "Filesystem type as a decimal number; usually 7 (4.2BSD) or 1 (swap).", 2044 pp->p_fstype, pp->p_fstype, 0, 0); 2045 if (ui == ULLONG_MAX - 1) { 2046 fputs("Command aborted\n", stderr); 2047 return(1); 2048 } if (ui == ULLONG_MAX) 2049 fputs("Invalid entry\n", stderr); 2050 else 2051 break; 2052 } 2053 pp->p_fstype = ui; 2054 } 2055 return(0); 2056 } 2057 2058 int 2059 get_mp(struct disklabel *lp, int partno) 2060 { 2061 struct partition *pp = &lp->d_partitions[partno]; 2062 char *p; 2063 int i; 2064 2065 if (fstabfile && pp->p_fstype != FS_UNUSED && 2066 pp->p_fstype != FS_SWAP && pp->p_fstype != FS_BOOT && 2067 pp->p_fstype != FS_OTHER) { 2068 for (;;) { 2069 p = getstring("mount point", 2070 "Where to mount this filesystem (ie: / /var /usr)", 2071 mountpoints[partno] ? mountpoints[partno] : "none"); 2072 if (p == NULL) { 2073 fputs("Command aborted\n", stderr); 2074 return(1); 2075 } 2076 if (strcasecmp(p, "none") == 0) { 2077 free(mountpoints[partno]); 2078 mountpoints[partno] = NULL; 2079 break; 2080 } 2081 for (i = 0; i < MAXPARTITIONS; i++) 2082 if (mountpoints[i] != NULL && i != partno && 2083 strcmp(p, mountpoints[i]) == 0) 2084 break; 2085 if (i < MAXPARTITIONS) { 2086 fprintf(stderr, "'%c' already being mounted at " 2087 "'%s'\n", 'a'+i, p); 2088 break; 2089 } 2090 if (*p == '/') { 2091 /* XXX - might as well realloc */ 2092 free(mountpoints[partno]); 2093 if ((mountpoints[partno] = strdup(p)) == NULL) 2094 errx(4, "out of memory"); 2095 break; 2096 } 2097 fputs("Mount points must start with '/'\n", stderr); 2098 } 2099 } 2100 return(0); 2101 } 2102 2103 int 2104 micmp(const void *a1, const void *a2) 2105 { 2106 struct mountinfo *mi1 = (struct mountinfo *)a1; 2107 struct mountinfo *mi2 = (struct mountinfo *)a2; 2108 2109 /* We want all the NULLs at the end... */ 2110 if (mi1->mountpoint == NULL && mi2->mountpoint == NULL) 2111 return(0); 2112 else if (mi1->mountpoint == NULL) 2113 return(1); 2114 else if (mi2->mountpoint == NULL) 2115 return(-1); 2116 else 2117 return(strcmp(mi1->mountpoint, mi2->mountpoint)); 2118 } 2119 2120 void 2121 get_geometry(int f, struct disklabel **dgpp) 2122 { 2123 struct stat st; 2124 struct disklabel *disk_geop; 2125 2126 if (fstat(f, &st) == -1) 2127 err(4, "Can't stat device"); 2128 2129 /* Get disk geometry */ 2130 if ((disk_geop = calloc(1, sizeof(struct disklabel))) == NULL) 2131 errx(4, "out of memory"); 2132 if (ioctl(f, DIOCGPDINFO, disk_geop) < 0 && 2133 ioctl(f, DIOCGDINFO, disk_geop) < 0) 2134 err(4, "ioctl DIOCGDINFO"); 2135 *dgpp = disk_geop; 2136 } 2137 2138 void 2139 set_geometry(struct disklabel *lp, struct disklabel *dgp, 2140 struct disklabel *ugp, char *p) 2141 { 2142 if (p == NULL) { 2143 p = getstring("[d]isk or [u]ser geometry", 2144 "Enter 'd' to use the geometry based on what the disk " 2145 "itself thinks it is, or 'u' to use the geometry that " 2146 "was found in the label.", 2147 "d"); 2148 } 2149 if (p == NULL) { 2150 fputs("Command aborted\n", stderr); 2151 return; 2152 } 2153 switch (*p) { 2154 case 'd': 2155 case 'D': 2156 if (dgp == NULL) 2157 fputs("BIOS geometry not defined.\n", stderr); 2158 else { 2159 lp->d_secsize = dgp->d_secsize; 2160 lp->d_nsectors = dgp->d_nsectors; 2161 lp->d_ntracks = dgp->d_ntracks; 2162 lp->d_ncylinders = dgp->d_ncylinders; 2163 lp->d_secpercyl = dgp->d_secpercyl; 2164 DL_SETDSIZE(lp, DL_GETDSIZE(dgp)); 2165 } 2166 break; 2167 case 'u': 2168 case 'U': 2169 if (ugp == NULL) 2170 fputs("BIOS geometry not defined.\n", stderr); 2171 else { 2172 lp->d_secsize = ugp->d_secsize; 2173 lp->d_nsectors = ugp->d_nsectors; 2174 lp->d_ntracks = ugp->d_ntracks; 2175 lp->d_ncylinders = ugp->d_ncylinders; 2176 lp->d_secpercyl = ugp->d_secpercyl; 2177 DL_SETDSIZE(lp, DL_GETDSIZE(ugp)); 2178 if (dgp != NULL && ugp->d_secsize == dgp->d_secsize && 2179 ugp->d_nsectors == dgp->d_nsectors && 2180 ugp->d_ntracks == dgp->d_ntracks && 2181 ugp->d_ncylinders == dgp->d_ncylinders && 2182 ugp->d_secpercyl == dgp->d_secpercyl && 2183 DL_GETDSIZE(ugp) == DL_GETDSIZE(dgp)) 2184 fputs("Note: user geometry is the same as disk " 2185 "geometry.\n", stderr); 2186 } 2187 break; 2188 default: 2189 fputs("You must enter either 'd' or 'u'.\n", stderr); 2190 break; 2191 } 2192 } 2193 2194 void 2195 zero_partitions(struct disklabel *lp) 2196 { 2197 int i; 2198 2199 for (i = 0; i < MAXPARTITIONS; i++) { 2200 memset(&lp->d_partitions[i], 0, sizeof(struct partition)); 2201 free(mountpoints[i]); 2202 mountpoints[i] = NULL; 2203 } 2204 2205 DL_SETPSIZE(&lp->d_partitions[RAW_PART], DL_GETDSIZE(lp)); 2206 } 2207 2208 u_int64_t 2209 max_partition_size(struct disklabel *lp, int partno) 2210 { 2211 struct partition *pp = &lp->d_partitions[partno]; 2212 struct diskchunk *chunks; 2213 u_int64_t maxsize, offset; 2214 int fstype, i; 2215 2216 fstype = pp->p_fstype; 2217 pp->p_fstype = FS_UNUSED; 2218 chunks = free_chunks(lp); 2219 pp->p_fstype = fstype; 2220 2221 offset = DL_GETPOFFSET(pp); 2222 for (i = 0; chunks[i].start != 0 || chunks[i].stop != 0; i++) { 2223 if (offset < chunks[i].start || offset >= chunks[i].stop) 2224 continue; 2225 maxsize = chunks[i].stop - offset; 2226 break; 2227 } 2228 return (maxsize); 2229 } 2230 2231 void 2232 psize(daddr64_t sz, char unit, struct disklabel *lp) 2233 { 2234 double d = scale(sz, unit, lp); 2235 if (d < 0) 2236 printf("%llu", sz); 2237 else 2238 printf("%.*f%c", unit == 'B' ? 0 : 1, d, unit); 2239 } 2240 2241 void 2242 display_edit(struct disklabel *lp, char unit, u_int64_t fr) 2243 { 2244 int i; 2245 2246 unit = canonical_unit(lp, unit); 2247 2248 printf("OpenBSD area: "); 2249 psize(starting_sector, 0, lp); 2250 printf("-"); 2251 psize(ending_sector, 0, lp); 2252 printf("; size: "); 2253 psize(ending_sector - starting_sector, unit, lp); 2254 printf("; free: "); 2255 psize(fr, unit, lp); 2256 2257 printf("\n# %16.16s %16.16s fstype [fsize bsize cpg]\n", 2258 "size", "offset"); 2259 for (i = 0; i < lp->d_npartitions; i++) 2260 display_partition(stdout, lp, i, unit); 2261 } 2262 2263