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