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