1 /* $OpenBSD: disklabel.c,v 1.235 2019/06/28 13:32:43 deraadt 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 char **cpp, *cp; 813 const char *errstr; 814 struct partition *pp; 815 char *mp, *tp, *s, line[BUFSIZ]; 816 char **omountpoints = NULL; 817 int lineno = 0, errors = 0; 818 u_int32_t v, fsize; 819 u_int64_t lv; 820 unsigned int part; 821 822 lp->d_version = 1; 823 lp->d_bbsize = BBSIZE; /* XXX */ 824 lp->d_sbsize = SBSIZE; /* XXX */ 825 826 if (!(omountpoints = calloc(MAXPARTITIONS, sizeof(char *)))) 827 errx(4, "out of memory"); 828 829 mpcopy(omountpoints, mountpoints); 830 for (part = 0; part < MAXPARTITIONS; part++) { 831 free(mountpoints[part]); 832 mountpoints[part] = NULL; 833 } 834 835 while (fgets(line, sizeof(line), f)) { 836 lineno++; 837 mp = NULL; 838 if ((cp = strpbrk(line, "\r\n"))) 839 *cp = '\0'; 840 if ((cp = strpbrk(line, "#"))) { 841 *cp = '\0'; 842 mp = skip(cp+1); 843 if (mp && *mp != '/') 844 mp = NULL; 845 } 846 cp = skip(line); 847 if (cp == NULL) 848 continue; 849 tp = strchr(cp, ':'); 850 if (tp == NULL) { 851 warnx("line %d: syntax error", lineno); 852 errors++; 853 continue; 854 } 855 *tp++ = '\0', tp = skip(tp); 856 if (!strcmp(cp, "type")) { 857 if (tp == NULL) 858 tp = "unknown"; 859 else if (strcasecmp(tp, "IDE") == 0) 860 tp = "ESDI"; 861 cpp = dktypenames; 862 for (; cpp < &dktypenames[DKMAXTYPES]; cpp++) 863 if ((s = *cpp) && !strcasecmp(s, tp)) { 864 lp->d_type = cpp - dktypenames; 865 goto next; 866 } 867 v = GETNUM(lp->d_type, tp, 0, &errstr); 868 if (errstr || v >= DKMAXTYPES) 869 warnx("line %d: warning, unknown disk type: %s", 870 lineno, tp); 871 lp->d_type = v; 872 continue; 873 } 874 if (!strcmp(cp, "flags")) { 875 for (v = 0; (cp = tp) && *cp != '\0';) { 876 tp = word(cp); 877 if (!strcmp(cp, "badsect")) 878 v |= D_BADSECT; 879 else if (!strcmp(cp, "vendor")) 880 v |= D_VENDOR; 881 else { 882 warnx("line %d: bad flag: %s", 883 lineno, cp); 884 errors++; 885 } 886 } 887 lp->d_flags = v; 888 continue; 889 } 890 if (!strcmp(cp, "drivedata")) { 891 int i; 892 893 for (i = 0; (cp = tp) && *cp != '\0' && i < NDDATA;) { 894 v = GETNUM(lp->d_drivedata[i], cp, 0, &errstr); 895 if (errstr) 896 warnx("line %d: bad drivedata %s", 897 lineno, cp); 898 lp->d_drivedata[i++] = v; 899 tp = word(cp); 900 } 901 continue; 902 } 903 if (sscanf(cp, "%d partitions", &v) == 1) { 904 if (v == 0 || v > MAXPARTITIONS) { 905 warnx("line %d: bad # of partitions", lineno); 906 lp->d_npartitions = MAXPARTITIONS; 907 errors++; 908 } else 909 lp->d_npartitions = v; 910 continue; 911 } 912 if (tp == NULL) 913 tp = ""; 914 if (!strcmp(cp, "disk")) { 915 strncpy(lp->d_typename, tp, sizeof (lp->d_typename)); 916 continue; 917 } 918 if (!strcmp(cp, "label")) { 919 strncpy(lp->d_packname, tp, sizeof (lp->d_packname)); 920 continue; 921 } 922 if (!strcmp(cp, "duid")) { 923 if (duid_parse(lp, tp) != 0) { 924 warnx("line %d: bad %s: %s", lineno, cp, tp); 925 errors++; 926 } 927 continue; 928 } 929 if (!strcmp(cp, "bytes/sector")) { 930 v = GETNUM(lp->d_secsize, tp, 1, &errstr); 931 if (errstr || (v % 512) != 0) { 932 warnx("line %d: bad %s: %s", lineno, cp, tp); 933 errors++; 934 } else 935 lp->d_secsize = v; 936 continue; 937 } 938 if (!strcmp(cp, "sectors/track")) { 939 v = GETNUM(lp->d_nsectors, tp, 1, &errstr); 940 if (errstr) { 941 warnx("line %d: bad %s: %s", lineno, cp, tp); 942 errors++; 943 } else 944 lp->d_nsectors = v; 945 continue; 946 } 947 if (!strcmp(cp, "sectors/cylinder")) { 948 v = GETNUM(lp->d_secpercyl, tp, 1, &errstr); 949 if (errstr) { 950 warnx("line %d: bad %s: %s", lineno, cp, tp); 951 errors++; 952 } else 953 lp->d_secpercyl = v; 954 continue; 955 } 956 if (!strcmp(cp, "tracks/cylinder")) { 957 v = GETNUM(lp->d_ntracks, tp, 1, &errstr); 958 if (errstr) { 959 warnx("line %d: bad %s: %s", lineno, cp, tp); 960 errors++; 961 } else 962 lp->d_ntracks = v; 963 continue; 964 } 965 if (!strcmp(cp, "cylinders")) { 966 v = GETNUM(lp->d_ncylinders, tp, 1, &errstr); 967 if (errstr) { 968 warnx("line %d: bad %s: %s", lineno, cp, tp); 969 errors++; 970 } else 971 lp->d_ncylinders = v; 972 continue; 973 } 974 975 /* Ignore fields that are no longer in the disklabel. */ 976 if (!strcmp(cp, "rpm") || 977 !strcmp(cp, "interleave") || 978 !strcmp(cp, "trackskew") || 979 !strcmp(cp, "cylinderskew") || 980 !strcmp(cp, "headswitch") || 981 !strcmp(cp, "track-to-track seek")) 982 continue; 983 984 /* Ignore fields that are forcibly set when label is read. */ 985 if (!strcmp(cp, "total sectors") || 986 !strcmp(cp, "boundstart") || 987 !strcmp(cp, "boundend")) 988 continue; 989 990 if ('a' <= *cp && *cp <= 'z' && cp[1] == '\0') { 991 unsigned int part = *cp - 'a'; 992 993 if (part >= lp->d_npartitions) { 994 if (part >= MAXPARTITIONS) { 995 warnx("line %d: bad partition name: %s", 996 lineno, cp); 997 errors++; 998 continue; 999 } else { 1000 lp->d_npartitions = part + 1; 1001 } 1002 } 1003 pp = &lp->d_partitions[part]; 1004 #define NXTNUM(n, field, errstr) { \ 1005 if (tp == NULL) { \ 1006 warnx("line %d: too few fields", lineno); \ 1007 errors++; \ 1008 break; \ 1009 } else \ 1010 cp = tp, tp = word(cp), (n) = GETNUM(field, cp, 0, errstr); \ 1011 } 1012 NXTNUM(lv, lv, &errstr); 1013 if (errstr) { 1014 warnx("line %d: bad partition size: %s", 1015 lineno, cp); 1016 errors++; 1017 } else { 1018 DL_SETPSIZE(pp, lv); 1019 } 1020 NXTNUM(lv, lv, &errstr); 1021 if (errstr) { 1022 warnx("line %d: bad partition offset: %s", 1023 lineno, cp); 1024 errors++; 1025 } else { 1026 DL_SETPOFFSET(pp, lv); 1027 } 1028 if (tp == NULL) { 1029 pp->p_fstype = FS_UNUSED; 1030 goto gottype; 1031 } 1032 cp = tp, tp = word(cp); 1033 cpp = fstypenames; 1034 for (; cpp < &fstypenames[FSMAXTYPES]; cpp++) 1035 if ((s = *cpp) && !strcasecmp(s, cp)) { 1036 pp->p_fstype = cpp - fstypenames; 1037 goto gottype; 1038 } 1039 if (isdigit((unsigned char)*cp)) 1040 v = GETNUM(pp->p_fstype, cp, 0, &errstr); 1041 else 1042 v = FSMAXTYPES; 1043 if (errstr || v >= FSMAXTYPES) { 1044 warnx("line %d: warning, unknown filesystem type: %s", 1045 lineno, cp); 1046 v = FS_UNUSED; 1047 } 1048 pp->p_fstype = v; 1049 gottype: 1050 switch (pp->p_fstype) { 1051 1052 case FS_UNUSED: /* XXX */ 1053 if (tp == NULL) /* ok to skip fsize/bsize */ 1054 break; 1055 NXTNUM(fsize, fsize, &errstr); 1056 if (fsize == 0) 1057 break; 1058 NXTNUM(v, v, &errstr); 1059 pp->p_fragblock = 1060 DISKLABELV1_FFS_FRAGBLOCK(fsize, v / fsize); 1061 break; 1062 1063 case FS_BSDFFS: 1064 NXTNUM(fsize, fsize, &errstr); 1065 if (fsize == 0) 1066 break; 1067 NXTNUM(v, v, &errstr); 1068 pp->p_fragblock = 1069 DISKLABELV1_FFS_FRAGBLOCK(fsize, v / fsize); 1070 NXTNUM(pp->p_cpg, pp->p_cpg, &errstr); 1071 break; 1072 1073 default: 1074 break; 1075 } 1076 if (mp) 1077 mountpoints[part] = strdup(mp); 1078 continue; 1079 } 1080 warnx("line %d: unknown field: %s", lineno, cp); 1081 errors++; 1082 next: 1083 ; 1084 } 1085 errors += checklabel(lp); 1086 1087 if (errors > 0) 1088 mpcopy(mountpoints, omountpoints); 1089 mpfree(omountpoints); 1090 1091 return (errors > 0); 1092 } 1093 1094 /* 1095 * Check disklabel for errors and fill in 1096 * derived fields according to supplied values. 1097 */ 1098 int 1099 checklabel(struct disklabel *lp) 1100 { 1101 struct partition *pp; 1102 int i, errors = 0; 1103 char part; 1104 1105 if (lp->d_secsize == 0) { 1106 warnx("sector size %d", lp->d_secsize); 1107 return (1); 1108 } 1109 if (lp->d_nsectors == 0) { 1110 warnx("sectors/track %d", lp->d_nsectors); 1111 return (1); 1112 } 1113 if (lp->d_ntracks == 0) { 1114 warnx("tracks/cylinder %d", lp->d_ntracks); 1115 return (1); 1116 } 1117 if (lp->d_ncylinders == 0) { 1118 warnx("cylinders/unit %d", lp->d_ncylinders); 1119 errors++; 1120 } 1121 if (lp->d_secpercyl == 0) 1122 lp->d_secpercyl = lp->d_nsectors * lp->d_ntracks; 1123 if (DL_GETDSIZE(lp) == 0) 1124 DL_SETDSIZE(lp, (u_int64_t)lp->d_secpercyl * lp->d_ncylinders); 1125 if (lp->d_bbsize == 0) { 1126 warnx("boot block size %d", lp->d_bbsize); 1127 errors++; 1128 } else if (lp->d_bbsize % lp->d_secsize) 1129 warnx("warning, boot block size %% sector-size != 0"); 1130 if (lp->d_sbsize == 0) { 1131 warnx("super block size %d", lp->d_sbsize); 1132 errors++; 1133 } else if (lp->d_sbsize % lp->d_secsize) 1134 warnx("warning, super block size %% sector-size != 0"); 1135 if (lp->d_npartitions > MAXPARTITIONS) 1136 warnx("warning, number of partitions (%d) > MAXPARTITIONS (%d)", 1137 lp->d_npartitions, MAXPARTITIONS); 1138 for (i = 0; i < lp->d_npartitions; i++) { 1139 part = 'a' + i; 1140 pp = &lp->d_partitions[i]; 1141 if (DL_GETPSIZE(pp) == 0 && DL_GETPOFFSET(pp) != 0) 1142 warnx("warning, partition %c: size 0, but offset %llu", 1143 part, DL_GETPOFFSET(pp)); 1144 #ifdef SUN_CYLCHECK 1145 if (lp->d_flags & D_VENDOR) { 1146 if (i != RAW_PART && DL_GETPSIZE(pp) % lp->d_secpercyl) 1147 warnx("warning, partition %c: size %% " 1148 "cylinder-size != 0", part); 1149 if (i != RAW_PART && DL_GETPOFFSET(pp) % lp->d_secpercyl) 1150 warnx("warning, partition %c: offset %% " 1151 "cylinder-size != 0", part); 1152 } 1153 #endif 1154 #ifdef SUN_AAT0 1155 if ((lp->d_flags & D_VENDOR) && 1156 i == 0 && DL_GETPSIZE(pp) != 0 && DL_GETPOFFSET(pp) != 0) { 1157 warnx("this architecture requires partition 'a' to " 1158 "start at sector 0"); 1159 errors++; 1160 } 1161 #endif 1162 if (DL_GETPOFFSET(pp) > DL_GETDSIZE(lp)) { 1163 warnx("partition %c: offset past end of unit", part); 1164 errors++; 1165 } 1166 if (DL_GETPOFFSET(pp) + DL_GETPSIZE(pp) > DL_GETDSIZE(lp)) { 1167 warnx("partition %c: partition extends past end of unit", 1168 part); 1169 errors++; 1170 } 1171 #if 0 1172 if (pp->p_frag == 0 && pp->p_fsize != 0) { 1173 warnx("partition %c: block size < fragment size", part); 1174 errors++; 1175 } 1176 #endif 1177 } 1178 for (; i < MAXPARTITIONS; i++) { 1179 part = 'a' + i; 1180 pp = &lp->d_partitions[i]; 1181 if (DL_GETPSIZE(pp) || DL_GETPOFFSET(pp)) 1182 warnx("warning, unused partition %c: size %llu " 1183 "offset %llu", part, DL_GETPSIZE(pp), 1184 DL_GETPOFFSET(pp)); 1185 } 1186 return (errors > 0); 1187 } 1188 1189 int 1190 cmplabel(struct disklabel *lp1, struct disklabel *lp2) 1191 { 1192 struct disklabel lab1 = *lp1; 1193 struct disklabel lab2 = *lp2; 1194 1195 /* We don't compare these fields */ 1196 lab1.d_magic = lab2.d_magic; 1197 lab1.d_magic2 = lab2.d_magic2; 1198 lab1.d_checksum = lab2.d_checksum; 1199 lab1.d_bbsize = lab2.d_bbsize; 1200 lab1.d_sbsize = lab2.d_sbsize; 1201 lab1.d_bstart = lab2.d_bstart; 1202 lab1.d_bstarth = lab2.d_bstarth; 1203 lab1.d_bend = lab2.d_bend; 1204 lab1.d_bendh = lab2.d_bendh; 1205 1206 return (memcmp(&lab1, &lab2, sizeof(struct disklabel))); 1207 } 1208 1209 void 1210 usage(void) 1211 { 1212 fprintf(stderr, 1213 "usage: disklabel [-Acdtv] [-h | -p unit] [-T file] disk\t(read)\n"); 1214 fprintf(stderr, 1215 " disklabel -w [-Acdnv] [-T file] disk disktype [packid]\t(write)\n"); 1216 fprintf(stderr, 1217 " disklabel -e [-Acdnv] [-T file] disk\t\t\t(edit)\n"); 1218 fprintf(stderr, 1219 " disklabel -E [-Acdnv] [-F|-f file] [-T file] disk\t(simple editor)" 1220 "\n"); 1221 fprintf(stderr, 1222 " disklabel -R [-nv] [-F|-f file] disk protofile\t\t(restore)\n\n"); 1223 fprintf(stderr, 1224 "`disk' may be of the form: sd0 or /dev/rsd0%c.\n", 'a'+RAW_PART); 1225 fprintf(stderr, 1226 "`disktype' is an entry from %s, see disktab(5) for more info.\n", 1227 DISKTAB); 1228 fprintf(stderr, 1229 "`packid' is an identification string for the device.\n"); 1230 fprintf(stderr, 1231 "`protofile' is the output from the read cmd form; -R is powerful.\n"); 1232 #ifdef SEEALSO 1233 fprintf(stderr, 1234 "For procedures specific to this architecture see: %s\n", SEEALSO); 1235 #endif 1236 exit(1); 1237 } 1238