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