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