1 /* $NetBSD: main.c,v 1.5 2005/08/27 22:21:14 uwe 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 #if HAVE_NBTOOL_CONFIG_H 36 #include "nbtool_config.h" 37 #endif 38 39 #include <sys/cdefs.h> 40 #ifndef lint 41 __COPYRIGHT("@(#) 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 46 #if 0 47 static char sccsid[] = "@(#)disklabel.c 8.4 (Berkeley) 5/4/95"; 48 /* from static char sccsid[] = "@(#)disklabel.c 1.2 (Symmetric) 11/28/85"; */ 49 #else 50 __RCSID("$NetBSD: main.c,v 1.5 2005/08/27 22:21:14 uwe Exp $"); 51 #endif 52 #endif /* not lint */ 53 54 #include <sys/param.h> 55 #include <sys/file.h> 56 #include <sys/stat.h> 57 #include <sys/wait.h> 58 #define DKTYPENAMES 59 #define FSTYPENAMES 60 61 #include <ctype.h> 62 #include <err.h> 63 #include <errno.h> 64 #include <unistd.h> 65 #include <signal.h> 66 #include <string.h> 67 #include <stdio.h> 68 #include <stdlib.h> 69 #include <unistd.h> 70 71 #include <ufs/ufs/dinode.h> 72 #include <ufs/ffs/fs.h> 73 74 #if HAVE_NBTOOL_CONFIG_H 75 #include <nbinclude/sys/disklabel.h> 76 #include <nbinclude/sys/bootblock.h> 77 #include "../../include/disktab.h" 78 #else 79 #include <sys/ioctl.h> 80 #include <sys/disklabel.h> 81 #include <sys/bootblock.h> 82 #include <util.h> 83 #include <disktab.h> 84 #endif /* HAVE_NBTOOL_CONFIG_H */ 85 86 #include "pathnames.h" 87 #include "extern.h" 88 #include "dkcksum.h" 89 90 /* 91 * Disklabel: read and write disklabels. 92 * The label is usually placed on one of the first sectors of the disk. 93 * Many machines also place a bootstrap in the same area, 94 * in which case the label is embedded in the bootstrap. 95 * The bootstrap source must leave space at the proper offset 96 * for the label on such machines. 97 */ 98 99 #ifndef BBSIZE 100 #define BBSIZE 8192 /* size of boot area, with label */ 101 #endif 102 103 #define DEFEDITOR _PATH_VI 104 105 static char *dkname; 106 static char tmpfil[MAXPATHLEN]; 107 108 static char namebuf[BBSIZE], *np = namebuf; 109 static struct disklabel lab; 110 111 char bootarea[BBSIZE]; 112 char *specname; 113 114 static enum { 115 UNSPEC, EDIT, READ, RESTORE, SETWRITABLE, WRITE, INTERACT 116 } op = UNSPEC; 117 118 static int Fflag; 119 static int rflag; 120 static int tflag; 121 int Cflag; 122 static int Iflag; 123 124 #define COMMON_OPTIONS "BCFINRWb:ef:irs:tw" 125 126 #ifdef DEBUG 127 static int debug; 128 #define OPTIONS COMMON_OPTIONS "d" 129 #else /* ! DEBUG */ 130 #define OPTIONS COMMON_OPTIONS 131 #endif /* ! DEBUG */ 132 133 #ifdef USE_MBR 134 static struct mbr_partition *dosdp; /* i386 DOS partition, if found */ 135 static struct mbr_partition *readmbr(int); 136 #endif /* USE_MBR */ 137 138 #ifdef USE_ACORN 139 static u_int filecore_partition_offset; 140 static u_int get_filecore_partition(int); 141 static int filecore_checksum(u_char *); 142 #endif /* USE_ACORN */ 143 144 #if defined(USE_MBR) || (defined(USE_ACORN) && defined(notyet)) 145 static void confirm(const char *); 146 #endif 147 148 int main(int, char *[]); 149 150 static void makedisktab(FILE *, struct disklabel *); 151 static void makelabel(const char *, const char *, 152 struct disklabel *); 153 static void l_perror(const char *); 154 static struct disklabel *readlabel(int); 155 static struct disklabel *makebootarea(char *, struct disklabel *, int); 156 static int edit(struct disklabel *, int); 157 static int editit(void); 158 static char *skip(char *); 159 static char *word(char *); 160 static int getasciilabel(FILE *, struct disklabel *); 161 static void usage(void); 162 static int getulong(const char *, char, char **, 163 unsigned long *, unsigned long); 164 #define GETNUM32(a, v) getulong(a, '\0', NULL, v, UINT32_MAX) 165 #define GETNUM16(a, v) getulong(a, '\0', NULL, v, UINT16_MAX) 166 #define GETNUM8(a, v) getulong(a, '\0', NULL, v, UINT8_MAX) 167 168 #if HAVE_NBTOOL_CONFIG_H 169 #define GETLABELOFFSET() LABELOFFSET 170 #define GETLABELSECTOR() LABELSECTOR 171 #else /* HAVE_NBTOOL_CONFIG_H */ 172 #define GETLABELOFFSET() getlabeloffset() 173 #define GETLABELSECTOR() getlabelsector() 174 #endif 175 176 int 177 main(int argc, char *argv[]) 178 { 179 struct disklabel *lp; 180 FILE *t; 181 int ch, f, writable, error; 182 183 error = 0; 184 while ((ch = getopt(argc, argv, OPTIONS)) != -1) 185 switch (ch) { 186 case 'C': 187 ++Cflag; 188 break; 189 case 'F': 190 ++Fflag; 191 break; 192 case 'I': 193 ++Iflag; 194 break; 195 case 'R': 196 if (op != UNSPEC) 197 usage(); 198 op = RESTORE; 199 break; 200 #if !HAVE_NBTOOL_CONFIG_H 201 case 'N': 202 if (op != UNSPEC) 203 usage(); 204 writable = 0; 205 op = SETWRITABLE; 206 break; 207 case 'W': 208 if (op != UNSPEC) 209 usage(); 210 writable = 1; 211 op = SETWRITABLE; 212 break; 213 #endif /* !HAVE_NBTOOL_CONFIG_H */ 214 case 'e': 215 if (op != UNSPEC) 216 usage(); 217 op = EDIT; 218 break; 219 case 'f': 220 if (setdisktab(optarg) == -1) 221 usage(); 222 break; 223 case 'i': 224 if (op != UNSPEC) 225 usage(); 226 op = INTERACT; 227 break; 228 case 't': 229 ++tflag; 230 break; 231 case 'r': 232 ++rflag; 233 break; 234 case 'w': 235 if (op != UNSPEC) 236 usage(); 237 op = WRITE; 238 break; 239 #ifdef DEBUG 240 case 'd': 241 debug++; 242 break; 243 #endif 244 case '?': 245 default: 246 usage(); 247 } 248 argc -= optind; 249 argv += optind; 250 251 if (op == UNSPEC) 252 op = READ; 253 254 if (argc < 1) 255 usage(); 256 257 if (Iflag && op != EDIT && op != INTERACT) 258 usage(); 259 260 dkname = argv[0]; 261 #if HAVE_NBTOOL_CONFIG_H 262 f = open(dkname, op == READ ? O_RDONLY : O_RDWR, 0); 263 strlcpy(np, dkname, MAXPATHLEN); 264 #else 265 f = opendisk(dkname, op == READ ? O_RDONLY : O_RDWR, np, MAXPATHLEN, 0); 266 #endif /* HAVE_NBTOOL_CONFIG_H */ 267 specname = np; 268 np += strlen(specname) + 1; 269 if (f < 0) 270 err(4, "%s", specname); 271 272 #ifdef USE_MBR 273 /* 274 * Check for presence of DOS partition table in 275 * master boot record. Return pointer to NetBSD/i386 276 * partition, if present. 277 */ 278 dosdp = readmbr(f); 279 #endif /* USE_MBR */ 280 281 #ifdef USE_ACORN 282 /* 283 * Check for the presence of a RiscOS filecore boot block 284 * indicating an ADFS file system on the disc. 285 * Return the offset to the NetBSD part of the disc if 286 * this can be determined. 287 * This routine will terminate disklabel if the disc 288 * is found to be ADFS only. 289 */ 290 filecore_partition_offset = get_filecore_partition(f); 291 #endif /* USE_ACORN */ 292 293 switch (op) { 294 295 case EDIT: 296 if (argc != 1) 297 usage(); 298 lp = readlabel(f); 299 error = edit(lp, f); 300 break; 301 302 case INTERACT: 303 if (argc != 1) 304 usage(); 305 lp = readlabel(f); 306 /* 307 * XXX: Fill some default values so checklabel does not fail 308 */ 309 if (lp->d_bbsize == 0) 310 lp->d_bbsize = BBSIZE; 311 if (lp->d_sbsize == 0) 312 lp->d_sbsize = SBLOCKSIZE; 313 interact(lp, f); 314 break; 315 316 case READ: 317 if (argc != 1) 318 usage(); 319 lp = readlabel(f); 320 if (tflag) 321 makedisktab(stdout, lp); 322 else { 323 showinfo(stdout, lp, specname); 324 showpartitions(stdout, lp, Cflag); 325 } 326 error = checklabel(lp); 327 if (error) 328 error += 100; 329 break; 330 331 case RESTORE: 332 if (argc < 2 || argc > 3) 333 usage(); 334 lp = makebootarea(bootarea, &lab, f); 335 if (!(t = fopen(argv[1], "r"))) 336 err(4, "%s", argv[1]); 337 if (getasciilabel(t, lp)) 338 error = writelabel(f, bootarea, lp); 339 else 340 error = 1; 341 break; 342 343 #if !HAVE_NBTOOL_CONFIG_H 344 case SETWRITABLE: 345 if (ioctl(f, DIOCWLABEL, (char *)&writable) < 0) 346 err(4, "ioctl DIOCWLABEL"); 347 break; 348 #endif /* !HAVE_NBTOOL_CONFIG_H */ 349 350 case WRITE: 351 if (argc < 2 || argc > 3) 352 usage(); 353 makelabel(argv[1], argc == 3 ? argv[2] : (char *)0, &lab); 354 lp = makebootarea(bootarea, &lab, f); 355 *lp = lab; 356 if (checklabel(lp) == 0) 357 error = writelabel(f, bootarea, lp); 358 else 359 error = 1; 360 break; 361 362 case UNSPEC: 363 usage(); 364 365 } 366 exit(error); 367 } 368 369 /* 370 * Construct a prototype disklabel from /etc/disktab. As a side 371 * effect, set the names of the primary and secondary boot files 372 * if specified. 373 */ 374 static void 375 makelabel(const char *type, const char *name, struct disklabel *lp) 376 { 377 struct disklabel *dp; 378 379 dp = getdiskbyname(type); 380 if (dp == NULL) 381 errx(1, "unknown disk type: %s", type); 382 *lp = *dp; 383 384 /* d_packname is union d_boot[01], so zero */ 385 (void) memset(lp->d_packname, 0, sizeof(lp->d_packname)); 386 if (name) 387 (void)strncpy(lp->d_packname, name, sizeof(lp->d_packname)); 388 } 389 390 #if defined(USE_MBR) || (defined(USE_ACORN) && defined(notyet)) 391 static void 392 confirm(const char *txt) 393 { 394 int first, ch; 395 396 (void) printf("%s? [n]: ", txt); 397 (void) fflush(stdout); 398 first = ch = getchar(); 399 while (ch != '\n' && ch != EOF) 400 ch = getchar(); 401 if (first != 'y' && first != 'Y') 402 exit(0); 403 } 404 #endif /* USE_MBR || USE_ACORN && notyet */ 405 406 int 407 writelabel(int f, char *boot, struct disklabel *lp) 408 { 409 int writable; 410 off_t sectoffset; 411 412 sectoffset = 0; 413 lp->d_magic = DISKMAGIC; 414 lp->d_magic2 = DISKMAGIC; 415 lp->d_checksum = 0; 416 lp->d_checksum = dkcksum(lp); 417 418 if (Fflag || rflag || Iflag) 419 { 420 #ifdef USE_MBR 421 struct partition *pp = &lp->d_partitions[2]; 422 423 /* 424 * If NetBSD/i386 DOS partition is missing, or if 425 * the label to be written is not within partition, 426 * prompt first. Need to allow this in case operator 427 * wants to convert the drive for dedicated use. 428 */ 429 if (dosdp) { 430 if (dosdp->mbrp_start != pp->p_offset) { 431 printf("NetBSD slice at %u, " 432 "partition C at %u\n", dosdp->mbrp_start, 433 pp->p_offset); 434 confirm("Write outside MBR partition"); 435 } 436 sectoffset = (off_t)pp->p_offset * lp->d_secsize; 437 } else { 438 sectoffset = 0; 439 } 440 #endif /* USE_MBR */ 441 442 #ifdef USE_ACORN 443 /* XXX */ 444 sectoffset = (off_t)filecore_partition_offset * DEV_BSIZE; 445 #endif /* USE_ACORN */ 446 447 /* 448 * First set the kernel disk label, 449 * then write a label to the raw disk. 450 * If the SDINFO ioctl fails because it is unimplemented, 451 * keep going; otherwise, the kernel consistency checks 452 * may prevent us from changing the current (in-core) 453 * label. 454 */ 455 #if !HAVE_NBTOOL_CONFIG_H 456 if (!Fflag && ioctl(f, DIOCSDINFO, lp) < 0 && 457 errno != ENODEV && errno != ENOTTY) { 458 l_perror("ioctl DIOCSDINFO"); 459 return (1); 460 } 461 #endif /* HAVE_NBTOOL_CONFIG_H */ 462 if (lseek(f, sectoffset, SEEK_SET) < 0) { 463 perror("lseek"); 464 return (1); 465 } 466 /* 467 * write enable label sector before write (if necessary), 468 * disable after writing. 469 */ 470 writable = 1; 471 #if !HAVE_NBTOOL_CONFIG_H 472 if (!Fflag && ioctl(f, DIOCWLABEL, &writable) < 0) 473 perror("ioctl DIOCWLABEL"); 474 #endif /* HAVE_NBTOOL_CONFIG_H */ 475 476 #ifdef __alpha__ 477 /* 478 * The Alpha requires that the boot block be checksummed. 479 * The NetBSD/alpha disklabel.h provides a macro to do it. 480 */ 481 { 482 struct alpha_boot_block *bb; 483 484 bb = (struct alpha_boot_block *)boot; 485 ALPHA_BOOT_BLOCK_CKSUM(bb, &bb->bb_cksum); 486 } 487 #endif /* __alpha__ */ 488 489 if (write(f, boot, lp->d_bbsize) != lp->d_bbsize) { 490 perror("write"); 491 return (1); 492 } 493 494 writable = 0; 495 #if !HAVE_NBTOOL_CONFIG_H 496 if (!Fflag && ioctl(f, DIOCWLABEL, &writable) < 0) 497 perror("ioctl DIOCWLABEL"); 498 /* 499 * Now issue a DIOCWDINFO. This will let the kernel convert the 500 * disklabel to some machdep format if needed. 501 */ 502 if (!Fflag && ioctl(f, DIOCWDINFO, lp) < 0) { 503 l_perror("ioctl DIOCWDINFO"); 504 return (1); 505 } 506 #endif /* !HAVE_NBTOOL_CONFIG_H */ 507 } else { 508 #if !HAVE_NBTOOL_CONFIG_H 509 if (ioctl(f, DIOCWDINFO, lp) < 0) { 510 l_perror("ioctl DIOCWDINFO"); 511 return (1); 512 } 513 #else 514 errx(1, "use -F, -r, or -I"); 515 #endif /* HAVE_NBTOOL_CONFIG_H */ 516 } 517 518 #ifdef __vax__ 519 if (lp->d_type == DTYPE_SMD && lp->d_flags & D_BADSECT) { 520 daddr_t alt; 521 int i; 522 523 alt = lp->d_ncylinders * lp->d_secpercyl - lp->d_nsectors; 524 for (i = 1; i < 11 && i < lp->d_nsectors; i += 2) { 525 (void)lseek(f, (off_t)(alt + i) * lp->d_secsize, 526 SEEK_SET); 527 if (write(f, boot, lp->d_secsize) < lp->d_secsize) 528 warn("alternate label %d write", i/2); 529 } 530 } 531 #endif /* __vax__ */ 532 533 return (0); 534 } 535 536 static void 537 l_perror(const char *s) 538 { 539 540 switch (errno) { 541 542 case ESRCH: 543 warnx("%s: No disk label on disk;\n" 544 "use \"disklabel -I\" to install initial label", s); 545 break; 546 547 case EINVAL: 548 warnx("%s: Label magic number or checksum is wrong!\n" 549 "(disklabel or kernel is out of date?)", s); 550 break; 551 552 case EBUSY: 553 warnx("%s: Open partition would move or shrink", s); 554 break; 555 556 case EXDEV: 557 warnx("%s: Labeled partition or 'a' partition must start" 558 " at beginning of disk", s); 559 break; 560 561 default: 562 warn("%s", s); 563 break; 564 } 565 } 566 567 #ifdef USE_MBR 568 /* 569 * Fetch DOS partition table from disk. 570 */ 571 static struct mbr_partition * 572 readmbr(int f) 573 { 574 struct mbr_partition *dp; 575 struct mbr_sector mbr; 576 int part; 577 u_int ext_base, next_ext, this_ext; 578 static struct mbr_partition netbsd_part; 579 580 /* 581 * Don't (yet) know disk geometry (BIOS), use 582 * partition table to find NetBSD/i386 partition, and obtain 583 * disklabel from there. 584 */ 585 586 ext_base = 0; 587 next_ext = 0; 588 for (;;) { 589 this_ext = next_ext; 590 next_ext = 0; 591 if (pread(f, &mbr, sizeof mbr, this_ext * (off_t)DEV_BSIZE) 592 != sizeof(mbr)) { 593 warn("Can't read master boot record %d", this_ext); 594 return 0; 595 } 596 597 /* Check if table is valid. */ 598 if (mbr.mbr_magic != htole16(MBR_MAGIC)) { 599 warnx("Invalid signature in mbr record %d", this_ext); 600 return 0; 601 } 602 603 dp = &mbr.mbr_parts[0]; 604 #if defined(_no_longer_needed) && !defined(__i386__) && !defined(__x86_64__) 605 /* avoid alignment error */ 606 memcpy(mbr, dp, MBR_PART_COUNT * sizeof(*dp)); 607 dp = (struct mbr_partition *)mbr; 608 #endif /* ! __i386__ */ 609 610 /* Find NetBSD partition. */ 611 for (part = 0; part < MBR_PART_COUNT; dp++, part++) { 612 dp->mbrp_start = le32toh(dp->mbrp_start); 613 dp->mbrp_size = le32toh(dp->mbrp_size); 614 switch (dp->mbrp_type) { 615 case MBR_PTYPE_NETBSD: 616 netbsd_part = *dp; 617 break; 618 case MBR_PTYPE_EXT: 619 case MBR_PTYPE_EXT_LBA: 620 case MBR_PTYPE_EXT_LNX: 621 next_ext = dp->mbrp_start; 622 continue; 623 #ifdef COMPAT_386BSD_MBRPART 624 case MBR_PTYPE_386BSD: 625 if (ext_base == 0) 626 netbsd_part = *dp; 627 continue; 628 #endif /* COMPAT_386BSD_MBRPART */ 629 default: 630 continue; 631 } 632 break; 633 } 634 if (part < MBR_PART_COUNT) 635 /* We found a netbsd partition */ 636 break; 637 if (next_ext == 0) 638 /* No more extended partitions */ 639 break; 640 next_ext += ext_base; 641 if (ext_base == 0) 642 ext_base = next_ext; 643 644 if (next_ext <= this_ext) { 645 warnx("Invalid extended chain %x <= %x", 646 next_ext, this_ext); 647 break; 648 } 649 } 650 651 if (netbsd_part.mbrp_type == 0) 652 return 0; 653 654 netbsd_part.mbrp_start += this_ext; 655 return &netbsd_part; 656 } 657 #endif /* USE_MBR */ 658 659 #ifdef USE_ACORN 660 /* 661 * static int filecore_checksum(u_char *bootblock) 662 * 663 * Calculates the filecore boot block checksum. This is used to validate 664 * a filecore boot block on the disk. If a boot block is validated then 665 * it is used to locate the partition table. If the boot block is not 666 * validated, it is assumed that the whole disk is NetBSD. 667 * 668 * The basic algorithm is: 669 * 670 * for (each byte in block, excluding checksum) { 671 * sum += byte; 672 * if (sum > 255) 673 * sum -= 255; 674 * } 675 * 676 * That's equivalent to summing all of the bytes in the block 677 * (excluding the checksum byte, of course), then calculating the 678 * checksum as "cksum = sum - ((sum - 1) / 255) * 255)". That 679 * expression may or may not yield a faster checksum function, 680 * but it's easier to reason about. 681 * 682 * Note that if you have a block filled with bytes of a single 683 * value "X" (regardless of that value!) and calculate the cksum 684 * of the block (excluding the checksum byte), you will _always_ 685 * end up with a checksum of X. (Do the math; that can be derived 686 * from the checksum calculation function!) That means that 687 * blocks which contain bytes which all have the same value will 688 * always checksum properly. That's a _very_ unlikely occurence 689 * (probably impossible, actually) for a valid filecore boot block, 690 * so we treat such blocks as invalid. 691 */ 692 static int 693 filecore_checksum(u_char *bootblock) 694 { 695 u_char byte0, accum_diff; 696 u_int sum; 697 int i; 698 699 sum = 0; 700 accum_diff = 0; 701 byte0 = bootblock[0]; 702 703 /* 704 * Sum the contents of the block, keeping track of whether 705 * or not all bytes are the same. If 'accum_diff' ends up 706 * being zero, all of the bytes are, in fact, the same. 707 */ 708 for (i = 0; i < 511; ++i) { 709 sum += bootblock[i]; 710 accum_diff |= bootblock[i] ^ byte0; 711 } 712 713 /* 714 * Check to see if the checksum byte is the same as the 715 * rest of the bytes, too. (Note that if all of the bytes 716 * are the same except the checksum, a checksum compare 717 * won't succeed, but that's not our problem.) 718 */ 719 accum_diff |= bootblock[i] ^ byte0; 720 721 /* All bytes in block are the same; call it invalid. */ 722 if (accum_diff == 0) 723 return (-1); 724 725 return (sum - ((sum - 1) / 255) * 255); 726 } 727 728 /* 729 * Fetch filecore bootblock from disk and analyse it 730 */ 731 static u_int 732 get_filecore_partition(int f) 733 { 734 struct filecore_bootblock *fcbb; 735 static char bb[DEV_BSIZE]; 736 u_int offset; 737 738 if (lseek(f, (off_t)FILECORE_BOOT_SECTOR * DEV_BSIZE, SEEK_SET) < 0 || 739 read(f, bb, sizeof(bb)) != sizeof(bb)) 740 err(4, "can't read filecore boot block"); 741 fcbb = (struct filecore_bootblock *)bb; 742 743 /* Check if table is valid. */ 744 if (filecore_checksum(bb) != fcbb->checksum) 745 return (0); 746 747 /* 748 * Check for NetBSD/arm32 (RiscBSD) partition marker. 749 * If found the NetBSD disklabel location is easy. 750 */ 751 offset = (fcbb->partition_cyl_low + (fcbb->partition_cyl_high << 8)) 752 * fcbb->heads * fcbb->secspertrack; 753 if (fcbb->partition_type == PARTITION_FORMAT_RISCBSD) 754 return (offset); 755 else if (fcbb->partition_type == PARTITION_FORMAT_RISCIX) { 756 struct riscix_partition_table *riscix_part; 757 int loop; 758 759 /* 760 * Read the RISCiX partition table and search for the 761 * first partition named "RiscBSD", "NetBSD", or "Empty:" 762 * 763 * XXX is use of 'Empty:' really desirable?! -- cgd 764 */ 765 766 if (lseek(f, (off_t)offset * DEV_BSIZE, SEEK_SET) < 0 || 767 read(f, bb, sizeof(bb)) != sizeof(bb)) 768 err(4, "can't read riscix partition table"); 769 riscix_part = (struct riscix_partition_table *)bb; 770 771 for (loop = 0; loop < NRISCIX_PARTITIONS; ++loop) { 772 if (strcmp(riscix_part->partitions[loop].rp_name, 773 "RiscBSD") == 0 || 774 strcmp(riscix_part->partitions[loop].rp_name, 775 "NetBSD") == 0 || 776 strcmp(riscix_part->partitions[loop].rp_name, 777 "Empty:") == 0) { 778 offset = riscix_part->partitions[loop].rp_start; 779 break; 780 } 781 } 782 if (loop == NRISCIX_PARTITIONS) { 783 /* 784 * Valid filecore boot block, RISCiX partition table 785 * but no NetBSD partition. We should leave this 786 * disc alone. 787 */ 788 errx(4, "cannot label: no NetBSD partition found" 789 " in RISCiX partition table"); 790 } 791 return (offset); 792 } else { 793 /* 794 * Valid filecore boot block and no non-ADFS partition. 795 * This means that the whole disc is allocated for ADFS 796 * so do not trash ! If the user really wants to put a 797 * NetBSD disklabel on the disc then they should remove 798 * the filecore boot block first with dd. 799 */ 800 errx(4, "cannot label: filecore-only disk" 801 " (no non-ADFS partition)"); 802 } 803 return (0); 804 } 805 #endif /* USE_ACORN */ 806 807 /* 808 * Fetch disklabel for disk. 809 * Use ioctl to get label unless -r flag is given. 810 */ 811 static struct disklabel * 812 readlabel(int f) 813 { 814 struct disklabel *lp; 815 816 if (Fflag || rflag || Iflag) { 817 const char *msg; 818 off_t sectoffset; 819 820 msg = NULL; 821 sectoffset = 0; 822 823 #ifdef USE_MBR 824 if (dosdp) 825 sectoffset = (off_t)dosdp->mbrp_start * DEV_BSIZE; 826 #endif /* USE_MBR */ 827 828 #ifdef USE_ACORN 829 /* XXX */ 830 sectoffset = (off_t)filecore_partition_offset * DEV_BSIZE; 831 #endif /* USE_ACORN */ 832 833 if (lseek(f, sectoffset, SEEK_SET) < 0 || 834 read(f, bootarea, BBSIZE) != BBSIZE) 835 err(4, "%s", specname); 836 837 msg = "no disklabel"; 838 for (lp = (struct disklabel *)bootarea; 839 lp <= (struct disklabel *)(bootarea + BBSIZE - sizeof(*lp)); 840 lp = (struct disklabel *)((char *)lp + sizeof(long))) { 841 if (lp->d_magic == DISKMAGIC && 842 lp->d_magic2 == DISKMAGIC) { 843 if (lp->d_npartitions <= MAXPARTITIONS && 844 dkcksum(lp) == 0) 845 return (lp); 846 msg = "disk label corrupted"; 847 } 848 } 849 if (msg != NULL && !Iflag) 850 errx(1, "%s", msg); 851 #if HAVE_NBTOOL_CONFIG_H 852 goto err; 853 #else 854 /* 855 * There was no label on the disk. Get the fictious one 856 * as a basis for initialisation. 857 */ 858 lp = makebootarea(bootarea, &lab, f); 859 if (ioctl(f, DIOCGDINFO, lp) < 0 && 860 ioctl(f, DIOCGDEFLABEL, lp) < 0) 861 goto err; 862 #endif /* HAVE_NBTOOL_CONFIG_H */ 863 } else { 864 #ifdef HAVE_NBTOOL_CONFIG_H 865 goto err; 866 #else 867 lp = &lab; 868 if (ioctl(f, DIOCGDINFO, lp) < 0) 869 err(4, "ioctl DIOCGDINFO"); 870 #endif /* HAVE_NBTOOL_CONFIG_H */ 871 } 872 return (lp); 873 err: 874 errx(1, "could not get initial label"); 875 } 876 877 /* 878 * Construct a bootarea (d_bbsize bytes) in the specified buffer ``boot'' 879 * Returns a pointer to the disklabel portion of the bootarea. 880 */ 881 static struct disklabel * 882 makebootarea(char *boot, struct disklabel *dp, int f) 883 { 884 struct disklabel *lp; 885 char *p; 886 daddr_t lsec; 887 off_t loff; 888 889 if ((lsec = GETLABELSECTOR()) < 0) 890 err(4, "getlabelsector()"); 891 if ((loff = GETLABELOFFSET()) < 0) 892 err(4, "getlabeloffset()"); 893 894 /* XXX */ 895 if (dp->d_secsize == 0) { 896 dp->d_secsize = DEV_BSIZE; 897 dp->d_bbsize = BBSIZE; 898 } 899 lp = (struct disklabel *) (boot + (lsec * dp->d_secsize) + loff); 900 (void) memset(lp, 0, sizeof(*lp)); 901 902 #ifdef SAVEBOOTAREA 903 /* 904 * We must read the current bootarea so we don't clobber the 905 * existing boot block, if any. 906 */ 907 if (rflag || Iflag) { 908 off_t sectoffset; 909 910 sectoffset = 0; 911 if (lseek(f, sectoffset, SEEK_SET) < 0 || 912 read(f, boot, BBSIZE) != BBSIZE) 913 err(4, "%s", specname); 914 (void) memset(lp, 0, sizeof(*lp)); 915 } 916 #endif /* SAVEBOOTAREA */ 917 918 /* 919 * Make sure no part of the bootstrap is written in the area 920 * reserved for the label. 921 */ 922 for (p = (char *)lp; p < (char *)lp + sizeof(struct disklabel); p++) 923 if (*p) 924 errx(2, "Bootstrap doesn't leave room for disk label"); 925 return (lp); 926 } 927 928 static void 929 makedisktab(FILE *f, struct disklabel *lp) 930 { 931 int i; 932 const char *did; 933 struct partition *pp; 934 935 did = "\\\n\t:"; 936 (void) fprintf(f, "%.*s|Automatically generated label:\\\n\t:dt=", 937 (int) sizeof(lp->d_typename), lp->d_typename); 938 if ((unsigned) lp->d_type < DKMAXTYPES) 939 (void) fprintf(f, "%s:", dktypenames[lp->d_type]); 940 else 941 (void) fprintf(f, "unknown%d:", lp->d_type); 942 943 (void) fprintf(f, "se#%d:", lp->d_secsize); 944 (void) fprintf(f, "ns#%d:", lp->d_nsectors); 945 (void) fprintf(f, "nt#%d:", lp->d_ntracks); 946 (void) fprintf(f, "sc#%d:", lp->d_secpercyl); 947 (void) fprintf(f, "nc#%d:", lp->d_ncylinders); 948 949 if ((lp->d_secpercyl * lp->d_ncylinders) != lp->d_secperunit) { 950 (void) fprintf(f, "%ssu#%d:", did, lp->d_secperunit); 951 did = ""; 952 } 953 if (lp->d_rpm != 3600) { 954 (void) fprintf(f, "%srm#%d:", did, lp->d_rpm); 955 did = ""; 956 } 957 if (lp->d_interleave != 1) { 958 (void) fprintf(f, "%sil#%d:", did, lp->d_interleave); 959 did = ""; 960 } 961 if (lp->d_trackskew != 0) { 962 (void) fprintf(f, "%ssk#%d:", did, lp->d_trackskew); 963 did = ""; 964 } 965 if (lp->d_cylskew != 0) { 966 (void) fprintf(f, "%scs#%d:", did, lp->d_cylskew); 967 did = ""; 968 } 969 if (lp->d_headswitch != 0) { 970 (void) fprintf(f, "%shs#%d:", did, lp->d_headswitch); 971 did = ""; 972 } 973 if (lp->d_trkseek != 0) { 974 (void) fprintf(f, "%sts#%d:", did, lp->d_trkseek); 975 did = ""; 976 } 977 #ifdef notyet 978 (void) fprintf(f, "drivedata: "); 979 for (i = NDDATA - 1; i >= 0; i--) 980 if (lp->d_drivedata[i]) 981 break; 982 if (i < 0) 983 i = 0; 984 for (j = 0; j <= i; j++) 985 (void) fprintf(f, "%d ", lp->d_drivedata[j]); 986 #endif /* notyet */ 987 pp = lp->d_partitions; 988 for (i = 0; i < lp->d_npartitions; i++, pp++) { 989 if (pp->p_size) { 990 char c = 'a' + i; 991 (void) fprintf(f, "\\\n\t:"); 992 (void) fprintf(f, "p%c#%d:", c, pp->p_size); 993 (void) fprintf(f, "o%c#%d:", c, pp->p_offset); 994 if (pp->p_fstype != FS_UNUSED) { 995 if ((unsigned) pp->p_fstype < FSMAXTYPES) 996 (void) fprintf(f, "t%c=%s:", c, 997 fstypenames[pp->p_fstype]); 998 else 999 (void) fprintf(f, "t%c=unknown%d:", 1000 c, pp->p_fstype); 1001 } 1002 switch (pp->p_fstype) { 1003 1004 case FS_UNUSED: 1005 break; 1006 1007 case FS_BSDFFS: 1008 case FS_BSDLFS: 1009 case FS_EX2FS: 1010 case FS_ADOS: 1011 case FS_APPLEUFS: 1012 (void) fprintf(f, "b%c#%d:", c, 1013 pp->p_fsize * pp->p_frag); 1014 (void) fprintf(f, "f%c#%d:", c, pp->p_fsize); 1015 break; 1016 default: 1017 break; 1018 } 1019 } 1020 } 1021 (void) fprintf(f, "\n"); 1022 (void) fflush(f); 1023 } 1024 1025 static int 1026 edit(struct disklabel *lp, int f) 1027 { 1028 struct disklabel label; 1029 const char *tmpdir; 1030 int first, ch, fd; 1031 FILE *fp; 1032 1033 if ((tmpdir = getenv("TMPDIR")) == NULL) 1034 tmpdir = _PATH_TMP; 1035 (void)snprintf(tmpfil, sizeof(tmpfil), "%s/%s", tmpdir, TMPFILE); 1036 if ((fd = mkstemp(tmpfil)) == -1 || (fp = fdopen(fd, "w")) == NULL) { 1037 warn("%s", tmpfil); 1038 return (1); 1039 } 1040 (void)fchmod(fd, 0600); 1041 showinfo(fp, lp, specname); 1042 showpartitions(fp, lp, Cflag); 1043 (void) fclose(fp); 1044 for (;;) { 1045 if (!editit()) 1046 break; 1047 fp = fopen(tmpfil, "r"); 1048 if (fp == NULL) { 1049 warn("%s", tmpfil); 1050 break; 1051 } 1052 (void) memset(&label, 0, sizeof(label)); 1053 if (getasciilabel(fp, &label)) { 1054 *lp = label; 1055 if (writelabel(f, bootarea, lp) == 0) { 1056 (void) unlink(tmpfil); 1057 return (0); 1058 } 1059 } 1060 (void) printf("re-edit the label? [y]: "); 1061 (void) fflush(stdout); 1062 first = ch = getchar(); 1063 while (ch != '\n' && ch != EOF) 1064 ch = getchar(); 1065 if (first == 'n' || first == 'N') 1066 break; 1067 } 1068 (void)unlink(tmpfil); 1069 return (1); 1070 } 1071 1072 static int 1073 editit(void) 1074 { 1075 int pid, xpid; 1076 int status; 1077 sigset_t nsigset, osigset; 1078 1079 sigemptyset(&nsigset); 1080 sigaddset(&nsigset, SIGINT); 1081 sigaddset(&nsigset, SIGQUIT); 1082 sigaddset(&nsigset, SIGHUP); 1083 sigprocmask(SIG_BLOCK, &nsigset, &osigset); 1084 while ((pid = fork()) < 0) { 1085 if (errno != EAGAIN) { 1086 sigprocmask(SIG_SETMASK, &osigset, (sigset_t *)0); 1087 warn("fork"); 1088 return (0); 1089 } 1090 sleep(1); 1091 } 1092 if (pid == 0) { 1093 const char *ed; 1094 char *buf; 1095 int retval; 1096 1097 sigprocmask(SIG_SETMASK, &osigset, (sigset_t *)0); 1098 setgid(getgid()); 1099 setuid(getuid()); 1100 if ((ed = getenv("EDITOR")) == (char *)0) 1101 ed = DEFEDITOR; 1102 /* 1103 * Jump through a few extra hoops in case someone's editor 1104 * is "editor arg1 arg2". 1105 */ 1106 asprintf(&buf, "%s %s", ed, tmpfil); 1107 if (!buf) 1108 err(1, "malloc"); 1109 retval = execlp(_PATH_BSHELL, _PATH_BSHELL, "-c", buf, NULL); 1110 if (retval == -1) 1111 perror(ed); 1112 exit(retval); 1113 } 1114 while ((xpid = wait(&status)) >= 0) 1115 if (xpid == pid) 1116 break; 1117 sigprocmask(SIG_SETMASK, &osigset, (sigset_t *)0); 1118 return (!status); 1119 } 1120 1121 static char * 1122 skip(char *cp) 1123 { 1124 1125 cp += strspn(cp, " \t"); 1126 if (*cp == '\0') 1127 return (NULL); 1128 return (cp); 1129 } 1130 1131 static char * 1132 word(char *cp) 1133 { 1134 1135 if (cp == NULL || *cp == '\0') 1136 return (NULL); 1137 1138 cp += strcspn(cp, " \t"); 1139 if (*cp == '\0') 1140 return (NULL); 1141 *cp++ = '\0'; 1142 cp += strspn(cp, " \t"); 1143 if (*cp == '\0') 1144 return (NULL); 1145 return (cp); 1146 } 1147 1148 #define _CHECKLINE \ 1149 if (tp == NULL || *tp == '\0') { \ 1150 warnx("line %d: too few fields", lineno); \ 1151 errors++; \ 1152 break; \ 1153 } 1154 1155 #define __CHECKLINE \ 1156 if (*tp == NULL || **tp == '\0') { \ 1157 warnx("line %d: too few fields", lineno); \ 1158 *tp = _error_; \ 1159 return 0; \ 1160 } 1161 1162 static char _error_[] = ""; 1163 #define NXTNUM(n) if ((n = nxtnum(&tp, lineno),0) + tp != _error_) \ 1164 ; else goto error 1165 #define NXTXNUM(n) if ((n = nxtxnum(&tp, lp, lineno),0) + tp != _error_) \ 1166 ; else goto error 1167 1168 static unsigned long 1169 nxtnum(char **tp, int lineno) 1170 { 1171 char *cp; 1172 unsigned long v; 1173 1174 __CHECKLINE 1175 if (getulong(*tp, '\0', &cp, &v, UINT32_MAX) != 0) { 1176 warnx("line %d: syntax error", lineno); 1177 *tp = _error_; 1178 return 0; 1179 } 1180 *tp = cp; 1181 return v; 1182 } 1183 1184 static unsigned long 1185 nxtxnum(char **tp, struct disklabel *lp, int lineno) 1186 { 1187 char *cp, *ncp; 1188 unsigned long n, v; 1189 1190 __CHECKLINE 1191 cp = *tp; 1192 if (getulong(cp, '/', &ncp, &n, UINT32_MAX) != 0) 1193 goto bad; 1194 1195 if (*ncp == '/') { 1196 n *= lp->d_secpercyl; 1197 cp = ncp + 1; 1198 if (getulong(cp, '/', &ncp, &v, UINT32_MAX) != 0) 1199 goto bad; 1200 n += v * lp->d_nsectors; 1201 cp = ncp + 1; 1202 if (getulong(cp, '\0', &ncp, &v, UINT32_MAX) != 0) 1203 goto bad; 1204 n += v; 1205 } 1206 *tp = ncp; 1207 return n; 1208 bad: 1209 warnx("line %d: invalid format", lineno); 1210 *tp = _error_; 1211 return 0; 1212 } 1213 1214 /* 1215 * Read an ascii label in from fd f, 1216 * in the same format as that put out by showinfo() and showpartitions(), 1217 * and fill in lp. 1218 */ 1219 static int 1220 getasciilabel(FILE *f, struct disklabel *lp) 1221 { 1222 const char *const *cpp, *s; 1223 struct partition *pp; 1224 char *cp, *tp, line[BUFSIZ], tbuf[15]; 1225 int lineno, errors; 1226 unsigned long v; 1227 unsigned int part; 1228 1229 lineno = 0; 1230 errors = 0; 1231 lp->d_bbsize = BBSIZE; /* XXX */ 1232 lp->d_sbsize = SBLOCKSIZE; /* XXX */ 1233 while (fgets(line, sizeof(line) - 1, f)) { 1234 lineno++; 1235 if ((cp = strpbrk(line, "#\r\n")) != NULL) 1236 *cp = '\0'; 1237 cp = skip(line); 1238 if (cp == NULL) /* blank line or comment line */ 1239 continue; 1240 tp = strchr(cp, ':'); /* everything has a colon in it */ 1241 if (tp == NULL) { 1242 warnx("line %d: syntax error", lineno); 1243 errors++; 1244 continue; 1245 } 1246 *tp++ = '\0', tp = skip(tp); 1247 if (!strcmp(cp, "type")) { 1248 if (tp == NULL) { 1249 strlcpy(tbuf, "unknown", sizeof(tbuf)); 1250 tp = tbuf; 1251 } 1252 cpp = dktypenames; 1253 for (; cpp < &dktypenames[DKMAXTYPES]; cpp++) 1254 if ((s = *cpp) && !strcasecmp(s, tp)) { 1255 lp->d_type = cpp - dktypenames; 1256 goto next; 1257 } 1258 if (GETNUM16(tp, &v) != 0) { 1259 warnx("line %d: syntax error", lineno); 1260 errors++; 1261 continue; 1262 } 1263 if (v >= DKMAXTYPES) 1264 warnx("line %d: warning, unknown disk type: %s", 1265 lineno, tp); 1266 lp->d_type = v; 1267 continue; 1268 } 1269 if (!strcmp(cp, "flags")) { 1270 for (v = 0; (cp = tp) && *cp != '\0';) { 1271 tp = word(cp); 1272 if (!strcasecmp(cp, "removable")) 1273 v |= D_REMOVABLE; 1274 else if (!strcasecmp(cp, "ecc")) 1275 v |= D_ECC; 1276 else if (!strcasecmp(cp, "badsect")) 1277 v |= D_BADSECT; 1278 else { 1279 warnx("line %d: bad flag: %s", 1280 lineno, cp); 1281 errors++; 1282 } 1283 } 1284 lp->d_flags = v; 1285 continue; 1286 } 1287 if (!strcmp(cp, "drivedata")) { 1288 int i; 1289 1290 for (i = 0; (cp = tp) && *cp != '\0' && i < NDDATA;) { 1291 if (GETNUM32(cp, &v) != 0) { 1292 warnx("line %d: bad drive data", 1293 lineno); 1294 errors++; 1295 } else 1296 lp->d_drivedata[i] = v; 1297 i++; 1298 tp = word(cp); 1299 } 1300 continue; 1301 } 1302 if (sscanf(cp, "%lu partitions", &v) == 1) { 1303 if (v == 0 || v > MAXPARTITIONS) { 1304 warnx("line %d: bad # of partitions", lineno); 1305 lp->d_npartitions = MAXPARTITIONS; 1306 errors++; 1307 } else 1308 lp->d_npartitions = v; 1309 continue; 1310 } 1311 if (tp == NULL) { 1312 tbuf[0] = '\0'; 1313 tp = tbuf; 1314 } 1315 if (!strcmp(cp, "disk")) { 1316 strncpy(lp->d_typename, tp, sizeof(lp->d_typename)); 1317 continue; 1318 } 1319 if (!strcmp(cp, "label")) { 1320 strncpy(lp->d_packname, tp, sizeof(lp->d_packname)); 1321 continue; 1322 } 1323 if (!strcmp(cp, "bytes/sector")) { 1324 if (GETNUM32(tp, &v) != 0 || v <= 0 || (v % 512) != 0) { 1325 warnx("line %d: bad %s: %s", lineno, cp, tp); 1326 errors++; 1327 } else 1328 lp->d_secsize = v; 1329 continue; 1330 } 1331 if (!strcmp(cp, "sectors/track")) { 1332 if (GETNUM32(tp, &v) != 0) { 1333 warnx("line %d: bad %s: %s", lineno, cp, tp); 1334 errors++; 1335 } else 1336 lp->d_nsectors = v; 1337 continue; 1338 } 1339 if (!strcmp(cp, "sectors/cylinder")) { 1340 if (GETNUM32(tp, &v) != 0) { 1341 warnx("line %d: bad %s: %s", lineno, cp, tp); 1342 errors++; 1343 } else 1344 lp->d_secpercyl = v; 1345 continue; 1346 } 1347 if (!strcmp(cp, "tracks/cylinder")) { 1348 if (GETNUM32(tp, &v) != 0) { 1349 warnx("line %d: bad %s: %s", lineno, cp, tp); 1350 errors++; 1351 } else 1352 lp->d_ntracks = v; 1353 continue; 1354 } 1355 if (!strcmp(cp, "cylinders")) { 1356 if (GETNUM32(tp, &v) != 0) { 1357 warnx("line %d: bad %s: %s", lineno, cp, tp); 1358 errors++; 1359 } else 1360 lp->d_ncylinders = v; 1361 continue; 1362 } 1363 if (!strcmp(cp, "total sectors")) { 1364 if (GETNUM32(tp, &v) != 0) { 1365 warnx("line %d: bad %s: %s", lineno, cp, tp); 1366 errors++; 1367 } else 1368 lp->d_secperunit = v; 1369 continue; 1370 } 1371 if (!strcmp(cp, "rpm")) { 1372 if (GETNUM16(tp, &v) != 0) { 1373 warnx("line %d: bad %s: %s", lineno, cp, tp); 1374 errors++; 1375 } else 1376 lp->d_rpm = v; 1377 continue; 1378 } 1379 if (!strcmp(cp, "interleave")) { 1380 if (GETNUM16(tp, &v) != 0) { 1381 warnx("line %d: bad %s: %s", lineno, cp, tp); 1382 errors++; 1383 } else 1384 lp->d_interleave = v; 1385 continue; 1386 } 1387 if (!strcmp(cp, "trackskew")) { 1388 if (GETNUM16(tp, &v) != 0) { 1389 warnx("line %d: bad %s: %s", lineno, cp, tp); 1390 errors++; 1391 } else 1392 lp->d_trackskew = v; 1393 continue; 1394 } 1395 if (!strcmp(cp, "cylinderskew")) { 1396 if (GETNUM16(tp, &v) != 0) { 1397 warnx("line %d: bad %s: %s", lineno, cp, tp); 1398 errors++; 1399 } else 1400 lp->d_cylskew = v; 1401 continue; 1402 } 1403 if (!strcmp(cp, "headswitch")) { 1404 if (GETNUM32(tp, &v) != 0) { 1405 warnx("line %d: bad %s: %s", lineno, cp, tp); 1406 errors++; 1407 } else 1408 lp->d_headswitch = v; 1409 continue; 1410 } 1411 if (!strcmp(cp, "track-to-track seek")) { 1412 if (GETNUM32(tp, &v) != 0) { 1413 warnx("line %d: bad %s: %s", lineno, cp, tp); 1414 errors++; 1415 } else 1416 lp->d_trkseek = v; 1417 continue; 1418 } 1419 if ('a' > *cp || *cp > 'z' || cp[1] != '\0') { 1420 warnx("line %d: unknown field: %s", lineno, cp); 1421 errors++; 1422 continue; 1423 } 1424 1425 /* We have a partition entry */ 1426 part = *cp - 'a'; 1427 1428 if (part >= MAXPARTITIONS) { 1429 warnx("line %d: bad partition name: %s", lineno, cp); 1430 errors++; 1431 continue; 1432 } 1433 pp = &lp->d_partitions[part]; 1434 1435 NXTXNUM(pp->p_size); 1436 NXTXNUM(pp->p_offset); 1437 /* can't use word() here because of blanks in fstypenames[] */ 1438 tp += strspn(tp, " \t"); 1439 _CHECKLINE 1440 cp = tp; 1441 cpp = fstypenames; 1442 for (; cpp < &fstypenames[FSMAXTYPES]; cpp++) { 1443 s = *cpp; 1444 if (s == NULL || 1445 (cp[strlen(s)] != ' ' && 1446 cp[strlen(s)] != '\t' && 1447 cp[strlen(s)] != '\0')) 1448 continue; 1449 if (!memcmp(s, cp, strlen(s))) { 1450 pp->p_fstype = cpp - fstypenames; 1451 tp += strlen(s); 1452 if (*tp == '\0') 1453 tp = NULL; 1454 else { 1455 tp += strspn(tp, " \t"); 1456 if (*tp == '\0') 1457 tp = NULL; 1458 } 1459 goto gottype; 1460 } 1461 } 1462 tp = word(cp); 1463 if (isdigit(*cp & 0xff)) { 1464 if (GETNUM8(cp, &v) != 0) { 1465 warnx("line %d: syntax error", lineno); 1466 errors++; 1467 } 1468 } else 1469 v = FSMAXTYPES; 1470 if ((unsigned)v >= FSMAXTYPES) { 1471 warnx("line %d: warning, unknown filesystem type: %s", 1472 lineno, cp); 1473 v = FS_UNUSED; 1474 } 1475 pp->p_fstype = v; 1476 gottype: 1477 switch (pp->p_fstype) { 1478 1479 case FS_UNUSED: /* XXX */ 1480 NXTNUM(pp->p_fsize); 1481 if (pp->p_fsize == 0) 1482 break; 1483 NXTNUM(v); 1484 pp->p_frag = v / pp->p_fsize; 1485 break; 1486 1487 case FS_BSDFFS: 1488 case FS_ADOS: 1489 case FS_APPLEUFS: 1490 NXTNUM(pp->p_fsize); 1491 if (pp->p_fsize == 0) 1492 break; 1493 NXTNUM(v); 1494 pp->p_frag = v / pp->p_fsize; 1495 NXTNUM(pp->p_cpg); 1496 break; 1497 case FS_BSDLFS: 1498 NXTNUM(pp->p_fsize); 1499 if (pp->p_fsize == 0) 1500 break; 1501 NXTNUM(v); 1502 pp->p_frag = v / pp->p_fsize; 1503 NXTNUM(pp->p_sgs); 1504 break; 1505 case FS_EX2FS: 1506 NXTNUM(pp->p_fsize); 1507 if (pp->p_fsize == 0) 1508 break; 1509 NXTNUM(v); 1510 pp->p_frag = v / pp->p_fsize; 1511 break; 1512 case FS_ISO9660: 1513 NXTNUM(pp->p_cdsession); 1514 break; 1515 default: 1516 break; 1517 } 1518 continue; 1519 error: 1520 errors++; 1521 next: 1522 ; 1523 } 1524 errors += checklabel(lp); 1525 return (errors == 0); 1526 } 1527 1528 /* 1529 * Check disklabel for errors and fill in 1530 * derived fields according to supplied values. 1531 */ 1532 int 1533 checklabel(struct disklabel *lp) 1534 { 1535 struct partition *pp, *qp; 1536 int i, j, errors; 1537 char part; 1538 1539 errors = 0; 1540 if (lp->d_secsize == 0) { 1541 warnx("sector size %d", lp->d_secsize); 1542 return (1); 1543 } 1544 if (lp->d_nsectors == 0) { 1545 warnx("sectors/track %d", lp->d_nsectors); 1546 return (1); 1547 } 1548 if (lp->d_ntracks == 0) { 1549 warnx("tracks/cylinder %d", lp->d_ntracks); 1550 return (1); 1551 } 1552 if (lp->d_ncylinders == 0) { 1553 warnx("cylinders/unit %d", lp->d_ncylinders); 1554 errors++; 1555 } 1556 if (lp->d_rpm == 0) 1557 warnx("warning, revolutions/minute %d", lp->d_rpm); 1558 if (lp->d_secpercyl == 0) 1559 lp->d_secpercyl = lp->d_nsectors * lp->d_ntracks; 1560 if (lp->d_secperunit == 0) 1561 lp->d_secperunit = lp->d_secpercyl * lp->d_ncylinders; 1562 #ifdef __i386__notyet__ 1563 if (dosdp && lp->d_secperunit > dosdp->mbrp_start + dosdp->mbrp_size) { 1564 warnx("exceeds DOS partition size"); 1565 errors++; 1566 lp->d_secperunit = dosdp->mbrp_start + dosdp->mbrp_size; 1567 } 1568 /* XXX should also check geometry against BIOS's idea */ 1569 #endif /* __i386__notyet__ */ 1570 #ifdef __arm32__notyet__ 1571 /* XXX similar code as for i386 */ 1572 #endif /* __arm32__notyet__ */ 1573 if (lp->d_bbsize == 0) { 1574 warnx("boot block size %d", lp->d_bbsize); 1575 errors++; 1576 } else if (lp->d_bbsize % lp->d_secsize) 1577 warnx("warning, boot block size %% sector-size != 0"); 1578 if (lp->d_sbsize == 0) { 1579 warnx("super block size %d", lp->d_sbsize); 1580 errors++; 1581 } else if (lp->d_sbsize % lp->d_secsize) 1582 warnx("warning, super block size %% sector-size != 0"); 1583 if (lp->d_npartitions > MAXPARTITIONS) 1584 warnx("warning, number of partitions (%d) > MAXPARTITIONS (%d)", 1585 lp->d_npartitions, MAXPARTITIONS); 1586 else 1587 for (i = MAXPARTITIONS - 1; i >= lp->d_npartitions; i--) { 1588 part = 'a' + i; 1589 pp = &lp->d_partitions[i]; 1590 if (pp->p_size || pp->p_offset) { 1591 warnx("warning, partition %c increased " 1592 "number of partitions from %d to %d", 1593 part, lp->d_npartitions, i + 1); 1594 lp->d_npartitions = i + 1; 1595 break; 1596 } 1597 } 1598 for (i = 0; i < lp->d_npartitions; i++) { 1599 part = 'a' + i; 1600 pp = &lp->d_partitions[i]; 1601 if (pp->p_size == 0 && pp->p_offset != 0) 1602 warnx("warning, partition %c: size 0, but offset %d", 1603 part, pp->p_offset); 1604 #ifdef STRICT_CYLINDER_ALIGNMENT 1605 if (pp->p_offset % lp->d_secpercyl) { 1606 warnx("warning, partition %c:" 1607 " not starting on cylinder boundary", 1608 part); 1609 errors++; 1610 } 1611 #endif /* STRICT_CYLINDER_ALIGNMENT */ 1612 if (pp->p_offset > lp->d_secperunit) { 1613 warnx("partition %c: offset past end of unit", part); 1614 errors++; 1615 } 1616 if (pp->p_offset + pp->p_size > lp->d_secperunit) { 1617 warnx("partition %c: partition extends" 1618 " past end of unit", 1619 part); 1620 errors++; 1621 } 1622 if (pp->p_fstype != FS_UNUSED) 1623 for (j = i + 1; j < lp->d_npartitions; j++) { 1624 qp = &lp->d_partitions[j]; 1625 if (qp->p_fstype == FS_UNUSED) 1626 continue; 1627 if (pp->p_offset < qp->p_offset + qp->p_size && 1628 qp->p_offset < pp->p_offset + pp->p_size) 1629 warnx("partitions %c and %c overlap", 1630 part, 'a' + j); 1631 } 1632 } 1633 return (errors); 1634 } 1635 1636 static void 1637 usage(void) 1638 { 1639 static const struct { 1640 const char *name; 1641 const char *expn; 1642 } usages[] = { 1643 { "[-CFrt] disk", "(to read label)" }, 1644 { "-w [-Fr] [-f disktab] disk disktype [packid]", "(to write label)" }, 1645 { "-e [-CFIr] disk", "(to edit label)" }, 1646 { "-i [-FIr] disk", "(to create a label interactively)" }, 1647 { "-R [-Fr] disk protofile", "(to restore label)" }, 1648 { "[-NW] disk", "(to write disable/enable label)" }, 1649 { NULL, NULL } 1650 }; 1651 int i; 1652 const char *pn = getprogname(); 1653 const char *t = "usage:"; 1654 1655 for (i = 0; usages[i].name != NULL; i++) { 1656 (void)fprintf(stderr, "%s %s %s\n\t%s\n", 1657 t, pn, usages[i].name, usages[i].expn); 1658 t = "or"; 1659 } 1660 exit(1); 1661 } 1662 1663 static int 1664 getulong(const char *str, char sep, char **epp, unsigned long *ul, 1665 unsigned long max) 1666 { 1667 char *ep; 1668 1669 if (epp == NULL) 1670 epp = &ep; 1671 1672 *ul = strtoul(str, epp, 10); 1673 1674 if ((*ul == ULONG_MAX && errno == ERANGE) || *ul > max) 1675 return ERANGE; 1676 1677 if (*str == '\0' || (**epp != '\0' && **epp != sep && 1678 !isspace((unsigned char)**epp))) 1679 return EFTYPE; 1680 1681 return 0; 1682 } 1683