bsdlabel.c revision 26542
1/* 2 * Copyright (c) 1987, 1993 3 * The Regents of the University of California. All rights reserved. 4 * 5 * This code is derived from software contributed to Berkeley by 6 * Symmetric Computer Systems. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 3. All advertising materials mentioning features or use of this software 17 * must display the following acknowledgement: 18 * This product includes software developed by the University of 19 * California, Berkeley and its contributors. 20 * 4. Neither the name of the University nor the names of its contributors 21 * may be used to endorse or promote products derived from this software 22 * without specific prior written permission. 23 * 24 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 25 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 27 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 28 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 30 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 34 * SUCH DAMAGE. 35 * 36 * $Id$ 37 */ 38 39#ifndef lint 40static char copyright[] = 41"@(#) Copyright (c) 1987, 1993\n\ 42 The Regents of the University of California. All rights reserved.\n"; 43#endif /* not lint */ 44 45#ifndef lint 46static char sccsid[] = "@(#)disklabel.c 8.2 (Berkeley) 1/7/94"; 47/* from static char sccsid[] = "@(#)disklabel.c 1.2 (Symmetric) 11/28/85"; */ 48#endif /* not lint */ 49 50#include <sys/param.h> 51#include <sys/signal.h> 52#include <sys/errno.h> 53#include <sys/file.h> 54#include <sys/ioctl.h> 55#include <sys/stat.h> 56#include <sys/wait.h> 57#define DKTYPENAMES 58#include <sys/disklabel.h> 59#include <ufs/ffs/fs.h> 60#include <unistd.h> 61#include <string.h> 62#include <stdio.h> 63#include <stdlib.h> 64#include <signal.h> 65#include <stdarg.h> 66#include <ctype.h> 67#include <err.h> 68#include "pathnames.h" 69 70/* 71 * Disklabel: read and write disklabels. 72 * The label is usually placed on one of the first sectors of the disk. 73 * Many machines also place a bootstrap in the same area, 74 * in which case the label is embedded in the bootstrap. 75 * The bootstrap source must leave space at the proper offset 76 * for the label on such machines. 77 */ 78 79#ifdef tahoe 80#define RAWPARTITION 'a' 81#else 82#define RAWPARTITION 'c' 83#endif 84 85#ifndef BBSIZE 86#define BBSIZE 8192 /* size of boot area, with label */ 87#endif 88 89#ifdef tahoe 90#define NUMBOOT 0 91#else 92#if defined(hp300) || defined(hp800) 93#define NUMBOOT 1 94#else 95#define NUMBOOT 2 96#endif 97#endif 98 99void makelabel __P((char *, char *, struct disklabel *)); 100int writelabel __P((int, char *, struct disklabel *)); 101void l_perror __P((char *)); 102struct disklabel * readlabel __P((int)); 103struct disklabel * makebootarea __P((char *, struct disklabel *, int)); 104void display __P((FILE *, struct disklabel *)); 105int edit __P((struct disklabel *, int)); 106int editit __P((void)); 107char * skip __P((char *)); 108char * word __P((char *)); 109int getasciilabel __P((FILE *, struct disklabel *)); 110int checklabel __P((struct disklabel *)); 111void setbootflag __P((struct disklabel *)); 112void Warning (char *, ...); 113void usage __P((void)); 114extern u_short dkcksum __P((struct disklabel *)); 115struct disklabel * getvirginlabel __P((void)); 116 117#define DEFEDITOR _PATH_VI 118#define streq(a,b) (strcmp(a,b) == 0) 119 120char *dkname; 121char *specname; 122char tmpfil[] = _PATH_TMP; 123 124extern int errno; 125char namebuf[BBSIZE], *np = namebuf; 126struct disklabel lab; 127struct disklabel *readlabel(), *makebootarea(); 128char bootarea[BBSIZE]; 129 130#if NUMBOOT > 0 131int installboot; /* non-zero if we should install a boot program */ 132char *bootbuf; /* pointer to buffer with remainder of boot prog */ 133int bootsize; /* size of remaining boot program */ 134char *xxboot; /* primary boot */ 135char *bootxx; /* secondary boot */ 136char boot0[MAXPATHLEN]; 137char boot1[MAXPATHLEN]; 138#endif 139 140enum { 141 UNSPEC, EDIT, NOWRITE, READ, RESTORE, WRITE, WRITEABLE, WRITEBOOT 142} op = UNSPEC; 143 144int rflag; 145 146#ifdef DEBUG 147int debug; 148#define OPTIONS "BNRWb:ders:w" 149#else 150#define OPTIONS "BNRWb:ers:w" 151#endif 152 153int 154main(argc, argv) 155 int argc; 156 char *argv[]; 157{ 158 extern char *optarg; 159 extern int optind; 160 register struct disklabel *lp; 161 FILE *t; 162 int ch, f, flag, error = 0; 163 char *name = 0; 164 165 while ((ch = getopt(argc, argv, OPTIONS)) != -1) 166 switch (ch) { 167#if NUMBOOT > 0 168 case 'B': 169 ++installboot; 170 break; 171 case 'b': 172 xxboot = optarg; 173 break; 174#if NUMBOOT > 1 175 case 's': 176 bootxx = optarg; 177 break; 178#endif 179#endif 180 case 'N': 181 if (op != UNSPEC) 182 usage(); 183 op = NOWRITE; 184 break; 185 case 'R': 186 if (op != UNSPEC) 187 usage(); 188 op = RESTORE; 189 break; 190 case 'W': 191 if (op != UNSPEC) 192 usage(); 193 op = WRITEABLE; 194 break; 195 case 'e': 196 if (op != UNSPEC) 197 usage(); 198 op = EDIT; 199 break; 200 case 'r': 201 ++rflag; 202 break; 203 case 'w': 204 if (op != UNSPEC) 205 usage(); 206 op = WRITE; 207 break; 208#ifdef DEBUG 209 case 'd': 210 debug++; 211 break; 212#endif 213 case '?': 214 default: 215 usage(); 216 } 217 argc -= optind; 218 argv += optind; 219#if NUMBOOT > 0 220 if (installboot) { 221 rflag++; 222 if (op == UNSPEC) 223 op = WRITEBOOT; 224 } else { 225 if (op == UNSPEC) 226 op = READ; 227 xxboot = bootxx = 0; 228 } 229#else 230 if (op == UNSPEC) 231 op = READ; 232#endif 233 if (argc < 1) 234 usage(); 235 236 dkname = argv[0]; 237 if (dkname[0] != '/') { 238 (void)sprintf(np, "%sr%s%c", _PATH_DEV, dkname, RAWPARTITION); 239 specname = np; 240 np += strlen(specname) + 1; 241 } else 242 specname = dkname; 243 f = open(specname, op == READ ? O_RDONLY : O_RDWR); 244 if (f < 0 && errno == ENOENT && dkname[0] != '/') { 245 (void)sprintf(specname, "%sr%s", _PATH_DEV, dkname); 246 np = namebuf + strlen(specname) + 1; 247 f = open(specname, op == READ ? O_RDONLY : O_RDWR); 248 } 249 if (f < 0) 250 err(4, "%s", specname); 251 252 switch(op) { 253 254 case EDIT: 255 if (argc != 1) 256 usage(); 257 lp = readlabel(f); 258 error = edit(lp, f); 259 break; 260 261 case NOWRITE: 262 flag = 0; 263 if (ioctl(f, DIOCWLABEL, (char *)&flag) < 0) 264 err(4, "ioctl DIOCWLABEL"); 265 break; 266 267 case READ: 268 if (argc != 1) 269 usage(); 270 lp = readlabel(f); 271 display(stdout, lp); 272 error = checklabel(lp); 273 break; 274 275 case RESTORE: 276#if NUMBOOT > 0 277 if (installboot && argc == 3) { 278 makelabel(argv[2], 0, &lab); 279 argc--; 280 } 281#endif 282 if (argc != 2) 283 usage(); 284 lp = makebootarea(bootarea, &lab, f); 285 if (!(t = fopen(argv[1], "r"))) 286 err(4, "%s", argv[1]); 287 if (getasciilabel(t, lp)) 288 error = writelabel(f, bootarea, lp); 289 break; 290 291 case WRITE: 292 if (argc == 3) { 293 name = argv[2]; 294 argc--; 295 } 296 if (argc != 2) 297 usage(); 298 makelabel(argv[1], name, &lab); 299 lp = makebootarea(bootarea, &lab, f); 300 *lp = lab; 301 if (checklabel(lp) == 0) 302 error = writelabel(f, bootarea, lp); 303 break; 304 305 case WRITEABLE: 306 flag = 1; 307 if (ioctl(f, DIOCWLABEL, (char *)&flag) < 0) 308 err(4, "ioctl DIOCWLABEL"); 309 break; 310 311#if NUMBOOT > 0 312 case WRITEBOOT: 313 { 314 struct disklabel tlab; 315 316 lp = readlabel(f); 317 tlab = *lp; 318 if (argc == 2) 319 makelabel(argv[1], 0, &lab); 320 lp = makebootarea(bootarea, &lab, f); 321 *lp = tlab; 322 if (checklabel(lp) == 0) 323 error = writelabel(f, bootarea, lp); 324 break; 325 } 326#endif 327 } 328 exit(error); 329} 330 331/* 332 * Construct a prototype disklabel from /etc/disktab. As a side 333 * effect, set the names of the primary and secondary boot files 334 * if specified. 335 */ 336void 337makelabel(type, name, lp) 338 char *type, *name; 339 register struct disklabel *lp; 340{ 341 register struct disklabel *dp; 342 343 if (strcmp(type, "auto") == 0) 344 dp = getvirginlabel(); 345 else 346 dp = getdiskbyname(type); 347 if (dp == NULL) { 348 fprintf(stderr, "%s: unknown disk type\n", type); 349 exit(1); 350 } 351 *lp = *dp; 352#if NUMBOOT > 0 353 /* 354 * Set bootstrap name(s). 355 * 1. If set from command line, use those, 356 * 2. otherwise, check if disktab specifies them (b0 or b1), 357 * 3. otherwise, makebootarea() will choose ones based on the name 358 * of the disk special file. E.g. /dev/ra0 -> raboot, bootra 359 */ 360 if (!xxboot && lp->d_boot0) { 361 if (*lp->d_boot0 != '/') 362 (void)sprintf(boot0, "%s/%s", 363 _PATH_BOOTDIR, lp->d_boot0); 364 else 365 (void)strcpy(boot0, lp->d_boot0); 366 xxboot = boot0; 367 } 368#if NUMBOOT > 1 369 if (!bootxx && lp->d_boot1) { 370 if (*lp->d_boot1 != '/') 371 (void)sprintf(boot1, "%s/%s", 372 _PATH_BOOTDIR, lp->d_boot1); 373 else 374 (void)strcpy(boot1, lp->d_boot1); 375 bootxx = boot1; 376 } 377#endif 378#endif 379 /* d_packname is union d_boot[01], so zero */ 380 bzero(lp->d_packname, sizeof(lp->d_packname)); 381 if (name) 382 (void)strncpy(lp->d_packname, name, sizeof(lp->d_packname)); 383} 384 385int 386writelabel(f, boot, lp) 387 int f; 388 char *boot; 389 register struct disklabel *lp; 390{ 391#ifdef vax 392 register int i; 393#endif 394 int flag; 395 396 setbootflag(lp); 397 lp->d_magic = DISKMAGIC; 398 lp->d_magic2 = DISKMAGIC; 399 lp->d_checksum = 0; 400 lp->d_checksum = dkcksum(lp); 401 if (rflag) { 402 /* 403 * First set the kernel disk label, 404 * then write a label to the raw disk. 405 * If the SDINFO ioctl fails because it is unimplemented, 406 * keep going; otherwise, the kernel consistency checks 407 * may prevent us from changing the current (in-core) 408 * label. 409 */ 410 if (ioctl(f, DIOCSDINFO, lp) < 0 && 411 errno != ENODEV && errno != ENOTTY) { 412 l_perror("ioctl DIOCSDINFO"); 413 return (1); 414 } 415 (void)lseek(f, (off_t)0, SEEK_SET); 416 /* 417 * write enable label sector before write (if necessary), 418 * disable after writing. 419 */ 420 flag = 1; 421 if (ioctl(f, DIOCWLABEL, &flag) < 0) 422 perror("ioctl DIOCWLABEL"); 423 if (write(f, boot, lp->d_bbsize) != lp->d_bbsize) { 424 perror("write"); 425 return (1); 426 } 427#if NUMBOOT > 0 428 /* 429 * Output the remainder of the disklabel 430 */ 431 if (bootbuf && write(f, bootbuf, bootsize) != bootsize) { 432 perror("write"); 433 return(1); 434 } 435#endif 436 flag = 0; 437 (void) ioctl(f, DIOCWLABEL, &flag); 438 } else if (ioctl(f, DIOCWDINFO, lp) < 0) { 439 l_perror("ioctl DIOCWDINFO"); 440 return (1); 441 } 442#ifdef vax 443 if (lp->d_type == DTYPE_SMD && lp->d_flags & D_BADSECT) { 444 daddr_t alt; 445 446 alt = lp->d_ncylinders * lp->d_secpercyl - lp->d_nsectors; 447 for (i = 1; i < 11 && i < lp->d_nsectors; i += 2) { 448 (void)lseek(f, (off_t)((alt + i) * lp->d_secsize), 449 SEEK_SET); 450 if (write(f, boot, lp->d_secsize) < lp->d_secsize) { 451 int oerrno = errno; 452 fprintf(stderr, "alternate label %d ", i/2); 453 errno = oerrno; 454 perror("write"); 455 } 456 } 457 } 458#endif 459 return (0); 460} 461 462void 463l_perror(s) 464 char *s; 465{ 466 int saverrno = errno; 467 468 fprintf(stderr, "disklabel: %s: ", s); 469 470 switch (saverrno) { 471 472 case ESRCH: 473 fprintf(stderr, "No disk label on disk;\n"); 474 fprintf(stderr, 475 "use \"disklabel -r\" to install initial label\n"); 476 break; 477 478 case EINVAL: 479 fprintf(stderr, "Label magic number or checksum is wrong!\n"); 480 fprintf(stderr, "(disklabel or kernel is out of date?)\n"); 481 break; 482 483 case EBUSY: 484 fprintf(stderr, "Open partition would move or shrink\n"); 485 break; 486 487 case EXDEV: 488 fprintf(stderr, 489 "Labeled partition or 'a' partition must start at beginning of disk\n"); 490 break; 491 492 default: 493 errno = saverrno; 494 perror((char *)NULL); 495 break; 496 } 497} 498 499/* 500 * Fetch disklabel for disk. 501 * Use ioctl to get label unless -r flag is given. 502 */ 503struct disklabel * 504readlabel(f) 505 int f; 506{ 507 register struct disklabel *lp; 508 509 if (rflag) { 510 if (read(f, bootarea, BBSIZE) < BBSIZE) 511 err(4, "%s", specname); 512 for (lp = (struct disklabel *)bootarea; 513 lp <= (struct disklabel *)(bootarea + BBSIZE - sizeof(*lp)); 514 lp = (struct disklabel *)((char *)lp + 16)) 515 if (lp->d_magic == DISKMAGIC && 516 lp->d_magic2 == DISKMAGIC) 517 break; 518 if (lp > (struct disklabel *)(bootarea+BBSIZE-sizeof(*lp)) || 519 lp->d_magic != DISKMAGIC || lp->d_magic2 != DISKMAGIC || 520 dkcksum(lp) != 0) { 521 fprintf(stderr, 522 "Bad pack magic number (label is damaged, or pack is unlabeled)\n"); 523 /* lp = (struct disklabel *)(bootarea + LABELOFFSET); */ 524 exit (1); 525 } 526 } else { 527 lp = &lab; 528 if (ioctl(f, DIOCGDINFO, lp) < 0) 529 err(4, "ioctl DIOCGDINFO"); 530 } 531 return (lp); 532} 533 534/* 535 * Construct a bootarea (d_bbsize bytes) in the specified buffer ``boot'' 536 * Returns a pointer to the disklabel portion of the bootarea. 537 */ 538struct disklabel * 539makebootarea(boot, dp, f) 540 char *boot; 541 register struct disklabel *dp; 542 int f; 543{ 544 struct disklabel *lp; 545 register char *p; 546 int b; 547#if NUMBOOT > 0 548 char *dkbasename; 549#if NUMBOOT == 1 550 struct stat sb; 551#endif 552#ifdef __i386__ 553 char *tmpbuf; 554 int i, found; 555#endif /* i386 */ 556#endif 557 558 /* XXX */ 559 if (dp->d_secsize == 0) { 560 dp->d_secsize = DEV_BSIZE; 561 dp->d_bbsize = BBSIZE; 562 } 563 lp = (struct disklabel *) 564 (boot + (LABELSECTOR * dp->d_secsize) + LABELOFFSET); 565 bzero((char *)lp, sizeof *lp); 566#if NUMBOOT > 0 567 /* 568 * If we are not installing a boot program but we are installing a 569 * label on disk then we must read the current bootarea so we don't 570 * clobber the existing boot. 571 */ 572 if (!installboot) { 573 if (rflag) { 574 if (read(f, boot, BBSIZE) < BBSIZE) 575 err(4, "%s", specname); 576 bzero((char *)lp, sizeof *lp); 577 } 578 return (lp); 579 } 580 /* 581 * We are installing a boot program. Determine the name(s) and 582 * read them into the appropriate places in the boot area. 583 */ 584 if (!xxboot || !bootxx) { 585 dkbasename = np; 586 if ((p = rindex(dkname, '/')) == NULL) 587 p = dkname; 588 else 589 p++; 590 while (*p && !isdigit(*p)) 591 *np++ = *p++; 592 *np++ = '\0'; 593 594 if (!xxboot) { 595 (void)sprintf(np, "%s/%sboot", 596 _PATH_BOOTDIR, dkbasename); 597 if (access(np, F_OK) < 0 && dkbasename[0] == 'r') 598 dkbasename++; 599 xxboot = np; 600 (void)sprintf(xxboot, "%s/%sboot", 601 _PATH_BOOTDIR, dkbasename); 602 np += strlen(xxboot) + 1; 603 } 604#if NUMBOOT > 1 605 if (!bootxx) { 606 (void)sprintf(np, "%s/boot%s", 607 _PATH_BOOTDIR, dkbasename); 608 if (access(np, F_OK) < 0 && dkbasename[0] == 'r') 609 dkbasename++; 610 bootxx = np; 611 (void)sprintf(bootxx, "%s/boot%s", 612 _PATH_BOOTDIR, dkbasename); 613 np += strlen(bootxx) + 1; 614 } 615#endif 616 } 617#ifdef DEBUG 618 if (debug) 619 fprintf(stderr, "bootstraps: xxboot = %s, bootxx = %s\n", 620 xxboot, bootxx ? bootxx : "NONE"); 621#endif 622 623 /* 624 * Strange rules: 625 * 1. One-piece bootstrap (hp300/hp800) 626 * up to d_bbsize bytes of ``xxboot'' go in bootarea, the rest 627 * is remembered and written later following the bootarea. 628 * 2. Two-piece bootstraps (vax/i386?/mips?) 629 * up to d_secsize bytes of ``xxboot'' go in first d_secsize 630 * bytes of bootarea, remaining d_bbsize-d_secsize filled 631 * from ``bootxx''. 632 */ 633 b = open(xxboot, O_RDONLY); 634 if (b < 0) 635 err(4, "%s", xxboot); 636#if NUMBOOT > 1 637#ifdef __i386__ 638 /* 639 * XXX Botch alert. 640 * The i386 has the so-called fdisk table embedded into the 641 * primary bootstrap. We take care to not clobber it, but 642 * only if it does already contain some data. (Otherwise, 643 * the xxboot provides a template.) 644 */ 645 if ((tmpbuf = (char *)malloc((int)dp->d_secsize)) == 0) 646 err(4, "%s", xxboot); 647 memcpy((void *)tmpbuf, (void *)boot, (int)dp->d_secsize); 648#endif /* i386 */ 649 if (read(b, boot, (int)dp->d_secsize) < 0) 650 err(4, "%s", xxboot); 651 (void)close(b); 652#ifdef __i386__ 653 for (i = DOSPARTOFF, found = 0; 654 !found && i < DOSPARTOFF + NDOSPART*sizeof(struct dos_partition); 655 i++) 656 found = tmpbuf[i] != 0; 657 if (found) 658 memcpy((void *)&boot[DOSPARTOFF], 659 (void *)&tmpbuf[DOSPARTOFF], 660 NDOSPART * sizeof(struct dos_partition)); 661 free(tmpbuf); 662#endif /* i386 */ 663 b = open(bootxx, O_RDONLY); 664 if (b < 0) 665 err(4, "%s", bootxx); 666 if (read(b, &boot[dp->d_secsize], 667 (int)(dp->d_bbsize-dp->d_secsize)) < 0) 668 err(4, "%s", bootxx); 669#else 670 if (read(b, boot, (int)dp->d_bbsize) < 0) 671 err(4, "%s", xxboot); 672 (void)fstat(b, &sb); 673 bootsize = (int)sb.st_size - dp->d_bbsize; 674 if (bootsize > 0) { 675 /* XXX assume d_secsize is a power of two */ 676 bootsize = (bootsize + dp->d_secsize-1) & ~(dp->d_secsize-1); 677 bootbuf = (char *)malloc((size_t)bootsize); 678 if (bootbuf == 0) 679 err(4, "%s", xxboot); 680 if (read(b, bootbuf, bootsize) < 0) { 681 free(bootbuf); 682 err(4, "%s", xxboot); 683 } 684 } 685#endif 686 (void)close(b); 687#endif 688 /* 689 * Make sure no part of the bootstrap is written in the area 690 * reserved for the label. 691 */ 692 for (p = (char *)lp; p < (char *)lp + sizeof(struct disklabel); p++) 693 if (*p) { 694 fprintf(stderr, 695 "Bootstrap doesn't leave room for disk label\n"); 696 exit(2); 697 } 698 return (lp); 699} 700 701void 702display(f, lp) 703 FILE *f; 704 register struct disklabel *lp; 705{ 706 register int i, j; 707 register struct partition *pp; 708 709 fprintf(f, "# %s:\n", specname); 710 if ((unsigned) lp->d_type < DKMAXTYPES) 711 fprintf(f, "type: %s\n", dktypenames[lp->d_type]); 712 else 713 fprintf(f, "type: %d\n", lp->d_type); 714 fprintf(f, "disk: %.*s\n", (int)sizeof(lp->d_typename), 715 lp->d_typename); 716 fprintf(f, "label: %.*s\n", (int)sizeof(lp->d_packname), 717 lp->d_packname); 718 fprintf(f, "flags:"); 719 if (lp->d_flags & D_REMOVABLE) 720 fprintf(f, " removeable"); 721 if (lp->d_flags & D_ECC) 722 fprintf(f, " ecc"); 723 if (lp->d_flags & D_BADSECT) 724 fprintf(f, " badsect"); 725 fprintf(f, "\n"); 726 fprintf(f, "bytes/sector: %ld\n", lp->d_secsize); 727 fprintf(f, "sectors/track: %ld\n", lp->d_nsectors); 728 fprintf(f, "tracks/cylinder: %ld\n", lp->d_ntracks); 729 fprintf(f, "sectors/cylinder: %ld\n", lp->d_secpercyl); 730 fprintf(f, "cylinders: %ld\n", lp->d_ncylinders); 731 fprintf(f, "sectors/unit: %ld\n", lp->d_secperunit); 732 fprintf(f, "rpm: %d\n", lp->d_rpm); 733 fprintf(f, "interleave: %d\n", lp->d_interleave); 734 fprintf(f, "trackskew: %d\n", lp->d_trackskew); 735 fprintf(f, "cylinderskew: %d\n", lp->d_cylskew); 736 fprintf(f, "headswitch: %ld\t\t# milliseconds\n", lp->d_headswitch); 737 fprintf(f, "track-to-track seek: %ld\t# milliseconds\n", 738 lp->d_trkseek); 739 fprintf(f, "drivedata: "); 740 for (i = NDDATA - 1; i >= 0; i--) 741 if (lp->d_drivedata[i]) 742 break; 743 if (i < 0) 744 i = 0; 745 for (j = 0; j <= i; j++) 746 fprintf(f, "%ld ", lp->d_drivedata[j]); 747 fprintf(f, "\n\n%d partitions:\n", lp->d_npartitions); 748 fprintf(f, 749 "# size offset fstype [fsize bsize bps/cpg]\n"); 750 pp = lp->d_partitions; 751 for (i = 0; i < lp->d_npartitions; i++, pp++) { 752 if (pp->p_size) { 753 fprintf(f, " %c: %8ld %8ld ", 'a' + i, 754 pp->p_size, pp->p_offset); 755 if ((unsigned) pp->p_fstype < FSMAXTYPES) 756 fprintf(f, "%8.8s", fstypenames[pp->p_fstype]); 757 else 758 fprintf(f, "%8d", pp->p_fstype); 759 switch (pp->p_fstype) { 760 761 case FS_UNUSED: /* XXX */ 762 fprintf(f, " %5ld %5ld %5.5s ", 763 pp->p_fsize, pp->p_fsize * pp->p_frag, ""); 764 break; 765 766 case FS_BSDFFS: 767 fprintf(f, " %5ld %5ld %5d ", 768 pp->p_fsize, pp->p_fsize * pp->p_frag, 769 pp->p_cpg); 770 break; 771 772 case FS_BSDLFS: 773 fprintf(f, " %5ld %5ld %5d", 774 pp->p_fsize, pp->p_fsize * pp->p_frag, 775 pp->p_cpg); 776 break; 777 778 default: 779 fprintf(f, "%20.20s", ""); 780 break; 781 } 782 fprintf(f, "\t# (Cyl. %4ld", 783 pp->p_offset / lp->d_secpercyl); 784 if (pp->p_offset % lp->d_secpercyl) 785 putc('*', f); 786 else 787 putc(' ', f); 788 fprintf(f, "- %ld", 789 (pp->p_offset + 790 pp->p_size + lp->d_secpercyl - 1) / 791 lp->d_secpercyl - 1); 792 if (pp->p_size % lp->d_secpercyl) 793 putc('*', f); 794 fprintf(f, ")\n"); 795 } 796 } 797 fflush(f); 798} 799 800int 801edit(lp, f) 802 struct disklabel *lp; 803 int f; 804{ 805 register int c, fd; 806 struct disklabel label; 807 FILE *fp; 808 809 if ((fd = mkstemp(tmpfil)) == -1 || 810 (fp = fdopen(fd, "w")) == NULL) { 811 fprintf(stderr, "%s: Can't create\n", tmpfil); 812 return (1); 813 } 814 display(fp, lp); 815 fclose(fp); 816 for (;;) { 817 if (!editit()) 818 break; 819 fp = fopen(tmpfil, "r"); 820 if (fp == NULL) { 821 fprintf(stderr, "%s: Can't reopen for reading\n", 822 tmpfil); 823 break; 824 } 825 bzero((char *)&label, sizeof(label)); 826 if (getasciilabel(fp, &label)) { 827 *lp = label; 828 if (writelabel(f, bootarea, lp) == 0) { 829 fclose(fp); 830 (void) unlink(tmpfil); 831 return (0); 832 } 833 } 834 fclose(fp); 835 printf("re-edit the label? [y]: "); fflush(stdout); 836 c = getchar(); 837 if (c != EOF && c != (int)'\n') 838 while (getchar() != (int)'\n') 839 ; 840 if (c == (int)'n') 841 break; 842 } 843 (void) unlink(tmpfil); 844 return (1); 845} 846 847int 848editit() 849{ 850 register int pid, xpid; 851 int stat, omask; 852 extern char *getenv(); 853 854 omask = sigblock(sigmask(SIGINT)|sigmask(SIGQUIT)|sigmask(SIGHUP)); 855 while ((pid = fork()) < 0) { 856 extern int errno; 857 858 if (errno == EPROCLIM) { 859 fprintf(stderr, "You have too many processes\n"); 860 return(0); 861 } 862 if (errno != EAGAIN) { 863 perror("fork"); 864 return(0); 865 } 866 sleep(1); 867 } 868 if (pid == 0) { 869 register char *ed; 870 871 sigsetmask(omask); 872 setgid(getgid()); 873 setuid(getuid()); 874 if ((ed = getenv("EDITOR")) == (char *)0) 875 ed = DEFEDITOR; 876 execlp(ed, ed, tmpfil, 0); 877 perror(ed); 878 exit(1); 879 } 880 while ((xpid = wait(&stat)) >= 0) 881 if (xpid == pid) 882 break; 883 sigsetmask(omask); 884 return(!stat); 885} 886 887char * 888skip(cp) 889 register char *cp; 890{ 891 892 while (*cp != '\0' && isspace(*cp)) 893 cp++; 894 if (*cp == '\0' || *cp == '#') 895 return ((char *)NULL); 896 return (cp); 897} 898 899char * 900word(cp) 901 register char *cp; 902{ 903 register char c; 904 905 while (*cp != '\0' && !isspace(*cp) && *cp != '#') 906 cp++; 907 if ((c = *cp) != '\0') { 908 *cp++ = '\0'; 909 if (c != '#') 910 return (skip(cp)); 911 } 912 return ((char *)NULL); 913} 914 915/* 916 * Read an ascii label in from fd f, 917 * in the same format as that put out by display(), 918 * and fill in lp. 919 */ 920int 921getasciilabel(f, lp) 922 FILE *f; 923 register struct disklabel *lp; 924{ 925 register char **cpp, *cp; 926 register struct partition *pp; 927 char *tp, *s, line[BUFSIZ]; 928 int v, lineno = 0, errors = 0; 929 930 lp->d_bbsize = BBSIZE; /* XXX */ 931 lp->d_sbsize = SBSIZE; /* XXX */ 932 while (fgets(line, sizeof(line) - 1, f)) { 933 lineno++; 934 if ((cp = index(line,'\n')) != 0) 935 *cp = '\0'; 936 cp = skip(line); 937 if (cp == NULL) 938 continue; 939 tp = index(cp, ':'); 940 if (tp == NULL) { 941 fprintf(stderr, "line %d: syntax error\n", lineno); 942 errors++; 943 continue; 944 } 945 *tp++ = '\0', tp = skip(tp); 946 if (streq(cp, "type")) { 947 if (tp == NULL) 948 tp = "unknown"; 949 cpp = dktypenames; 950 for (; cpp < &dktypenames[DKMAXTYPES]; cpp++) 951 if ((s = *cpp) && streq(s, tp)) { 952 lp->d_type = cpp - dktypenames; 953 goto next; 954 } 955 v = atoi(tp); 956 if ((unsigned)v >= DKMAXTYPES) 957 fprintf(stderr, "line %d:%s %d\n", lineno, 958 "Warning, unknown disk type", v); 959 lp->d_type = v; 960 continue; 961 } 962 if (streq(cp, "flags")) { 963 for (v = 0; (cp = tp) && *cp != '\0';) { 964 tp = word(cp); 965 if (streq(cp, "removeable")) 966 v |= D_REMOVABLE; 967 else if (streq(cp, "ecc")) 968 v |= D_ECC; 969 else if (streq(cp, "badsect")) 970 v |= D_BADSECT; 971 else { 972 fprintf(stderr, 973 "line %d: %s: bad flag\n", 974 lineno, cp); 975 errors++; 976 } 977 } 978 lp->d_flags = v; 979 continue; 980 } 981 if (streq(cp, "drivedata")) { 982 register int i; 983 984 for (i = 0; (cp = tp) && *cp != '\0' && i < NDDATA;) { 985 lp->d_drivedata[i++] = atoi(cp); 986 tp = word(cp); 987 } 988 continue; 989 } 990 if (sscanf(cp, "%d partitions", &v) == 1) { 991 if (v == 0 || (unsigned)v > MAXPARTITIONS) { 992 fprintf(stderr, 993 "line %d: bad # of partitions\n", lineno); 994 lp->d_npartitions = MAXPARTITIONS; 995 errors++; 996 } else 997 lp->d_npartitions = v; 998 continue; 999 } 1000 if (tp == NULL) 1001 tp = ""; 1002 if (streq(cp, "disk")) { 1003 strncpy(lp->d_typename, tp, sizeof (lp->d_typename)); 1004 continue; 1005 } 1006 if (streq(cp, "label")) { 1007 strncpy(lp->d_packname, tp, sizeof (lp->d_packname)); 1008 continue; 1009 } 1010 if (streq(cp, "bytes/sector")) { 1011 v = atoi(tp); 1012 if (v <= 0 || (v % 512) != 0) { 1013 fprintf(stderr, 1014 "line %d: %s: bad sector size\n", 1015 lineno, tp); 1016 errors++; 1017 } else 1018 lp->d_secsize = v; 1019 continue; 1020 } 1021 if (streq(cp, "sectors/track")) { 1022 v = atoi(tp); 1023 if (v <= 0) { 1024 fprintf(stderr, "line %d: %s: bad %s\n", 1025 lineno, tp, cp); 1026 errors++; 1027 } else 1028 lp->d_nsectors = v; 1029 continue; 1030 } 1031 if (streq(cp, "sectors/cylinder")) { 1032 v = atoi(tp); 1033 if (v <= 0) { 1034 fprintf(stderr, "line %d: %s: bad %s\n", 1035 lineno, tp, cp); 1036 errors++; 1037 } else 1038 lp->d_secpercyl = v; 1039 continue; 1040 } 1041 if (streq(cp, "tracks/cylinder")) { 1042 v = atoi(tp); 1043 if (v <= 0) { 1044 fprintf(stderr, "line %d: %s: bad %s\n", 1045 lineno, tp, cp); 1046 errors++; 1047 } else 1048 lp->d_ntracks = v; 1049 continue; 1050 } 1051 if (streq(cp, "cylinders")) { 1052 v = atoi(tp); 1053 if (v <= 0) { 1054 fprintf(stderr, "line %d: %s: bad %s\n", 1055 lineno, tp, cp); 1056 errors++; 1057 } else 1058 lp->d_ncylinders = v; 1059 continue; 1060 } 1061 if (streq(cp, "sectors/unit")) { 1062 v = atoi(tp); 1063 if (v <= 0) { 1064 fprintf(stderr, "line %d: %s: bad %s\n", 1065 lineno, tp, cp); 1066 errors++; 1067 } else 1068 lp->d_secperunit = v; 1069 continue; 1070 } 1071 if (streq(cp, "rpm")) { 1072 v = atoi(tp); 1073 if (v <= 0) { 1074 fprintf(stderr, "line %d: %s: bad %s\n", 1075 lineno, tp, cp); 1076 errors++; 1077 } else 1078 lp->d_rpm = v; 1079 continue; 1080 } 1081 if (streq(cp, "interleave")) { 1082 v = atoi(tp); 1083 if (v <= 0) { 1084 fprintf(stderr, "line %d: %s: bad %s\n", 1085 lineno, tp, cp); 1086 errors++; 1087 } else 1088 lp->d_interleave = v; 1089 continue; 1090 } 1091 if (streq(cp, "trackskew")) { 1092 v = atoi(tp); 1093 if (v < 0) { 1094 fprintf(stderr, "line %d: %s: bad %s\n", 1095 lineno, tp, cp); 1096 errors++; 1097 } else 1098 lp->d_trackskew = v; 1099 continue; 1100 } 1101 if (streq(cp, "cylinderskew")) { 1102 v = atoi(tp); 1103 if (v < 0) { 1104 fprintf(stderr, "line %d: %s: bad %s\n", 1105 lineno, tp, cp); 1106 errors++; 1107 } else 1108 lp->d_cylskew = v; 1109 continue; 1110 } 1111 if (streq(cp, "headswitch")) { 1112 v = atoi(tp); 1113 if (v < 0) { 1114 fprintf(stderr, "line %d: %s: bad %s\n", 1115 lineno, tp, cp); 1116 errors++; 1117 } else 1118 lp->d_headswitch = v; 1119 continue; 1120 } 1121 if (streq(cp, "track-to-track seek")) { 1122 v = atoi(tp); 1123 if (v < 0) { 1124 fprintf(stderr, "line %d: %s: bad %s\n", 1125 lineno, tp, cp); 1126 errors++; 1127 } else 1128 lp->d_trkseek = v; 1129 continue; 1130 } 1131 if ('a' <= *cp && *cp <= 'z' && cp[1] == '\0') { 1132 unsigned part = *cp - 'a'; 1133 1134 if (part > lp->d_npartitions) { 1135 fprintf(stderr, 1136 "line %d: bad partition name\n", lineno); 1137 errors++; 1138 continue; 1139 } 1140 pp = &lp->d_partitions[part]; 1141#define NXTNUM(n) { \ 1142 if (tp == NULL) { \ 1143 fprintf(stderr, "line %d: too few numeric fields\n", lineno); \ 1144 errors++; \ 1145 break; \ 1146 } else { \ 1147 cp = tp, tp = word(cp); \ 1148 if (tp == NULL) \ 1149 tp = cp; \ 1150 (n) = atoi(cp); \ 1151 } \ 1152 } 1153 1154 NXTNUM(v); 1155 if (v < 0) { 1156 fprintf(stderr, 1157 "line %d: %s: bad partition size\n", 1158 lineno, cp); 1159 errors++; 1160 } else 1161 pp->p_size = v; 1162 NXTNUM(v); 1163 if (v < 0) { 1164 fprintf(stderr, 1165 "line %d: %s: bad partition offset\n", 1166 lineno, cp); 1167 errors++; 1168 } else 1169 pp->p_offset = v; 1170 cp = tp, tp = word(cp); 1171 cpp = fstypenames; 1172 for (; cpp < &fstypenames[FSMAXTYPES]; cpp++) 1173 if ((s = *cpp) && streq(s, cp)) { 1174 pp->p_fstype = cpp - fstypenames; 1175 goto gottype; 1176 } 1177 if (isdigit(*cp)) 1178 v = atoi(cp); 1179 else 1180 v = FSMAXTYPES; 1181 if ((unsigned)v >= FSMAXTYPES) { 1182 fprintf(stderr, "line %d: %s %s\n", lineno, 1183 "Warning, unknown filesystem type", cp); 1184 v = FS_UNUSED; 1185 } 1186 pp->p_fstype = v; 1187 gottype: 1188 1189 switch (pp->p_fstype) { 1190 1191 case FS_UNUSED: /* XXX */ 1192 NXTNUM(pp->p_fsize); 1193 if (pp->p_fsize == 0) 1194 break; 1195 NXTNUM(v); 1196 pp->p_frag = v / pp->p_fsize; 1197 break; 1198 1199 case FS_BSDFFS: 1200 NXTNUM(pp->p_fsize); 1201 if (pp->p_fsize == 0) 1202 break; 1203 NXTNUM(v); 1204 pp->p_frag = v / pp->p_fsize; 1205 NXTNUM(pp->p_cpg); 1206 break; 1207 1208 case FS_BSDLFS: 1209 NXTNUM(pp->p_fsize); 1210 if (pp->p_fsize == 0) 1211 break; 1212 NXTNUM(v); 1213 pp->p_frag = v / pp->p_fsize; 1214 NXTNUM(pp->p_cpg); 1215 break; 1216 1217 default: 1218 break; 1219 } 1220 continue; 1221 } 1222 fprintf(stderr, "line %d: %s: Unknown disklabel field\n", 1223 lineno, cp); 1224 errors++; 1225 next: 1226 ; 1227 } 1228 errors += checklabel(lp); 1229 return (errors == 0); 1230} 1231 1232/* 1233 * Check disklabel for errors and fill in 1234 * derived fields according to supplied values. 1235 */ 1236int 1237checklabel(lp) 1238 register struct disklabel *lp; 1239{ 1240 register struct partition *pp; 1241 int i, errors = 0; 1242 char part; 1243 1244 if (lp->d_secsize == 0) { 1245 fprintf(stderr, "sector size %ld\n", lp->d_secsize); 1246 return (1); 1247 } 1248 if (lp->d_nsectors == 0) { 1249 fprintf(stderr, "sectors/track %ld\n", lp->d_nsectors); 1250 return (1); 1251 } 1252 if (lp->d_ntracks == 0) { 1253 fprintf(stderr, "tracks/cylinder %ld\n", lp->d_ntracks); 1254 return (1); 1255 } 1256 if (lp->d_ncylinders == 0) { 1257 fprintf(stderr, "cylinders/unit %ld\n", lp->d_ncylinders); 1258 errors++; 1259 } 1260 if (lp->d_rpm == 0) 1261 Warning("revolutions/minute %d", lp->d_rpm); 1262 if (lp->d_secpercyl == 0) 1263 lp->d_secpercyl = lp->d_nsectors * lp->d_ntracks; 1264 if (lp->d_secperunit == 0) 1265 lp->d_secperunit = lp->d_secpercyl * lp->d_ncylinders; 1266 if (lp->d_bbsize == 0) { 1267 fprintf(stderr, "boot block size %ld\n", lp->d_bbsize); 1268 errors++; 1269 } else if (lp->d_bbsize % lp->d_secsize) 1270 Warning("boot block size %% sector-size != 0"); 1271 if (lp->d_sbsize == 0) { 1272 fprintf(stderr, "super block size %ld\n", lp->d_sbsize); 1273 errors++; 1274 } else if (lp->d_sbsize % lp->d_secsize) 1275 Warning("super block size %% sector-size != 0"); 1276 if (lp->d_npartitions > MAXPARTITIONS) 1277 Warning("number of partitions (%d) > MAXPARTITIONS (%d)", 1278 lp->d_npartitions, MAXPARTITIONS); 1279 for (i = 0; i < lp->d_npartitions; i++) { 1280 part = 'a' + i; 1281 pp = &lp->d_partitions[i]; 1282 if (pp->p_size == 0 && pp->p_offset != 0) 1283 Warning("partition %c: size 0, but offset %d", 1284 part, pp->p_offset); 1285#ifdef notdef 1286 if (pp->p_size % lp->d_secpercyl) 1287 Warning("partition %c: size %% cylinder-size != 0", 1288 part); 1289 if (pp->p_offset % lp->d_secpercyl) 1290 Warning("partition %c: offset %% cylinder-size != 0", 1291 part); 1292#endif 1293 if (pp->p_offset > lp->d_secperunit) { 1294 fprintf(stderr, 1295 "partition %c: offset past end of unit\n", part); 1296 errors++; 1297 } 1298 if (pp->p_offset + pp->p_size > lp->d_secperunit) { 1299 fprintf(stderr, 1300 "partition %c: partition extends past end of unit\n", 1301 part); 1302 errors++; 1303 } 1304 } 1305 for (; i < MAXPARTITIONS; i++) { 1306 part = 'a' + i; 1307 pp = &lp->d_partitions[i]; 1308 if (pp->p_size || pp->p_offset) 1309 Warning("unused partition %c: size %d offset %d", 1310 'a' + i, pp->p_size, pp->p_offset); 1311 } 1312 return (errors); 1313} 1314 1315/* 1316 * When operating on a "virgin" disk, try getting an initial label 1317 * from the associated device driver. This might work for all device 1318 * drivers that are able to fetch some initial device parameters 1319 * without even having access to a (BSD) disklabel, like SCSI disks, 1320 * most IDE drives, or vn devices. 1321 * 1322 * The device name must be given in its "canonical" form. 1323 */ 1324struct disklabel * 1325getvirginlabel(void) 1326{ 1327 static struct disklabel lab; 1328 char namebuf[BBSIZE]; 1329 int f; 1330 1331 if (dkname[0] == '/') { 1332 fprintf(stderr, 1333 "\"auto\" requires the usage of a canonical disk name.\n"); 1334 return (NULL); 1335 } 1336 (void)snprintf(namebuf, BBSIZE, "%sr%s", _PATH_DEV, dkname); 1337 if ((f = open(namebuf, O_RDONLY, 0)) == -1) { 1338 err(4, "open()"); 1339 return (NULL); 1340 } 1341 if (ioctl(f, DIOCGDINFO, &lab) < 0) { 1342 err(4, "ioctl DIOCGDINFO"); 1343 close(f); 1344 return (NULL); 1345 } 1346 close(f); 1347 return (&lab); 1348} 1349 1350 1351/* 1352 * If we are installing a boot program that doesn't fit in d_bbsize 1353 * we need to mark those partitions that the boot overflows into. 1354 * This allows newfs to prevent creation of a filesystem where it might 1355 * clobber bootstrap code. 1356 */ 1357void 1358setbootflag(lp) 1359 register struct disklabel *lp; 1360{ 1361 register struct partition *pp; 1362 int i, errors = 0; 1363 char part; 1364 u_long boffset; 1365 1366 if (bootbuf == 0) 1367 return; 1368 boffset = bootsize / lp->d_secsize; 1369 for (i = 0; i < lp->d_npartitions; i++) { 1370 part = 'a' + i; 1371 pp = &lp->d_partitions[i]; 1372 if (pp->p_size == 0) 1373 continue; 1374 if (boffset <= pp->p_offset) { 1375 if (pp->p_fstype == FS_BOOT) 1376 pp->p_fstype = FS_UNUSED; 1377 } else if (pp->p_fstype != FS_BOOT) { 1378 if (pp->p_fstype != FS_UNUSED) { 1379 fprintf(stderr, 1380 "boot overlaps used partition %c\n", 1381 part); 1382 errors++; 1383 } else { 1384 pp->p_fstype = FS_BOOT; 1385 Warning("boot overlaps partition %c, %s", 1386 part, "marked as FS_BOOT"); 1387 } 1388 } 1389 } 1390 if (errors) { 1391 fprintf(stderr, "Cannot install boot program\n"); 1392 exit(4); 1393 } 1394} 1395 1396/*VARARGS1*/ 1397void 1398Warning(char *fmt, ...) 1399{ 1400 va_list ap; 1401 1402 fprintf(stderr, "Warning, "); 1403 va_start(ap, fmt); 1404 vfprintf(stderr, fmt, ap); 1405 fprintf(stderr, "\n"); 1406 va_end(ap); 1407} 1408 1409void 1410usage() 1411{ 1412#if NUMBOOT > 0 1413 fprintf(stderr, "%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n", 1414 "usage: disklabel [-r] disk", 1415 "\t\t(to read label)", 1416 " disklabel -w [-r] disk type [ packid ]", 1417 "\t\t(to write label with existing boot program)", 1418 " disklabel -e [-r] disk", 1419 "\t\t(to edit label)", 1420 " disklabel -R [-r] disk protofile", 1421 "\t\t(to restore label with existing boot program)", 1422#if NUMBOOT > 1 1423 " disklabel -B [ -b boot1 [ -s boot2 ] ] disk [ type ]", 1424 "\t\t(to install boot program with existing label)", 1425 " disklabel -w -B [ -b boot1 [ -s boot2 ] ] disk type [ packid ]", 1426 "\t\t(to write label and boot program)", 1427 " disklabel -R -B [ -b boot1 [ -s boot2 ] ] disk protofile [ type ]", 1428 "\t\t(to restore label and boot program)", 1429#else 1430 " disklabel -B [ -b bootprog ] disk [ type ]", 1431 "\t\t(to install boot program with existing on-disk label)", 1432 " disklabel -w -B [ -b bootprog ] disk type [ packid ]", 1433 "\t\t(to write label and install boot program)", 1434 " disklabel -R -B [ -b bootprog ] disk protofile [ type ]", 1435 "\t\t(to restore label and install boot program)", 1436#endif 1437 " disklabel [-NW] disk", 1438 "\t\t(to write disable/enable label)"); 1439#else 1440 fprintf(stderr, "%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n", 1441 "usage: disklabel [-r] disk", "(to read label)", 1442 " disklabel -w [-r] disk type [ packid ]", 1443 "\t\t(to write label)", 1444 " disklabel -e [-r] disk", 1445 "\t\t(to edit label)", 1446 " disklabel -R [-r] disk protofile", 1447 "\t\t(to restore label)", 1448 " disklabel [-NW] disk", 1449 "\t\t(to write disable/enable label)"); 1450#endif 1451 exit(1); 1452} 1453