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