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