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