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