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