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
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.
23 #include <sys/types.h>
26 #include <sys/errno.h>
28 #define PRINT_HOLE 0x1
29 #define PRINT_DATA 0x2
30 #define PRINT_VERBOSE 0x4
35 usage(char *msg
, int exit_value
)
37 (void) fprintf(stderr
, "getholes [-dhv] filename\n");
38 (void) fprintf(stderr
, "%s\n", msg
);
42 typedef struct segment
{
50 * Return an appropriate whence value, depending on whether the file begins
51 * with a holes or data.
54 starts_with_hole(int fd
)
58 if ((off
= lseek(fd
, 0, SEEK_HOLE
)) == -1) {
59 /* ENXIO means no holes were found */
62 perror("lseek failed");
66 return (off
== 0 ? SEEK_HOLE
: SEEK_DATA
);
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;
77 if (zfs_get_hole_count(fname
, &lz_holes
, &bs
) != 0) {
78 perror("zfs_get_hole_count");
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
;
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
);
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
);
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
[])
119 int c
, fd
, options
= 0, whence
= SEEK_DATA
;
125 list_create(&seg_list
, sizeof (seg_t
), offsetof(seg_t
, seg_node
));
127 while ((c
= getopt(argc
, argv
, "dhv")) != -1) {
130 options
|= PRINT_DATA
;
133 options
|= PRINT_HOLE
;
136 options
|= PRINT_VERBOSE
;
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");
154 if (fstat(fd
, &statbuf
) != 0) {
155 perror("fstat failed");
158 len
= statbuf
.st_size
;
160 whence
= starts_with_hole(fd
);
161 while ((off
= lseek(fd
, off
, whence
)) != -1) {
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");
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
;
188 (void) list_remove_tail(&seg_list
);
191 print_list(&seg_list
, fname
, options
);
192 list_destroy(&seg_list
);