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