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