1/* $NetBSD: main.c,v 1.25 2011/08/29 14:34:59 joerg Exp $ */ 2 3/* 4 * Copyright (c) 2006 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by Julio M. Merino Vidal. 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 * 19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 * POSSIBILITY OF SUCH DAMAGE. 30 */ 31 32/* 33 * Copyright (c) 1987, 1993 34 * The Regents of the University of California. All rights reserved. 35 * 36 * This code is derived from software contributed to Berkeley by 37 * Symmetric Computer Systems. 38 * 39 * Redistribution and use in source and binary forms, with or without 40 * modification, are permitted provided that the following conditions 41 * are met: 42 * 1. Redistributions of source code must retain the above copyright 43 * notice, this list of conditions and the following disclaimer. 44 * 2. Redistributions in binary form must reproduce the above copyright 45 * notice, this list of conditions and the following disclaimer in the 46 * documentation and/or other materials provided with the distribution. 47 * 3. Neither the name of the University nor the names of its contributors 48 * may be used to endorse or promote products derived from this software 49 * without specific prior written permission. 50 * 51 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 52 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 53 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 54 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 55 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 56 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 57 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 58 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 59 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 60 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 61 * SUCH DAMAGE. 62 */ 63 64#if HAVE_NBTOOL_CONFIG_H 65#include "nbtool_config.h" 66#endif 67 68#include <sys/cdefs.h> 69#ifndef lint 70__COPYRIGHT("@(#) Copyright (c) 1987, 1993\ 71 The Regents of the University of California. All rights reserved."); 72#endif /* not lint */ 73 74#ifndef lint 75#if 0 76static char sccsid[] = "@(#)disklabel.c 8.4 (Berkeley) 5/4/95"; 77/* from static char sccsid[] = "@(#)disklabel.c 1.2 (Symmetric) 11/28/85"; */ 78#else 79__RCSID("$NetBSD: main.c,v 1.25 2011/08/29 14:34:59 joerg Exp $"); 80#endif 81#endif /* not lint */ 82 83#include <sys/param.h> 84#include <sys/file.h> 85#include <sys/stat.h> 86#include <sys/wait.h> 87#define DKTYPENAMES 88#define FSTYPENAMES 89 90#include <ctype.h> 91#include <err.h> 92#include <errno.h> 93#include <signal.h> 94#include <string.h> 95#include <stdio.h> 96#include <stdlib.h> 97#include <limits.h> 98#include <unistd.h> 99 100#include <ufs/ufs/dinode.h> 101#include <ufs/ffs/fs.h> 102 103#if HAVE_NBTOOL_CONFIG_H 104#include <nbinclude/sys/disklabel.h> 105#include <nbinclude/sys/disklabel_acorn.h> 106#include <nbinclude/sys/bootblock.h> 107#include "../../include/disktab.h" 108#else 109#include <sys/ioctl.h> 110#include <sys/disklabel.h> 111#include <sys/disklabel_acorn.h> 112#include <sys/bootblock.h> 113#include <util.h> 114#include <disktab.h> 115#endif /* HAVE_NBTOOL_CONFIG_H */ 116 117#include "pathnames.h" 118#include "extern.h" 119#include "dkcksum.h" 120#include "bswap.h" 121 122/* 123 * Disklabel: read and write disklabels. 124 * The label is usually placed on one of the first sectors of the disk. 125 * Many machines also place a bootstrap in the same area, 126 * in which case the label is embedded in the bootstrap. 127 * The bootstrap source must leave space at the proper offset 128 * for the label on such machines. 129 */ 130 131#ifndef BBSIZE 132#define BBSIZE 8192 /* size of boot area, with label */ 133#endif 134 135#define DISKMAGIC_REV bswap32(DISKMAGIC) 136/* To delete a label, we just invert the magic numbers */ 137#define DISKMAGIC_DELETED (~DISKMAGIC) 138#define DISKMAGIC_DELETED_REV bswap32(~DISKMAGIC) 139 140#define DEFEDITOR _PATH_VI 141 142char specname[MAXPATHLEN]; 143 144/* Some global data, all too hard to pass about */ 145char bootarea[BBSIZE]; /* Buffer matching part of disk */ 146int bootarea_len; /* Number of bytes we actually read */ 147static struct disklabel lab; /* The label we have updated */ 148 149static int Aflag; /* Action all labels */ 150static int Fflag; /* Read/write from file */ 151static int rflag; /* Read/write direct from disk */ 152static int tflag; /* Format output as disktab */ 153 int Cflag; /* CHS format output */ 154static int Dflag; /* Delete old labels (use with write) */ 155static int Iflag; /* Read/write direct, but default if absent */ 156static int lflag; /* List all known file system types and exit */ 157static int mflag; /* Expect disk to contain an MBR */ 158static int verbose; 159static int read_all; /* set if op = READ && Aflag */ 160 161static int write_label(int); 162static int readlabel_direct(int); 163static void writelabel_direct(int); 164static int update_label(int, u_int, u_int); 165static struct disklabel *find_label(int, u_int); 166 167static void makedisktab(FILE *, struct disklabel *); 168static void makelabel(const char *, const char *); 169static void l_perror(const char *); 170static void readlabel(int); 171static int edit(int); 172static int editit(const char *); 173static char *skip(char *); 174static char *word(char *); 175static int getasciilabel(FILE *, struct disklabel *); 176__dead static void usage(void); 177static int qsort_strcmp(const void *, const void *); 178static int getulong(const char *, char, char **, 179 unsigned long *, unsigned long); 180#define GETNUM32(a, v) getulong(a, '\0', NULL, v, UINT32_MAX) 181#define GETNUM16(a, v) getulong(a, '\0', NULL, v, UINT16_MAX) 182#define GETNUM8(a, v) getulong(a, '\0', NULL, v, UINT8_MAX) 183 184static int set_writable_fd = -1; 185 186#if HAVE_NBTOOL_CONFIG_H 187#define GETLABELOFFSET() LABELOFFSET 188#define GETLABELSECTOR() LABELSECTOR 189#define GETLABELUSESMBR() LABELUSESMBR 190#else /* HAVE_NBTOOL_CONFIG_H */ 191#define GETLABELOFFSET() getlabeloffset() 192#define GETLABELSECTOR() getlabelsector() 193#define GETLABELUSESMBR() getlabelusesmbr() 194#endif 195 196/* Default location for label - only used if we don't find one to update */ 197#define LABEL_OFFSET (dklabel_getlabelsector() * DEV_BSIZE + dklabel_getlabeloffset()) 198 199/* 200 * For portability it doesn't make sense to use any other value.... 201 * Except, maybe, the size of a physical sector. 202 * This value is used if we have to write a label to the start of an mbr ptn. 203 */ 204#ifndef LABELOFFSET_MBR 205#define LABELOFFSET_MBR 512 206#endif 207 208#if HAVE_NBTOOL_CONFIG_H 209static int 210opendisk(const char *path, int flags, char *buf, int buflen, int cooked) 211{ 212 int f; 213 f = open(path, flags, 0); 214 strlcpy(buf, path, buflen); 215 return f; 216} 217 218static int 219dk_ioctl(int f, void *arg) 220{ 221 errno = ENOTTY; 222 return -1; 223} 224#define dk_ioctl(f, cmd, arg) dk_ioctl(f, arg) 225#else 226#define dk_ioctl(f, cmd, arg) ioctl(f, cmd, arg) 227#endif /* HAVE_NBTOOL_CONFIG_H */ 228 229static daddr_t 230dklabel_getlabelsector(void) 231{ 232 unsigned long int nval; 233 char *end; 234 const char *val; 235 236 if ((val = getenv("DISKLABELSECTOR")) == NULL) 237 return GETLABELSECTOR(); 238 if ((nval = strtoul(val, &end, 10)) == ULONG_MAX && errno == ERANGE) 239 err(EXIT_FAILURE, "DISKLABELSECTOR in environment"); 240 return nval; 241} 242 243static off_t 244dklabel_getlabeloffset(void) 245{ 246 unsigned long int nval; 247 char *end; 248 const char *val; 249 250 if ((val = getenv("DISKLABELOFFSET")) == NULL) 251 return GETLABELOFFSET(); 252 if ((nval = strtoul(val, &end, 10)) == ULONG_MAX && errno == ERANGE) 253 err(EXIT_FAILURE, "DISKLABELOFFSET in environment"); 254 return nval; 255} 256 257static void 258clear_writable(void) 259{ 260 static int zero = 0; 261 dk_ioctl(set_writable_fd, DIOCWLABEL, &zero); 262} 263 264int 265main(int argc, char *argv[]) 266{ 267 FILE *t; 268 int ch, f, error; 269 char *dkname; 270 struct stat sb; 271 int writable; 272 enum { 273 UNSPEC, EDIT, READ, RESTORE, SETWRITABLE, SETREADONLY, 274 WRITE, INTERACT, DELETE 275 } op = UNSPEC, old_op; 276 277 mflag = GETLABELUSESMBR(); 278 if (mflag < 0) { 279 warn("getlabelusesmbr() failed"); 280 mflag = LABELUSESMBR; 281 } 282#if HAVE_NBTOOL_CONFIG_H 283 /* We must avoid doing any ioctl requests */ 284 Fflag = rflag = 1; 285#endif 286 287 error = 0; 288 while ((ch = getopt(argc, argv, "ACDFINRWb:ef:ilmrs:tvw")) != -1) { 289 old_op = op; 290 switch (ch) { 291 case 'A': /* Action all labels */ 292 Aflag = 1; 293 rflag = 1; 294 break; 295 case 'C': /* Display in CHS format */ 296 Cflag = 1; 297 break; 298 case 'D': /* Delete all existing labels */ 299 Dflag = 1; 300 rflag = 1; 301 break; 302 case 'F': /* Treat 'disk' as a regular file */ 303 Fflag = 1; 304 rflag = 1; /* Force direct access */ 305 break; 306 case 'I': /* Use default label if none found */ 307 Iflag = 1; 308 rflag = 1; /* Implies direct access */ 309 break; 310 case 'R': /* Restore label from text file */ 311 op = RESTORE; 312 break; 313 case 'N': /* Disallow writes to label sector */ 314 op = SETREADONLY; 315 break; 316 case 'W': /* Allow writes to label sector */ 317 op = SETWRITABLE; 318 break; 319 case 'e': /* Edit label with $EDITOR */ 320 op = EDIT; 321 break; 322 case 'f': /* Name of disktab file */ 323 if (setdisktab(optarg) == -1) 324 usage(); 325 break; 326 case 'i': /* Edit using built-in editor */ 327 op = INTERACT; 328 break; 329 case 'l': /* List all known file system types and exit */ 330 lflag = 1; 331 break; 332 case 'm': /* Expect disk to have an MBR */ 333 mflag ^= 1; 334 break; 335 case 'r': /* Read/write label directly from disk */ 336 rflag = 1; 337 break; 338 case 't': /* Format output as a disktab entry */ 339 tflag = 1; 340 break; 341 case 'v': /* verbose/diag output */ 342 verbose++; 343 break; 344 case 'w': /* Write label based on disktab entry */ 345 op = WRITE; 346 break; 347 case '?': 348 default: 349 usage(); 350 } 351 if (old_op != UNSPEC && old_op != op) 352 usage(); 353 } 354 argc -= optind; 355 argv += optind; 356 357 if (lflag) 358 exit(list_fs_types() ? EXIT_SUCCESS : EXIT_FAILURE); 359 360 if (op == UNSPEC) 361 op = Dflag ? DELETE : READ; 362 363 if (argc < 1) 364 usage(); 365 366 if (Iflag && op != EDIT && op != INTERACT) 367 usage(); 368 369 dkname = argv[0]; 370 f = opendisk(dkname, op == READ ? O_RDONLY : O_RDWR, 371 specname, sizeof specname, 0); 372 if (f < 0) 373 err(4, "%s", specname); 374 375 if (!Fflag && fstat(f, &sb) == 0 && S_ISREG(sb.st_mode)) 376 Fflag = rflag = 1; 377 378 switch (op) { 379 380 case DELETE: /* Remove all existing labels */ 381 if (argc != 1) 382 usage(); 383 Dflag = 2; 384 writelabel_direct(f); 385 break; 386 387 case EDIT: 388 if (argc != 1) 389 usage(); 390 readlabel(f); 391 error = edit(f); 392 break; 393 394 case INTERACT: 395 if (argc != 1) 396 usage(); 397 readlabel(f); 398 /* 399 * XXX: Fill some default values so checklabel does not fail 400 */ 401 if (lab.d_bbsize == 0) 402 lab.d_bbsize = BBSIZE; 403 if (lab.d_sbsize == 0) 404 lab.d_sbsize = SBLOCKSIZE; 405 interact(&lab, f); 406 break; 407 408 case READ: 409 if (argc != 1) 410 usage(); 411 read_all = Aflag; 412 readlabel(f); 413 if (read_all) 414 /* Label got printed in the bowels of readlabel */ 415 break; 416 if (tflag) 417 makedisktab(stdout, &lab); 418 else { 419 showinfo(stdout, &lab, specname); 420 showpartitions(stdout, &lab, Cflag); 421 } 422 error = checklabel(&lab); 423 if (error) 424 error += 100; 425 break; 426 427 case RESTORE: 428 if (argc != 2) 429 usage(); 430 if (!(t = fopen(argv[1], "r"))) 431 err(4, "%s", argv[1]); 432 if (getasciilabel(t, &lab)) 433 error = write_label(f); 434 else 435 error = 1; 436 break; 437 438 case SETREADONLY: 439 writable = 0; 440 goto do_diocwlabel; 441 case SETWRITABLE: 442 writable = 1; 443 do_diocwlabel: 444 if (argc != 1) 445 usage(); 446 if (dk_ioctl(f, DIOCWLABEL, &writable) < 0) 447 err(4, "ioctl DIOCWLABEL"); 448 break; 449 450 case WRITE: /* Create label from /etc/disktab entry & write */ 451 if (argc < 2 || argc > 3) 452 usage(); 453 makelabel(argv[1], argv[2]); 454 if (checklabel(&lab) == 0) 455 error = write_label(f); 456 else 457 error = 1; 458 break; 459 460 case UNSPEC: 461 usage(); 462 463 } 464 exit(error); 465} 466 467/* 468 * Construct a prototype disklabel from /etc/disktab. 469 */ 470static void 471makelabel(const char *type, const char *name) 472{ 473 struct disklabel *dp; 474 475 dp = getdiskbyname(type); 476 if (dp == NULL) 477 errx(1, "unknown disk type: %s", type); 478 lab = *dp; 479 480 /* d_packname is union d_boot[01], so zero */ 481 (void)memset(lab.d_packname, 0, sizeof(lab.d_packname)); 482 if (name) 483 (void)strncpy(lab.d_packname, name, sizeof(lab.d_packname)); 484} 485 486static int 487write_label(int f) 488{ 489 int writable; 490 491 lab.d_magic = DISKMAGIC; 492 lab.d_magic2 = DISKMAGIC; 493 lab.d_checksum = 0; 494 lab.d_checksum = dkcksum(&lab); 495 496 if (rflag) { 497 /* Write the label directly to the disk */ 498 499 /* 500 * First set the kernel disk label, 501 * then write a label to the raw disk. 502 * If the SDINFO ioctl fails because it is unimplemented, 503 * keep going; otherwise, the kernel consistency checks 504 * may prevent us from changing the current (in-core) 505 * label. 506 */ 507 if (!Fflag && dk_ioctl(f, DIOCSDINFO, &lab) < 0 && 508 errno != ENODEV && errno != ENOTTY) { 509 l_perror("ioctl DIOCSDINFO"); 510 return (1); 511 } 512 /* 513 * write enable label sector before write (if necessary), 514 * disable after writing. 515 */ 516 writable = 1; 517 if (!Fflag) { 518 if (dk_ioctl(f, DIOCWLABEL, &writable) < 0) 519 perror("ioctl DIOCWLABEL"); 520 set_writable_fd = f; 521 atexit(clear_writable); 522 } 523 524 writelabel_direct(f); 525 526 /* 527 * Now issue a DIOCWDINFO. This will let the kernel convert the 528 * disklabel to some machdep format if needed. 529 */ 530 /* XXX: This is stupid! */ 531 if (!Fflag && dk_ioctl(f, DIOCWDINFO, &lab) < 0) { 532 l_perror("ioctl DIOCWDINFO"); 533 return (1); 534 } 535 } else { 536 /* Get the kernel to write the label */ 537 if (dk_ioctl(f, DIOCWDINFO, &lab) < 0) { 538 l_perror("ioctl DIOCWDINFO"); 539 return (1); 540 } 541 } 542 543#ifdef VAX_ALTLABELS 544 if (lab.d_type == DTYPE_SMD && lab.d_flags & D_BADSECT && 545 lab.d_secsize == 512) { 546 /* Write the label to the odd sectors of the last track! */ 547 daddr_t alt; 548 int i; 549 uint8_t sec0[512]; 550 551 if (pread(f, sec0, 512, 0) < 512) { 552 warn("read master label to write alternates"); 553 return 0; 554 } 555 556 alt = lab.d_ncylinders * lab.d_secpercyl - lab.d_nsectors; 557 for (i = 1; i < 11 && (uint32_t)i < lab.d_nsectors; i += 2) { 558 if (pwrite(f, sec0, 512, (off_t)(alt + i) * 512) < 512) 559 warn("alternate label %d write", i/2); 560 } 561 } 562#endif /* VAX_ALTLABELS */ 563 564 return 0; 565} 566 567int 568writelabel(int f, struct disklabel *lp) 569{ 570 if (lp != &lab) 571 lab = *lp; 572 return write_label(f); 573} 574 575static void 576l_perror(const char *s) 577{ 578 579 switch (errno) { 580 581 case ESRCH: 582 warnx("%s: No disk label on disk;\n" 583 "use \"disklabel -I\" to install initial label", s); 584 break; 585 586 case EINVAL: 587 warnx("%s: Label magic number or checksum is wrong!\n" 588 "(disklabel or kernel is out of date?)", s); 589 break; 590 591 case EBUSY: 592 warnx("%s: Open partition would move or shrink", s); 593 break; 594 595 case EXDEV: 596 warnx("%s: Labeled partition or 'a' partition must start" 597 " at beginning of disk", s); 598 break; 599 600 default: 601 warn("%s", s); 602 break; 603 } 604} 605 606#ifdef NO_MBR_SUPPORT 607#define process_mbr(f, action) 1 608#else 609/* 610 * Scan DOS/MBR partition table and extended partition list for NetBSD ptns. 611 */ 612static int 613process_mbr(int f, int (*action)(int, u_int)) 614{ 615 struct mbr_partition *dp; 616 struct mbr_sector mbr; 617 int rval = 1, res; 618 int part; 619 u_int ext_base, next_ext, this_ext, start; 620 621 ext_base = 0; 622 next_ext = 0; 623 for (;;) { 624 this_ext = next_ext; 625 next_ext = 0; 626 if (verbose > 1) 627 warnx("reading mbr sector %u", this_ext); 628 if (pread(f, &mbr, sizeof mbr, this_ext * (off_t)DEV_BSIZE) 629 != sizeof(mbr)) { 630 if (verbose) 631 warn("Can't read master boot record %u", 632 this_ext); 633 break; 634 } 635 636 /* Check if table is valid. */ 637 if (mbr.mbr_magic != htole16(MBR_MAGIC)) { 638 if (verbose) 639 warnx("Invalid signature in mbr record %u", 640 this_ext); 641 break; 642 } 643 644 dp = &mbr.mbr_parts[0]; 645 646 /* Find NetBSD partition(s). */ 647 for (part = 0; part < MBR_PART_COUNT; dp++, part++) { 648 start = le32toh(dp->mbrp_start); 649 switch (dp->mbrp_type) { 650#ifdef COMPAT_386BSD_MBRPART 651 case MBR_PTYPE_386BSD: 652 if (ext_base != 0) 653 break; 654 /* FALLTHROUGH */ 655#endif 656 case MBR_PTYPE_NETBSD: 657 res = action(f, this_ext + start); 658 if (res <= 0) 659 /* Found or failure */ 660 return res; 661 if (res > rval) 662 /* Keep largest value */ 663 rval = res; 664 break; 665 case MBR_PTYPE_EXT: 666 case MBR_PTYPE_EXT_LBA: 667 case MBR_PTYPE_EXT_LNX: 668 next_ext = start; 669 break; 670 default: 671 break; 672 } 673 } 674 if (next_ext == 0) 675 /* No more extended partitions */ 676 break; 677 next_ext += ext_base; 678 if (ext_base == 0) 679 ext_base = next_ext; 680 681 if (next_ext <= this_ext) { 682 if (verbose) 683 warnx("Invalid extended chain %x <= %x", 684 next_ext, this_ext); 685 break; 686 } 687 /* Maybe we should check against the disk size... */ 688 } 689 690 return rval; 691} 692 693static int 694readlabel_mbr(int f, u_int sector) 695{ 696 struct disklabel *disk_lp; 697 698 disk_lp = find_label(f, sector); 699 if (disk_lp == NULL) 700 return 1; 701 targettohlabel(&lab, disk_lp); 702 return 0; 703} 704 705static int 706writelabel_mbr(int f, u_int sector) 707{ 708 return update_label(f, sector, mflag ? LABELOFFSET_MBR : ~0U) ? 2 : 0; 709} 710 711#endif /* !NO_MBR_SUPPORT */ 712 713#ifndef USE_ACORN 714#define get_filecore_partition(f) 0 715#else 716/* 717 * static int filecore_checksum(u_char *bootblock) 718 * 719 * Calculates the filecore boot block checksum. This is used to validate 720 * a filecore boot block on the disk. If a boot block is validated then 721 * it is used to locate the partition table. If the boot block is not 722 * validated, it is assumed that the whole disk is NetBSD. 723 * 724 * The basic algorithm is: 725 * 726 * for (each byte in block, excluding checksum) { 727 * sum += byte; 728 * if (sum > 255) 729 * sum -= 255; 730 * } 731 * 732 * That's equivalent to summing all of the bytes in the block 733 * (excluding the checksum byte, of course), then calculating the 734 * checksum as "cksum = sum - ((sum - 1) / 255) * 255)". That 735 * expression may or may not yield a faster checksum function, 736 * but it's easier to reason about. 737 * 738 * Note that if you have a block filled with bytes of a single 739 * value "X" (regardless of that value!) and calculate the cksum 740 * of the block (excluding the checksum byte), you will _always_ 741 * end up with a checksum of X. (Do the math; that can be derived 742 * from the checksum calculation function!) That means that 743 * blocks which contain bytes which all have the same value will 744 * always checksum properly. That's a _very_ unlikely occurence 745 * (probably impossible, actually) for a valid filecore boot block, 746 * so we treat such blocks as invalid. 747 */ 748static int 749filecore_checksum(u_char *bootblock) 750{ 751 u_char byte0, accum_diff; 752 u_int sum; 753 int i; 754 755 sum = 0; 756 accum_diff = 0; 757 byte0 = bootblock[0]; 758 759 /* 760 * Sum the contents of the block, keeping track of whether 761 * or not all bytes are the same. If 'accum_diff' ends up 762 * being zero, all of the bytes are, in fact, the same. 763 */ 764 for (i = 0; i < 511; ++i) { 765 sum += bootblock[i]; 766 accum_diff |= bootblock[i] ^ byte0; 767 } 768 769 /* 770 * Check to see if the checksum byte is the same as the 771 * rest of the bytes, too. (Note that if all of the bytes 772 * are the same except the checksum, a checksum compare 773 * won't succeed, but that's not our problem.) 774 */ 775 accum_diff |= bootblock[i] ^ byte0; 776 777 /* All bytes in block are the same; call it invalid. */ 778 if (accum_diff == 0) 779 return (-1); 780 781 return (sum - ((sum - 1) / 255) * 255); 782} 783 784/* 785 * Check for the presence of a RiscOS filecore boot block 786 * indicating an ADFS file system on the disc. 787 * Return the offset to the NetBSD part of the disc if 788 * this can be determined. 789 * This routine will terminate disklabel if the disc 790 * is found to be ADFS only. 791 */ 792static u_int 793get_filecore_partition(int f) 794{ 795 struct filecore_bootblock *fcbb; 796 static u_char bb[DEV_BSIZE]; 797 u_int offset; 798 struct riscix_partition_table *riscix_part; 799 int loop; 800 801 if (pread(f, bb, sizeof(bb), (off_t)FILECORE_BOOT_SECTOR * DEV_BSIZE) != sizeof(bb)) 802 err(4, "can't read filecore boot block"); 803 fcbb = (struct filecore_bootblock *)bb; 804 805 /* Check if table is valid. */ 806 if (filecore_checksum(bb) != fcbb->checksum) 807 return (0); 808 809 /* 810 * Check for NetBSD/arm32 (RiscBSD) partition marker. 811 * If found the NetBSD disklabel location is easy. 812 */ 813 offset = (fcbb->partition_cyl_low + (fcbb->partition_cyl_high << 8)) 814 * fcbb->heads * fcbb->secspertrack; 815 816 switch (fcbb->partition_type) { 817 818 case PARTITION_FORMAT_RISCBSD: 819 return (offset); 820 821 case PARTITION_FORMAT_RISCIX: 822 /* 823 * Read the RISCiX partition table and search for the 824 * first partition named "RiscBSD", "NetBSD", or "Empty:" 825 * 826 * XXX is use of 'Empty:' really desirable?! -- cgd 827 */ 828 829 if (pread(f, bb, sizeof(bb), (off_t)offset * DEV_BSIZE) != sizeof(bb)) 830 err(4, "can't read riscix partition table"); 831 riscix_part = (struct riscix_partition_table *)bb; 832 833 for (loop = 0; loop < NRISCIX_PARTITIONS; ++loop) { 834 if (strcmp((char *)riscix_part->partitions[loop].rp_name, 835 "RiscBSD") == 0 || 836 strcmp((char *)riscix_part->partitions[loop].rp_name, 837 "NetBSD") == 0 || 838 strcmp((char *)riscix_part->partitions[loop].rp_name, 839 "Empty:") == 0) { 840 return riscix_part->partitions[loop].rp_start; 841 break; 842 } 843 } 844 /* 845 * Valid filecore boot block, RISCiX partition table 846 * but no NetBSD partition. We should leave this 847 * disc alone. 848 */ 849 errx(4, "cannot label: no NetBSD partition found" 850 " in RISCiX partition table"); 851 852 default: 853 /* 854 * Valid filecore boot block and no non-ADFS partition. 855 * This means that the whole disc is allocated for ADFS 856 * so do not trash ! If the user really wants to put a 857 * NetBSD disklabel on the disc then they should remove 858 * the filecore boot block first with dd. 859 */ 860 errx(4, "cannot label: filecore-only disk" 861 " (no non-ADFS partition)"); 862 } 863 return (0); 864} 865#endif /* USE_ACORN */ 866 867/* 868 * Fetch disklabel for disk to 'lab'. 869 * Use ioctl to get label unless -r flag is given. 870 */ 871static void 872readlabel(int f) 873{ 874 if (rflag) { 875 /* Get label directly from disk */ 876 if (readlabel_direct(f) == 0) 877 return; 878 /* 879 * There was no label on the disk. Get the fictious one 880 * as a basis for initialisation. 881 */ 882 if (!Fflag && Iflag && (dk_ioctl(f, DIOCGDINFO, &lab) == 0 || 883 dk_ioctl(f, DIOCGDEFLABEL, &lab) == 0)) 884 return; 885 } else { 886 /* Get label from kernel. */ 887 if (dk_ioctl(f, DIOCGDINFO, &lab) < 0) 888 err(4, "ioctl DIOCGDINFO"); 889 return; 890 } 891 892 if (read_all == 2) 893 /* We actually found one, and printed it... */ 894 exit(0); 895 errx(1, "could not read existing label"); 896} 897 898/* 899 * Reading the label from the disk is largely a case of 'hunt the label'. 900 * and since different architectures default to different places there 901 * could even be more than one label that contradict each other! 902 * For now we look in the expected place, then search through likely 903 * other locations. 904 */ 905static struct disklabel * 906find_label(int f, u_int sector) 907{ 908 struct disklabel *disk_lp, hlp; 909 int i; 910 u_int offset; 911 const char *is_deleted; 912 913 bootarea_len = pread(f, bootarea, sizeof bootarea, 914 sector * (off_t)DEV_BSIZE); 915 if (bootarea_len <= 0) { 916 if (verbose) 917 warn("failed to read bootarea from sector %u", sector); 918 return NULL; 919 } 920 921 if (verbose > 2) 922 warnx("read sector %u len %d looking for label", 923 sector, bootarea_len); 924 925 /* Check expected offset first */ 926 for (offset = LABEL_OFFSET, i = -4;; offset = i += 4) { 927 is_deleted = ""; 928 disk_lp = (void *)(bootarea + offset); 929 if (i == LABEL_OFFSET) 930 continue; 931 if ((char *)(disk_lp + 1) > bootarea + bootarea_len) 932 break; 933 if (disk_lp->d_magic2 != disk_lp->d_magic) 934 continue; 935 if (read_all && (disk_lp->d_magic == DISKMAGIC_DELETED || 936 disk_lp->d_magic == DISKMAGIC_DELETED_REV)) { 937 disk_lp->d_magic ^= ~0u; 938 disk_lp->d_magic2 ^= ~0u; 939 is_deleted = "deleted "; 940 } 941 if (target32toh(disk_lp->d_magic) != DISKMAGIC) { 942 /* XXX: Do something about byte-swapped labels ? */ 943 if (target32toh(disk_lp->d_magic) == DISKMAGIC_REV && 944 target32toh(disk_lp->d_magic2) == DISKMAGIC_REV) 945 warnx("ignoring %sbyteswapped label" 946 " at offset %u from sector %u", 947 is_deleted, offset, sector); 948 continue; 949 } 950 if (target16toh(disk_lp->d_npartitions) > MAXPARTITIONS || 951 dkcksum_target(disk_lp) != 0) { 952 if (verbose > 0) 953 warnx("corrupt label found at offset %u in " 954 "sector %u", offset, sector); 955 continue; 956 } 957 if (verbose > 1) 958 warnx("%slabel found at offset %u from sector %u", 959 is_deleted, offset, sector); 960 if (!read_all) 961 return disk_lp; 962 963 /* To print all the labels we have to do it here */ 964 /* XXX: maybe we should compare them? */ 965 targettohlabel(&hlp, disk_lp); 966 printf("# %ssector %u offset %u bytes\n", 967 is_deleted, sector, offset); 968 if (tflag) 969 makedisktab(stdout, &hlp); 970 else { 971 showinfo(stdout, &hlp, specname); 972 showpartitions(stdout, &hlp, Cflag); 973 } 974 checklabel(&hlp); 975 htotargetlabel(disk_lp, &hlp); 976 /* Remember we've found a label */ 977 read_all = 2; 978 } 979 return NULL; 980} 981 982static void 983write_bootarea(int f, u_int sector) 984{ 985 int wlen; 986 987 if (bootarea_len <= 0) 988 errx(1, "attempting to write after failed read"); 989 990#ifdef ALPHA_BOOTBLOCK_CKSUM 991 /* 992 * The Alpha requires that the boot block be checksummed. 993 * <sys/bootblock.h> provides a macro to do it. 994 */ 995 if (sector == 0) { 996 struct alpha_boot_block *bb; 997 998 bb = (struct alpha_boot_block *)(void *)bootarea; 999 bb->bb_cksum = 0; 1000 ALPHA_BOOT_BLOCK_CKSUM(bb, &bb->bb_cksum); 1001 } 1002#endif /* ALPHA_BOOTBLOCK_CKSUM */ 1003 1004 wlen = pwrite(f, bootarea, bootarea_len, sector * (off_t)DEV_BSIZE); 1005 if (wlen == bootarea_len) 1006 return; 1007 if (wlen == -1) 1008 err(1, "disklabel write (sector %u) size %d failed", 1009 sector, bootarea_len); 1010 errx(1, "disklabel write (sector %u) size %d truncated to %d", 1011 sector, bootarea_len, wlen); 1012} 1013 1014static int 1015update_label(int f, u_int label_sector, u_int label_offset) 1016{ 1017 struct disklabel *disk_lp; 1018 1019 disk_lp = find_label(f, label_sector); 1020 1021 if (disk_lp && Dflag) { 1022 /* Invalidate the existing label */ 1023 disk_lp->d_magic ^= ~0u; 1024 disk_lp->d_magic2 ^= ~0u; 1025 if (Dflag == 2) 1026 write_bootarea(f, label_sector); 1027 /* Force label to default location */ 1028 disk_lp = NULL; 1029 } 1030 1031 if (Dflag == 2) 1032 /* We are just deleting the label */ 1033 return 0; 1034 1035 if (disk_lp == NULL) { 1036 if (label_offset == ~0u) 1037 return 0; 1038 /* Nothing on the disk - we need to add it */ 1039 disk_lp = (void *)(bootarea + label_offset); 1040 if ((char *)(disk_lp + 1) > bootarea + bootarea_len) 1041 errx(1, "no space in bootarea (sector %u) " 1042 "to create label", label_sector); 1043 } 1044 1045 htotargetlabel(disk_lp, &lab); 1046 write_bootarea(f, label_sector); 1047 return 1; 1048} 1049 1050static void 1051writelabel_direct(int f) 1052{ 1053 u_int label_sector; 1054 int written = 0; 1055 int rval; 1056 1057 label_sector = get_filecore_partition(f); 1058 if (label_sector != 0) 1059 /* The offset needs to be that from the acorn ports... */ 1060 written = update_label(f, label_sector, DEV_BSIZE); 1061 1062 rval = process_mbr(f, writelabel_mbr); 1063 1064 if (rval == 2 || written) 1065 /* Don't add a label to sector 0, but update one if there */ 1066 update_label(f, 0, ~0u); 1067 else 1068 update_label(f, 0, LABEL_OFFSET); 1069} 1070 1071static int 1072readlabel_direct(int f) 1073{ 1074 struct disklabel *disk_lp; 1075 u_int filecore_partition_offset; 1076 1077 filecore_partition_offset = get_filecore_partition(f); 1078 if (filecore_partition_offset != 0) { 1079 disk_lp = find_label(f, filecore_partition_offset); 1080 if (disk_lp != NULL) { 1081 targettohlabel(&lab, disk_lp); 1082 return 0; 1083 } 1084 } 1085 1086 if (mflag && process_mbr(f, readlabel_mbr) == 0) 1087 return 0; 1088 1089 disk_lp = find_label(f, 0); 1090 if (disk_lp != NULL) { 1091 targettohlabel(&lab, disk_lp); 1092 return 0; 1093 } 1094 1095 if (!mflag && process_mbr(f, readlabel_mbr) == 0) 1096 return 0; 1097 1098 return 1; 1099} 1100 1101static void 1102makedisktab(FILE *f, struct disklabel *lp) 1103{ 1104 int i; 1105 const char *did; 1106 struct partition *pp; 1107 1108 did = "\\\n\t:"; 1109 (void) fprintf(f, "%.*s|Automatically generated label:\\\n\t:dt=", 1110 (int) sizeof(lp->d_typename), lp->d_typename); 1111 if ((unsigned) lp->d_type < DKMAXTYPES) 1112 (void) fprintf(f, "%s:", dktypenames[lp->d_type]); 1113 else 1114 (void) fprintf(f, "unknown%" PRIu16 ":", lp->d_type); 1115 1116 (void) fprintf(f, "se#%" PRIu32 ":", lp->d_secsize); 1117 (void) fprintf(f, "ns#%" PRIu32 ":", lp->d_nsectors); 1118 (void) fprintf(f, "nt#%" PRIu32 ":", lp->d_ntracks); 1119 (void) fprintf(f, "sc#%" PRIu32 ":", lp->d_secpercyl); 1120 (void) fprintf(f, "nc#%" PRIu32 ":", lp->d_ncylinders); 1121 1122 if ((lp->d_secpercyl * lp->d_ncylinders) != lp->d_secperunit) { 1123 (void) fprintf(f, "%ssu#%" PRIu32 ":", did, lp->d_secperunit); 1124 did = ""; 1125 } 1126 if (lp->d_rpm != 3600) { 1127 (void) fprintf(f, "%srm#%" PRIu16 ":", did, lp->d_rpm); 1128 did = ""; 1129 } 1130 if (lp->d_interleave != 1) { 1131 (void) fprintf(f, "%sil#%" PRIu16 ":", did, lp->d_interleave); 1132 did = ""; 1133 } 1134 if (lp->d_trackskew != 0) { 1135 (void) fprintf(f, "%ssk#%" PRIu16 ":", did, lp->d_trackskew); 1136 did = ""; 1137 } 1138 if (lp->d_cylskew != 0) { 1139 (void) fprintf(f, "%scs#%" PRIu16 ":", did, lp->d_cylskew); 1140 did = ""; 1141 } 1142 if (lp->d_headswitch != 0) { 1143 (void) fprintf(f, "%shs#%" PRIu16 ":", did, lp->d_headswitch); 1144 did = ""; 1145 } 1146 if (lp->d_trkseek != 0) { 1147 (void) fprintf(f, "%sts#%" PRIu32 ":", did, lp->d_trkseek); 1148 did = ""; 1149 } 1150#ifdef notyet 1151 (void) fprintf(f, "drivedata: "); 1152 for (i = NDDATA - 1; i >= 0; i--) 1153 if (lp->d_drivedata[i]) 1154 break; 1155 if (i < 0) 1156 i = 0; 1157 for (j = 0; j <= i; j++) 1158 (void) fprintf(f, "%" PRIu32 " ", lp->d_drivedata[j]); 1159#endif /* notyet */ 1160 pp = lp->d_partitions; 1161 for (i = 0; i < lp->d_npartitions; i++, pp++) { 1162 if (pp->p_size) { 1163 char c = 'a' + i; 1164 (void) fprintf(f, "\\\n\t:"); 1165 (void) fprintf(f, "p%c#%" PRIu32 ":", c, pp->p_size); 1166 (void) fprintf(f, "o%c#%" PRIu32 ":", c, pp->p_offset); 1167 if (pp->p_fstype != FS_UNUSED) { 1168 if ((unsigned) pp->p_fstype < FSMAXTYPES) 1169 (void) fprintf(f, "t%c=%s:", c, 1170 fstypenames[pp->p_fstype]); 1171 else 1172 (void) fprintf(f, 1173 "t%c=unknown%" PRIu8 ":", 1174 c, pp->p_fstype); 1175 } 1176 switch (pp->p_fstype) { 1177 1178 case FS_UNUSED: 1179 break; 1180 1181 case FS_BSDFFS: 1182 case FS_BSDLFS: 1183 case FS_EX2FS: 1184 case FS_ADOS: 1185 case FS_APPLEUFS: 1186 (void) fprintf(f, "b%c#%" PRIu64 ":", c, 1187 (uint64_t)pp->p_fsize * pp->p_frag); 1188 (void) fprintf(f, "f%c#%" PRIu32 ":", c, 1189 pp->p_fsize); 1190 break; 1191 default: 1192 break; 1193 } 1194 } 1195 } 1196 (void) fprintf(f, "\n"); 1197 (void) fflush(f); 1198} 1199 1200static int 1201edit(int f) 1202{ 1203 const char *tmpdir; 1204 char tmpfil[MAXPATHLEN]; 1205 int first, ch, fd; 1206 int get_ok; 1207 FILE *fp; 1208 1209 if ((tmpdir = getenv("TMPDIR")) == NULL) 1210 tmpdir = _PATH_TMP; 1211 (void)snprintf(tmpfil, sizeof(tmpfil), "%s/%s", tmpdir, TMPFILE); 1212 if ((fd = mkstemp(tmpfil)) == -1 || (fp = fdopen(fd, "w")) == NULL) { 1213 warn("%s", tmpfil); 1214 return (1); 1215 } 1216 (void)fchmod(fd, 0600); 1217 showinfo(fp, &lab, specname); 1218 showpartitions(fp, &lab, Cflag); 1219 (void) fclose(fp); 1220 for (;;) { 1221 if (!editit(tmpfil)) 1222 break; 1223 fp = fopen(tmpfil, "r"); 1224 if (fp == NULL) { 1225 warn("%s", tmpfil); 1226 break; 1227 } 1228 (void) memset(&lab, 0, sizeof(lab)); 1229 get_ok = getasciilabel(fp, &lab); 1230 fclose(fp); 1231 if (get_ok && write_label(f) == 0) { 1232 (void) unlink(tmpfil); 1233 return (0); 1234 } 1235 (void) printf("re-edit the label? [y]: "); 1236 (void) fflush(stdout); 1237 first = ch = getchar(); 1238 while (ch != '\n' && ch != EOF) 1239 ch = getchar(); 1240 if (first == 'n' || first == 'N') 1241 break; 1242 } 1243 (void)unlink(tmpfil); 1244 return (1); 1245} 1246 1247static int 1248editit(const char *tmpfil) 1249{ 1250 int pid, xpid; 1251 int status; 1252 sigset_t nsigset, osigset; 1253 1254 sigemptyset(&nsigset); 1255 sigaddset(&nsigset, SIGINT); 1256 sigaddset(&nsigset, SIGQUIT); 1257 sigaddset(&nsigset, SIGHUP); 1258 sigprocmask(SIG_BLOCK, &nsigset, &osigset); 1259 while ((pid = fork()) < 0) { 1260 if (errno != EAGAIN) { 1261 sigprocmask(SIG_SETMASK, &osigset, (sigset_t *)0); 1262 warn("fork"); 1263 return (0); 1264 } 1265 sleep(1); 1266 } 1267 if (pid == 0) { 1268 const char *ed; 1269 char *buf; 1270 int retval; 1271 1272 sigprocmask(SIG_SETMASK, &osigset, (sigset_t *)0); 1273 setgid(getgid()); 1274 setuid(getuid()); 1275 if ((ed = getenv("EDITOR")) == (char *)0) 1276 ed = DEFEDITOR; 1277 /* 1278 * Jump through a few extra hoops in case someone's editor 1279 * is "editor arg1 arg2". 1280 */ 1281 asprintf(&buf, "%s %s", ed, tmpfil); 1282 if (!buf) 1283 err(1, "malloc"); 1284 retval = execlp(_PATH_BSHELL, _PATH_BSHELL, "-c", buf, NULL); 1285 if (retval == -1) 1286 perror(ed); 1287 exit(retval); 1288 } 1289 while ((xpid = wait(&status)) >= 0) 1290 if (xpid == pid) 1291 break; 1292 sigprocmask(SIG_SETMASK, &osigset, (sigset_t *)0); 1293 return (!status); 1294} 1295 1296static char * 1297skip(char *cp) 1298{ 1299 1300 cp += strspn(cp, " \t"); 1301 if (*cp == '\0') 1302 return (NULL); 1303 return (cp); 1304} 1305 1306static char * 1307word(char *cp) 1308{ 1309 1310 if (cp == NULL || *cp == '\0') 1311 return (NULL); 1312 1313 cp += strcspn(cp, " \t"); 1314 if (*cp == '\0') 1315 return (NULL); 1316 *cp++ = '\0'; 1317 cp += strspn(cp, " \t"); 1318 if (*cp == '\0') 1319 return (NULL); 1320 return (cp); 1321} 1322 1323#define _CHECKLINE \ 1324 if (tp == NULL || *tp == '\0') { \ 1325 warnx("line %d: too few fields", lineno); \ 1326 errors++; \ 1327 break; \ 1328 } 1329 1330#define __CHECKLINE \ 1331 if (*tp == NULL || **tp == '\0') { \ 1332 warnx("line %d: too few fields", lineno); \ 1333 *tp = _error_; \ 1334 return 0; \ 1335 } 1336 1337static char _error_[] = ""; 1338#define NXTNUM(n) if ((n = nxtnum(&tp, lineno),0) + tp != _error_) \ 1339 ; else goto error 1340#define NXTXNUM(n) if ((n = nxtxnum(&tp, lp, lineno),0) + tp != _error_) \ 1341 ; else goto error 1342 1343static unsigned long 1344nxtnum(char **tp, int lineno) 1345{ 1346 char *cp; 1347 unsigned long v; 1348 1349 __CHECKLINE 1350 if (getulong(*tp, '\0', &cp, &v, UINT32_MAX) != 0) { 1351 warnx("line %d: syntax error", lineno); 1352 *tp = _error_; 1353 return 0; 1354 } 1355 *tp = cp; 1356 return v; 1357} 1358 1359static unsigned long 1360nxtxnum(char **tp, struct disklabel *lp, int lineno) 1361{ 1362 char *cp, *ncp; 1363 unsigned long n, v; 1364 1365 __CHECKLINE 1366 cp = *tp; 1367 if (getulong(cp, '/', &ncp, &n, UINT32_MAX) != 0) 1368 goto bad; 1369 1370 if (*ncp == '/') { 1371 n *= lp->d_secpercyl; 1372 cp = ncp + 1; 1373 if (getulong(cp, '/', &ncp, &v, UINT32_MAX) != 0) 1374 goto bad; 1375 n += v * lp->d_nsectors; 1376 cp = ncp + 1; 1377 if (getulong(cp, '\0', &ncp, &v, UINT32_MAX) != 0) 1378 goto bad; 1379 n += v; 1380 } 1381 *tp = ncp; 1382 return n; 1383bad: 1384 warnx("line %d: invalid format", lineno); 1385 *tp = _error_; 1386 return 0; 1387} 1388 1389/* 1390 * Read an ascii label in from fd f, 1391 * in the same format as that put out by showinfo() and showpartitions(), 1392 * and fill in lp. 1393 */ 1394static int 1395getasciilabel(FILE *f, struct disklabel *lp) 1396{ 1397 const char *const *cpp, *s; 1398 struct partition *pp; 1399 char *cp, *tp, line[BUFSIZ], tbuf[15]; 1400 int lineno, errors; 1401 unsigned long v; 1402 unsigned int part; 1403 1404 lineno = 0; 1405 errors = 0; 1406 lp->d_bbsize = BBSIZE; /* XXX */ 1407 lp->d_sbsize = SBLOCKSIZE; /* XXX */ 1408 while (fgets(line, sizeof(line) - 1, f)) { 1409 lineno++; 1410 if ((cp = strpbrk(line, "#\r\n")) != NULL) 1411 *cp = '\0'; 1412 cp = skip(line); 1413 if (cp == NULL) /* blank line or comment line */ 1414 continue; 1415 tp = strchr(cp, ':'); /* everything has a colon in it */ 1416 if (tp == NULL) { 1417 warnx("line %d: syntax error", lineno); 1418 errors++; 1419 continue; 1420 } 1421 *tp++ = '\0', tp = skip(tp); 1422 if (!strcmp(cp, "type")) { 1423 if (tp == NULL) { 1424 strlcpy(tbuf, "unknown", sizeof(tbuf)); 1425 tp = tbuf; 1426 } 1427 cpp = dktypenames; 1428 for (; cpp < &dktypenames[DKMAXTYPES]; cpp++) 1429 if ((s = *cpp) && !strcasecmp(s, tp)) { 1430 lp->d_type = cpp - dktypenames; 1431 goto next; 1432 } 1433 if (GETNUM16(tp, &v) != 0) { 1434 warnx("line %d: syntax error", lineno); 1435 errors++; 1436 continue; 1437 } 1438 if (v >= DKMAXTYPES) 1439 warnx("line %d: warning, unknown disk type: %s", 1440 lineno, tp); 1441 lp->d_type = v; 1442 continue; 1443 } 1444 if (!strcmp(cp, "flags")) { 1445 for (v = 0; (cp = tp) && *cp != '\0';) { 1446 tp = word(cp); 1447 if (!strcasecmp(cp, "removable")) 1448 v |= D_REMOVABLE; 1449 else if (!strcasecmp(cp, "ecc")) 1450 v |= D_ECC; 1451 else if (!strcasecmp(cp, "badsect")) 1452 v |= D_BADSECT; 1453 else { 1454 warnx("line %d: bad flag: %s", 1455 lineno, cp); 1456 errors++; 1457 } 1458 } 1459 lp->d_flags = v; 1460 continue; 1461 } 1462 if (!strcmp(cp, "drivedata")) { 1463 int i; 1464 1465 for (i = 0; (cp = tp) && *cp != '\0' && i < NDDATA;) { 1466 if (GETNUM32(cp, &v) != 0) { 1467 warnx("line %d: bad drive data", 1468 lineno); 1469 errors++; 1470 } else 1471 lp->d_drivedata[i] = v; 1472 i++; 1473 tp = word(cp); 1474 } 1475 continue; 1476 } 1477 if (sscanf(cp, "%lu partitions", &v) == 1) { 1478 if (v == 0 || v > MAXPARTITIONS) { 1479 warnx("line %d: bad # of partitions", lineno); 1480 lp->d_npartitions = MAXPARTITIONS; 1481 errors++; 1482 } else 1483 lp->d_npartitions = v; 1484 continue; 1485 } 1486 if (tp == NULL) { 1487 tbuf[0] = '\0'; 1488 tp = tbuf; 1489 } 1490 if (!strcmp(cp, "disk")) { 1491 strncpy(lp->d_typename, tp, sizeof(lp->d_typename)); 1492 continue; 1493 } 1494 if (!strcmp(cp, "label")) { 1495 strncpy(lp->d_packname, tp, sizeof(lp->d_packname)); 1496 continue; 1497 } 1498 if (!strcmp(cp, "bytes/sector")) { 1499 if (GETNUM32(tp, &v) != 0 || v <= 0 || (v % 512) != 0) { 1500 warnx("line %d: bad %s: %s", lineno, cp, tp); 1501 errors++; 1502 } else 1503 lp->d_secsize = v; 1504 continue; 1505 } 1506 if (!strcmp(cp, "sectors/track")) { 1507 if (GETNUM32(tp, &v) != 0) { 1508 warnx("line %d: bad %s: %s", lineno, cp, tp); 1509 errors++; 1510 } else 1511 lp->d_nsectors = v; 1512 continue; 1513 } 1514 if (!strcmp(cp, "sectors/cylinder")) { 1515 if (GETNUM32(tp, &v) != 0) { 1516 warnx("line %d: bad %s: %s", lineno, cp, tp); 1517 errors++; 1518 } else 1519 lp->d_secpercyl = v; 1520 continue; 1521 } 1522 if (!strcmp(cp, "tracks/cylinder")) { 1523 if (GETNUM32(tp, &v) != 0) { 1524 warnx("line %d: bad %s: %s", lineno, cp, tp); 1525 errors++; 1526 } else 1527 lp->d_ntracks = v; 1528 continue; 1529 } 1530 if (!strcmp(cp, "cylinders")) { 1531 if (GETNUM32(tp, &v) != 0) { 1532 warnx("line %d: bad %s: %s", lineno, cp, tp); 1533 errors++; 1534 } else 1535 lp->d_ncylinders = v; 1536 continue; 1537 } 1538 if (!strcmp(cp, "total sectors") || 1539 !strcmp(cp, "sectors/unit")) { 1540 if (GETNUM32(tp, &v) != 0) { 1541 warnx("line %d: bad %s: %s", lineno, cp, tp); 1542 errors++; 1543 } else 1544 lp->d_secperunit = v; 1545 continue; 1546 } 1547 if (!strcmp(cp, "rpm")) { 1548 if (GETNUM16(tp, &v) != 0) { 1549 warnx("line %d: bad %s: %s", lineno, cp, tp); 1550 errors++; 1551 } else 1552 lp->d_rpm = v; 1553 continue; 1554 } 1555 if (!strcmp(cp, "interleave")) { 1556 if (GETNUM16(tp, &v) != 0) { 1557 warnx("line %d: bad %s: %s", lineno, cp, tp); 1558 errors++; 1559 } else 1560 lp->d_interleave = v; 1561 continue; 1562 } 1563 if (!strcmp(cp, "trackskew")) { 1564 if (GETNUM16(tp, &v) != 0) { 1565 warnx("line %d: bad %s: %s", lineno, cp, tp); 1566 errors++; 1567 } else 1568 lp->d_trackskew = v; 1569 continue; 1570 } 1571 if (!strcmp(cp, "cylinderskew")) { 1572 if (GETNUM16(tp, &v) != 0) { 1573 warnx("line %d: bad %s: %s", lineno, cp, tp); 1574 errors++; 1575 } else 1576 lp->d_cylskew = v; 1577 continue; 1578 } 1579 if (!strcmp(cp, "headswitch")) { 1580 if (GETNUM32(tp, &v) != 0) { 1581 warnx("line %d: bad %s: %s", lineno, cp, tp); 1582 errors++; 1583 } else 1584 lp->d_headswitch = v; 1585 continue; 1586 } 1587 if (!strcmp(cp, "track-to-track seek")) { 1588 if (GETNUM32(tp, &v) != 0) { 1589 warnx("line %d: bad %s: %s", lineno, cp, tp); 1590 errors++; 1591 } else 1592 lp->d_trkseek = v; 1593 continue; 1594 } 1595 if ('a' > *cp || *cp > 'z' || cp[1] != '\0') { 1596 warnx("line %d: unknown field: %s", lineno, cp); 1597 errors++; 1598 continue; 1599 } 1600 1601 /* We have a partition entry */ 1602 part = *cp - 'a'; 1603 1604 if (part >= MAXPARTITIONS) { 1605 warnx("line %d: bad partition name: %s", lineno, cp); 1606 errors++; 1607 continue; 1608 } 1609 pp = &lp->d_partitions[part]; 1610 1611 NXTXNUM(pp->p_size); 1612 NXTXNUM(pp->p_offset); 1613 /* can't use word() here because of blanks in fstypenames[] */ 1614 tp += strspn(tp, " \t"); 1615 _CHECKLINE 1616 cp = tp; 1617 cpp = fstypenames; 1618 for (; cpp < &fstypenames[FSMAXTYPES]; cpp++) { 1619 s = *cpp; 1620 if (s == NULL || 1621 (cp[strlen(s)] != ' ' && 1622 cp[strlen(s)] != '\t' && 1623 cp[strlen(s)] != '\0')) 1624 continue; 1625 if (!memcmp(s, cp, strlen(s))) { 1626 pp->p_fstype = cpp - fstypenames; 1627 tp += strlen(s); 1628 if (*tp == '\0') 1629 tp = NULL; 1630 else { 1631 tp += strspn(tp, " \t"); 1632 if (*tp == '\0') 1633 tp = NULL; 1634 } 1635 goto gottype; 1636 } 1637 } 1638 tp = word(cp); 1639 if (isdigit(*cp & 0xff)) { 1640 if (GETNUM8(cp, &v) != 0) { 1641 warnx("line %d: syntax error", lineno); 1642 errors++; 1643 } 1644 } else 1645 v = FSMAXTYPES; 1646 if ((unsigned)v >= FSMAXTYPES) { 1647 warnx("line %d: warning, unknown file system type: %s", 1648 lineno, cp); 1649 warnx("tip: use -l to see all valid file system " 1650 "types"); 1651 v = FS_UNUSED; 1652 } 1653 pp->p_fstype = v; 1654gottype: 1655 switch (pp->p_fstype) { 1656 1657 case FS_UNUSED: /* XXX */ 1658 NXTNUM(pp->p_fsize); 1659 if (pp->p_fsize == 0) 1660 break; 1661 NXTNUM(v); 1662 pp->p_frag = v / pp->p_fsize; 1663 break; 1664 1665 case FS_BSDFFS: 1666 case FS_ADOS: 1667 case FS_APPLEUFS: 1668 NXTNUM(pp->p_fsize); 1669 if (pp->p_fsize == 0) 1670 break; 1671 NXTNUM(v); 1672 pp->p_frag = v / pp->p_fsize; 1673 NXTNUM(pp->p_cpg); 1674 break; 1675 case FS_BSDLFS: 1676 NXTNUM(pp->p_fsize); 1677 if (pp->p_fsize == 0) 1678 break; 1679 NXTNUM(v); 1680 pp->p_frag = v / pp->p_fsize; 1681 NXTNUM(pp->p_sgs); 1682 break; 1683 case FS_EX2FS: 1684 NXTNUM(pp->p_fsize); 1685 if (pp->p_fsize == 0) 1686 break; 1687 NXTNUM(v); 1688 pp->p_frag = v / pp->p_fsize; 1689 break; 1690 case FS_ISO9660: 1691 NXTNUM(pp->p_cdsession); 1692 break; 1693 default: 1694 break; 1695 } 1696 continue; 1697 error: 1698 errors++; 1699 next: 1700 ; 1701 } 1702 errors += checklabel(lp); 1703 return (errors == 0); 1704} 1705 1706/* 1707 * Check disklabel for errors and fill in 1708 * derived fields according to supplied values. 1709 */ 1710int 1711checklabel(struct disklabel *lp) 1712{ 1713 struct partition *pp, *qp; 1714 int i, j, errors; 1715 char part; 1716 1717 errors = 0; 1718 if (lp->d_secsize == 0) { 1719 warnx("sector size %" PRIu32, lp->d_secsize); 1720 return (1); 1721 } 1722 if (lp->d_nsectors == 0) { 1723 warnx("sectors/track %" PRIu32, lp->d_nsectors); 1724 return (1); 1725 } 1726 if (lp->d_ntracks == 0) { 1727 warnx("tracks/cylinder %" PRIu32, lp->d_ntracks); 1728 return (1); 1729 } 1730 if (lp->d_ncylinders == 0) { 1731 warnx("cylinders/unit %" PRIu32, lp->d_ncylinders); 1732 errors++; 1733 } 1734 if (lp->d_rpm == 0) 1735 warnx("warning, revolutions/minute %" PRIu16, lp->d_rpm); 1736 if (lp->d_secpercyl == 0) 1737 lp->d_secpercyl = lp->d_nsectors * lp->d_ntracks; 1738 if (lp->d_secperunit == 0) 1739 lp->d_secperunit = lp->d_secpercyl * lp->d_ncylinders; 1740 if (lp->d_bbsize == 0) { 1741 warnx("boot block size %" PRIu32, lp->d_bbsize); 1742 errors++; 1743 } else if (lp->d_bbsize % lp->d_secsize) 1744 warnx("warning, boot block size %% sector-size != 0"); 1745 if (lp->d_sbsize == 0) { 1746 warnx("super block size %" PRIu32, lp->d_sbsize); 1747 errors++; 1748 } else if (lp->d_sbsize % lp->d_secsize) 1749 warnx("warning, super block size %% sector-size != 0"); 1750 if (lp->d_npartitions > MAXPARTITIONS) 1751 warnx("warning, number of partitions (%" PRIu16 ") > " 1752 "MAXPARTITIONS (%d)", 1753 lp->d_npartitions, MAXPARTITIONS); 1754 else 1755 for (i = MAXPARTITIONS - 1; i >= lp->d_npartitions; i--) { 1756 part = 'a' + i; 1757 pp = &lp->d_partitions[i]; 1758 if (pp->p_size || pp->p_offset) { 1759 warnx("warning, partition %c increased " 1760 "number of partitions from %" PRIu16 1761 " to %d", 1762 part, lp->d_npartitions, i + 1); 1763 lp->d_npartitions = i + 1; 1764 break; 1765 } 1766 } 1767 for (i = 0; i < lp->d_npartitions; i++) { 1768 part = 'a' + i; 1769 pp = &lp->d_partitions[i]; 1770 if (pp->p_size == 0 && pp->p_offset != 0) 1771 warnx("warning, partition %c: size 0, but " 1772 "offset %" PRIu32, 1773 part, pp->p_offset); 1774#ifdef STRICT_CYLINDER_ALIGNMENT 1775 if (pp->p_offset % lp->d_secpercyl) { 1776 warnx("warning, partition %c:" 1777 " not starting on cylinder boundary", 1778 part); 1779 errors++; 1780 } 1781#endif /* STRICT_CYLINDER_ALIGNMENT */ 1782 if (pp->p_offset > lp->d_secperunit) { 1783 warnx("partition %c: offset past end of unit", part); 1784 errors++; 1785 } 1786 if (pp->p_offset + pp->p_size > lp->d_secperunit) { 1787 warnx("partition %c: partition extends" 1788 " past end of unit", 1789 part); 1790 errors++; 1791 } 1792 if (pp->p_fstype != FS_UNUSED) 1793 for (j = i + 1; j < lp->d_npartitions; j++) { 1794 qp = &lp->d_partitions[j]; 1795 if (qp->p_fstype == FS_UNUSED) 1796 continue; 1797 if (pp->p_offset < qp->p_offset + qp->p_size && 1798 qp->p_offset < pp->p_offset + pp->p_size) 1799 warnx("partitions %c and %c overlap", 1800 part, 'a' + j); 1801 } 1802 } 1803 return (errors); 1804} 1805 1806static void 1807usage(void) 1808{ 1809 static const struct { 1810 const char *name; 1811 const char *expn; 1812 } usages[] = { 1813 { "[-ACFrtv] disk", "(to read label)" }, 1814 { "-w [-DFrv] [-f disktab] disk disktype [packid]", "(to write label)" }, 1815 { "-e [-CDFIrv] disk", "(to edit label)" }, 1816 { "-i [-DFIrv] disk", "(to create a label interactively)" }, 1817 { "-D [-v] disk", "(to delete existing label(s))" }, 1818 { "-R [-DFrv] disk protofile", "(to restore label)" }, 1819 { "[-NW] disk", "(to write disable/enable label)" }, 1820 { "-l", "(to show all known file system types)" }, 1821 { NULL, NULL } 1822 }; 1823 int i; 1824 const char *pn = getprogname(); 1825 const char *t = "usage:"; 1826 1827 for (i = 0; usages[i].name != NULL; i++) { 1828 (void)fprintf(stderr, "%s %s %s\n\t%s\n", 1829 t, pn, usages[i].name, usages[i].expn); 1830 t = "or"; 1831 } 1832 exit(1); 1833} 1834 1835static int 1836getulong(const char *str, char sep, char **epp, unsigned long *ul, 1837 unsigned long max) 1838{ 1839 char *ep; 1840 1841 if (epp == NULL) 1842 epp = &ep; 1843 1844 *ul = strtoul(str, epp, 10); 1845 1846 if ((*ul == ULONG_MAX && errno == ERANGE) || *ul > max) 1847 return ERANGE; 1848 1849 if (*str == '\0' || (**epp != '\0' && **epp != sep && 1850 !isspace((unsigned char)**epp))) 1851 return EFTYPE; 1852 1853 return 0; 1854} 1855 1856/* 1857 * This is a wrapper over the standard strcmp function to be used with 1858 * qsort on an array of pointers to strings. 1859 */ 1860static int 1861qsort_strcmp(const void *v1, const void *v2) 1862{ 1863 const char *const *sp1 = (const char *const *)v1; 1864 const char *const *sp2 = (const char *const *)v2; 1865 1866 return strcmp(*sp1, *sp2); 1867} 1868 1869/* 1870 * Prints all know file system types for a partition. 1871 * Returns 1 on success, 0 on failure. 1872 */ 1873int 1874list_fs_types(void) 1875{ 1876 int ret; 1877 size_t nelems; 1878 1879 nelems = 0; 1880 { 1881 const char *const *namep; 1882 1883 namep = fstypenames; 1884 while (*namep++ != NULL) 1885 nelems++; 1886 } 1887 1888 ret = 1; 1889 if (nelems > 0) { 1890 const char **list; 1891 size_t i; 1892 1893 list = (const char **)malloc(sizeof(char *) * nelems); 1894 if (list == NULL) { 1895 warnx("sorry, could not allocate memory for list"); 1896 ret = 0; 1897 } else { 1898 for (i = 0; i < nelems; i++) 1899 list[i] = fstypenames[i]; 1900 1901 qsort(list, nelems, sizeof(char *), qsort_strcmp); 1902 1903 for (i = 0; i < nelems; i++) 1904 (void)printf("%s\n", list[i]); 1905 1906 free(list); 1907 } 1908 } 1909 1910 return ret; 1911} 1912