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