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