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