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