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