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