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