1 /* $OpenBSD: editor.c,v 1.74 2000/10/22 23:59:40 millert 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.74 2000/10/22 23:59:40 millert 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, **tmpmountpoints; 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 struct partition *pp = &lp->d_partitions[RAW_PART]; 1696 1697 /* Defaults */ 1698 /* XXX - reserve a cylinder for hp300? */ 1699 starting_sector = 0; 1700 ending_sector = lp->d_secperunit; 1701 1702 #ifdef DOSLABEL 1703 /* 1704 * If we have an MBR, use values from the {Open,Free,Net}BSD partition 1705 */ 1706 if (dosdp) { 1707 if (dosdp->dp_typ == DOSPTYP_OPENBSD || 1708 dosdp->dp_typ == DOSPTYP_FREEBSD || 1709 dosdp->dp_typ == DOSPTYP_NETBSD) { 1710 u_int32_t i, new_end; 1711 1712 /* Set start and end based on fdisk partition bounds */ 1713 starting_sector = get_le(&dosdp->dp_start); 1714 ending_sector = starting_sector + get_le(&dosdp->dp_size); 1715 1716 /* 1717 * If the ending sector of the BSD fdisk partition 1718 * is equal to the ending sector of the BIOS geometry 1719 * but the real sector count > BIOS sector count, 1720 * adjust the bounds accordingly. We do this because 1721 * the BIOS geometry is limited to disks of ~4gig. 1722 */ 1723 if (bios_lp && ending_sector == bios_lp->d_secperunit && 1724 lp->d_secperunit > bios_lp->d_secperunit) 1725 ending_sector = lp->d_secperunit; 1726 1727 /* 1728 * If there are any BSD or SWAP partitions beyond 1729 * ending_sector we extend ending_sector to include 1730 * them. This is done because the BIOS geometry is 1731 * generally different from the disk geometry. 1732 */ 1733 for (i = new_end = 0; i < lp->d_npartitions; i++) { 1734 pp = &lp->d_partitions[i]; 1735 if ((pp->p_fstype == FS_BSDFFS || 1736 pp->p_fstype == FS_SWAP) && 1737 pp->p_size + pp->p_offset > new_end) 1738 new_end = pp->p_size + pp->p_offset; 1739 } 1740 if (new_end > ending_sector) 1741 ending_sector = new_end; 1742 } else { 1743 /* Don't trounce the MBR */ 1744 starting_sector = 63; 1745 } 1746 1747 printf("\nTreating sectors %u-%u as the OpenBSD portion of the " 1748 "disk.\nYou can use the 'b' command to change this.\n", 1749 starting_sector, ending_sector); 1750 } 1751 #elif (NUMBOOT == 1) 1752 /* Boot blocks take up the first cylinder */ 1753 starting_sector = lp->d_secpercyl; 1754 printf("\nReserving the first data cylinder for boot blocks.\n" 1755 "You can use the 'b' command to change this.\n"); 1756 #endif 1757 } 1758 1759 /* 1760 * Calculate free space. 1761 */ 1762 void 1763 editor_countfree(lp, freep) 1764 struct disklabel *lp; 1765 u_int32_t *freep; 1766 { 1767 struct partition *pp; 1768 int i; 1769 1770 *freep = ending_sector - starting_sector; 1771 for (i = 0; i < lp->d_npartitions; i++) { 1772 pp = &lp->d_partitions[i]; 1773 if (pp->p_fstype != FS_UNUSED && pp->p_fstype != FS_BOOT && 1774 pp->p_size > 0 && 1775 pp->p_offset + pp->p_size <= ending_sector && 1776 pp->p_offset >= starting_sector) 1777 *freep -= pp->p_size; 1778 } 1779 } 1780 1781 void 1782 editor_help(arg) 1783 char *arg; 1784 { 1785 1786 /* XXX - put these strings in a table instead? */ 1787 switch (*arg) { 1788 case 'p': 1789 puts( 1790 "The 'p' command prints the current disk label. By default, it prints the\n" 1791 "size and offset in sectors (a sector is usually 512 bytes). The 'p' command\n" 1792 "takes an optional units argument. Possible values are 'b' for bytes, 'c'\n" 1793 "for cylinders, 'k' for kilobytes, 'm' for megabytes, and 'g' for gigabytes.\n"); 1794 break; 1795 case 'M': 1796 puts( 1797 "The 'M' command pipes the entire OpenBSD manual page for disk label through\n" 1798 "the pager specified by the PAGER environment variable or 'less' if PAGER is\n" 1799 "not set. It is especially useful during install when the normal system\n" 1800 "manual is not available.\n"); 1801 break; 1802 case 'e': 1803 puts( 1804 "The 'e' command is used to edit the disk drive parameters. These include\n" 1805 "the number of sectors/track, tracks/cylinder, sectors/cylinder, number of\n" 1806 "cylinders on the disk , total sectors on the disk, rpm, interleave, disk\n" 1807 "type, and a descriptive label string. You should not change these unless\n" 1808 "you know what you are doing\n"); 1809 break; 1810 case 'a': 1811 puts( 1812 "The 'a' command adds new partitions to the disk. It takes as an optional\n" 1813 "argument the partition letter to add. If you do not specify a partition\n" 1814 "letter, you will be prompted for it; the next available letter will be the\n" 1815 "default answer\n"); 1816 break; 1817 case 'b': 1818 puts( 1819 "The 'b' command is used to change the boundaries of the OpenBSD portion of\n" 1820 "the disk. This is only useful on disks with an fdisk partition. By default,\n" 1821 "on a disk with an fdisk partition, the boundaries are set to be the first\n" 1822 "and last sectors of the OpenBSD fdisk partition. You should only change\n" 1823 "these if your fdisk partition table is incorrect or you have a disk larger\n" 1824 "than 8gig, since 8gig is the maximum size an fdisk partition can be. You\n" 1825 "may enter '*' at the 'Size' prompt to indicate the entire size of the disk\n" 1826 "(minus the starting sector). Use this option with care; if you extend the\n" 1827 "boundaries such that they overlap with another operating system you will\n" 1828 "corrupt the other operating system's data.\n"); 1829 break; 1830 case 'c': 1831 puts( 1832 "The 'c' command is used to change the size of an existing partition. It\n" 1833 "takes as an optional argument the partition letter to change. If you do not\n" 1834 "specify a partition letter, you will be prompted for one. You may add a '+'\n" 1835 "or '-' prefix to the new size to increase or decrease the existing value\n" 1836 "instead of entering an absolute value. You may also use a suffix to indicate\n" 1837 "the units the values is in terms of. Possible suffixes are 'b' for bytes,\n" 1838 "'c' for cylinders, 'k' for kilobytes, 'm' for megabytes, 'g' for gigabytes or\n" 1839 "no suffix for sectors (usually 512 bytes). You may also enter '*' to change\n" 1840 "the size to be the total number of free sectors remaining.\n"); 1841 break; 1842 case 'D': 1843 puts( 1844 "The 'D' command will set the disk label to the default values as reported\n" 1845 "by the disk itself. This similates the case where there is no disk label.\n"); 1846 break; 1847 case 'd': 1848 puts( 1849 "The 'd' command is used to delete an existing partition. It takes as an\n" 1850 "optional argument the partition letter to change. If you do not specify a\n" 1851 "partition letter, you will be prompted for one. You may not delete the ``c''\n" 1852 "partition as 'c' must always exist and by default is marked as 'unused' (so\n" 1853 "it does not take up any space).\n"); 1854 break; 1855 case 'g': 1856 puts( 1857 "The 'g' command is used select which disk geometry to use, the disk, BIOS, or\n" 1858 "user geometry. It takes as an optional argument ``d'', ``b'', or ``u''. If \n" 1859 "you do not specify the type as an argument, you will be prompted for it.\n"); 1860 break; 1861 case 'm': 1862 puts( 1863 "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" 1864 "partition letter, you will be prompted for one. This option allows the user\n" 1865 "to change the filesystem type, starting offset, partition size, block fragment\n" 1866 "size, block size, and cylinders per group for the specified partition (not all\n" 1867 "parameters are configurable for non-BSD partitions).\n"); 1868 break; 1869 case 'n': 1870 puts( 1871 "The 'n' command is used to set the mount point for a partition (ie: name it).\n" 1872 "It takes as an optional argument the partition letter to name. If you do\n" 1873 "not specify a partition letter, you will be prompted for one. This option\n" 1874 "is only valid if disklabel was invoked with the -F flag.\n"); 1875 break; 1876 case 'r': 1877 puts( 1878 "The 'r' command is used to recalculate the free space available. This option\n" 1879 "should really not be necessary under normal circumstances but can be useful if\n" 1880 "disklabel gets confused.\n"); 1881 break; 1882 case 'u': 1883 puts( 1884 "The 'u' command will undo (or redo) the last change. Entering 'u' once will\n" 1885 "undo your last change. Entering it again will restore the change.\n"); 1886 break; 1887 case 's': 1888 puts( 1889 "The 's' command is used to save a copy of the label to a file in ascii format\n" 1890 "(suitable for loading via disklabel's [-R] option). It takes as an optional\n" 1891 "argument the filename to save the label to. If you do not specify a filename,\n" 1892 "you will be prompted for one.\n"); 1893 break; 1894 case 'w': 1895 puts( 1896 "The 'w' command will write the current label to disk. This option will\n" 1897 "commit any changes to the on-disk label.\n"); 1898 break; 1899 case 'q': 1900 puts( 1901 "The 'q' command quits the label editor. If any changes have been made you\n" 1902 "will be asked whether or not to save the changes to the on-disk label.\n"); 1903 break; 1904 case 'X': 1905 puts( 1906 "The 'X' command toggles disklabel in to/out of 'expert mode'. By default,\n" 1907 "some settings are reserved for experts only (such as the block and fragment\n" 1908 "size on ffs partitions).\n"); 1909 break; 1910 case 'x': 1911 puts( 1912 "The 'x' command exits the label editor without saving any changes to the\n" 1913 "on-disk label.\n"); 1914 break; 1915 case 'z': 1916 puts( 1917 "The 'z' command zeroes out the existing partition table, leaving only the 'c'\n" 1918 "partition. The drive parameters are not changed.\n"); 1919 break; 1920 default: 1921 puts("Available commands:"); 1922 puts("\tp [unit] - print label."); 1923 puts("\tM - show entire OpenBSD man page for disklabel."); 1924 puts("\te - edit drive parameters."); 1925 puts("\ta [part] - add new partition."); 1926 puts("\tb - set OpenBSD disk boundaries."); 1927 puts("\tc [part] - change partition size."); 1928 puts("\td [part] - delete partition."); 1929 puts("\tD - set label to default."); 1930 puts("\tg [d|b] - Use [d]isk or [b]ios geometry."); 1931 puts("\tm [part] - modify existing partition."); 1932 puts("\tn [part] - set the mount point for a partition."); 1933 puts("\tr - recalculate free space."); 1934 puts("\tu - undo last change."); 1935 puts("\ts [path] - save label to file."); 1936 puts("\tw - write label to disk."); 1937 puts("\tq - quit and save changes."); 1938 puts("\tx - exit without saving changes."); 1939 puts("\tX - toggle expert mode."); 1940 puts("\tz - zero out partition table."); 1941 puts("\t? [cmnd] - this message or command specific help."); 1942 puts( 1943 "Numeric parameters may use suffixes to indicate units:\n\t" 1944 "'b' for bytes, 'c' for cylinders, 'k' for kilobytes, 'm' for megabytes,\n\t" 1945 "'g' for gigabytes or no suffix for sectors (usually 512 bytes).\n\t" 1946 "Non-sector units will be rounded to the nearest cylinder.\n" 1947 "Entering '?' at most prompts will give you (simple) context sensitive help."); 1948 break; 1949 } 1950 } 1951 1952 char ** 1953 mpcopy(to, from) 1954 char **to; 1955 char **from; 1956 { 1957 int i; 1958 1959 for (i = 0; i < MAXPARTITIONS; i++) { 1960 if (from[i] != NULL) { 1961 to[i] = realloc(to[i], strlen(from[i]) + 1); 1962 if (to[i] == NULL) 1963 errx(4, "out of memory"); 1964 (void)strcpy(to[i], from[i]); 1965 } else if (to[i] != NULL) { 1966 free(to[i]); 1967 to[i] = NULL; 1968 } 1969 } 1970 return(to); 1971 } 1972 1973 int 1974 mpequal(mp1, mp2) 1975 char **mp1; 1976 char **mp2; 1977 { 1978 int i; 1979 1980 for (i = 0; i < MAXPARTITIONS; i++) { 1981 if (mp1[i] == NULL && mp2[i] == NULL) 1982 continue; 1983 1984 if ((mp1[i] != NULL && mp2[i] == NULL) || 1985 (mp1[i] == NULL && mp2[i] != NULL) || 1986 (strcmp(mp1[i], mp2[i]) != 0)) 1987 return(0); 1988 } 1989 return(1); 1990 } 1991 1992 int 1993 mpsave(lp, mp, cdev, fstabfile) 1994 struct disklabel *lp; 1995 char **mp; 1996 char *cdev; 1997 char *fstabfile; 1998 { 1999 int i, j, mpset; 2000 char bdev[MAXPATHLEN], *p; 2001 struct mountinfo mi[MAXPARTITIONS]; 2002 FILE *fp; 2003 2004 memset(&mi, 0, sizeof(mi)); 2005 2006 for (i = 0, mpset = 0; i < MAXPARTITIONS; i++) { 2007 if (mp[i] != NULL) { 2008 mi[i].mountpoint = mp[i]; 2009 mi[i].partno = i; 2010 mpset = 1; 2011 } 2012 } 2013 /* Exit if there is nothing to do... */ 2014 if (!mpset) 2015 return(0); 2016 2017 /* Convert cdev to bdev */ 2018 if (strncmp(_PATH_DEV, cdev, sizeof(_PATH_DEV) - 1) == 0 && 2019 cdev[sizeof(_PATH_DEV) - 1] == 'r') { 2020 snprintf(bdev, sizeof(bdev), "%s%s", _PATH_DEV, 2021 &cdev[sizeof(_PATH_DEV)]); 2022 } else { 2023 if ((p = strrchr(cdev, '/')) == NULL || *(++p) != 'r') 2024 return(1); 2025 *p = '\0'; 2026 snprintf(bdev, sizeof(bdev), "%s%s", cdev, p + 1); 2027 *p = 'r'; 2028 } 2029 bdev[strlen(bdev) - 1] = '\0'; 2030 2031 /* Sort mountpoints so we don't try to mount /usr/local before /usr */ 2032 qsort((void *)mi, MAXPARTITIONS, sizeof(struct mountinfo), micmp); 2033 2034 if ((fp = fopen(fstabfile, "w")) == NULL) 2035 return(1); 2036 2037 for (i = 0; i < MAXPARTITIONS && mi[i].mountpoint != NULL; i++) { 2038 j = mi[i].partno; 2039 fprintf(fp, "%s%c %s %s rw 1 %d\n", bdev, 'a' + j, 2040 mi[i].mountpoint, 2041 fstypesnames[lp->d_partitions[j].p_fstype], 2042 j == 0 ? 1 : 2); 2043 } 2044 fclose(fp); 2045 return(0); 2046 } 2047 2048 int 2049 get_offset(lp, partno) 2050 struct disklabel *lp; 2051 int partno; 2052 { 2053 u_int32_t ui; 2054 struct partition *pp = &lp->d_partitions[partno]; 2055 2056 for (;;) { 2057 ui = getuint(lp, partno, "offset", 2058 "Starting sector for this partition.", pp->p_offset, 2059 pp->p_offset, 0, DO_CONVERSIONS | 2060 (pp->p_fstype == FS_BSDFFS ? DO_ROUNDING : 0)); 2061 if (ui == UINT_MAX - 1) { 2062 fputs("Command aborted\n", stderr); 2063 return(1); 2064 } else if (ui == UINT_MAX) 2065 fputs("Invalid entry\n", stderr); 2066 else if (ui < starting_sector) 2067 fprintf(stderr, "The OpenBSD portion of the disk starts" 2068 " at sector %u, you tried to add a partition at %u." 2069 " You can use the 'b' command to change the size " 2070 "of the OpenBSD portion.\n" , starting_sector, ui); 2071 else if (ui >= ending_sector) 2072 fprintf(stderr, "The OpenBSD portion of the disk ends " 2073 "at sector %u, you tried to add a partition at %u." 2074 " You can use the 'b' command to change the size " 2075 "of the OpenBSD portion.\n", ending_sector, ui); 2076 #ifdef AAT0 2077 else if (partno == 0 && ui != 0) 2078 fprintf(stderr, "This architecture requires that " 2079 "partition 'a' start at sector 0.\n"); 2080 #endif 2081 else 2082 break; 2083 } 2084 pp->p_offset = ui; 2085 return(0); 2086 } 2087 2088 int 2089 get_size(lp, partno, freep, new) 2090 struct disklabel *lp; 2091 int partno; 2092 u_int32_t *freep; 2093 int new; 2094 { 2095 u_int32_t ui; 2096 struct partition *pp = &lp->d_partitions[partno]; 2097 2098 for (;;) { 2099 ui = getuint(lp, partno, "size", "Size of the partition.", 2100 pp->p_size, *freep, pp->p_offset, DO_CONVERSIONS | 2101 ((pp->p_fstype == FS_BSDFFS || pp->p_fstype == FS_SWAP) ? 2102 DO_ROUNDING : 0)); 2103 if (ui == UINT_MAX - 1) { 2104 fputs("Command aborted\n", stderr); 2105 return(1); 2106 } else if (ui == UINT_MAX) { 2107 fputs("Invalid entry\n", stderr); 2108 continue; 2109 } 2110 if (new) { 2111 if (ui > *freep) 2112 /* XXX - steal space from another partition */ 2113 fprintf(stderr,"Sorry, there are only %u " 2114 "sectors left\n", *freep); 2115 else if (pp->p_offset + ui > ending_sector) 2116 fprintf(stderr, "The OpenBSD portion of the " 2117 "disk ends at sector %u, you tried to add " 2118 "a partition ending at sector %u. You can " 2119 "use the 'b' command to change the size of " 2120 "the OpenBSD portion.\n", 2121 ending_sector, pp->p_offset + ui); 2122 else 2123 break; /* ok */ 2124 } else { 2125 if (ui == pp->p_size) 2126 break; /* no change */ 2127 if (partno == RAW_PART && 2128 ui + pp->p_offset > lp->d_secperunit) { 2129 fputs("'c' partition may not be larger than the disk\n", 2130 stderr); 2131 } else if (pp->p_fstype == FS_UNUSED || 2132 pp->p_fstype == FS_BOOT) { 2133 /* don't care what's free */ 2134 pp->p_size = ui; 2135 break; 2136 } else { 2137 if (ui > pp->p_size + *freep) 2138 /* XXX - steal from another partition */ 2139 fprintf(stderr, 2140 "Size may not be larger than %u " 2141 "sectors\n", pp->p_size + *freep); 2142 else { 2143 *freep += pp->p_size - ui; 2144 pp->p_size = ui; 2145 break; /* ok */ 2146 } 2147 } 2148 } 2149 } 2150 pp->p_size = ui; 2151 return(0); 2152 } 2153 2154 int 2155 get_fsize(lp, partno) 2156 struct disklabel *lp; 2157 int partno; 2158 { 2159 u_int32_t ui; 2160 struct partition *pp = &lp->d_partitions[partno]; 2161 2162 for (;;) { 2163 ui = getuint(lp, partno, "fragment size", 2164 "Size of fs block fragments. Usually 1024 or 512.", 2165 pp->p_fsize, pp->p_fsize, 0, 0); 2166 if (ui == UINT_MAX - 1) { 2167 fputs("Command aborted\n", stderr); 2168 return(1); 2169 } else if (ui == UINT_MAX) 2170 fputs("Invalid entry\n", stderr); 2171 else 2172 break; 2173 } 2174 if (ui == 0) 2175 puts("Zero fragment size implies zero block size"); 2176 pp->p_fsize = ui; 2177 return(0); 2178 } 2179 2180 int 2181 get_bsize(lp, partno) 2182 struct disklabel *lp; 2183 int partno; 2184 { 2185 u_int32_t ui; 2186 struct partition *pp = &lp->d_partitions[partno]; 2187 2188 /* Avoid dividing by zero... */ 2189 if (pp->p_fsize == 0) { 2190 pp->p_frag = 0; 2191 return(1); 2192 } 2193 2194 for (;;) { 2195 ui = getuint(lp, partno, "block size", 2196 "Size of filesystem blocks. Usually 8192 or 4096.", 2197 pp->p_fsize * pp->p_frag, pp->p_fsize * pp->p_frag, 2198 0, 0); 2199 2200 /* sanity checks */ 2201 if (ui == UINT_MAX - 1) { 2202 fputs("Command aborted\n", stderr); 2203 return(1); 2204 } else if (ui == UINT_MAX) 2205 fputs("Invalid entry\n", stderr); 2206 else if (ui < getpagesize()) 2207 fprintf(stderr, 2208 "Error: block size must be at least as big " 2209 "as page size (%d).\n", getpagesize()); 2210 else if (ui % pp->p_fsize != 0) 2211 fputs("Error: block size must be a multiple of the " 2212 "fragment size.\n", stderr); 2213 else if (ui / pp->p_fsize < 1) 2214 fputs("Error: block size must be at least as big as " 2215 "fragment size.\n", stderr); 2216 else 2217 break; 2218 } 2219 pp->p_frag = ui / pp->p_fsize; 2220 return(0); 2221 } 2222 2223 int 2224 get_cpg(lp, partno) 2225 struct disklabel *lp; 2226 int partno; 2227 { 2228 u_int32_t ui; 2229 struct partition *pp = &lp->d_partitions[partno]; 2230 2231 for (;;) { 2232 ui = getuint(lp, partno, "cpg", 2233 "Number of filesystem cylinders per group." 2234 " Usually 16 or 8.", 2235 pp->p_cpg ? pp->p_cpg : 16, 16, 0, 0); 2236 if (ui == UINT_MAX - 1) { 2237 fputs("Command aborted\n", stderr); 2238 return(1); 2239 } else if (ui == UINT_MAX) 2240 fputs("Invalid entry\n", stderr); 2241 else 2242 break; 2243 } 2244 pp->p_cpg = ui; 2245 return(0); 2246 } 2247 2248 int 2249 get_fstype(lp, partno) 2250 struct disklabel *lp; 2251 int partno; 2252 { 2253 char *p; 2254 u_int32_t ui; 2255 struct partition *pp = &lp->d_partitions[partno]; 2256 2257 if (pp->p_fstype < FSMAXTYPES) { 2258 p = getstring("FS type", 2259 "Filesystem type (usually 4.2BSD or swap)", 2260 fstypenames[pp->p_fstype]); 2261 if (p == NULL) { 2262 fputs("Command aborted\n", stderr); 2263 return(1); 2264 } 2265 for (ui = 0; ui < FSMAXTYPES; ui++) { 2266 if (!strcasecmp(p, fstypenames[ui])) { 2267 pp->p_fstype = ui; 2268 break; 2269 } 2270 } 2271 if (ui >= FSMAXTYPES) { 2272 printf("Unrecognized filesystem type '%s', treating as 'unknown'\n", p); 2273 pp->p_fstype = FS_OTHER; 2274 } 2275 } else { 2276 for (;;) { 2277 ui = getuint(lp, partno, "FS type (decimal)", 2278 "Filesystem type as a decimal number; usually 7 (4.2BSD) or 1 (swap).", 2279 pp->p_fstype, pp->p_fstype, 0, 0); 2280 if (ui == UINT_MAX - 1) { 2281 fputs("Command aborted\n", stderr); 2282 return(1); 2283 } if (ui == UINT_MAX) 2284 fputs("Invalid entry\n", stderr); 2285 else 2286 break; 2287 } 2288 pp->p_fstype = ui; 2289 } 2290 return(0); 2291 } 2292 2293 int 2294 get_mp(lp, mp, partno) 2295 struct disklabel *lp; 2296 char **mp; 2297 int partno; 2298 { 2299 char *p; 2300 struct partition *pp = &lp->d_partitions[partno]; 2301 2302 if (mp != NULL && pp->p_fstype != FS_UNUSED && 2303 pp->p_fstype != FS_SWAP && pp->p_fstype != FS_BOOT && 2304 pp->p_fstype != FS_OTHER) { 2305 for (;;) { 2306 p = getstring("mount point", 2307 "Where to mount this filesystem (ie: / /var /usr)", 2308 mp[partno] ? mp[partno] : "none"); 2309 if (p == NULL) { 2310 fputs("Command aborted\n", stderr); 2311 return(1); 2312 } 2313 if (strcasecmp(p, "none") == 0) { 2314 if (mp[partno] != NULL) { 2315 free(mp[partno]); 2316 mp[partno] = NULL; 2317 } 2318 break; 2319 } 2320 if (*p == '/') { 2321 /* XXX - might as well realloc */ 2322 if (mp[partno] != NULL) 2323 free(mp[partno]); 2324 if ((mp[partno] = strdup(p)) == NULL) 2325 errx(4, "out of memory"); 2326 break; 2327 } 2328 fputs("Mount points must start with '/'\n", stderr); 2329 } 2330 } 2331 return(0); 2332 } 2333 2334 int 2335 micmp(a1, a2) 2336 const void *a1; 2337 const void *a2; 2338 { 2339 struct mountinfo *mi1 = (struct mountinfo *)a1; 2340 struct mountinfo *mi2 = (struct mountinfo *)a2; 2341 2342 /* We want all the NULLs at the end... */ 2343 if (mi1->mountpoint == NULL && mi2->mountpoint == NULL) 2344 return(0); 2345 else if (mi1->mountpoint == NULL) 2346 return(1); 2347 else if (mi2->mountpoint == NULL) 2348 return(-1); 2349 else 2350 return(strcmp(mi1->mountpoint, mi2->mountpoint)); 2351 } 2352 2353 void 2354 get_geometry(f, dgpp, bgpp) 2355 int f; 2356 struct disklabel **dgpp; 2357 struct disklabel **bgpp; 2358 { 2359 #ifdef CPU_BIOS 2360 int mib[4]; 2361 size_t size; 2362 dev_t devno; 2363 bios_diskinfo_t di; 2364 #endif 2365 struct stat st; 2366 struct disklabel *disk_geop; 2367 struct disklabel *bios_geop; 2368 2369 if (fstat(f, &st) == -1) 2370 err(4, "Can't stat device"); 2371 2372 /* Get disk geometry */ 2373 if ((disk_geop = calloc(1, sizeof(struct disklabel))) == NULL) 2374 errx(4, "out of memory"); 2375 if (ioctl(f, DIOCGPDINFO, disk_geop) < 0 && 2376 ioctl(f, DIOCGDINFO, disk_geop) < 0) 2377 err(4, "ioctl DIOCGDINFO"); 2378 *dgpp = disk_geop; 2379 2380 /* Get BIOS geometry */ 2381 *bgpp = NULL; 2382 #ifdef CPU_BIOS 2383 mib[0] = CTL_MACHDEP; 2384 mib[1] = CPU_CHR2BLK; 2385 mib[2] = st.st_rdev; 2386 size = sizeof(devno); 2387 if (sysctl(mib, 3, &devno, &size, NULL, 0) == -1) { 2388 warn("sysctl(machdep.chr2blk)"); 2389 return; 2390 } 2391 devno = MAKEBOOTDEV(major(devno), 0, 0, DISKUNIT(devno), RAW_PART); 2392 2393 mib[0] = CTL_MACHDEP; 2394 mib[1] = CPU_BIOS; 2395 mib[2] = BIOS_DISKINFO; 2396 mib[3] = devno; 2397 size = sizeof(di); 2398 if (sysctl(mib, 4, &di, &size, NULL, 0) == -1) { 2399 warn("Can't get bios geometry"); 2400 return; 2401 } 2402 if ((bios_geop = calloc(1, sizeof(struct disklabel))) == NULL) 2403 errx(4, "out of memory"); 2404 2405 bios_geop->d_secsize = DEV_BSIZE; 2406 bios_geop->d_nsectors = di.bios_sectors; 2407 bios_geop->d_ntracks = di.bios_heads; 2408 bios_geop->d_ncylinders = di.bios_cylinders; 2409 bios_geop->d_secpercyl = di.bios_sectors * di.bios_heads; 2410 bios_geop->d_secperunit = di.bios_cylinders * 2411 di.bios_heads * di.bios_sectors; 2412 *bgpp = bios_geop; 2413 #endif 2414 } 2415 2416 void 2417 set_geometry(lp, dgp, bgp, ugp, p) 2418 struct disklabel *lp; 2419 struct disklabel *dgp; 2420 struct disklabel *bgp; 2421 struct disklabel *ugp; 2422 char *p; 2423 { 2424 if (p == NULL) { 2425 p = getstring("[d]isk, [b]ios, or [u]ser geometry", 2426 "Enter 'd' to use the geometry based on what the disk " 2427 "itself thinks it is, 'b' to use what the BIOS says," 2428 "or 'u' to use the geometry that was found on in the label.", 2429 "d"); 2430 } 2431 if (p == NULL) { 2432 fputs("Command aborted\n", stderr); 2433 return; 2434 } 2435 switch (*p) { 2436 case 'b': 2437 case 'B': 2438 if (bgp == NULL) 2439 fputs("BIOS geometry not defined.\n", stderr); 2440 else { 2441 lp->d_secsize = bgp->d_secsize; 2442 lp->d_nsectors = bgp->d_nsectors; 2443 lp->d_ntracks = bgp->d_ntracks; 2444 lp->d_ncylinders = bgp->d_ncylinders; 2445 lp->d_secpercyl = bgp->d_secpercyl; 2446 lp->d_secperunit = bgp->d_secperunit; 2447 } 2448 break; 2449 case 'd': 2450 case 'D': 2451 if (dgp == NULL) 2452 fputs("BIOS geometry not defined.\n", stderr); 2453 else { 2454 lp->d_secsize = dgp->d_secsize; 2455 lp->d_nsectors = dgp->d_nsectors; 2456 lp->d_ntracks = dgp->d_ntracks; 2457 lp->d_ncylinders = dgp->d_ncylinders; 2458 lp->d_secpercyl = dgp->d_secpercyl; 2459 lp->d_secperunit = dgp->d_secperunit; 2460 } 2461 break; 2462 case 'u': 2463 case 'U': 2464 if (ugp == NULL) 2465 fputs("BIOS geometry not defined.\n", stderr); 2466 else { 2467 lp->d_secsize = ugp->d_secsize; 2468 lp->d_nsectors = ugp->d_nsectors; 2469 lp->d_ntracks = ugp->d_ntracks; 2470 lp->d_ncylinders = ugp->d_ncylinders; 2471 lp->d_secpercyl = ugp->d_secpercyl; 2472 lp->d_secperunit = ugp->d_secperunit; 2473 if (dgp != NULL && ugp->d_secsize == dgp->d_secsize && 2474 ugp->d_nsectors == dgp->d_nsectors && 2475 ugp->d_ntracks == dgp->d_ntracks && 2476 ugp->d_ncylinders == dgp->d_ncylinders && 2477 ugp->d_secpercyl == dgp->d_secpercyl && 2478 ugp->d_secperunit == dgp->d_secperunit) 2479 fputs("Note: user geometry is the same as disk " 2480 "geometry.\n", stderr); 2481 } 2482 break; 2483 default: 2484 fputs("You must enter either 'd', 'b', or 'u'.\n", stderr); 2485 break; 2486 } 2487 } 2488 2489 void 2490 zero_partitions(lp, freep) 2491 struct disklabel *lp; 2492 u_int32_t *freep; 2493 { 2494 int i; 2495 2496 for (i = 0; i < MAXPARTITIONS; i++) 2497 memset(&lp->d_partitions[i], 0, sizeof(struct partition)); 2498 lp->d_partitions[RAW_PART].p_offset = starting_sector; 2499 lp->d_partitions[RAW_PART].p_size = ending_sector - starting_sector; 2500 editor_countfree(lp, freep); 2501 } 2502