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