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