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