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