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
main(int ac,char ** av)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 *
findfree(struct dk_geom * geom,struct extvtoc * vtoc)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 *
findfree64(struct dk_gpt * efi)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 **
getmntpt(major_t slot,minor_t nopartminor)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
partcmp(const void * one,const void * two)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
partcmp64(const void * one,const void * two)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
prtvtoc(char * devname)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
putfree(struct extvtoc * vtoc,freemap_t * freemap)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
putfree64(struct dk_gpt * efi,freemap_t * freemap)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
puttable(struct dk_geom * geom,struct extvtoc * vtoc,freemap_t * freemap,char * name,char ** mtab)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
puttable64(struct dk_gpt * efi,freemap_t * freemap,char * name,char ** mtab)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
readgeom(int fd,char * name,struct dk_geom * geom)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
readvtoc(int fd,char * name,struct extvtoc * vtoc)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
readefi(int fd,char * name,struct dk_gpt ** efi)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 *
safe_strdup(char * str)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
usage()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
warn(char * what,char * why)674 warn(char *what, char *why)
675 {
676 (void) fprintf(stderr, "%s: %s: %s\n", progname, what, why);
677 return (-1);
678 }
679