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