1 /* $OpenBSD: disklabel.c,v 1.237 2021/06/24 21:11:40 jmc Exp $ */ 2 3 /* 4 * Copyright (c) 1987, 1993 5 * The Regents of the University of California. All rights reserved. 6 * 7 * This code is derived from software contributed to Berkeley by 8 * Symmetric Computer Systems. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 3. Neither the name of the University nor the names of its contributors 19 * may be used to endorse or promote products derived from this software 20 * without specific prior written permission. 21 * 22 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 25 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 32 * SUCH DAMAGE. 33 */ 34 35 #include <sys/param.h> /* DEV_BSIZE */ 36 #include <sys/sysctl.h> 37 #include <sys/ioctl.h> 38 #include <sys/dkio.h> 39 #include <sys/stat.h> 40 #include <sys/wait.h> 41 #define DKTYPENAMES 42 #include <sys/disklabel.h> 43 44 #include <ufs/ffs/fs.h> 45 46 #include <ctype.h> 47 #include <err.h> 48 #include <errno.h> 49 #include <fcntl.h> 50 #include <limits.h> 51 #include <signal.h> 52 #include <string.h> 53 #include <stdio.h> 54 #include <stdlib.h> 55 #include <unistd.h> 56 #include <util.h> 57 #include <fstab.h> 58 #include "pathnames.h" 59 #include "extern.h" 60 61 /* 62 * Disklabel: read and write disklabels. 63 * The label is usually placed on one of the first sectors of the disk. 64 * Many machines also place a bootstrap in the same area, 65 * in which case the label is embedded in the bootstrap. 66 * The bootstrap source must leave space at the proper offset 67 * for the label on such machines. 68 */ 69 70 #ifndef BBSIZE 71 #define BBSIZE 8192 /* size of boot area, with label */ 72 #endif 73 74 char *dkname, *specname, *fstabfile; 75 char tmpfil[] = _PATH_TMPFILE; 76 char *mountpoints[MAXPARTITIONS]; 77 struct disklabel lab; 78 enum { 79 UNSPEC, EDIT, EDITOR, READ, RESTORE, WRITE 80 } op = UNSPEC; 81 82 int aflag; 83 int cflag; 84 int dflag; 85 int tflag; 86 int uidflag; 87 int verbose; 88 int quiet; 89 int donothing; 90 91 void makedisktab(FILE *, struct disklabel *); 92 void makelabel(char *, char *, struct disklabel *); 93 int writelabel(int, struct disklabel *); 94 int edit(struct disklabel *, int); 95 int editit(const char *); 96 char *skip(char *); 97 char *word(char *); 98 int getasciilabel(FILE *, struct disklabel *); 99 int cmplabel(struct disklabel *, struct disklabel *); 100 void usage(void); 101 u_int64_t getnum(char *, u_int64_t, u_int64_t, const char **); 102 103 int64_t physmem; 104 105 void 106 getphysmem(void) 107 { 108 size_t sz = sizeof(physmem); 109 int mib[] = { CTL_HW, HW_PHYSMEM64 }; 110 111 if (sysctl(mib, 2, &physmem, &sz, NULL, (size_t)0) == -1) 112 errx(4, "can't get mem size"); 113 } 114 115 int 116 main(int argc, char *argv[]) 117 { 118 FILE *t; 119 char *autotable = NULL; 120 int ch, f, error = 0; 121 char print_unit = '\0'; 122 123 getphysmem(); 124 125 while ((ch = getopt(argc, argv, "AEf:F:hRcdenp:tT:vw")) != -1) 126 switch (ch) { 127 case 'A': 128 aflag = 1; 129 break; 130 case 'R': 131 if (op != UNSPEC) 132 usage(); 133 op = RESTORE; 134 break; 135 case 'c': 136 cflag = 1; 137 break; 138 case 'd': 139 dflag = 1; 140 break; 141 case 'e': 142 if (op != UNSPEC) 143 usage(); 144 op = EDIT; 145 break; 146 case 'E': 147 if (op != UNSPEC) 148 usage(); 149 op = EDITOR; 150 break; 151 case 'f': 152 fstabfile = optarg; 153 uidflag = 0; 154 break; 155 case 'F': 156 fstabfile = optarg; 157 uidflag = 1; 158 break; 159 case 'h': 160 print_unit = '*'; 161 break; 162 case 't': 163 tflag = 1; 164 break; 165 case 'T': 166 autotable = optarg; 167 break; 168 case 'w': 169 if (op != UNSPEC) 170 usage(); 171 op = WRITE; 172 break; 173 case 'p': 174 if (strchr("bckmgtBCKMGT", optarg[0]) == NULL || 175 optarg[1] != '\0') { 176 fprintf(stderr, "Valid units are bckmgt\n"); 177 return 1; 178 } 179 print_unit = tolower((unsigned char)optarg[0]); 180 break; 181 case 'n': 182 donothing = 1; 183 break; 184 case 'v': 185 verbose = 1; 186 break; 187 case '?': 188 default: 189 usage(); 190 } 191 argc -= optind; 192 argv += optind; 193 194 if (op == UNSPEC) 195 op = READ; 196 197 if (argc < 1 || (fstabfile && !(op == EDITOR || op == RESTORE || 198 aflag))) 199 usage(); 200 201 if (argv[0] == NULL) 202 usage(); 203 dkname = argv[0]; 204 f = opendev(dkname, (op == READ ? O_RDONLY : O_RDWR), OPENDEV_PART, 205 &specname); 206 if (f == -1) 207 err(4, "%s", specname); 208 209 if (op != WRITE || aflag || dflag) { 210 readlabel(f); 211 212 if (op == EDIT || op == EDITOR || aflag) { 213 if (pledge("stdio rpath wpath cpath disklabel proc " 214 "exec", NULL) == -1) 215 err(1, "pledge"); 216 } else if (fstabfile) { 217 if (pledge("stdio rpath wpath cpath disklabel", NULL) 218 == -1) 219 err(1, "pledge"); 220 } else { 221 if (pledge("stdio rpath wpath disklabel", NULL) == -1) 222 err(1, "pledge"); 223 } 224 225 if (autotable != NULL) 226 parse_autotable(autotable); 227 error = parselabel(); 228 if (op == WRITE && aflag && error) 229 errx(1, "autoalloc failed"); 230 } else if (argc == 2 || argc == 3) { 231 /* Ensure f is a disk device before pledging. */ 232 if (ioctl(f, DIOCGDINFO, &lab) == -1) 233 err(4, "DIOCGDINFO"); 234 235 if (pledge("stdio rpath wpath disklabel", NULL) == -1) 236 err(1, "pledge"); 237 238 makelabel(argv[1], argc == 3 ? argv[2] : NULL, &lab); 239 } else 240 usage(); 241 242 switch (op) { 243 case EDIT: 244 if (argc != 1) 245 usage(); 246 error = edit(&lab, f); 247 break; 248 case EDITOR: 249 if (argc != 1) 250 usage(); 251 error = editor(f); 252 break; 253 case READ: 254 if (argc != 1) 255 usage(); 256 257 if (pledge("stdio", NULL) == -1) 258 err(1, "pledge"); 259 260 if (tflag) 261 makedisktab(stdout, &lab); 262 else 263 display(stdout, &lab, print_unit, 1); 264 error = checklabel(&lab); 265 break; 266 case RESTORE: 267 if (argc < 2 || argc > 3) 268 usage(); 269 if (!(t = fopen(argv[1], "r"))) 270 err(4, "%s", argv[1]); 271 error = getasciilabel(t, &lab); 272 if (error == 0) { 273 memset(&lab.d_uid, 0, sizeof(lab.d_uid)); 274 error = writelabel(f, &lab); 275 } 276 fclose(t); 277 break; 278 case WRITE: 279 error = checklabel(&lab); 280 if (error == 0) 281 error = writelabel(f, &lab); 282 break; 283 default: 284 break; 285 } 286 return error; 287 } 288 289 /* 290 * Construct a prototype disklabel from /etc/disktab. As a side 291 * effect, set the names of the primary and secondary boot files 292 * if specified. 293 */ 294 void 295 makelabel(char *type, char *name, struct disklabel *lp) 296 { 297 struct disklabel *dp; 298 299 dp = getdiskbyname(type); 300 if (dp == NULL) 301 errx(1, "unknown disk type: %s", type); 302 *lp = *dp; 303 /* d_packname is union d_boot[01], so zero */ 304 memset(lp->d_packname, 0, sizeof(lp->d_packname)); 305 if (name) 306 (void)strncpy(lp->d_packname, name, sizeof(lp->d_packname)); 307 } 308 309 310 int 311 writelabel(int f, struct disklabel *lp) 312 { 313 lp->d_magic = DISKMAGIC; 314 lp->d_magic2 = DISKMAGIC; 315 lp->d_checksum = 0; 316 lp->d_checksum = dkcksum(lp); 317 318 if (!donothing) { 319 /* Write new label to disk. */ 320 if (ioctl(f, DIOCWDINFO, lp) == -1) { 321 warn("DIOCWDINFO"); 322 return (1); 323 } 324 325 /* Refresh our copy of the on-disk current label to get UID. */ 326 if (ioctl(f, DIOCGDINFO, &lab) == -1) 327 err(4, "DIOCGDINFO"); 328 329 /* Finally, write out any mount point information. */ 330 mpsave(lp); 331 } 332 333 return (0); 334 } 335 336 /* 337 * Fetch requested disklabel into 'lab' using ioctl. 338 */ 339 void 340 readlabel(int f) 341 { 342 343 if (cflag && ioctl(f, DIOCRLDINFO) == -1) 344 err(4, "DIOCRLDINFO"); 345 346 if ((op == RESTORE) || dflag || aflag) { 347 if (ioctl(f, DIOCGPDINFO, &lab) == -1) 348 err(4, "DIOCGPDINFO"); 349 } else { 350 if (ioctl(f, DIOCGDINFO, &lab) == -1) 351 err(4, "DIOCGDINFO"); 352 } 353 } 354 355 int 356 parselabel(void) 357 { 358 char *partname, *partduid; 359 struct fstab *fsent; 360 int i; 361 362 i = asprintf(&partname, "/dev/%s%c", dkname, 'a'); 363 if (i == -1) 364 err(4, NULL); 365 i = asprintf(&partduid, 366 "%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx.a", 367 lab.d_uid[0], lab.d_uid[1], lab.d_uid[2], lab.d_uid[3], 368 lab.d_uid[4], lab.d_uid[5], lab.d_uid[6], lab.d_uid[7]); 369 if (i == -1) 370 err(4, NULL); 371 setfsent(); 372 for (i = 0; i < MAXPARTITIONS; i++) { 373 partname[strlen(dkname) + 5] = 'a' + i; 374 partduid[strlen(partduid) - 1] = 'a' + i; 375 fsent = getfsspec(partname); 376 if (fsent == NULL) 377 fsent = getfsspec(partduid); 378 if (fsent) 379 mountpoints[i] = strdup(fsent->fs_file); 380 } 381 endfsent(); 382 free(partduid); 383 free(partname); 384 385 if (aflag) 386 return editor_allocspace(&lab); 387 return 0; 388 } 389 390 void 391 makedisktab(FILE *f, struct disklabel *lp) 392 { 393 int i; 394 struct partition *pp; 395 396 if (lp->d_packname[0]) 397 (void)fprintf(f, "%.*s|", (int)sizeof(lp->d_packname), 398 lp->d_packname); 399 if (lp->d_typename[0]) 400 (void)fprintf(f, "%.*s|", (int)sizeof(lp->d_typename), 401 lp->d_typename); 402 (void)fputs("Automatically generated label:\\\n\t:dt=", f); 403 if (lp->d_type < DKMAXTYPES) 404 (void)fprintf(f, "%s:", dktypenames[lp->d_type]); 405 else 406 (void)fprintf(f, "unknown%d:", lp->d_type); 407 408 (void)fprintf(f, "se#%u:", lp->d_secsize); 409 (void)fprintf(f, "ns#%u:", lp->d_nsectors); 410 (void)fprintf(f, "nt#%u:", lp->d_ntracks); 411 (void)fprintf(f, "nc#%u:", lp->d_ncylinders); 412 (void)fprintf(f, "sc#%u:", lp->d_secpercyl); 413 (void)fprintf(f, "su#%llu:", DL_GETDSIZE(lp)); 414 415 /* 416 * XXX We do not print have disktab information yet for 417 * XXX DL_GETBSTART DL_GETBEND 418 */ 419 for (i = 0; i < NDDATA; i++) 420 if (lp->d_drivedata[i]) 421 (void)fprintf(f, "d%d#%u", i, lp->d_drivedata[i]); 422 pp = lp->d_partitions; 423 for (i = 0; i < lp->d_npartitions; i++, pp++) { 424 if (DL_GETPSIZE(pp)) { 425 char c = 'a' + i; 426 427 (void)fprintf(f, "\\\n\t:"); 428 (void)fprintf(f, "p%c#%llu:", c, DL_GETPSIZE(pp)); 429 (void)fprintf(f, "o%c#%llu:", c, DL_GETPOFFSET(pp)); 430 if (pp->p_fstype != FS_UNUSED) { 431 if (pp->p_fstype < FSMAXTYPES) 432 (void)fprintf(f, "t%c=%s:", c, 433 fstypenames[pp->p_fstype]); 434 else 435 (void)fprintf(f, "t%c=unknown%d:", 436 c, pp->p_fstype); 437 } 438 switch (pp->p_fstype) { 439 440 case FS_UNUSED: 441 break; 442 443 case FS_BSDFFS: 444 (void)fprintf(f, "b%c#%u:", c, 445 DISKLABELV1_FFS_BSIZE(pp->p_fragblock)); 446 (void)fprintf(f, "f%c#%u:", c, 447 DISKLABELV1_FFS_FSIZE(pp->p_fragblock)); 448 break; 449 450 default: 451 break; 452 } 453 } 454 } 455 (void)fputc('\n', f); 456 (void)fflush(f); 457 } 458 459 double 460 scale(u_int64_t sz, char unit, struct disklabel *lp) 461 { 462 double fsz; 463 464 fsz = (double)sz * lp->d_secsize; 465 466 switch (unit) { 467 case 'B': 468 return fsz; 469 case 'C': 470 return fsz / lp->d_secsize / lp->d_secpercyl; 471 case 'K': 472 return fsz / 1024; 473 case 'M': 474 return fsz / (1024 * 1024); 475 case 'G': 476 return fsz / (1024 * 1024 * 1024); 477 case 'T': 478 return fsz / (1024ULL * 1024 * 1024 * 1024); 479 default: 480 return -1.0; 481 } 482 } 483 484 /* 485 * Display a particular partition. 486 */ 487 void 488 display_partition(FILE *f, struct disklabel *lp, int i, char unit) 489 { 490 struct partition *pp = &lp->d_partitions[i]; 491 double p_size; 492 493 p_size = scale(DL_GETPSIZE(pp), unit, lp); 494 if (DL_GETPSIZE(pp)) { 495 u_int32_t frag = DISKLABELV1_FFS_FRAG(pp->p_fragblock); 496 u_int32_t fsize = DISKLABELV1_FFS_FSIZE(pp->p_fragblock); 497 498 if (p_size < 0) 499 fprintf(f, " %c: %16llu %16llu ", 'a' + i, 500 DL_GETPSIZE(pp), DL_GETPOFFSET(pp)); 501 else 502 fprintf(f, " %c: %15.*f%c %16llu ", 'a' + i, 503 unit == 'B' ? 0 : 1, p_size, unit, 504 DL_GETPOFFSET(pp)); 505 if (pp->p_fstype < FSMAXTYPES) 506 fprintf(f, "%7.7s", fstypenames[pp->p_fstype]); 507 else 508 fprintf(f, "%7d", pp->p_fstype); 509 510 switch (pp->p_fstype) { 511 case FS_BSDFFS: 512 fprintf(f, " %5u %5u %5hu ", 513 fsize, fsize * frag, 514 pp->p_cpg); 515 break; 516 default: 517 fprintf(f, "%20.20s", ""); 518 break; 519 } 520 521 if (mountpoints[i] != NULL) 522 fprintf(f, "# %s", mountpoints[i]); 523 putc('\n', f); 524 } 525 } 526 527 char 528 canonical_unit(struct disklabel *lp, char unit) 529 { 530 struct partition *pp; 531 u_int64_t small; 532 int i; 533 534 if (unit == '*') { 535 small = DL_GETDSIZE(lp); 536 pp = &lp->d_partitions[0]; 537 for (i = 0; i < lp->d_npartitions; i++, pp++) 538 if (DL_GETPSIZE(pp) > 0 && DL_GETPSIZE(pp) < small) 539 small = DL_GETPSIZE(pp); 540 if (small < DL_BLKTOSEC(lp, MEG(1))) 541 unit = 'K'; 542 else if (small < DL_BLKTOSEC(lp, MEG(1024))) 543 unit = 'M'; 544 else if (small < DL_BLKTOSEC(lp, GIG(1024))) 545 unit = 'G'; 546 else 547 unit = 'T'; 548 } 549 unit = toupper((unsigned char)unit); 550 551 return (unit); 552 } 553 554 void 555 display(FILE *f, struct disklabel *lp, char unit, int all) 556 { 557 int i, j; 558 double d; 559 560 unit = canonical_unit(lp, unit); 561 562 fprintf(f, "# %s:\n", specname); 563 564 if (lp->d_type < DKMAXTYPES) 565 fprintf(f, "type: %s\n", dktypenames[lp->d_type]); 566 else 567 fprintf(f, "type: %d\n", lp->d_type); 568 fprintf(f, "disk: %.*s\n", (int)sizeof(lp->d_typename), 569 lp->d_typename); 570 fprintf(f, "label: %.*s\n", (int)sizeof(lp->d_packname), 571 lp->d_packname); 572 fprintf(f, "duid: %02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx\n", 573 lp->d_uid[0], lp->d_uid[1], lp->d_uid[2], lp->d_uid[3], 574 lp->d_uid[4], lp->d_uid[5], lp->d_uid[6], lp->d_uid[7]); 575 fprintf(f, "flags:"); 576 if (lp->d_flags & D_BADSECT) 577 fprintf(f, " badsect"); 578 if (lp->d_flags & D_VENDOR) 579 fprintf(f, " vendor"); 580 putc('\n', f); 581 582 fprintf(f, "bytes/sector: %u\n", lp->d_secsize); 583 fprintf(f, "sectors/track: %u\n", lp->d_nsectors); 584 fprintf(f, "tracks/cylinder: %u\n", lp->d_ntracks); 585 fprintf(f, "sectors/cylinder: %u\n", lp->d_secpercyl); 586 fprintf(f, "cylinders: %u\n", lp->d_ncylinders); 587 fprintf(f, "total sectors: %llu", DL_GETDSIZE(lp)); 588 d = scale(DL_GETDSIZE(lp), unit, lp); 589 if (d > 0) 590 fprintf(f, " # total bytes: %.*f%c", unit == 'B' ? 0 : 1, 591 d, unit); 592 fprintf(f, "\n"); 593 594 fprintf(f, "boundstart: %llu\n", DL_GETBSTART(lp)); 595 fprintf(f, "boundend: %llu\n", DL_GETBEND(lp)); 596 fprintf(f, "drivedata: "); 597 for (i = NDDATA - 1; i >= 0; i--) 598 if (lp->d_drivedata[i]) 599 break; 600 if (i < 0) 601 i = 0; 602 for (j = 0; j <= i; j++) 603 fprintf(f, "%d ", lp->d_drivedata[j]); 604 fprintf(f, "\n"); 605 if (all) { 606 fprintf(f, "\n%hu partitions:\n", lp->d_npartitions); 607 fprintf(f, "# %16.16s %16.16s fstype [fsize bsize cpg]\n", 608 "size", "offset"); 609 for (i = 0; i < lp->d_npartitions; i++) 610 display_partition(f, lp, i, unit); 611 } 612 fflush(f); 613 } 614 615 int 616 edit(struct disklabel *lp, int f) 617 { 618 int first, ch, fd, error = 0; 619 struct disklabel label; 620 FILE *fp; 621 622 if ((fd = mkstemp(tmpfil)) == -1 || (fp = fdopen(fd, "w")) == NULL) { 623 warn("%s", tmpfil); 624 if (fd != -1) 625 close(fd); 626 return (1); 627 } 628 display(fp, lp, 0, 1); 629 fprintf(fp, "\n# Notes:\n"); 630 fprintf(fp, 631 "# Up to 16 partitions are valid, named from 'a' to 'p'. Partition 'a' is\n" 632 "# your root filesystem, 'b' is your swap, and 'c' should cover your whole\n" 633 "# disk. Any other partition is free for any use. 'size' and 'offset' are\n" 634 "# in 512-byte blocks. fstype should be '4.2BSD', 'swap', or 'none' or some\n" 635 "# other values. fsize/bsize/cpg should typically be '2048 16384 16' for a\n" 636 "# 4.2BSD filesystem (or '512 4096 16' except on alpha, sun4, ...)\n"); 637 fclose(fp); 638 for (;;) { 639 if (editit(tmpfil) == -1) 640 break; 641 fp = fopen(tmpfil, "r"); 642 if (fp == NULL) { 643 warn("%s", tmpfil); 644 break; 645 } 646 /* Start with the kernel's idea of the default label. */ 647 if (ioctl(f, DIOCGPDINFO, &label) == -1) 648 err(4, "DIOCGPDINFO"); 649 error = getasciilabel(fp, &label); 650 if (error == 0) { 651 if (cmplabel(lp, &label) == 0) { 652 puts("No changes."); 653 fclose(fp); 654 (void) unlink(tmpfil); 655 return (0); 656 } 657 *lp = label; 658 if (writelabel(f, lp) == 0) { 659 fclose(fp); 660 (void) unlink(tmpfil); 661 return (0); 662 } 663 } 664 fclose(fp); 665 printf("re-edit the label? [y]: "); 666 fflush(stdout); 667 first = ch = getchar(); 668 while (ch != '\n' && ch != EOF) 669 ch = getchar(); 670 if (first == 'n' || first == 'N') 671 break; 672 } 673 (void)unlink(tmpfil); 674 return (1); 675 } 676 677 /* 678 * Execute an editor on the specified pathname, which is interpreted 679 * from the shell. This means flags may be included. 680 * 681 * Returns -1 on error, or the exit value on success. 682 */ 683 int 684 editit(const char *pathname) 685 { 686 char *argp[] = {"sh", "-c", NULL, NULL}, *ed, *p; 687 sig_t sighup, sigint, sigquit, sigchld; 688 pid_t pid; 689 int saved_errno, st, ret = -1; 690 691 ed = getenv("VISUAL"); 692 if (ed == NULL || ed[0] == '\0') 693 ed = getenv("EDITOR"); 694 if (ed == NULL || ed[0] == '\0') 695 ed = _PATH_VI; 696 if (asprintf(&p, "%s %s", ed, pathname) == -1) 697 return (-1); 698 argp[2] = p; 699 700 sighup = signal(SIGHUP, SIG_IGN); 701 sigint = signal(SIGINT, SIG_IGN); 702 sigquit = signal(SIGQUIT, SIG_IGN); 703 sigchld = signal(SIGCHLD, SIG_DFL); 704 if ((pid = fork()) == -1) 705 goto fail; 706 if (pid == 0) { 707 execv(_PATH_BSHELL, argp); 708 _exit(127); 709 } 710 while (waitpid(pid, &st, 0) == -1) 711 if (errno != EINTR) 712 goto fail; 713 if (!WIFEXITED(st)) 714 errno = EINTR; 715 else 716 ret = WEXITSTATUS(st); 717 718 fail: 719 saved_errno = errno; 720 (void)signal(SIGHUP, sighup); 721 (void)signal(SIGINT, sigint); 722 (void)signal(SIGQUIT, sigquit); 723 (void)signal(SIGCHLD, sigchld); 724 free(p); 725 errno = saved_errno; 726 return (ret); 727 } 728 729 char * 730 skip(char *cp) 731 { 732 733 cp += strspn(cp, " \t"); 734 if (*cp == '\0') 735 return (NULL); 736 return (cp); 737 } 738 739 char * 740 word(char *cp) 741 { 742 743 cp += strcspn(cp, " \t"); 744 if (*cp == '\0') 745 return (NULL); 746 *cp++ = '\0'; 747 cp += strspn(cp, " \t"); 748 if (*cp == '\0') 749 return (NULL); 750 return (cp); 751 } 752 753 /* Base the max value on the sizeof of the value we are reading */ 754 #define GETNUM(field, nptr, min, errstr) \ 755 getnum((nptr), (min), \ 756 sizeof(field) == 8 ? LLONG_MAX : \ 757 (sizeof(field) == 4 ? UINT_MAX : \ 758 (sizeof(field) == 2 ? USHRT_MAX : UCHAR_MAX)), (errstr)) 759 760 u_int64_t 761 getnum(char *nptr, u_int64_t min, u_int64_t max, const char **errstr) 762 { 763 char *p, c; 764 u_int64_t ret; 765 766 for (p = nptr; *p != '\0' && !isspace((unsigned char)*p); p++) 767 ; 768 c = *p; 769 *p = '\0'; 770 ret = strtonum(nptr, min, max, errstr); 771 *p = c; 772 return (ret); 773 } 774 775 int 776 duid_parse(struct disklabel *lp, char *s) 777 { 778 u_char duid[8]; 779 char c; 780 int i; 781 782 if (strlen(s) != 16) 783 return -1; 784 785 memset(duid, 0, sizeof(duid)); 786 for (i = 0; i < 16; i++) { 787 c = s[i]; 788 if (c >= '0' && c <= '9') 789 c -= '0'; 790 else if (c >= 'a' && c <= 'f') 791 c -= ('a' - 10); 792 else if (c >= 'A' && c <= 'F') 793 c -= ('A' - 10); 794 else 795 return -1; 796 duid[i / 2] <<= 4; 797 duid[i / 2] |= c & 0xf; 798 } 799 800 memcpy(lp->d_uid, &duid, sizeof(lp->d_uid)); 801 return 0; 802 } 803 804 /* 805 * Read an ascii label in from FILE f, 806 * in the same format as that put out by display(), 807 * and fill in lp. 808 */ 809 int 810 getasciilabel(FILE *f, struct disklabel *lp) 811 { 812 const char * const *cpp; 813 const char *s; 814 char *cp; 815 const char *errstr; 816 struct partition *pp; 817 char *mp, *tp, line[BUFSIZ]; 818 char **omountpoints = NULL; 819 int lineno = 0, errors = 0; 820 u_int32_t v, fsize; 821 u_int64_t lv; 822 unsigned int part; 823 824 lp->d_version = 1; 825 lp->d_bbsize = BBSIZE; /* XXX */ 826 lp->d_sbsize = SBSIZE; /* XXX */ 827 828 if (!(omountpoints = calloc(MAXPARTITIONS, sizeof(char *)))) 829 errx(4, "out of memory"); 830 831 mpcopy(omountpoints, mountpoints); 832 for (part = 0; part < MAXPARTITIONS; part++) { 833 free(mountpoints[part]); 834 mountpoints[part] = NULL; 835 } 836 837 while (fgets(line, sizeof(line), f)) { 838 lineno++; 839 mp = NULL; 840 if ((cp = strpbrk(line, "\r\n"))) 841 *cp = '\0'; 842 if ((cp = strpbrk(line, "#"))) { 843 *cp = '\0'; 844 mp = skip(cp+1); 845 if (mp && *mp != '/') 846 mp = NULL; 847 } 848 cp = skip(line); 849 if (cp == NULL) 850 continue; 851 tp = strchr(cp, ':'); 852 if (tp == NULL) { 853 warnx("line %d: syntax error", lineno); 854 errors++; 855 continue; 856 } 857 *tp++ = '\0', tp = skip(tp); 858 if (!strcmp(cp, "type")) { 859 if (tp == NULL) 860 tp = "unknown"; 861 else if (strcasecmp(tp, "IDE") == 0) 862 tp = "ESDI"; 863 cpp = dktypenames; 864 for (; cpp < &dktypenames[DKMAXTYPES]; cpp++) 865 if ((s = *cpp) && !strcasecmp(s, tp)) { 866 lp->d_type = cpp - dktypenames; 867 goto next; 868 } 869 v = GETNUM(lp->d_type, tp, 0, &errstr); 870 if (errstr || v >= DKMAXTYPES) 871 warnx("line %d: warning, unknown disk type: %s", 872 lineno, tp); 873 lp->d_type = v; 874 continue; 875 } 876 if (!strcmp(cp, "flags")) { 877 for (v = 0; (cp = tp) && *cp != '\0';) { 878 tp = word(cp); 879 if (!strcmp(cp, "badsect")) 880 v |= D_BADSECT; 881 else if (!strcmp(cp, "vendor")) 882 v |= D_VENDOR; 883 else { 884 warnx("line %d: bad flag: %s", 885 lineno, cp); 886 errors++; 887 } 888 } 889 lp->d_flags = v; 890 continue; 891 } 892 if (!strcmp(cp, "drivedata")) { 893 int i; 894 895 for (i = 0; (cp = tp) && *cp != '\0' && i < NDDATA;) { 896 v = GETNUM(lp->d_drivedata[i], cp, 0, &errstr); 897 if (errstr) 898 warnx("line %d: bad drivedata %s", 899 lineno, cp); 900 lp->d_drivedata[i++] = v; 901 tp = word(cp); 902 } 903 continue; 904 } 905 if (sscanf(cp, "%d partitions", &v) == 1) { 906 if (v == 0 || v > MAXPARTITIONS) { 907 warnx("line %d: bad # of partitions", lineno); 908 lp->d_npartitions = MAXPARTITIONS; 909 errors++; 910 } else 911 lp->d_npartitions = v; 912 continue; 913 } 914 if (tp == NULL) 915 tp = ""; 916 if (!strcmp(cp, "disk")) { 917 strncpy(lp->d_typename, tp, sizeof (lp->d_typename)); 918 continue; 919 } 920 if (!strcmp(cp, "label")) { 921 strncpy(lp->d_packname, tp, sizeof (lp->d_packname)); 922 continue; 923 } 924 if (!strcmp(cp, "duid")) { 925 if (duid_parse(lp, tp) != 0) { 926 warnx("line %d: bad %s: %s", lineno, cp, tp); 927 errors++; 928 } 929 continue; 930 } 931 if (!strcmp(cp, "bytes/sector")) { 932 v = GETNUM(lp->d_secsize, tp, 1, &errstr); 933 if (errstr || (v % 512) != 0) { 934 warnx("line %d: bad %s: %s", lineno, cp, tp); 935 errors++; 936 } else 937 lp->d_secsize = v; 938 continue; 939 } 940 if (!strcmp(cp, "sectors/track")) { 941 v = GETNUM(lp->d_nsectors, tp, 1, &errstr); 942 if (errstr) { 943 warnx("line %d: bad %s: %s", lineno, cp, tp); 944 errors++; 945 } else 946 lp->d_nsectors = v; 947 continue; 948 } 949 if (!strcmp(cp, "sectors/cylinder")) { 950 v = GETNUM(lp->d_secpercyl, tp, 1, &errstr); 951 if (errstr) { 952 warnx("line %d: bad %s: %s", lineno, cp, tp); 953 errors++; 954 } else 955 lp->d_secpercyl = v; 956 continue; 957 } 958 if (!strcmp(cp, "tracks/cylinder")) { 959 v = GETNUM(lp->d_ntracks, tp, 1, &errstr); 960 if (errstr) { 961 warnx("line %d: bad %s: %s", lineno, cp, tp); 962 errors++; 963 } else 964 lp->d_ntracks = v; 965 continue; 966 } 967 if (!strcmp(cp, "cylinders")) { 968 v = GETNUM(lp->d_ncylinders, tp, 1, &errstr); 969 if (errstr) { 970 warnx("line %d: bad %s: %s", lineno, cp, tp); 971 errors++; 972 } else 973 lp->d_ncylinders = v; 974 continue; 975 } 976 977 /* Ignore fields that are no longer in the disklabel. */ 978 if (!strcmp(cp, "rpm") || 979 !strcmp(cp, "interleave") || 980 !strcmp(cp, "trackskew") || 981 !strcmp(cp, "cylinderskew") || 982 !strcmp(cp, "headswitch") || 983 !strcmp(cp, "track-to-track seek")) 984 continue; 985 986 /* Ignore fields that are forcibly set when label is read. */ 987 if (!strcmp(cp, "total sectors") || 988 !strcmp(cp, "boundstart") || 989 !strcmp(cp, "boundend")) 990 continue; 991 992 if ('a' <= *cp && *cp <= 'z' && cp[1] == '\0') { 993 unsigned int part = *cp - 'a'; 994 995 if (part >= lp->d_npartitions) { 996 if (part >= MAXPARTITIONS) { 997 warnx("line %d: bad partition name: %s", 998 lineno, cp); 999 errors++; 1000 continue; 1001 } else { 1002 lp->d_npartitions = part + 1; 1003 } 1004 } 1005 pp = &lp->d_partitions[part]; 1006 #define NXTNUM(n, field, errstr) { \ 1007 if (tp == NULL) { \ 1008 warnx("line %d: too few fields", lineno); \ 1009 errors++; \ 1010 break; \ 1011 } else \ 1012 cp = tp, tp = word(cp), (n) = GETNUM(field, cp, 0, errstr); \ 1013 } 1014 NXTNUM(lv, lv, &errstr); 1015 if (errstr) { 1016 warnx("line %d: bad partition size: %s", 1017 lineno, cp); 1018 errors++; 1019 } else { 1020 DL_SETPSIZE(pp, lv); 1021 } 1022 NXTNUM(lv, lv, &errstr); 1023 if (errstr) { 1024 warnx("line %d: bad partition offset: %s", 1025 lineno, cp); 1026 errors++; 1027 } else { 1028 DL_SETPOFFSET(pp, lv); 1029 } 1030 if (tp == NULL) { 1031 pp->p_fstype = FS_UNUSED; 1032 goto gottype; 1033 } 1034 cp = tp, tp = word(cp); 1035 cpp = fstypenames; 1036 for (; cpp < &fstypenames[FSMAXTYPES]; cpp++) 1037 if ((s = *cpp) && !strcasecmp(s, cp)) { 1038 pp->p_fstype = cpp - fstypenames; 1039 goto gottype; 1040 } 1041 if (isdigit((unsigned char)*cp)) 1042 v = GETNUM(pp->p_fstype, cp, 0, &errstr); 1043 else 1044 v = FSMAXTYPES; 1045 if (errstr || v >= FSMAXTYPES) { 1046 warnx("line %d: warning, unknown filesystem type: %s", 1047 lineno, cp); 1048 v = FS_UNUSED; 1049 } 1050 pp->p_fstype = v; 1051 gottype: 1052 switch (pp->p_fstype) { 1053 1054 case FS_UNUSED: /* XXX */ 1055 if (tp == NULL) /* ok to skip fsize/bsize */ 1056 break; 1057 NXTNUM(fsize, fsize, &errstr); 1058 if (fsize == 0) 1059 break; 1060 NXTNUM(v, v, &errstr); 1061 pp->p_fragblock = 1062 DISKLABELV1_FFS_FRAGBLOCK(fsize, v / fsize); 1063 break; 1064 1065 case FS_BSDFFS: 1066 NXTNUM(fsize, fsize, &errstr); 1067 if (fsize == 0) 1068 break; 1069 NXTNUM(v, v, &errstr); 1070 pp->p_fragblock = 1071 DISKLABELV1_FFS_FRAGBLOCK(fsize, v / fsize); 1072 NXTNUM(pp->p_cpg, pp->p_cpg, &errstr); 1073 break; 1074 1075 default: 1076 break; 1077 } 1078 if (mp) 1079 mountpoints[part] = strdup(mp); 1080 continue; 1081 } 1082 warnx("line %d: unknown field: %s", lineno, cp); 1083 errors++; 1084 next: 1085 ; 1086 } 1087 errors += checklabel(lp); 1088 1089 if (errors > 0) 1090 mpcopy(mountpoints, omountpoints); 1091 mpfree(omountpoints); 1092 1093 return (errors > 0); 1094 } 1095 1096 /* 1097 * Check disklabel for errors and fill in 1098 * derived fields according to supplied values. 1099 */ 1100 int 1101 checklabel(struct disklabel *lp) 1102 { 1103 struct partition *pp; 1104 int i, errors = 0; 1105 char part; 1106 1107 if (lp->d_secsize == 0) { 1108 warnx("sector size %d", lp->d_secsize); 1109 return (1); 1110 } 1111 if (lp->d_nsectors == 0) { 1112 warnx("sectors/track %d", lp->d_nsectors); 1113 return (1); 1114 } 1115 if (lp->d_ntracks == 0) { 1116 warnx("tracks/cylinder %d", lp->d_ntracks); 1117 return (1); 1118 } 1119 if (lp->d_ncylinders == 0) { 1120 warnx("cylinders/unit %d", lp->d_ncylinders); 1121 errors++; 1122 } 1123 if (lp->d_secpercyl == 0) 1124 lp->d_secpercyl = lp->d_nsectors * lp->d_ntracks; 1125 if (DL_GETDSIZE(lp) == 0) 1126 DL_SETDSIZE(lp, (u_int64_t)lp->d_secpercyl * lp->d_ncylinders); 1127 if (lp->d_bbsize == 0) { 1128 warnx("boot block size %d", lp->d_bbsize); 1129 errors++; 1130 } else if (lp->d_bbsize % lp->d_secsize) 1131 warnx("warning, boot block size %% sector-size != 0"); 1132 if (lp->d_sbsize == 0) { 1133 warnx("super block size %d", lp->d_sbsize); 1134 errors++; 1135 } else if (lp->d_sbsize % lp->d_secsize) 1136 warnx("warning, super block size %% sector-size != 0"); 1137 if (lp->d_npartitions > MAXPARTITIONS) 1138 warnx("warning, number of partitions (%d) > MAXPARTITIONS (%d)", 1139 lp->d_npartitions, MAXPARTITIONS); 1140 for (i = 0; i < lp->d_npartitions; i++) { 1141 part = 'a' + i; 1142 pp = &lp->d_partitions[i]; 1143 if (DL_GETPSIZE(pp) == 0 && DL_GETPOFFSET(pp) != 0) 1144 warnx("warning, partition %c: size 0, but offset %llu", 1145 part, DL_GETPOFFSET(pp)); 1146 #ifdef SUN_CYLCHECK 1147 if (lp->d_flags & D_VENDOR) { 1148 if (i != RAW_PART && DL_GETPSIZE(pp) % lp->d_secpercyl) 1149 warnx("warning, partition %c: size %% " 1150 "cylinder-size != 0", part); 1151 if (i != RAW_PART && DL_GETPOFFSET(pp) % lp->d_secpercyl) 1152 warnx("warning, partition %c: offset %% " 1153 "cylinder-size != 0", part); 1154 } 1155 #endif 1156 #ifdef SUN_AAT0 1157 if ((lp->d_flags & D_VENDOR) && 1158 i == 0 && DL_GETPSIZE(pp) != 0 && DL_GETPOFFSET(pp) != 0) { 1159 warnx("this architecture requires partition 'a' to " 1160 "start at sector 0"); 1161 errors++; 1162 } 1163 #endif 1164 if (DL_GETPOFFSET(pp) > DL_GETDSIZE(lp)) { 1165 warnx("partition %c: offset past end of unit", part); 1166 errors++; 1167 } 1168 if (DL_GETPOFFSET(pp) + DL_GETPSIZE(pp) > DL_GETDSIZE(lp)) { 1169 warnx("partition %c: partition extends past end of unit", 1170 part); 1171 errors++; 1172 } 1173 #if 0 1174 if (pp->p_frag == 0 && pp->p_fsize != 0) { 1175 warnx("partition %c: block size < fragment size", part); 1176 errors++; 1177 } 1178 #endif 1179 } 1180 for (; i < MAXPARTITIONS; i++) { 1181 part = 'a' + i; 1182 pp = &lp->d_partitions[i]; 1183 if (DL_GETPSIZE(pp) || DL_GETPOFFSET(pp)) 1184 warnx("warning, unused partition %c: size %llu " 1185 "offset %llu", part, DL_GETPSIZE(pp), 1186 DL_GETPOFFSET(pp)); 1187 } 1188 return (errors > 0); 1189 } 1190 1191 int 1192 cmplabel(struct disklabel *lp1, struct disklabel *lp2) 1193 { 1194 struct disklabel lab1 = *lp1; 1195 struct disklabel lab2 = *lp2; 1196 1197 /* We don't compare these fields */ 1198 lab1.d_magic = lab2.d_magic; 1199 lab1.d_magic2 = lab2.d_magic2; 1200 lab1.d_checksum = lab2.d_checksum; 1201 lab1.d_bbsize = lab2.d_bbsize; 1202 lab1.d_sbsize = lab2.d_sbsize; 1203 lab1.d_bstart = lab2.d_bstart; 1204 lab1.d_bstarth = lab2.d_bstarth; 1205 lab1.d_bend = lab2.d_bend; 1206 lab1.d_bendh = lab2.d_bendh; 1207 1208 return (memcmp(&lab1, &lab2, sizeof(struct disklabel))); 1209 } 1210 1211 void 1212 usage(void) 1213 { 1214 fprintf(stderr, 1215 "usage: disklabel [-Acdtv] [-h | -p unit] [-T file] disk\n"); 1216 fprintf(stderr, 1217 " disklabel -w [-Acdnv] [-T file] disk disktype [packid]\n"); 1218 fprintf(stderr, 1219 " disklabel -e [-Acdnv] [-T file] disk\n"); 1220 fprintf(stderr, 1221 " disklabel -E [-Acdnv] [-F|-f file] [-T file] disk\n"); 1222 fprintf(stderr, 1223 " disklabel -R [-nv] [-F|-f file] disk protofile\n"); 1224 1225 exit(1); 1226 } 1227