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