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
main(int argc,char * argv[])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 *
findfree(struct dk_geom * geom,struct extvtoc * vtoc)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 *
findfree64(struct dk_gpt * efi)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 **
getmntpt(major_t slot,minor_t nopartminor)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
partcmp(const void * one,const void * two)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
partcmp64(const void * one,const void * two)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
prtvtoc(char * devname)384 prtvtoc(char *devname)
385 {
386 int fd;
387 int idx = 0;
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
putfree(struct extvtoc * vtoc,freemap_t * freemap)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
putfree64(struct dk_gpt * efi,freemap_t * freemap)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
print_table_header()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
print_table_row(uint_t partition,uint_t tag,uint_t flag,u_longlong_t first_sector,u_longlong_t sector_count,u_longlong_t last_sector,const char * mount_dir)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
print_freemap(freemap_t * freemap)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
puttable(struct dk_geom * geom,struct extvtoc * vtoc,freemap_t * freemap,char * name,char ** mtab)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
puttable64(struct dk_gpt * efi,freemap_t * freemap,char * name,char ** mtab)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
readgeom(int fd,char * name,struct dk_geom * geom)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
readvtoc(int fd,char * name,struct extvtoc * vtoc)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
readefi(int fd,char * name,struct dk_gpt ** efi)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
memory_err(size_t l,int e,const char * fname)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 *
safe_calloc(const char * fname,size_t nelem,size_t elsize)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 *
safe_strdup(const char * fname,const char * str)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
usage()772 usage()
773 {
774 (void) fprintf(stderr, "Usage:\t%s [ -fhs ] [ -t fstab ] [ -m mnttab ] "
775 "rawdisk ...\n", getprogname());
776 exit(1);
777 }
778