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