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