1 /* $OpenBSD: editor.c,v 1.418 2024/03/22 21:49:52 jan 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 { GIG(2), GIG(5), 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 struct partition *pp; 524 u_int64_t pstart, pend; 525 int i; 526 527 /* How big is the OpenBSD portion of the disk? */ 528 find_bounds(lp_org); 529 530 resizeok = 1; 531 for (i = 0; i < MAXPARTITIONS; i++) { 532 if (i == RAW_PART) 533 continue; 534 pp = &lp_org->d_partitions[i]; 535 if (DL_GETPSIZE(pp) == 0 || pp->p_fstype == FS_UNUSED) 536 continue; 537 pstart = DL_GETPOFFSET(pp); 538 pend = pstart + DL_GETPSIZE(pp); 539 if (((pstart >= starting_sector && pstart < ending_sector) || 540 (pend > starting_sector && pend <= ending_sector))) 541 resizeok = 0; /* Part of OBSD area is in use! */ 542 } 543 544 for (i = 0; i < alloc_table_nitems; i++) { 545 memcpy(&label, lp_org, sizeof(label)); 546 if (allocate_space(&label, &alloc_table[i]) == 0) { 547 memcpy(lp_org, &label, sizeof(struct disklabel)); 548 return 0; 549 } 550 } 551 552 return 1; 553 } 554 555 const struct diskchunk * 556 allocate_diskchunk(const struct disklabel *lp, 557 const struct space_allocation *sa) 558 { 559 const struct diskchunk *chunk; 560 static struct diskchunk largest; 561 uint64_t maxstop; 562 563 largest.start = largest.stop = 0; 564 565 chunk = free_chunks(lp, -1); 566 for (; chunk->start != 0 || chunk->stop != 0; chunk++) { 567 if (CHUNKSZ(chunk) > CHUNKSZ(&largest)) 568 largest = *chunk; 569 } 570 maxstop = largest.start + DL_BLKTOSEC(lp, sa->maxsz); 571 if (maxstop > largest.stop) 572 maxstop = largest.stop; 573 #ifdef SUN_CYLCHECK 574 if (lp->d_flags & D_VENDOR) { 575 largest.start = ROUNDUP(largest.start, lp->d_secpercyl); 576 maxstop = ROUNDUP(maxstop, lp->d_secpercyl); 577 if (maxstop > largest.stop) 578 maxstop -= lp->d_secpercyl; 579 if (largest.start >= maxstop) 580 largest.start = largest.stop = maxstop = 0; 581 } 582 #endif 583 if (maxstop < largest.stop) 584 largest.stop = maxstop; 585 if (CHUNKSZ(&largest) < DL_BLKTOSEC(lp, sa->minsz)) 586 return NULL; 587 588 return &largest; 589 } 590 591 int 592 allocate_partition(struct disklabel *lp, struct space_allocation *sa) 593 { 594 const struct diskchunk *chunk; 595 struct partition *pp; 596 unsigned int partno; 597 598 for (partno = 0; partno < nitems(lp->d_partitions); partno++) { 599 if (partno == RAW_PART) 600 continue; 601 pp = &lp->d_partitions[partno]; 602 if (DL_GETPSIZE(pp) == 0 || pp->p_fstype == FS_UNUSED) 603 break; 604 } 605 if (partno >= nitems(lp->d_partitions)) 606 return 1; /* No free partition. */ 607 608 /* Find appropriate chunk of free space. */ 609 chunk = allocate_diskchunk(lp, sa); 610 if (chunk == NULL) 611 return 1; 612 613 if (strcasecmp(sa->mp, "raid") == 0) 614 pp->p_fstype = FS_RAID; 615 else if (strcasecmp(sa->mp, "swap") == 0) 616 pp->p_fstype = FS_SWAP; 617 else if (sa->mp[0] == '/') 618 pp->p_fstype = FS_BSDFFS; 619 else 620 return 1; 621 622 DL_SETPSIZE(pp, chunk->stop - chunk->start); 623 DL_SETPOFFSET(pp, chunk->start); 624 625 if (pp->p_fstype == FS_BSDFFS && DL_GETPSIZE(pp) > 0) { 626 mountpoints[partno] = strdup(sa->mp); 627 if (mountpoints[partno] == NULL) 628 err(1, NULL); 629 if (set_fragblock(lp, partno)) 630 return 1; 631 } 632 633 return 0; 634 } 635 636 void 637 allocate_physmemincr(struct space_allocation *sa) 638 { 639 u_int64_t memblks; 640 extern int64_t physmem; 641 642 if (physmem == 0) 643 return; 644 645 memblks = physmem / DEV_BSIZE; 646 if (strcasecmp(sa->mp, "swap") == 0) { 647 if (memblks < MEG(256)) 648 sa->minsz = sa->maxsz = 2 * memblks; 649 else 650 sa->maxsz += memblks; 651 } else if (strcasecmp(sa->mp, "/var") == 0) { 652 sa->maxsz += 2 * memblks; 653 } 654 } 655 656 int 657 allocate_space(struct disklabel *lp, const struct alloc_table *alloc_table) 658 { 659 struct space_allocation sa[MAXPARTITIONS]; 660 u_int64_t maxsz, xtrablks; 661 int i; 662 663 xtrablks = DL_SECTOBLK(lp, editor_countfree(lp)); 664 memset(sa, 0, sizeof(sa)); 665 for (i = 0; i < alloc_table->sz; i++) { 666 sa[i] = alloc_table->table[i]; 667 if (alloc_table->table == alloc_big) 668 allocate_physmemincr(&sa[i]); 669 if (xtrablks < sa[i].minsz) 670 return 1; /* Too few free blocks. */ 671 xtrablks -= sa[i].minsz; 672 } 673 sa[alloc_table->sz - 1].rate = 100; /* Last allocation is greedy. */ 674 675 for (i = lp->d_npartitions; i < MAXPARTITIONS; i++) { 676 if (i == RAW_PART) 677 continue; 678 memset(&lp->d_partitions[i], 0, sizeof(lp->d_partitions[i])); 679 } 680 lp->d_npartitions = MAXPARTITIONS; 681 682 mpfree(mountpoints, KEEP); 683 for (i = 0; i < alloc_table->sz; i++) { 684 if (sa[i].rate == 100) 685 maxsz = sa[i].minsz + xtrablks; 686 else 687 maxsz = sa[i].minsz + (xtrablks / 100) * sa[i].rate; 688 if (maxsz < sa[i].maxsz) 689 sa[i].maxsz = maxsz; 690 if (allocate_partition(lp, &sa[i])) { 691 mpfree(mountpoints, KEEP); 692 return 1; 693 } 694 } 695 696 return 0; 697 } 698 699 /* 700 * Resize a partition, moving all subsequent partitions 701 */ 702 void 703 editor_resize(struct disklabel *lp, const char *p) 704 { 705 struct disklabel label; 706 struct partition *pp, *prev; 707 u_int64_t ui, sz, off; 708 int partno, i, flags, shrunk; 709 710 label = *lp; 711 712 if ((partno = getpartno(&label, p, "resize")) == -1) 713 return; 714 715 pp = &label.d_partitions[partno]; 716 sz = DL_GETPSIZE(pp); 717 if (pp->p_fstype != FS_BSDFFS && pp->p_fstype != FS_SWAP) { 718 fputs("Cannot resize spoofed partition\n", stderr); 719 return; 720 } 721 flags = DO_CONVERSIONS; 722 ui = getuint64(lp, "[+|-]new size (with unit)", 723 "new size or amount to grow (+) or shrink (-) partition including " 724 "unit", sz, sz + editor_countfree(lp), &flags); 725 726 if (ui == CMD_ABORTED) 727 return; 728 else if (ui == CMD_BADVALUE) 729 return; 730 else if (ui == 0) { 731 fputs("The size must be > 0 sectors\n", stderr); 732 return; 733 } 734 735 #ifdef SUN_CYLCHECK 736 if (lp->d_flags & D_VENDOR) 737 ui = ROUNDUP(ui, lp->d_secpercyl); 738 #endif 739 if (DL_GETPOFFSET(pp) + ui > ending_sector) { 740 fputs("Amount too big\n", stderr); 741 return; 742 } 743 744 DL_SETPSIZE(pp, ui); 745 pp->p_fragblock = 0; 746 if (set_fragblock(&label, partno) == 1) 747 return; 748 749 /* 750 * Pack partitions above the resized partition, leaving unused 751 * partitions alone. 752 */ 753 shrunk = -1; 754 prev = pp; 755 for (i = partno + 1; i < MAXPARTITIONS; i++) { 756 if (i == RAW_PART) 757 continue; 758 pp = &label.d_partitions[i]; 759 if (pp->p_fstype != FS_BSDFFS && pp->p_fstype != FS_SWAP) 760 continue; 761 sz = DL_GETPSIZE(pp); 762 if (sz == 0) 763 continue; 764 765 off = DL_GETPOFFSET(prev) + DL_GETPSIZE(prev); 766 767 if (off < ending_sector) { 768 DL_SETPOFFSET(pp, off); 769 if (off + DL_GETPSIZE(pp) > ending_sector) { 770 DL_SETPSIZE(pp, ending_sector - off); 771 pp->p_fragblock = 0; 772 if (set_fragblock(&label, i) == 1) 773 return; 774 shrunk = i; 775 } 776 } else { 777 fputs("Amount too big\n", stderr); 778 return; 779 } 780 prev = pp; 781 } 782 783 if (shrunk != -1) 784 fprintf(stderr, "Partition %c shrunk to %llu sectors to make " 785 "room\n", 'a' + shrunk, 786 DL_GETPSIZE(&label.d_partitions[shrunk])); 787 *lp = label; 788 } 789 790 /* 791 * Add a new partition. 792 */ 793 void 794 editor_add(struct disklabel *lp, const char *p) 795 { 796 struct partition *pp; 797 const struct diskchunk *chunk; 798 int partno; 799 u_int64_t new_offset, new_size; 800 801 chunk = free_chunks(lp, -1); 802 new_size = new_offset = 0; 803 for (; chunk->start != 0 || chunk->stop != 0; chunk++) { 804 if (CHUNKSZ(chunk) > new_size) { 805 new_size = CHUNKSZ(chunk); 806 new_offset = chunk->start; 807 } 808 } 809 810 #ifdef SUN_CYLCHECK 811 if ((lp->d_flags & D_VENDOR) && new_size < lp->d_secpercyl) { 812 fputs("No space left, you need to shrink a partition " 813 "(need at least one full cylinder)\n", 814 stderr); 815 return; 816 } 817 #endif 818 if (new_size == 0) { 819 fputs("No space left, you need to shrink a partition\n", 820 stderr); 821 return; 822 } 823 824 if ((partno = getpartno(lp, p, "add")) == -1) 825 return; 826 pp = &lp->d_partitions[partno]; 827 memset(pp, 0, sizeof(*pp)); 828 829 /* 830 * Increase d_npartitions if necessary. Ensure all new partitions are 831 * zero'ed to avoid inadvertent overlaps. 832 */ 833 for(; lp->d_npartitions <= partno; lp->d_npartitions++) 834 memset(&lp->d_partitions[lp->d_npartitions], 0, sizeof(*pp)); 835 836 DL_SETPSIZE(pp, new_size); 837 DL_SETPOFFSET(pp, new_offset); 838 pp->p_fstype = partno == 1 ? FS_SWAP : FS_BSDFFS; 839 840 if (get_offset(lp, partno) == 0 && 841 get_size(lp, partno) == 0 && 842 get_fstype(lp, partno) == 0 && 843 get_mp(lp, partno) == 0 && 844 set_fragblock(lp, partno) == 0) 845 return; 846 847 /* Bailed out at some point, so effectively delete the partition. */ 848 memset(pp, 0, sizeof(*pp)); 849 } 850 851 /* 852 * Set the mountpoint of an existing partition ('name'). 853 */ 854 void 855 editor_name(const struct disklabel *lp, const char *p) 856 { 857 int partno; 858 859 if ((partno = getpartno(lp, p, "name")) == -1) 860 return; 861 862 get_mp(lp, partno); 863 } 864 865 /* 866 * Change an existing partition. 867 */ 868 void 869 editor_modify(struct disklabel *lp, const char *p) 870 { 871 struct partition opp, *pp; 872 int partno; 873 874 if ((partno = getpartno(lp, p, "modify")) == -1) 875 return; 876 877 pp = &lp->d_partitions[partno]; 878 opp = *pp; 879 880 if (get_offset(lp, partno) == 0 && 881 get_size(lp, partno) == 0 && 882 get_fstype(lp, partno) == 0 && 883 get_mp(lp, partno) == 0 && 884 set_fragblock(lp, partno) == 0) 885 return; 886 887 /* Bailed out at some point, so undo any changes. */ 888 *pp = opp; 889 } 890 891 /* 892 * Delete an existing partition. 893 */ 894 void 895 editor_delete(struct disklabel *lp, const char *p) 896 { 897 struct partition *pp; 898 int partno; 899 900 if ((partno = getpartno(lp, p, "delete")) == -1) 901 return; 902 if (partno == lp->d_npartitions) { 903 zero_partitions(lp); 904 return; 905 } 906 pp = &lp->d_partitions[partno]; 907 908 /* Really delete it (as opposed to just setting to "unused") */ 909 memset(pp, 0, sizeof(*pp)); 910 free(mountpoints[partno]); 911 mountpoints[partno] = NULL; 912 } 913 914 /* 915 * Change the size of an existing partition. 916 */ 917 void 918 editor_change(struct disklabel *lp, const char *p) 919 { 920 struct partition *pp; 921 int partno; 922 923 if ((partno = getpartno(lp, p, "change size")) == -1) 924 return; 925 926 pp = &lp->d_partitions[partno]; 927 printf("Partition %c is currently %llu sectors in size, and can have " 928 "a maximum\nsize of %llu sectors.\n", 929 'a' + partno, DL_GETPSIZE(pp), max_partition_size(lp, partno)); 930 931 /* Get new size */ 932 get_size(lp, partno); 933 } 934 935 /* 936 * Sort the partitions based on starting offset. 937 * This assumes there can be no overlap. 938 */ 939 int 940 partition_cmp(const void *e1, const void *e2) 941 { 942 struct partition *p1 = *(struct partition **)e1; 943 struct partition *p2 = *(struct partition **)e2; 944 u_int64_t o1 = DL_GETPOFFSET(p1); 945 u_int64_t o2 = DL_GETPOFFSET(p2); 946 947 if (o1 < o2) 948 return -1; 949 else if (o1 > o2) 950 return 1; 951 else 952 return 0; 953 } 954 955 char * 956 getstring(const char *prompt, const char *helpstring, const char *oval) 957 { 958 static char buf[BUFSIZ]; 959 int n; 960 961 buf[0] = '\0'; 962 do { 963 printf("%s: [%s] ", prompt, oval ? oval : ""); 964 if (fgets(buf, sizeof(buf), stdin) == NULL) { 965 buf[0] = '\0'; 966 if (feof(stdin)) { 967 clearerr(stdin); 968 putchar('\n'); 969 fputs("Command aborted\n", stderr); 970 return NULL; 971 } 972 } 973 n = strlen(buf); 974 if (n > 0 && buf[n-1] == '\n') 975 buf[--n] = '\0'; 976 if (buf[0] == '?') 977 puts(helpstring); 978 else if (oval != NULL && buf[0] == '\0') 979 strlcpy(buf, oval, sizeof(buf)); 980 } while (buf[0] == '?'); 981 982 return &buf[0]; 983 } 984 985 int 986 getpartno(const struct disklabel *lp, const char *p, const char *action) 987 { 988 char buf[2] = { '\0', '\0'}; 989 const char *promptfmt = "partition to %s"; 990 const char *helpfmt = "Partition must be between 'a' and '%c' " 991 "(excluding 'c')%s.\n"; 992 const struct partition *pp; 993 char *help = NULL, *prompt = NULL; 994 unsigned char maxpart; 995 unsigned int partno; 996 int add, delete, inuse; 997 998 add = strcmp("add", action) == 0; 999 delete = strcmp("delete", action) == 0; 1000 maxpart = 'a' - 1 + (add ? MAXPARTITIONS : lp->d_npartitions); 1001 1002 if (p == NULL) { 1003 if (asprintf(&prompt, promptfmt, action) == -1 || 1004 asprintf(&help, helpfmt, maxpart, delete ? ", or '*'" : "") 1005 == -1) { 1006 fprintf(stderr, "Unable to build prompt or help\n"); 1007 goto done; 1008 } 1009 if (add) { 1010 /* Default to first unused partition. */ 1011 for (partno = 0; partno < MAXPARTITIONS; partno++) { 1012 if (partno == RAW_PART) 1013 continue; 1014 pp = &lp->d_partitions[partno]; 1015 if (partno >= lp->d_npartitions || 1016 DL_GETPSIZE(pp) == 0 || 1017 pp->p_fstype == FS_UNUSED) { 1018 buf[0] = 'a' + partno; 1019 p = buf; 1020 break; 1021 } 1022 } 1023 } 1024 p = getstring(prompt, help, p); 1025 free(prompt); 1026 free(help); 1027 if (p == NULL || *p == '\0') 1028 goto done; 1029 } 1030 1031 if (delete && strlen(p) == 1 && *p == '*') 1032 return lp->d_npartitions; 1033 1034 if (strlen(p) > 1 || *p < 'a' || *p > maxpart || *p == 'c') { 1035 fprintf(stderr, helpfmt, maxpart, delete ? ", or '*'" : ""); 1036 goto done; 1037 } 1038 1039 partno = *p - 'a'; 1040 pp = &lp->d_partitions[partno]; 1041 inuse = partno < lp->d_npartitions && DL_GETPSIZE(pp) > 0 && 1042 pp->p_fstype != FS_UNUSED; 1043 1044 if ((add && !inuse) || (!add && inuse)) 1045 return partno; 1046 1047 fprintf(stderr, "Partition '%c' is %sin use.\n", *p, 1048 inuse ? "" : "not "); 1049 1050 done: 1051 return -1; 1052 } 1053 1054 /* 1055 * Returns 1056 * 0 .. CMD_ABORTED - 1 ==> valid value 1057 * CMD_BADVALUE ==> invalid value 1058 * CMD_ABORTED ==> ^D on input 1059 */ 1060 u_int64_t 1061 getnumber(const char *prompt, const char *helpstring, u_int32_t oval, 1062 u_int32_t maxval) 1063 { 1064 char buf[BUFSIZ], *p; 1065 int rslt; 1066 long long rval; 1067 const char *errstr; 1068 1069 rslt = snprintf(buf, sizeof(buf), "%u", oval); 1070 if (rslt < 0 || (unsigned int)rslt >= sizeof(buf)) 1071 return CMD_BADVALUE; 1072 1073 p = getstring(prompt, helpstring, buf); 1074 if (p == NULL) 1075 return CMD_ABORTED; 1076 if (strlen(p) == 0) 1077 return oval; 1078 1079 rval = strtonum(p, 0, maxval, &errstr); 1080 if (errstr != NULL) { 1081 printf("%s must be between 0 and %u\n", prompt, maxval); 1082 return CMD_BADVALUE; 1083 } 1084 1085 return rval; 1086 } 1087 1088 /* 1089 * Returns 1090 * 0 .. CMD_ABORTED - 1 ==> valid value 1091 * CMD_BADVALUE ==> invalid value 1092 * CMD_ABORTED ==> ^D on input 1093 */ 1094 u_int64_t 1095 getuint64(const struct disklabel *lp, char *prompt, char *helpstring, 1096 u_int64_t oval, u_int64_t maxval, int *flags) 1097 { 1098 char buf[21], *p, operator = '\0'; 1099 char *unit = NULL; 1100 u_int64_t rval = oval; 1101 double d; 1102 int rslt; 1103 1104 rslt = snprintf(buf, sizeof(buf), "%llu", oval); 1105 if (rslt < 0 || (unsigned int)rslt >= sizeof(buf)) 1106 goto invalid; 1107 1108 p = getstring(prompt, helpstring, buf); 1109 if (p == NULL) 1110 return CMD_ABORTED; 1111 else if (p[0] == '\0') 1112 rval = oval; 1113 else if (p[0] == '*' && p[1] == '\0') 1114 rval = maxval; 1115 else { 1116 if (*p == '+' || *p == '-') 1117 operator = *p++; 1118 if (parse_sizespec(p, &d, &unit) == -1) 1119 goto invalid; 1120 if (unit == NULL) 1121 rval = d; 1122 else if (flags != NULL && (*flags & DO_CONVERSIONS) == 0) 1123 goto invalid; 1124 else { 1125 switch (tolower((unsigned char)*unit)) { 1126 case 'b': 1127 rval = d / lp->d_secsize; 1128 break; 1129 case 'c': 1130 rval = d * lp->d_secpercyl; 1131 break; 1132 case '%': 1133 rval = DL_GETDSIZE(lp) * (d / 100.0); 1134 break; 1135 case '&': 1136 rval = maxval * (d / 100.0); 1137 break; 1138 default: 1139 if (apply_unit(d, *unit, &rval) == -1) 1140 goto invalid; 1141 rval = DL_BLKTOSEC(lp, rval); 1142 break; 1143 } 1144 } 1145 1146 /* Range check then apply [+-] operator */ 1147 if (operator == '+') { 1148 if (CMD_ABORTED - oval > rval) 1149 rval += oval; 1150 else { 1151 goto invalid; 1152 } 1153 } else if (operator == '-') { 1154 if (oval >= rval) 1155 rval = oval - rval; 1156 else { 1157 goto invalid; 1158 } 1159 } 1160 } 1161 1162 if (flags != NULL) { 1163 if (unit != NULL) 1164 *flags |= DO_ROUNDING; 1165 #ifdef SUN_CYLCHECK 1166 if (lp->d_flags & D_VENDOR) 1167 *flags |= DO_ROUNDING; 1168 #endif 1169 } 1170 return rval; 1171 1172 invalid: 1173 fputs("Invalid entry\n", stderr); 1174 return CMD_BADVALUE; 1175 } 1176 1177 /* 1178 * Check for partition overlap in lp and prompt the user to resolve the overlap 1179 * if any is found. Returns 1 if unable to resolve, else 0. 1180 */ 1181 int 1182 has_overlap(struct disklabel *lp) 1183 { 1184 const struct partition **spp; 1185 int i, p1, p2; 1186 char *line = NULL; 1187 size_t linesize = 0; 1188 ssize_t linelen; 1189 1190 for (;;) { 1191 spp = sort_partitions(lp, -1); 1192 for (i = 0; spp[i+1] != NULL; i++) { 1193 if (DL_GETPOFFSET(spp[i]) + DL_GETPSIZE(spp[i]) > 1194 DL_GETPOFFSET(spp[i+1])) 1195 break; 1196 } 1197 if (spp[i+1] == NULL) { 1198 free(line); 1199 return 0; 1200 } 1201 1202 p1 = 'a' + (spp[i] - lp->d_partitions); 1203 p2 = 'a' + (spp[i+1] - lp->d_partitions); 1204 printf("\nError, partitions %c and %c overlap:\n", p1, p2); 1205 printf("# %16.16s %16.16s fstype [fsize bsize cpg]\n", 1206 "size", "offset"); 1207 display_partition(stdout, lp, p1 - 'a', 0); 1208 display_partition(stdout, lp, p2 - 'a', 0); 1209 1210 for (;;) { 1211 printf("Disable which one? (%c %c) ", p1, p2); 1212 linelen = getline(&line, &linesize, stdin); 1213 if (linelen == -1) 1214 goto done; 1215 if (linelen == 2 && (line[0] == p1 || line[0] == p2)) 1216 break; 1217 } 1218 lp->d_partitions[line[0] - 'a'].p_fstype = FS_UNUSED; 1219 } 1220 1221 done: 1222 putchar('\n'); 1223 free(line); 1224 return 1; 1225 } 1226 1227 void 1228 edit_packname(struct disklabel *lp) 1229 { 1230 char *p; 1231 struct disklabel oldlabel = *lp; 1232 1233 printf("Changing label description for %s:\n", specname); 1234 1235 /* pack/label id */ 1236 p = getstring("label name", 1237 "15 char string that describes this label, usually the disk name.", 1238 lp->d_packname); 1239 if (p == NULL) { 1240 *lp = oldlabel; /* undo damage */ 1241 return; 1242 } 1243 strncpy(lp->d_packname, p, sizeof(lp->d_packname)); /* checked */ 1244 } 1245 1246 const struct partition ** 1247 sort_partitions(const struct disklabel *lp, int ignore) 1248 { 1249 const static struct partition *spp[MAXPARTITIONS+2]; 1250 int i, npartitions; 1251 1252 memset(spp, 0, sizeof(spp)); 1253 1254 for (npartitions = 0, i = 0; i < lp->d_npartitions; i++) { 1255 if (i != ignore && lp->d_partitions[i].p_fstype != FS_UNUSED && 1256 DL_GETPSIZE(&lp->d_partitions[i]) != 0) 1257 spp[npartitions++] = &lp->d_partitions[i]; 1258 } 1259 1260 /* 1261 * Sort the partitions based on starting offset. 1262 * This is safe because we guarantee no overlap. 1263 */ 1264 if (npartitions > 1) 1265 if (mergesort((void *)spp, npartitions, sizeof(spp[0]), 1266 partition_cmp)) 1267 err(4, "failed to sort partition table"); 1268 1269 return spp; 1270 } 1271 1272 /* 1273 * Get beginning and ending sectors of the OpenBSD portion of the disk 1274 * from the user. 1275 */ 1276 void 1277 set_bounds(struct disklabel *lp) 1278 { 1279 u_int64_t ui, start_temp; 1280 1281 /* Starting sector */ 1282 for (;;) { 1283 ui = getuint64(lp, "Starting sector", 1284 "The start of the OpenBSD portion of the disk.", 1285 starting_sector, DL_GETDSIZE(lp), NULL); 1286 if (ui == CMD_ABORTED) 1287 return; 1288 else if (ui == CMD_BADVALUE) 1289 ; /* Try again. */ 1290 else if (ui >= DL_GETDSIZE(lp)) 1291 fprintf(stderr, "starting sector must be < %llu\n", 1292 DL_GETDSIZE(lp)); 1293 else 1294 break; 1295 } 1296 start_temp = ui; 1297 1298 /* Size */ 1299 for (;;) { 1300 ui = getuint64(lp, "Size ('*' for entire disk)", 1301 "The size of the OpenBSD portion of the disk ('*' for the " 1302 "entire disk).", ending_sector - starting_sector, 1303 DL_GETDSIZE(lp) - start_temp, NULL); 1304 if (ui == CMD_ABORTED) 1305 return; 1306 else if (ui == CMD_BADVALUE) 1307 ; /* Try again. */ 1308 else if (ui > DL_GETDSIZE(lp) - start_temp) 1309 fprintf(stderr, "size must be <= %llu\n", 1310 DL_GETDSIZE(lp) - start_temp); 1311 else 1312 break; 1313 } 1314 ending_sector = start_temp + ui; 1315 DL_SETBEND(lp, ending_sector); 1316 starting_sector = start_temp; 1317 DL_SETBSTART(lp, starting_sector); 1318 } 1319 1320 /* 1321 * Allow user to interactively change disklabel UID. 1322 */ 1323 void 1324 set_duid(struct disklabel *lp) 1325 { 1326 char *s; 1327 int i; 1328 1329 printf("The disklabel UID is currently: " 1330 "%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx\n", 1331 lp->d_uid[0], lp->d_uid[1], lp->d_uid[2], lp->d_uid[3], 1332 lp->d_uid[4], lp->d_uid[5], lp->d_uid[6], lp->d_uid[7]); 1333 1334 do { 1335 s = getstring("duid", "The disklabel UID, given as a 16 " 1336 "character hexadecimal string.", NULL); 1337 if (s == NULL || strlen(s) == 0) { 1338 fputs("Command aborted\n", stderr); 1339 return; 1340 } 1341 i = duid_parse(lp, s); 1342 if (i != 0) 1343 fputs("Invalid UID entered.\n", stderr); 1344 } while (i != 0); 1345 } 1346 1347 /* 1348 * Return a list of the "chunks" of free space available 1349 */ 1350 const struct diskchunk * 1351 free_chunks(const struct disklabel *lp, int partno) 1352 { 1353 const struct partition **spp; 1354 static struct diskchunk chunks[MAXPARTITIONS + 2]; 1355 u_int64_t start, stop; 1356 int i, numchunks; 1357 1358 /* Sort the in-use partitions based on offset */ 1359 spp = sort_partitions(lp, partno); 1360 1361 /* If there are no partitions, it's all free. */ 1362 if (spp[0] == NULL) { 1363 chunks[0].start = starting_sector; 1364 chunks[0].stop = ending_sector; 1365 chunks[1].start = chunks[1].stop = 0; 1366 return chunks; 1367 } 1368 1369 /* Find chunks of free space */ 1370 numchunks = 0; 1371 if (DL_GETPOFFSET(spp[0]) > starting_sector) { 1372 chunks[0].start = starting_sector; 1373 chunks[0].stop = DL_GETPOFFSET(spp[0]); 1374 numchunks++; 1375 } 1376 for (i = 0; spp[i] != NULL; i++) { 1377 start = DL_GETPOFFSET(spp[i]) + DL_GETPSIZE(spp[i]); 1378 if (start < starting_sector) 1379 start = starting_sector; 1380 else if (start > ending_sector) 1381 start = ending_sector; 1382 if (spp[i + 1] != NULL) 1383 stop = DL_GETPOFFSET(spp[i+1]); 1384 else 1385 stop = ending_sector; 1386 if (stop < starting_sector) 1387 stop = starting_sector; 1388 else if (stop > ending_sector) 1389 stop = ending_sector; 1390 if (start < stop) { 1391 chunks[numchunks].start = start; 1392 chunks[numchunks].stop = stop; 1393 numchunks++; 1394 } 1395 } 1396 1397 /* Terminate and return */ 1398 chunks[numchunks].start = chunks[numchunks].stop = 0; 1399 return chunks; 1400 } 1401 1402 void 1403 find_bounds(const struct disklabel *lp) 1404 { 1405 starting_sector = DL_GETBSTART(lp); 1406 ending_sector = DL_GETBEND(lp); 1407 1408 if (ending_sector) { 1409 if (verbose) 1410 printf("Treating sectors %llu-%llu as the OpenBSD" 1411 " portion of the disk.\nYou can use the 'b'" 1412 " command to change this.\n\n", starting_sector, 1413 ending_sector); 1414 } 1415 } 1416 1417 /* 1418 * Calculate free space. 1419 */ 1420 u_int64_t 1421 editor_countfree(const struct disklabel *lp) 1422 { 1423 const struct diskchunk *chunk; 1424 u_int64_t freesectors = 0; 1425 1426 chunk = free_chunks(lp, -1); 1427 1428 for (; chunk->start != 0 || chunk->stop != 0; chunk++) 1429 freesectors += CHUNKSZ(chunk); 1430 1431 return freesectors; 1432 } 1433 1434 void 1435 editor_help(void) 1436 { 1437 puts("Available commands:"); 1438 puts( 1439 " ? | h - show help n [part] - set mount point\n" 1440 " A - auto partition all space p [unit] - print partitions\n" 1441 " a [part] - add partition q - quit & save changes\n" 1442 " b - set OpenBSD boundaries R [part] - resize auto allocated partition\n" 1443 " c [part] - change partition size r - display free space\n" 1444 " D - reset label to default s [path] - save label to file\n" 1445 " d [part] - delete partition U - undo all changes\n" 1446 " e - edit label description u - undo last change\n" 1447 " i - modify disklabel UID w - write label to disk\n" 1448 " l [unit] - print disk label header x - exit & lose changes\n" 1449 " M - disklabel(8) man page z - delete all partitions\n" 1450 " m [part] - modify partition\n" 1451 "\n" 1452 "Suffixes can be used to indicate units other than sectors:\n" 1453 " 'b' (bytes), 'k' (kilobytes), 'm' (megabytes), 'g' (gigabytes) 't' (terabytes)\n" 1454 " 'c' (cylinders), '%' (% of total disk), '&' (% of free space).\n" 1455 "Values in non-sector units are truncated to the nearest cylinder boundary."); 1456 1457 } 1458 1459 void 1460 mpcopy(char **to, char **from) 1461 { 1462 int i; 1463 1464 for (i = 0; i < MAXPARTITIONS; i++) { 1465 free(to[i]); 1466 to[i] = NULL; 1467 if (from[i] != NULL) { 1468 to[i] = strdup(from[i]); 1469 if (to[i] == NULL) 1470 err(1, NULL); 1471 } 1472 } 1473 } 1474 1475 int 1476 mpequal(char **mp1, char **mp2) 1477 { 1478 int i; 1479 1480 for (i = 0; i < MAXPARTITIONS; i++) { 1481 if (mp1[i] == NULL && mp2[i] == NULL) 1482 continue; 1483 1484 if ((mp1[i] != NULL && mp2[i] == NULL) || 1485 (mp1[i] == NULL && mp2[i] != NULL) || 1486 (strcmp(mp1[i], mp2[i]) != 0)) 1487 return 0; 1488 } 1489 return 1; 1490 } 1491 1492 void 1493 mpsave(const struct disklabel *lp) 1494 { 1495 int i, j; 1496 char bdev[PATH_MAX], *p; 1497 struct mountinfo mi[MAXPARTITIONS]; 1498 FILE *fp; 1499 u_int8_t fstype; 1500 1501 if (!fstabfile) 1502 return; 1503 1504 memset(&mi, 0, sizeof(mi)); 1505 1506 for (i = 0; i < MAXPARTITIONS; i++) { 1507 fstype = lp->d_partitions[i].p_fstype; 1508 if (mountpoints[i] != NULL || fstype == FS_SWAP) { 1509 mi[i].mountpoint = mountpoints[i]; 1510 mi[i].partno = i; 1511 } 1512 } 1513 1514 /* Convert specname to bdev */ 1515 if (uidflag) { 1516 snprintf(bdev, sizeof(bdev), 1517 "%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx.%c", 1518 lab.d_uid[0], lab.d_uid[1], lab.d_uid[2], lab.d_uid[3], 1519 lab.d_uid[4], lab.d_uid[5], lab.d_uid[6], lab.d_uid[7], 1520 specname[strlen(specname)-1]); 1521 } else if (strncmp(_PATH_DEV, specname, sizeof(_PATH_DEV) - 1) == 0 && 1522 specname[sizeof(_PATH_DEV) - 1] == 'r') { 1523 snprintf(bdev, sizeof(bdev), "%s%s", _PATH_DEV, 1524 &specname[sizeof(_PATH_DEV)]); 1525 } else { 1526 if ((p = strrchr(specname, '/')) == NULL || *(++p) != 'r') 1527 return; 1528 *p = '\0'; 1529 snprintf(bdev, sizeof(bdev), "%s%s", specname, p + 1); 1530 *p = 'r'; 1531 } 1532 bdev[strlen(bdev) - 1] = '\0'; 1533 1534 /* Sort mountpoints so we don't try to mount /usr/local before /usr */ 1535 qsort((void *)mi, MAXPARTITIONS, sizeof(struct mountinfo), micmp); 1536 1537 if ((fp = fopen(fstabfile, "w"))) { 1538 for (i = 0; i < MAXPARTITIONS; i++) { 1539 j = mi[i].partno; 1540 fstype = lp->d_partitions[j].p_fstype; 1541 if (fstype == FS_RAID) 1542 continue; 1543 if (fstype == FS_SWAP) { 1544 fprintf(fp, "%s%c none swap sw\n", bdev, 'a'+j); 1545 } else if (mi[i].mountpoint) { 1546 fprintf(fp, "%s%c %s %s rw 1 %d\n", bdev, 1547 'a' + j, mi[i].mountpoint, 1548 fstypesnames[fstype], j == 0 ? 1 : 2); 1549 } 1550 } 1551 fclose(fp); 1552 } 1553 } 1554 1555 void 1556 mpfree(char **mp, int action) 1557 { 1558 int part; 1559 1560 if (mp == NULL) 1561 return; 1562 1563 for (part = 0; part < MAXPARTITIONS; part++) { 1564 free(mp[part]); 1565 mp[part] = NULL; 1566 } 1567 1568 if (action == DISCARD) { 1569 free(mp); 1570 mp = NULL; 1571 } 1572 } 1573 1574 int 1575 get_offset(struct disklabel *lp, int partno) 1576 { 1577 struct partition opp, *pp = &lp->d_partitions[partno]; 1578 u_int64_t ui, offsetalign; 1579 int flags; 1580 1581 flags = DO_CONVERSIONS; 1582 ui = getuint64(lp, "offset", 1583 "Starting sector for this partition.", 1584 DL_GETPOFFSET(pp), 1585 DL_GETPOFFSET(pp), &flags); 1586 1587 if (ui == CMD_ABORTED || ui == CMD_BADVALUE) 1588 return 1; 1589 #ifdef SUN_AAT0 1590 if (partno == 0 && ui != 0) { 1591 fprintf(stderr, "This architecture requires that " 1592 "partition 'a' start at sector 0.\n"); 1593 return 1; 1594 } 1595 #endif 1596 opp = *pp; 1597 DL_SETPOFFSET(pp, ui); 1598 offsetalign = 1; 1599 if ((flags & DO_ROUNDING) != 0 && pp->p_fstype == FS_BSDFFS) 1600 offsetalign = lp->d_secpercyl; 1601 1602 if (alignpartition(lp, partno, offsetalign, 1, ROUND_OFFSET_UP) == 1) { 1603 *pp = opp; 1604 return 1; 1605 } 1606 1607 return 0; 1608 } 1609 1610 int 1611 get_size(struct disklabel *lp, int partno) 1612 { 1613 struct partition opp, *pp = &lp->d_partitions[partno]; 1614 u_int64_t maxsize, ui, sizealign; 1615 int flags; 1616 1617 maxsize = max_partition_size(lp, partno); 1618 flags = DO_CONVERSIONS; 1619 ui = getuint64(lp, "size", "Size of the partition. " 1620 "You may also say +/- amount for a relative change.", 1621 DL_GETPSIZE(pp), maxsize, &flags); 1622 1623 if (ui == CMD_ABORTED || ui == CMD_BADVALUE) 1624 return 1; 1625 1626 opp = *pp; 1627 DL_SETPSIZE(pp, ui); 1628 sizealign = 1; 1629 if ((flags & DO_ROUNDING) != 0 && (pp->p_fstype == FS_SWAP || 1630 pp->p_fstype == FS_BSDFFS)) 1631 sizealign = lp->d_secpercyl; 1632 1633 if (alignpartition(lp, partno, 1, sizealign, ROUND_SIZE_UP) == 1) { 1634 *pp = opp; 1635 return 1; 1636 } 1637 1638 return 0; 1639 } 1640 1641 int 1642 set_fragblock(struct disklabel *lp, int partno) 1643 { 1644 struct partition opp, *pp = &lp->d_partitions[partno]; 1645 u_int64_t bytes, offsetalign, sizealign; 1646 u_int32_t frag, fsize; 1647 1648 if (pp->p_fstype != FS_BSDFFS) 1649 return 0; 1650 1651 if (pp->p_cpg == 0) 1652 pp->p_cpg = 1; 1653 1654 fsize = DISKLABELV1_FFS_FSIZE(pp->p_fragblock); 1655 frag = DISKLABELV1_FFS_FRAG(pp->p_fragblock); 1656 if (fsize == 0) { 1657 fsize = 2048; 1658 frag = 8; 1659 bytes = DL_GETPSIZE(pp) * lp->d_secsize; 1660 if (bytes > 128ULL * 1024 * 1024 * 1024) 1661 fsize *= 2; 1662 if (bytes > 512ULL * 1024 * 1024 * 1024) 1663 fsize *= 2; 1664 if (fsize < lp->d_secsize) 1665 fsize = lp->d_secsize; 1666 if (fsize > MAXBSIZE / frag) 1667 fsize = MAXBSIZE / frag; 1668 pp->p_fragblock = DISKLABELV1_FFS_FRAGBLOCK(fsize, frag); 1669 } 1670 #ifdef SUN_CYLCHECK 1671 return 0; 1672 #endif 1673 opp = *pp; 1674 sizealign = (DISKLABELV1_FFS_FRAG(pp->p_fragblock) * 1675 DISKLABELV1_FFS_FSIZE(pp->p_fragblock)) / lp->d_secsize; 1676 offsetalign = 1; 1677 if (DL_GETPOFFSET(pp) != starting_sector) 1678 offsetalign = sizealign; 1679 1680 if (alignpartition(lp, partno, offsetalign, sizealign, ROUND_OFFSET_UP | 1681 ROUND_SIZE_DOWN | ROUND_SIZE_OVERLAP) == 1) { 1682 *pp = opp; 1683 return 1; 1684 } 1685 1686 return 0; 1687 } 1688 1689 int 1690 get_fstype(struct disklabel *lp, int partno) 1691 { 1692 char *p; 1693 u_int64_t ui; 1694 struct partition *pp = &lp->d_partitions[partno]; 1695 1696 if (pp->p_fstype < FSMAXTYPES) { 1697 p = getstring("FS type", 1698 "Filesystem type (usually 4.2BSD or swap)", 1699 fstypenames[pp->p_fstype]); 1700 if (p == NULL) { 1701 return 1; 1702 } 1703 for (ui = 0; ui < FSMAXTYPES; ui++) { 1704 if (!strcasecmp(p, fstypenames[ui])) { 1705 pp->p_fstype = ui; 1706 break; 1707 } 1708 } 1709 if (ui >= FSMAXTYPES) { 1710 printf("Unrecognized filesystem type '%s', treating " 1711 "as 'unknown'\n", p); 1712 pp->p_fstype = FS_OTHER; 1713 } 1714 } else { 1715 for (;;) { 1716 ui = getnumber("FS type (decimal)", 1717 "Filesystem type as a decimal number; usually 7 " 1718 "(4.2BSD) or 1 (swap).", 1719 pp->p_fstype, UINT8_MAX); 1720 if (ui == CMD_ABORTED) 1721 return 1; 1722 else if (ui == CMD_BADVALUE) 1723 ; /* Try again. */ 1724 else 1725 break; 1726 } 1727 pp->p_fstype = ui; 1728 } 1729 return 0; 1730 } 1731 1732 int 1733 get_mp(const struct disklabel *lp, int partno) 1734 { 1735 const struct partition *pp = &lp->d_partitions[partno]; 1736 char *p; 1737 int i; 1738 1739 if (fstabfile == NULL || 1740 pp->p_fstype == FS_UNUSED || 1741 pp->p_fstype == FS_SWAP || 1742 pp->p_fstype == FS_BOOT || 1743 pp->p_fstype == FS_OTHER || 1744 pp->p_fstype == FS_RAID) { 1745 /* No fstabfile, no names. Not all fstypes can be named */ 1746 return 0; 1747 } 1748 1749 for (;;) { 1750 p = getstring("mount point", 1751 "Where to mount this filesystem (ie: / /var /usr)", 1752 mountpoints[partno] ? mountpoints[partno] : "none"); 1753 if (p == NULL) 1754 return 1; 1755 if (strcasecmp(p, "none") == 0) { 1756 free(mountpoints[partno]); 1757 mountpoints[partno] = NULL; 1758 break; 1759 } 1760 for (i = 0; i < MAXPARTITIONS; i++) 1761 if (mountpoints[i] != NULL && i != partno && 1762 strcmp(p, mountpoints[i]) == 0) 1763 break; 1764 if (i < MAXPARTITIONS) { 1765 fprintf(stderr, "'%c' already being mounted at " 1766 "'%s'\n", 'a'+i, p); 1767 break; 1768 } 1769 if (*p == '/') { 1770 /* XXX - might as well realloc */ 1771 free(mountpoints[partno]); 1772 if ((mountpoints[partno] = strdup(p)) == NULL) 1773 err(1, NULL); 1774 break; 1775 } 1776 fputs("Mount points must start with '/'\n", stderr); 1777 } 1778 1779 return 0; 1780 } 1781 1782 int 1783 micmp(const void *a1, const void *a2) 1784 { 1785 struct mountinfo *mi1 = (struct mountinfo *)a1; 1786 struct mountinfo *mi2 = (struct mountinfo *)a2; 1787 1788 /* We want all the NULLs at the end... */ 1789 if (mi1->mountpoint == NULL && mi2->mountpoint == NULL) 1790 return 0; 1791 else if (mi1->mountpoint == NULL) 1792 return 1; 1793 else if (mi2->mountpoint == NULL) 1794 return -1; 1795 else 1796 return strcmp(mi1->mountpoint, mi2->mountpoint); 1797 } 1798 1799 void 1800 zero_partitions(struct disklabel *lp) 1801 { 1802 memset(lp->d_partitions, 0, sizeof(lp->d_partitions)); 1803 DL_SETPSIZE(&lp->d_partitions[RAW_PART], DL_GETDSIZE(lp)); 1804 1805 mpfree(mountpoints, KEEP); 1806 } 1807 1808 u_int64_t 1809 max_partition_size(const struct disklabel *lp, int partno) 1810 { 1811 const struct diskchunk *chunk; 1812 u_int64_t maxsize = 0, offset; 1813 1814 chunk = free_chunks(lp, partno); 1815 1816 offset = DL_GETPOFFSET(&lp->d_partitions[partno]); 1817 for (; chunk->start != 0 || chunk->stop != 0; chunk++) { 1818 if (offset < chunk->start || offset >= chunk->stop) 1819 continue; 1820 maxsize = chunk->stop - offset; 1821 break; 1822 } 1823 return maxsize; 1824 } 1825 1826 void 1827 psize(u_int64_t sz, char unit, const struct disklabel *lp) 1828 { 1829 double d = scale(sz, unit, lp); 1830 if (d < 0) 1831 printf("%llu", sz); 1832 else 1833 printf("%.*f%c", unit == 'B' ? 0 : 1, d, unit); 1834 } 1835 1836 void 1837 display_edit(const struct disklabel *lp, char unit) 1838 { 1839 u_int64_t fr; 1840 int i; 1841 1842 fr = editor_countfree(lp); 1843 unit = canonical_unit(lp, unit); 1844 1845 printf("OpenBSD area: "); 1846 psize(starting_sector, 0, lp); 1847 printf("-"); 1848 psize(ending_sector, 0, lp); 1849 printf("; size: "); 1850 psize(ending_sector - starting_sector, unit, lp); 1851 printf("; free: "); 1852 psize(fr, unit, lp); 1853 1854 printf("\n# %16.16s %16.16s fstype [fsize bsize cpg]\n", 1855 "size", "offset"); 1856 for (i = 0; i < lp->d_npartitions; i++) 1857 display_partition(stdout, lp, i, unit); 1858 } 1859 1860 void 1861 parse_autotable(char *filename) 1862 { 1863 FILE *cfile; 1864 size_t linesize = 0; 1865 char *line = NULL, *buf, *t; 1866 uint idx = 0, pctsum = 0; 1867 struct space_allocation *sa; 1868 1869 if (strcmp(filename, "-") == 0) 1870 cfile = stdin; 1871 else if ((cfile = fopen(filename, "r")) == NULL) 1872 err(1, "%s", filename); 1873 if ((alloc_table = calloc(1, sizeof(struct alloc_table))) == NULL) 1874 err(1, NULL); 1875 alloc_table_nitems = 1; 1876 1877 while (getline(&line, &linesize, cfile) != -1) { 1878 if ((alloc_table[0].table = reallocarray(alloc_table[0].table, 1879 idx + 1, sizeof(*sa))) == NULL) 1880 err(1, NULL); 1881 sa = &(alloc_table[0].table[idx]); 1882 memset(sa, 0, sizeof(*sa)); 1883 idx++; 1884 1885 buf = line; 1886 if ((sa->mp = get_token(&buf)) == NULL || 1887 (sa->mp[0] != '/' && strcasecmp(sa->mp, "swap") && 1888 strcasecmp(sa->mp, "raid"))) 1889 errx(1, "%s: parse error on line %u", filename, idx); 1890 if ((t = get_token(&buf)) == NULL || 1891 parse_sizerange(t, &sa->minsz, &sa->maxsz) == -1) 1892 errx(1, "%s: parse error on line %u", filename, idx); 1893 if ((t = get_token(&buf)) != NULL && 1894 parse_pct(t, &sa->rate) == -1) 1895 errx(1, "%s: parse error on line %u", filename, idx); 1896 if (sa->minsz > sa->maxsz) 1897 errx(1, "%s: min size > max size on line %u", filename, 1898 idx); 1899 pctsum += sa->rate; 1900 } 1901 if (pctsum > 100) 1902 errx(1, "%s: sum of extra space allocation > 100%%", filename); 1903 alloc_table[0].sz = idx; 1904 free(line); 1905 fclose(cfile); 1906 } 1907 1908 char * 1909 get_token(char **s) 1910 { 1911 char *p, *r; 1912 size_t tlen = 0; 1913 1914 p = *s; 1915 while (**s != '\0' && !isspace((u_char)**s)) { 1916 (*s)++; 1917 tlen++; 1918 } 1919 if (tlen == 0) 1920 return NULL; 1921 1922 /* eat whitespace */ 1923 while (isspace((u_char)**s)) 1924 (*s)++; 1925 1926 if ((r = strndup(p, tlen)) == NULL) 1927 err(1, NULL); 1928 return r; 1929 } 1930 1931 int 1932 apply_unit(double val, u_char unit, u_int64_t *n) 1933 { 1934 u_int64_t factor = 1; 1935 1936 switch (tolower(unit)) { 1937 case 't': 1938 factor *= 1024; 1939 /* FALLTHROUGH */ 1940 case 'g': 1941 factor *= 1024; 1942 /* FALLTHROUGH */ 1943 case 'm': 1944 factor *= 1024; 1945 /* FALLTHROUGH */ 1946 case 'k': 1947 factor *= 1024; 1948 break; 1949 default: 1950 return -1; 1951 } 1952 1953 val *= factor / DEV_BSIZE; 1954 if (val > (double)ULLONG_MAX) 1955 return -1; 1956 *n = val; 1957 return 0; 1958 } 1959 1960 int 1961 parse_sizespec(const char *buf, double *val, char **unit) 1962 { 1963 errno = 0; 1964 *val = strtod(buf, unit); 1965 if (errno == ERANGE || *val < 0 || *val > (double)ULLONG_MAX) 1966 return -1; /* too big/small */ 1967 if (*val == 0 && *unit == buf) 1968 return -1; /* No conversion performed. */ 1969 if (*unit != NULL && *unit[0] == '\0') 1970 *unit = NULL; 1971 return 0; 1972 } 1973 1974 int 1975 parse_sizerange(char *buf, u_int64_t *min, u_int64_t *max) 1976 { 1977 char *p, *unit1 = NULL, *unit2 = NULL; 1978 double val1 = 0, val2 = 0; 1979 1980 if (strcmp(buf, "*") == 0) { 1981 *min = 0; 1982 *max = UINT64_MAX; 1983 goto done; 1984 } 1985 1986 if ((p = strchr(buf, '-')) != NULL) { 1987 p[0] = '\0'; 1988 p++; 1989 } 1990 *max = 0; 1991 if (parse_sizespec(buf, &val1, &unit1) == -1) 1992 return -1; 1993 if (p != NULL && p[0] != '\0') { 1994 if (p[0] == '*') 1995 *max = UINT64_MAX; 1996 else 1997 if (parse_sizespec(p, &val2, &unit2) == -1) 1998 return -1; 1999 } 2000 if (unit1 == NULL && (unit1 = unit2) == NULL) 2001 return -1; 2002 if (apply_unit(val1, unit1[0], min) == -1) 2003 return -1; 2004 if (val2 > 0) { 2005 if (apply_unit(val2, unit2[0], max) == -1) 2006 return -1; 2007 } else 2008 if (*max == 0) 2009 *max = *min; 2010 done: 2011 free(buf); 2012 return 0; 2013 } 2014 2015 int 2016 parse_pct(char *buf, int *n) 2017 { 2018 const char *errstr; 2019 2020 if (buf[strlen(buf) - 1] == '%') 2021 buf[strlen(buf) - 1] = '\0'; 2022 *n = strtonum(buf, 0, 100, &errstr); 2023 if (errstr) { 2024 warnx("parse percent %s: %s", buf, errstr); 2025 return -1; 2026 } 2027 free(buf); 2028 return 0; 2029 } 2030 2031 int 2032 alignpartition(struct disklabel *lp, int partno, u_int64_t startalign, 2033 u_int64_t stopalign, int flags) 2034 { 2035 struct partition *pp = &lp->d_partitions[partno]; 2036 const struct diskchunk *chunk; 2037 u_int64_t start, stop, maxstop; 2038 2039 start = DL_GETPOFFSET(pp); 2040 if ((flags & ROUND_OFFSET_UP) == ROUND_OFFSET_UP) 2041 start = ROUNDUP(start, startalign); 2042 else if ((flags & ROUND_OFFSET_DOWN) == ROUND_OFFSET_DOWN) 2043 start = ROUNDDOWN(start, startalign); 2044 2045 /* Find the chunk that contains 'start'. */ 2046 chunk = free_chunks(lp, partno); 2047 for (; chunk->start != 0 || chunk->stop != 0; chunk++) { 2048 if (start >= chunk->start && start < chunk->stop) 2049 break; 2050 } 2051 if (chunk->stop == 0) { 2052 fprintf(stderr, "'%c' aligned offset %llu lies outside " 2053 "the OpenBSD bounds or inside another partition\n", 2054 'a' + partno, start); 2055 return 1; 2056 } 2057 2058 /* Calculate the new 'stop' sector, the sector after the partition. */ 2059 if ((flags & ROUND_SIZE_OVERLAP) == 0) 2060 maxstop = ROUNDDOWN(chunk->stop, stopalign); 2061 else 2062 maxstop = ROUNDDOWN(ending_sector, stopalign); 2063 2064 stop = DL_GETPOFFSET(pp) + DL_GETPSIZE(pp); 2065 if ((flags & ROUND_SIZE_UP) == ROUND_SIZE_UP) 2066 stop = ROUNDUP(stop, stopalign); 2067 else if ((flags & ROUND_SIZE_DOWN) == ROUND_SIZE_DOWN) 2068 stop = ROUNDDOWN(stop, stopalign); 2069 if (stop > maxstop) 2070 stop = maxstop; 2071 2072 if (stop <= start) { 2073 fprintf(stderr, "not enough space\n"); 2074 return 1; 2075 } 2076 2077 if (start != DL_GETPOFFSET(pp)) 2078 DL_SETPOFFSET(pp, start); 2079 if (stop != DL_GETPOFFSET(pp) + DL_GETPSIZE(pp)) 2080 DL_SETPSIZE(pp, stop - start); 2081 2082 return 0; 2083 } 2084