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 */
70typedef 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
75static	freemap_t	*findfree(struct dk_geom *, struct extvtoc *);
76static	int	partcmp(const void *, const void *);
77static	int	partcmp64(const void *, const void *);
78static	int	prtvtoc(char *);
79static	void	putfree(struct extvtoc *, freemap_t *);
80static	void	putfree64(struct dk_gpt *, freemap_t *);
81static	void	puttable(struct dk_geom *, struct extvtoc *, freemap_t *,
82			char *, char **);
83static	void	puttable64(struct dk_gpt *, freemap_t *,
84			char *, char **);
85static	int	readgeom(int, char *, struct dk_geom *);
86static	int	readvtoc(int, char *, struct extvtoc *);
87static	int	readefi(int, char *, struct dk_gpt **);
88static	void	usage(void);
89static	int	warn(char *, char *);
90static	char	*safe_strdup(char *);
91
92/*
93 * External variables.
94 */
95extern char	*getfullrawname();
96/*
97 * Static variables.
98 */
99static short	fflag;			/* Print freespace shell assignments */
100static short	hflag;			/* Omit headers */
101static short	sflag;			/* Omit all but the column header */
102static char	*fstab = VFSTAB;	/* Fstab pathname */
103static char	*mnttab = MNTTAB;	/* mnttab pathname */
104static char	*progname;		/* Last qualifier of arg0 */
105
106int
107main(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
143static freemap_t	*freemap;
144/*
145 * findfree(): Find free space on a disk.
146 */
147static freemap_t *
148findfree(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 */
191static freemap_t *
192findfree64(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 */
236static char **
237getmntpt(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 */
338static int
339partcmp(const void *one, const void *two)
340{
341	return ((*(struct partition **)one)->p_start -
342		(*(struct partition **)two)->p_start);
343}
344
345static int
346partcmp64(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 */
362static int
363prtvtoc(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 */
431static void
432putfree(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
451static void
452putfree64(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 */
474static void
475puttable(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 */
532static void
533puttable64(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 */
588static int
589readgeom(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 */
607static int
608readvtoc(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 */
629static int
630readefi(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
648static char *
649safe_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 */
662static void
663usage()
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 */
673static int
674warn(char *what, char *why)
675{
676	(void) fprintf(stderr, "%s: %s: %s\n", progname, what, why);
677	return (-1);
678}
679