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