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