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