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