1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ 22 /* All Rights Reserved */ 23 24 25 /* Copyright (c) 1984 AT&T */ 26 /* All Rights Reserved */ 27 28 29 /* 30 * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 31 * Use is subject to license terms. 32 * Copyright 2021 Jason King 33 */ 34 35 /* 36 * Print a disk partition map (volume table of contents, or VTOC). 37 */ 38 39 #include <errno.h> 40 #include <fcntl.h> 41 #include <unistd.h> 42 #include <stdlib.h> 43 #include <string.h> 44 #include <stdio.h> 45 #include <limits.h> 46 #include <err.h> 47 48 #include <sys/types.h> 49 #include <sys/stat.h> 50 #include <sys/dkio.h> 51 #include <sys/vtoc.h> 52 #include <sys/mnttab.h> 53 #include <sys/vfstab.h> 54 #include <sys/mkdev.h> 55 56 #include <sys/efi_partition.h> 57 /* 58 * Assumes V_NUMPAR must be a power of 2. 59 * 60 * for V_NUMPAR = 8, we have 61 * parttn(x)=(x & 0x07) noparttn(x)=(x & 0x3fff8) 62 * 63 * for V_NUMPAR = 16, we have 64 * parttn(x)=(x & 0x0f) noparttn(x)=(x & 0x3fff0) 65 */ 66 #define parttn(x) (x % V_NUMPAR) 67 #define noparttn(x) (x & (MAXMIN & ~(V_NUMPAR-1))) 68 69 /* 70 * Disk freespace structure. 71 */ 72 typedef struct { 73 u_longlong_t fr_start; /* Start of free space */ 74 u_longlong_t fr_size; /* Length of free space */ 75 } freemap_t; 76 77 static freemap_t *findfree(struct dk_geom *, struct extvtoc *); 78 static int partcmp(const void *, const void *); 79 static int partcmp64(const void *, const void *); 80 static int prtvtoc(char *); 81 static void putfree(struct extvtoc *, freemap_t *); 82 static void putfree64(struct dk_gpt *, freemap_t *); 83 static void puttable(struct dk_geom *, struct extvtoc *, freemap_t *, 84 char *, char **); 85 static void puttable64(struct dk_gpt *, freemap_t *, 86 char *, char **); 87 static int readgeom(int, char *, struct dk_geom *); 88 static int readvtoc(int, char *, struct extvtoc *); 89 static int readefi(int, char *, struct dk_gpt **); 90 static void usage(void); 91 static char *safe_strdup(const char *, const char *); 92 static void *safe_calloc(const char *, size_t, size_t); 93 94 #define SAFE_STRDUP(a) safe_strdup(__func__, (a)) 95 #define SAFE_CALLOC(a, b) safe_calloc(__func__, (a), (b)) 96 97 /* 98 * External variables. 99 */ 100 extern char *getfullrawname(); 101 /* 102 * Static variables. 103 */ 104 static short fflag; /* Print freespace shell assignments */ 105 static short hflag; /* Omit headers */ 106 static short sflag; /* Omit all but the column header */ 107 static char *fstab = VFSTAB; /* Fstab pathname */ 108 static char *mnttab = MNTTAB; /* mnttab pathname */ 109 110 int 111 main(int argc, char *argv[]) 112 { 113 int status = EXIT_SUCCESS; 114 int c; 115 116 while ((c = getopt(argc, argv, "fhst:m:")) != -1) { 117 switch (c) { 118 case 'f': 119 ++fflag; 120 break; 121 case 'h': 122 ++hflag; 123 break; 124 case 's': 125 ++sflag; 126 break; 127 case 't': 128 fstab = optarg; 129 break; 130 case 'm': 131 mnttab = optarg; 132 break; 133 default: 134 usage(); 135 } 136 } 137 138 if (optind >= argc) 139 usage(); 140 141 for (int i = optind; i < argc; i++) { 142 if (prtvtoc(argv[i]) != 0) { 143 status = EXIT_FAILURE; 144 } 145 } 146 147 return (status); 148 } 149 150 static freemap_t *freemap; 151 /* 152 * findfree(): Find free space on a disk. 153 */ 154 static freemap_t * 155 findfree(struct dk_geom *geom, struct extvtoc *vtoc) 156 { 157 struct extpartition *part; 158 struct extpartition **list; 159 freemap_t *freeidx; 160 diskaddr_t fullsize; 161 ulong_t cylsize; 162 struct extpartition *sorted[V_NUMPAR + 1]; 163 164 if (vtoc->v_nparts > V_NUMPAR) { 165 errx(EXIT_FAILURE, "putfree(): Too many partitions on disk!"); 166 } 167 168 freemap = SAFE_CALLOC(sizeof (freemap_t), V_NUMPAR + 1); 169 cylsize = (geom->dkg_nsect) * (geom->dkg_nhead); 170 fullsize = (diskaddr_t)(geom->dkg_ncyl) * cylsize; 171 list = sorted; 172 for (part = vtoc->v_part; part < vtoc->v_part + vtoc->v_nparts; 173 ++part) { 174 if (part->p_size && part->p_tag != V_BACKUP) 175 *list++ = part; 176 } 177 *list = 0; 178 qsort(sorted, list - sorted, sizeof (*sorted), partcmp); 179 freeidx = freemap; 180 freeidx->fr_start = 0; 181 for (list = sorted; (part = *list) != NULL; ++list) { 182 if (part->p_start <= freeidx->fr_start) { 183 freeidx->fr_start += part->p_size; 184 } else { 185 freeidx->fr_size = part->p_start - freeidx->fr_start; 186 (++freeidx)->fr_start = part->p_start + part->p_size; 187 } 188 } 189 if (freeidx->fr_start < fullsize) { 190 freeidx->fr_size = fullsize - freeidx->fr_start; 191 ++freeidx; 192 } 193 freeidx->fr_start = freeidx->fr_size = 0; 194 return (freemap); 195 } 196 197 /* 198 * findfree64(): Find free space on a disk. 199 */ 200 static freemap_t * 201 findfree64(struct dk_gpt *efi) 202 { 203 struct dk_part *part; 204 struct dk_part **list; 205 freemap_t *freeidx; 206 diskaddr_t fullsize; 207 struct dk_part **sorted; 208 209 freemap = SAFE_CALLOC(sizeof (freemap_t), efi->efi_nparts + 1); 210 sorted = SAFE_CALLOC(sizeof (struct dk_part), efi->efi_nparts + 1); 211 fullsize = efi->efi_last_u_lba; 212 list = sorted; 213 for (part = efi->efi_parts; part < efi->efi_parts + efi->efi_nparts; 214 ++part) { 215 if (part->p_size && part->p_tag != V_BACKUP) 216 *list++ = part; 217 } 218 *list = 0; 219 qsort(sorted, list - sorted, sizeof (*sorted), partcmp64); 220 freeidx = freemap; 221 freeidx->fr_start = efi->efi_first_u_lba; 222 for (list = sorted; (part = *list) != NULL; ++list) { 223 if (part->p_start == freeidx->fr_start) { 224 freeidx->fr_start += part->p_size; 225 } else { 226 freeidx->fr_size = part->p_start - freeidx->fr_start; 227 (++freeidx)->fr_start = part->p_start + part->p_size; 228 } 229 } 230 if (freeidx->fr_start < fullsize) { 231 freeidx->fr_size = fullsize - freeidx->fr_start; 232 ++freeidx; 233 } 234 freeidx->fr_start = freeidx->fr_size = 0; 235 return (freemap); 236 } 237 238 /* 239 * getmntpt() 240 * 241 * Get the filesystem mountpoint of each partition on the disk 242 * from the fstab or mnttab. Returns a pointer to an array of pointers to 243 * directory names (indexed by partition number). 244 */ 245 static char ** 246 getmntpt(major_t slot, minor_t nopartminor) 247 { 248 FILE *file; 249 char devbuf[PATH_MAX], *item; 250 static char *list[V_NUMPAR]; 251 struct stat sb; 252 struct mnttab mtab; 253 struct vfstab vtab; 254 255 for (unsigned idx = 0; idx < V_NUMPAR; ++idx) 256 list[idx] = NULL; 257 258 /* read mnttab for partition mountpoints */ 259 if ((file = fopen(mnttab, "r")) == NULL) { 260 warn("failed to open %s", mnttab); 261 } else { 262 while (getmntent(file, &mtab) == 0) { 263 item = mtab.mnt_special; 264 if (item == NULL || mtab.mnt_mountp == NULL) 265 continue; 266 267 /* 268 * Is it from /dev? 269 */ 270 if (strncmp(item, "/dev/", strlen("/dev/")) != 0) 271 continue; 272 273 /* 274 * Is it a character device? 275 */ 276 (void) snprintf(devbuf, sizeof (devbuf), "/dev/r%s", 277 item + strlen("/dev/")); 278 279 if (stat(devbuf, &sb) != 0 || 280 (sb.st_mode & S_IFMT) != S_IFCHR) { 281 continue; 282 } 283 284 /* 285 * device must match input slot and nopartminor 286 */ 287 if (major(sb.st_rdev) != slot || 288 noparttn(minor(sb.st_rdev)) != nopartminor) { 289 continue; 290 } 291 292 list[parttn(minor(sb.st_rdev))] = 293 SAFE_STRDUP(mtab.mnt_mountp); 294 } 295 (void) fclose(file); 296 } 297 298 if ((file = fopen(fstab, "r")) == NULL) { 299 warn("failed to open %s", fstab); 300 return (list); 301 } 302 303 /* 304 * Look for the disk in the vfstab so that we can report its mount 305 * point even if it isn't currently mounted. 306 */ 307 while (getvfsent(file, &vtab) == 0) { 308 item = vtab.vfs_special; 309 if (item == NULL || vtab.vfs_mountp == NULL) 310 continue; 311 312 if (strncmp(item, "/dev/", strlen("/dev/")) != 0) 313 continue; 314 315 /* 316 * Is it a character device? 317 */ 318 (void) snprintf(devbuf, sizeof (devbuf), "/dev/r%s", 319 item + strlen("/dev/")); 320 321 if (stat(devbuf, &sb) != 0 || 322 (sb.st_mode & S_IFMT) != S_IFCHR) { 323 continue; 324 } 325 326 /* 327 * device must match input slot and nopartminor 328 */ 329 if (major(sb.st_rdev) != slot || 330 noparttn(minor(sb.st_rdev)) != nopartminor) { 331 continue; 332 } 333 334 /* 335 * use mnttab entry if both tables have entries 336 */ 337 if (list[parttn(minor(sb.st_rdev))] != NULL) 338 continue; 339 340 list[parttn(minor(sb.st_rdev))] = SAFE_STRDUP(vtab.vfs_mountp); 341 } 342 (void) fclose(file); 343 344 return (list); 345 } 346 347 /* 348 * partcmp(): Qsort() key comparison of partitions by starting sector numbers. 349 */ 350 static int 351 partcmp(const void *one, const void *two) 352 { 353 struct partition *p1 = *(struct partition **)one; 354 struct partition *p2 = *(struct partition **)two; 355 356 if (p1->p_start > p2->p_start) { 357 return (1); 358 } else if (p1->p_start < p2->p_start) { 359 return (-1); 360 } else { 361 return (0); 362 } 363 } 364 365 static int 366 partcmp64(const void *one, const void *two) 367 { 368 dk_part_t *p1 = *(dk_part_t **)one; 369 dk_part_t *p2 = *(dk_part_t **)two; 370 371 if (p1->p_start > p2->p_start) { 372 return (1); 373 } else if (p1->p_start < p2->p_start) { 374 return (-1); 375 } else { 376 return (0); 377 } 378 } 379 380 /* 381 * prtvtoc(): Read and print a VTOC. 382 */ 383 static int 384 prtvtoc(char *devname) 385 { 386 int fd; 387 int idx = 0; 388 freemap_t *freemap; 389 struct stat sb; 390 struct extvtoc vtoc; 391 int geo; 392 struct dk_geom geom; 393 char *name; 394 int newvtoc = 0; 395 struct dk_gpt *efi; 396 397 name = getfullrawname(devname); 398 if (name == NULL) { 399 warnx("%s: internal administrative call (getfullrawname) " 400 "failed", devname); 401 return (-1); 402 } 403 if (strcmp(name, "") == 0) 404 name = devname; 405 if ((fd = open(name, O_NONBLOCK|O_RDONLY)) < 0) { 406 warn("%s: failed to open device", name); 407 return (-1); 408 } 409 if (fstat(fd, &sb) < 0) { 410 warn("%s: failed to stat device", name); 411 return (-1); 412 } 413 if ((sb.st_mode & S_IFMT) != S_IFCHR) { 414 warnx("%s: Not a raw device", name); 415 return (-1); 416 } 417 418 geo = (readgeom(fd, name, &geom) == 0); 419 if (geo) { 420 if ((idx = readvtoc(fd, name, &vtoc)) == VT_ENOTSUP) { 421 idx = (readefi(fd, name, &efi) == 0); 422 newvtoc = 1; 423 } else { 424 idx = (idx == 0); 425 } 426 } 427 (void) close(fd); 428 if ((!geo) || (!idx)) 429 return (-1); 430 if (!newvtoc) 431 freemap = findfree(&geom, &vtoc); 432 else 433 freemap = findfree64(efi); 434 if (fflag) { 435 if (!newvtoc) 436 putfree(&vtoc, freemap); 437 else 438 putfree64(efi, freemap); 439 } else { 440 if (!newvtoc) { 441 puttable(&geom, &vtoc, freemap, devname, 442 getmntpt(major(sb.st_rdev), 443 noparttn(minor(sb.st_rdev)))); 444 } else { 445 puttable64(efi, freemap, devname, 446 getmntpt(major(sb.st_rdev), 447 noparttn(minor(sb.st_rdev)))); 448 } 449 } 450 if (newvtoc) 451 efi_free(efi); 452 return (0); 453 } 454 455 /* 456 * putfree(): 457 * 458 * Print shell assignments for disk free space. FREE_START and FREE_SIZE 459 * represent the starting block and number of blocks of the first chunk 460 * of free space. FREE_PART lists the unassigned partitions. 461 */ 462 static void 463 putfree(struct extvtoc *vtoc, freemap_t *freemap) 464 { 465 freemap_t *freeidx; 466 ushort_t idx; 467 int free_count = 0; 468 469 for (freeidx = freemap; freeidx->fr_size; ++freeidx) 470 free_count++; 471 472 (void) printf("FREE_START=%llu FREE_SIZE=%llu FREE_COUNT=%d FREE_PART=", 473 freemap->fr_start, freemap->fr_size, free_count); 474 475 for (idx = 0; idx < vtoc->v_nparts; ++idx) { 476 if (vtoc->v_part[idx].p_size == 0 && idx != 2) 477 (void) printf("%x", idx); 478 } 479 (void) printf("\n"); 480 } 481 482 static void 483 putfree64(struct dk_gpt *efi, freemap_t *freemap) 484 { 485 freemap_t *freeidx; 486 ushort_t idx; 487 int free_count = 0; 488 489 for (freeidx = freemap; freeidx->fr_size; ++freeidx) 490 free_count++; 491 492 (void) printf("FREE_START=%llu FREE_SIZE=%llu FREE_COUNT=%d FREE_PART=", 493 freemap->fr_start, freemap->fr_size, free_count); 494 495 for (idx = 0; idx < efi->efi_nparts; ++idx) { 496 if (efi->efi_parts[idx].p_size == 0 && idx != 2) 497 (void) printf("%x", idx); 498 } 499 (void) printf("\n"); 500 } 501 502 static void 503 print_table_header() 504 { 505 (void) printf("* First Sector" 506 " Last\n"); 507 (void) printf("* Partition Tag Flags Sector Count" 508 " Sector Mount Directory\n"); 509 } 510 511 static void 512 print_table_row(uint_t partition, uint_t tag, uint_t flag, 513 u_longlong_t first_sector, u_longlong_t sector_count, 514 u_longlong_t last_sector, const char *mount_dir) 515 { 516 (void) printf(" %6u %4u %02x %11llu %11llu %11llu", 517 partition, tag, flag, first_sector, sector_count, last_sector); 518 if (mount_dir != NULL) { 519 (void) printf(" %s", mount_dir); 520 } 521 (void) printf("\n"); 522 } 523 524 static void 525 print_freemap(freemap_t *freemap) 526 { 527 if (freemap->fr_size == 0) { 528 /* 529 * The freemap is completely empty, so do not print the header. 530 */ 531 return; 532 } 533 534 (void) printf("* Unallocated space:\n" 535 "* First Sector Last\n" 536 "* Sector Count Sector\n"); 537 538 do { 539 (void) printf("* %11llu %11llu %11llu\n", 540 freemap->fr_start, freemap->fr_size, 541 freemap->fr_size + freemap->fr_start - 1); 542 } while ((++freemap)->fr_size != 0); 543 544 (void) printf("*\n"); 545 } 546 547 /* 548 * puttable(): Print a human-readable VTOC. 549 */ 550 static void 551 puttable(struct dk_geom *geom, struct extvtoc *vtoc, freemap_t *freemap, 552 char *name, char **mtab) 553 { 554 ushort_t idx; 555 ulong_t cylsize; 556 557 cylsize = (geom->dkg_nsect) * (geom->dkg_nhead); 558 if (!hflag && !sflag) { 559 u_longlong_t asectors = (u_longlong_t)cylsize * geom->dkg_ncyl; 560 u_longlong_t sectors = (u_longlong_t)cylsize * geom->dkg_pcyl; 561 562 (void) printf("* %s", name); 563 if (vtoc->v_volume[0] != '\0') 564 (void) printf(" (volume \"%.8s\")", vtoc->v_volume); 565 566 (void) printf(" partition map\n"); 567 (void) printf("*\n* Dimensions:\n"); 568 (void) printf("* %11u bytes/sector\n", vtoc->v_sectorsz); 569 (void) printf("* %11u sectors/track\n", geom->dkg_nsect); 570 (void) printf("* %11u tracks/cylinder\n", geom->dkg_nhead); 571 (void) printf("* %11lu sectors/cylinder\n", cylsize); 572 (void) printf("* %11u cylinders\n", geom->dkg_pcyl); 573 (void) printf("* %11u accessible cylinders\n", geom->dkg_ncyl); 574 (void) printf("* %11llu sectors\n", sectors); 575 (void) printf("* %11llu accessible sectors\n", asectors); 576 (void) printf("*\n* Flags:\n"); 577 (void) printf("* 1: unmountable\n"); 578 (void) printf("* 10: read-only\n*\n"); 579 580 print_freemap(freemap); 581 } 582 583 if (!hflag) { 584 print_table_header(); 585 } 586 587 for (idx = 0; idx < vtoc->v_nparts; ++idx) { 588 const char *mount_dir = NULL; 589 struct extpartition *p = &vtoc->v_part[idx]; 590 591 if (p->p_size == 0) 592 continue; 593 594 if (mtab != NULL) { 595 mount_dir = mtab[idx]; 596 } 597 598 print_table_row(idx, p->p_tag, p->p_flag, p->p_start, 599 p->p_size, p->p_start + p->p_size - 1, mount_dir); 600 } 601 } 602 603 /* 604 * puttable(): Print a human-readable VTOC. 605 */ 606 static void 607 puttable64(struct dk_gpt *efi, freemap_t *freemap, char *name, char **mtab) 608 { 609 if (!hflag && !sflag) { 610 (void) printf("* %s", name); 611 for (uint_t idx = 0; idx < efi->efi_nparts; idx++) { 612 if (efi->efi_parts[idx].p_tag == V_RESERVED && 613 efi->efi_parts[idx].p_name[0] != '\0') { 614 (void) printf(" (volume \"%.8s\")", 615 efi->efi_parts[idx].p_name); 616 } 617 } 618 (void) printf(" partition map\n"); 619 (void) printf("*\n* Dimensions:\n"); 620 (void) printf("* %11u bytes/sector\n", efi->efi_lbasize); 621 (void) printf("* %11llu sectors\n", efi->efi_last_lba + 1); 622 (void) printf("* %11llu accessible sectors\n", 623 efi->efi_last_u_lba - efi->efi_first_u_lba + 1); 624 (void) printf("*\n* Flags:\n"); 625 (void) printf("* 1: unmountable\n"); 626 (void) printf("* 10: read-only\n*\n"); 627 628 print_freemap(freemap); 629 } 630 631 if (!hflag) { 632 print_table_header(); 633 } 634 635 for (uint_t idx = 0; idx < efi->efi_nparts; ++idx) { 636 const char *mount_dir = NULL; 637 dk_part_t *p = &efi->efi_parts[idx]; 638 639 if (p->p_size == 0) 640 continue; 641 642 if (idx < 7 && mtab != NULL) { 643 mount_dir = mtab[idx]; 644 } 645 646 print_table_row(idx, p->p_tag, p->p_flag, p->p_start, 647 p->p_size, p->p_start + p->p_size - 1, mount_dir); 648 } 649 } 650 651 /* 652 * readgeom(): Read the disk geometry. 653 */ 654 static int 655 readgeom(int fd, char *name, struct dk_geom *geom) 656 { 657 if (ioctl(fd, DKIOCGGEOM, geom) < 0) { 658 if (errno != ENOTSUP) { 659 warnx("%s: Unable to read Disk geometry errno = 0x%x", 660 name, errno); 661 return (-1); 662 } 663 664 (void) memset(geom, 0, sizeof (struct dk_geom)); 665 } 666 667 return (0); 668 } 669 670 /* 671 * readvtoc(): Read a partition map. 672 */ 673 static int 674 readvtoc(int fd, char *name, struct extvtoc *vtoc) 675 { 676 int retval; 677 678 if ((retval = read_extvtoc(fd, vtoc)) >= 0) 679 return (0); 680 681 switch (retval) { 682 case VT_EIO: 683 warnx("%s: Unable to read VTOC", name); 684 return (-1); 685 case VT_EINVAL: 686 warnx("%s: Invalid VTOC", name); 687 return (-1); 688 case VT_ERROR: 689 warnx("%s: Unknown problem reading VTOC", name); 690 return (-1); 691 } 692 693 return (retval); 694 } 695 696 /* 697 * readefi(): Read a partition map. 698 */ 699 static int 700 readefi(int fd, char *name, struct dk_gpt **efi) 701 { 702 int retval; 703 704 if ((retval = efi_alloc_and_read(fd, efi)) >= 0) 705 return (0); 706 707 switch (retval) { 708 case VT_EIO: 709 warnx("%s: Unable to read VTOC", name); 710 return (-1); 711 case VT_EINVAL: 712 warnx("%s: Invalid VTOC", name); 713 return (-1); 714 case VT_ERROR: 715 warnx("%s: Unknown problem reading VTOC", name); 716 return (-1); 717 } 718 719 return (retval); 720 } 721 722 static void 723 memory_err(size_t l, int e, const char *fname) 724 { 725 const char *reason; 726 727 switch (e) { 728 case EAGAIN: 729 reason = "not enough memory was available, please try again"; 730 break; 731 case ENOMEM: 732 reason = "allocation size was too large"; 733 break; 734 default: 735 reason = strerror(e); 736 break; 737 } 738 739 errx(EXIT_FAILURE, "%s: failed to allocate %llu bytes of memory: %s", 740 fname, (u_longlong_t)l, reason); 741 } 742 743 static void * 744 safe_calloc(const char *fname, size_t nelem, size_t elsize) 745 { 746 void *r; 747 748 if ((r = calloc(nelem, elsize)) == NULL) { 749 memory_err(nelem * elsize, errno, fname); 750 } 751 752 return (r); 753 } 754 755 static char * 756 safe_strdup(const char *fname, const char *str) 757 { 758 size_t l = strlen(str); 759 char *r; 760 761 if ((r = strndup(str, l)) == NULL) { 762 memory_err(l + 1, errno, fname); 763 } 764 765 return (r); 766 } 767 768 /* 769 * usage(): Print a helpful message and exit. 770 */ 771 static void 772 usage() 773 { 774 (void) fprintf(stderr, "Usage:\t%s [ -fhs ] [ -t fstab ] [ -m mnttab ] " 775 "rawdisk ...\n", getprogname()); 776 exit(1); 777 } 778