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