xref: /illumos-gate/usr/src/test/zfs-tests/cmd/getholes/getholes.c (revision 52244c0958bdf281ca42932b449f644b4decfdc2)
1*52244c09SJohn Wren Kennedy /*
2*52244c09SJohn Wren Kennedy  * This file and its contents are supplied under the terms of the
3*52244c09SJohn Wren Kennedy  * Common Development and Distribution License ("CDDL"), version 1.0.
4*52244c09SJohn Wren Kennedy  * You may only use this file in accordance with the terms of version
5*52244c09SJohn Wren Kennedy  * 1.0 of the CDDL.
6*52244c09SJohn Wren Kennedy  *
7*52244c09SJohn Wren Kennedy  * A full copy of the text of the CDDL should have accompanied this
8*52244c09SJohn Wren Kennedy  * source.  A copy of the CDDL is also available via the Internet at
9*52244c09SJohn Wren Kennedy  * http://www.illumos.org/license/CDDL.
10*52244c09SJohn Wren Kennedy  */
11*52244c09SJohn Wren Kennedy 
12*52244c09SJohn Wren Kennedy /*
13*52244c09SJohn Wren Kennedy  * Copyright (c) 2014 by Delphix. All rights reserved.
14*52244c09SJohn Wren Kennedy  */
15*52244c09SJohn Wren Kennedy 
16*52244c09SJohn Wren Kennedy #include <stdio.h>
17*52244c09SJohn Wren Kennedy #include <fcntl.h>
18*52244c09SJohn Wren Kennedy #include <unistd.h>
19*52244c09SJohn Wren Kennedy #include <libzfs.h>
20*52244c09SJohn Wren Kennedy #include <umem.h>
21*52244c09SJohn Wren Kennedy #include <stdlib.h>
22*52244c09SJohn Wren Kennedy #include <stddef.h>
23*52244c09SJohn Wren Kennedy #include <sys/types.h>
24*52244c09SJohn Wren Kennedy #include <sys/list.h>
25*52244c09SJohn Wren Kennedy #include <sys/stat.h>
26*52244c09SJohn Wren Kennedy #include <sys/errno.h>
27*52244c09SJohn Wren Kennedy 
28*52244c09SJohn Wren Kennedy #define	PRINT_HOLE 0x1
29*52244c09SJohn Wren Kennedy #define	PRINT_DATA 0x2
30*52244c09SJohn Wren Kennedy #define	PRINT_VERBOSE 0x4
31*52244c09SJohn Wren Kennedy 
32*52244c09SJohn Wren Kennedy extern int errno;
33*52244c09SJohn Wren Kennedy 
34*52244c09SJohn Wren Kennedy static void
usage(char * msg,int exit_value)35*52244c09SJohn Wren Kennedy usage(char *msg, int exit_value)
36*52244c09SJohn Wren Kennedy {
37*52244c09SJohn Wren Kennedy 	(void) fprintf(stderr, "getholes [-dhv] filename\n");
38*52244c09SJohn Wren Kennedy 	(void) fprintf(stderr, "%s\n", msg);
39*52244c09SJohn Wren Kennedy 	exit(exit_value);
40*52244c09SJohn Wren Kennedy }
41*52244c09SJohn Wren Kennedy 
42*52244c09SJohn Wren Kennedy typedef struct segment {
43*52244c09SJohn Wren Kennedy 	list_node_t	seg_node;
44*52244c09SJohn Wren Kennedy 	int		seg_type;
45*52244c09SJohn Wren Kennedy 	off_t		seg_offset;
46*52244c09SJohn Wren Kennedy 	off_t		seg_len;
47*52244c09SJohn Wren Kennedy } seg_t;
48*52244c09SJohn Wren Kennedy 
49*52244c09SJohn Wren Kennedy /*
50*52244c09SJohn Wren Kennedy  * Return an appropriate whence value, depending on whether the file begins
51*52244c09SJohn Wren Kennedy  * with a holes or data.
52*52244c09SJohn Wren Kennedy  */
53*52244c09SJohn Wren Kennedy static int
starts_with_hole(int fd)54*52244c09SJohn Wren Kennedy starts_with_hole(int fd)
55*52244c09SJohn Wren Kennedy {
56*52244c09SJohn Wren Kennedy 	off_t	off;
57*52244c09SJohn Wren Kennedy 
58*52244c09SJohn Wren Kennedy 	if ((off = lseek(fd, 0, SEEK_HOLE)) == -1) {
59*52244c09SJohn Wren Kennedy 		/* ENXIO means no holes were found */
60*52244c09SJohn Wren Kennedy 		if (errno == ENXIO)
61*52244c09SJohn Wren Kennedy 			return (SEEK_DATA);
62*52244c09SJohn Wren Kennedy 		perror("lseek failed");
63*52244c09SJohn Wren Kennedy 		exit(1);
64*52244c09SJohn Wren Kennedy 	}
65*52244c09SJohn Wren Kennedy 
66*52244c09SJohn Wren Kennedy 	return (off == 0 ? SEEK_HOLE : SEEK_DATA);
67*52244c09SJohn Wren Kennedy }
68*52244c09SJohn Wren Kennedy 
69*52244c09SJohn Wren Kennedy static void
print_list(list_t * seg_list,char * fname,int options)70*52244c09SJohn Wren Kennedy print_list(list_t *seg_list, char *fname, int options)
71*52244c09SJohn Wren Kennedy {
72*52244c09SJohn Wren Kennedy 	uint64_t	lz_holes, bs = 0;
73*52244c09SJohn Wren Kennedy 	uint64_t	hole_blks_seen = 0, data_blks_seen = 0;
74*52244c09SJohn Wren Kennedy 	seg_t		*seg;
75*52244c09SJohn Wren Kennedy 
76*52244c09SJohn Wren Kennedy 	if (0 == bs)
77*52244c09SJohn Wren Kennedy 		if (zfs_get_hole_count(fname, &lz_holes, &bs) != 0) {
78*52244c09SJohn Wren Kennedy 			perror("zfs_get_hole_count");
79*52244c09SJohn Wren Kennedy 			exit(1);
80*52244c09SJohn Wren Kennedy 		}
81*52244c09SJohn Wren Kennedy 
82*52244c09SJohn Wren Kennedy 	while ((seg = list_remove_head(seg_list)) != NULL) {
83*52244c09SJohn Wren Kennedy 		if (options & PRINT_VERBOSE)
84*52244c09SJohn Wren Kennedy 			(void) fprintf(stdout, "%c %llu:%llu\n",
85*52244c09SJohn Wren Kennedy 			    seg->seg_type == SEEK_HOLE ? 'h' : 'd',
86*52244c09SJohn Wren Kennedy 			    seg->seg_offset, seg->seg_len);
87*52244c09SJohn Wren Kennedy 
88*52244c09SJohn Wren Kennedy 		if (seg->seg_type == SEEK_HOLE) {
89*52244c09SJohn Wren Kennedy 			hole_blks_seen += seg->seg_len / bs;
90*52244c09SJohn Wren Kennedy 		} else {
91*52244c09SJohn Wren Kennedy 			data_blks_seen += seg->seg_len / bs;
92*52244c09SJohn Wren Kennedy 		}
93*52244c09SJohn Wren Kennedy 		umem_free(seg, sizeof (seg_t));
94*52244c09SJohn Wren Kennedy 	}
95*52244c09SJohn Wren Kennedy 
96*52244c09SJohn Wren Kennedy 	/* Verify libzfs sees the same number of hole blocks found manually. */
97*52244c09SJohn Wren Kennedy 	if (lz_holes != hole_blks_seen) {
98*52244c09SJohn Wren Kennedy 		(void) fprintf(stderr, "Counted %llu holes, but libzfs found "
99*52244c09SJohn Wren Kennedy 		    "%llu\n", hole_blks_seen, lz_holes);
100*52244c09SJohn Wren Kennedy 		exit(1);
101*52244c09SJohn Wren Kennedy 	}
102*52244c09SJohn Wren Kennedy 
103*52244c09SJohn Wren Kennedy 	if (options & PRINT_HOLE && options & PRINT_DATA) {
104*52244c09SJohn Wren Kennedy 		(void) fprintf(stdout, "datablks: %llu\n", data_blks_seen);
105*52244c09SJohn Wren Kennedy 		(void) fprintf(stdout, "holeblks: %llu\n", hole_blks_seen);
106*52244c09SJohn Wren Kennedy 		return;
107*52244c09SJohn Wren Kennedy 	}
108*52244c09SJohn Wren Kennedy 
109*52244c09SJohn Wren Kennedy 	if (options & PRINT_DATA)
110*52244c09SJohn Wren Kennedy 		(void) fprintf(stdout, "%llu\n", data_blks_seen);
111*52244c09SJohn Wren Kennedy 	if (options & PRINT_HOLE)
112*52244c09SJohn Wren Kennedy 		(void) fprintf(stdout, "%llu\n", hole_blks_seen);
113*52244c09SJohn Wren Kennedy }
114*52244c09SJohn Wren Kennedy 
115*52244c09SJohn Wren Kennedy int
main(int argc,char * argv[])116*52244c09SJohn Wren Kennedy main(int argc, char *argv[])
117*52244c09SJohn Wren Kennedy {
118*52244c09SJohn Wren Kennedy 	off_t		len, off = 0;
119*52244c09SJohn Wren Kennedy 	int		c, fd, options = 0, whence = SEEK_DATA;
120*52244c09SJohn Wren Kennedy 	struct stat	statbuf;
121*52244c09SJohn Wren Kennedy 	char		*fname;
122*52244c09SJohn Wren Kennedy 	list_t		seg_list;
123*52244c09SJohn Wren Kennedy 	seg_t		*seg = NULL;
124*52244c09SJohn Wren Kennedy 
125*52244c09SJohn Wren Kennedy 	list_create(&seg_list, sizeof (seg_t), offsetof(seg_t, seg_node));
126*52244c09SJohn Wren Kennedy 
127*52244c09SJohn Wren Kennedy 	while ((c = getopt(argc, argv, "dhv")) != -1) {
128*52244c09SJohn Wren Kennedy 		switch (c) {
129*52244c09SJohn Wren Kennedy 		case 'd':
130*52244c09SJohn Wren Kennedy 			options |= PRINT_DATA;
131*52244c09SJohn Wren Kennedy 			break;
132*52244c09SJohn Wren Kennedy 		case 'h':
133*52244c09SJohn Wren Kennedy 			options |= PRINT_HOLE;
134*52244c09SJohn Wren Kennedy 			break;
135*52244c09SJohn Wren Kennedy 		case 'v':
136*52244c09SJohn Wren Kennedy 			options |= PRINT_VERBOSE;
137*52244c09SJohn Wren Kennedy 			break;
138*52244c09SJohn Wren Kennedy 		}
139*52244c09SJohn Wren Kennedy 	}
140*52244c09SJohn Wren Kennedy 	argc -= optind;
141*52244c09SJohn Wren Kennedy 	argv += optind;
142*52244c09SJohn Wren Kennedy 
143*52244c09SJohn Wren Kennedy 	if (argc != 1)
144*52244c09SJohn Wren Kennedy 		usage("Incorrect number of arguments.", 1);
145*52244c09SJohn Wren Kennedy 
146*52244c09SJohn Wren Kennedy 	if ((fname = argv[0]) == NULL)
147*52244c09SJohn Wren Kennedy 		usage("No filename provided.", 1);
148*52244c09SJohn Wren Kennedy 
149*52244c09SJohn Wren Kennedy 	if ((fd = open(fname, O_LARGEFILE | O_RDONLY)) < 0) {
150*52244c09SJohn Wren Kennedy 		perror("open failed");
151*52244c09SJohn Wren Kennedy 		exit(1);
152*52244c09SJohn Wren Kennedy 	}
153*52244c09SJohn Wren Kennedy 
154*52244c09SJohn Wren Kennedy 	if (fstat(fd, &statbuf) != 0) {
155*52244c09SJohn Wren Kennedy 		perror("fstat failed");
156*52244c09SJohn Wren Kennedy 		exit(1);
157*52244c09SJohn Wren Kennedy 	}
158*52244c09SJohn Wren Kennedy 	len = statbuf.st_size;
159*52244c09SJohn Wren Kennedy 
160*52244c09SJohn Wren Kennedy 	whence = starts_with_hole(fd);
161*52244c09SJohn Wren Kennedy 	while ((off = lseek(fd, off, whence)) != -1) {
162*52244c09SJohn Wren Kennedy 		seg_t	*s;
163*52244c09SJohn Wren Kennedy 
164*52244c09SJohn Wren Kennedy 		seg = umem_alloc(sizeof (seg_t), UMEM_DEFAULT);
165*52244c09SJohn Wren Kennedy 		seg->seg_type = whence;
166*52244c09SJohn Wren Kennedy 		seg->seg_offset = off;
167*52244c09SJohn Wren Kennedy 
168*52244c09SJohn Wren Kennedy 		list_insert_tail(&seg_list, seg);
169*52244c09SJohn Wren Kennedy 		if ((s = list_prev(&seg_list, seg)) != NULL)
170*52244c09SJohn Wren Kennedy 			s->seg_len = seg->seg_offset - s->seg_offset;
171*52244c09SJohn Wren Kennedy 
172*52244c09SJohn Wren Kennedy 		whence = whence == SEEK_HOLE ? SEEK_DATA : SEEK_HOLE;
173*52244c09SJohn Wren Kennedy 	}
174*52244c09SJohn Wren Kennedy 	if (errno != ENXIO) {
175*52244c09SJohn Wren Kennedy 		perror("lseek failed");
176*52244c09SJohn Wren Kennedy 		exit(1);
177*52244c09SJohn Wren Kennedy 	}
178*52244c09SJohn Wren Kennedy 	(void) close(fd);
179*52244c09SJohn Wren Kennedy 
180*52244c09SJohn Wren Kennedy 	/*
181*52244c09SJohn Wren Kennedy 	 * If this file ends with a hole block, then populate the length of
182*52244c09SJohn Wren Kennedy 	 * the last segment, otherwise this is the end of the file, so
183*52244c09SJohn Wren Kennedy 	 * discard the remaining zero length segment.
184*52244c09SJohn Wren Kennedy 	 */
185*52244c09SJohn Wren Kennedy 	if (seg && seg->seg_offset != len) {
186*52244c09SJohn Wren Kennedy 		seg->seg_len = len - seg->seg_offset;
187*52244c09SJohn Wren Kennedy 	} else {
188*52244c09SJohn Wren Kennedy 		(void) list_remove_tail(&seg_list);
189*52244c09SJohn Wren Kennedy 	}
190*52244c09SJohn Wren Kennedy 
191*52244c09SJohn Wren Kennedy 	print_list(&seg_list, fname, options);
192*52244c09SJohn Wren Kennedy 	list_destroy(&seg_list);
193*52244c09SJohn Wren Kennedy 	return (0);
194*52244c09SJohn Wren Kennedy }
195