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